Bakulog

獏の夢日記的な何か。

【AquesTalk】ゆっくりボイスのピッチ変更法【C#】

C#でwavデータを直接書き換えて高音/低音化する方法を紹介します。

 

つい先日、本サイトの監視をしているGoogle Analytics先生に近況を聞いたところ「"AquesTalk.dll 音程 C#"で検索して来てる人が居るのに音程調整の記事が無いぞコラ!」という有りがたい指摘を頂きました。先生は何でもお見通しですね。そこでリクエスト?にお答えして、AquesTalk音声の高低を変える方法を紹介します。たぶん「ゆっくりMovieMaker(YMM)」で音程調整とかできるのを見て「自分でもやりたい!」と思う人がいらっしゃるのでしょう。私もそうです。

 

本記事の前に基本的な声の出し方を解説した記事を上げているので、まずはこちらをご覧ください。また応用として口パク動作を付ける方法も紹介していますが、こちらは見ないでもいいです。

最低限の声出しプログラムでは「C/C++用のライブラリ(dll)はDllImport経由でC#でも使えるよ」という事、口パク連動の記事では「wavファイルって中身ふつうに見られるんですよ」という事を解説しています。今回の音程変更でもwavデータをバイト配列として直接いじります。wavファイルのフォーマットについては口パクの記事の最下部にある補足とか参考元であるコチラのページで詳しく解説されています。

 

今回はwavファイルヘッダの中でも次の2つのパラメータに注目します。

  • サンプリングレート(Hz)
  • データ速度(Byte / Sec)

これらの数字は1秒間にwavから何バイト分データを読み込むかを示し、再生速度の指示値になっています。(※ただしデータ速度はサンプリングレートを含めた他のデータから勝手に定まる数値でもあります。例えば「モノラル(1ch)、1サンプル16bit(=2byte)、サンプリングレート8,000Hz」の場合、データ速度は"1 × 2 × 8,000 = 16,000"と求まります。)

この2パラメータを適切に書き換えることで再生速度が変化します。たとえば本来8000Hzのサンプリングレートを16,000Hzに書き換えた場合、wavファイルが本来の速度の2倍速で再生されるようになり、同時に音声が1オクターブ上がります。いわゆる「早送り」状態ですね。当然0.5倍にすれば低速、低音再生になります。

 

これを実際にC#で試すサンプルは以下の通りです。下記の例は最低限の声出しプログラムで紹介したコードにほんの少しだけ追記したものなので、コード本体以外の準備(AquesTalkの評価版をダウンロード配置する所とか)については以前の記事を参照ください。

using System;
using System.IO;
using System.Media;
using System.Runtime.InteropServices;

namespace EvalAquesTalk
{
    class Program
    {
        static void Main(string[] args)
        {
            while(true)
            {
                const int speed = 100;
                int size = 0;

                Console.WriteLine("何かひらがなで入力してください...");
                string koe = Console.ReadLine();

                //音声ファイルとしてそのまま保存可能なバイト列の先頭ポイントを取得
                IntPtr wavPtr = AquesTalk_Synthe(koe, speed, ref size);

                //成功判定
                if (wavPtr == IntPtr.Zero)
                {
                    Console.WriteLine("ERROR: 音声生成に失敗しました。不正な文字が使われた可能性があります");
                    continue;
                }

                //C#で扱えるようにマネージド側へコピー
                byte[] wav = new byte[size];
                Marshal.Copy(wavPtr, wav, 0, size);

                //アンマネージドポインタは用が無くなった瞬間に解放
                AquesTalk_FreeWave(wavPtr);

                #region 音程変更コードを追加(※リトルエンディアンCPU専用かも)

                uint samplingRate = BitConverter.ToUInt32(wav, 24);
                uint dataRate = BitConverter.ToUInt32(wav, 28);

                //ここでは一例として倍速再生してみる, 2という数値を変えれば低速化も可能
                samplingRate *= 2;
                dataRate *= 2;

                byte[] samplingRateBytes = BitConverter.GetBytes(samplingRate);
                byte[] dataRateBytes = BitConverter.GetBytes(dataRate);

                Array.Copy(samplingRateBytes, 0, wav, 24, 4);
                Array.Copy(dataRateBytes, 0, wav, 28, 4);

                #endregion

                //そのまま再生
                using (var ms = new MemoryStream(wav))
                using (var sp = new SoundPlayer(ms))
                {
                    sp.Play();
                }

            }

        }

        const string dllName = "aqtk1-win-eva\\lib\\AquesTalk.dll";

        [DllImport(dllName)]
        extern static IntPtr AquesTalk_Synthe(string koe, int speed, ref int size);

        [DllImport(dllName)]
        extern static void AquesTalk_FreeWave(IntPtr wavPtr);
    }
}

 

#regionディレクティブで囲った部分だけが追記部分です。これを実行すると通常より1オクターブ高いゆっくりボイスが聞けます。コメントアウトしたり、係数の"2"という数値を別の値に変えたりして遊んでみてください。なお、コード中に出てくる"24"とか"28"といった数値はサンプリングレートとデータ速度の格納される位置で、通常のwavならコレで何とかなります。

 

またAquesTalk特有の小技になりますが、再生速度を維持したまま声の高さだけ変えたい場合はAquesTalk_Synthe関数に渡すspeed引数の値と今回見せた音程変更の値との整合性を取ることで再生速度を維持できます。具体例で言うと「1オクターブ高いけど等倍速の声にしたい」という場合、音程を2倍に引き上げるので、その分speedを通常の0.5倍である50に設定すればOKです。

 

短いですが今回は以上です。快適なAquesTalkライフをお過ごし下さい。