死ぬほどありふれたネタですが一応。C#からAquesTalk.dllを使ってゆっくりボイスを出させる典型的な方法を紹介します。C言語/C#の基本知識を前提としています。
1. 記事書いた理由
「ゆっくりWindowWalker(YWW)」で実装している会話機能について情報公開しよっかなーと思い立っただけです。
このYWWはC#で実装されていますが、ゆっくりボイスの元である"AquesTalk.dll"はC#用のライブラリではありません。評価版をダウンロードして公式ドキュメンテーションなどを見ればわかるんですが、このライブラリはもともとC言語から触る想定のライブラリなので、C#から触るにはちょっとした手順を踏む必要があります。他人が作ったラッパーライブラリを挟んで使うのでも良いんですが、本記事では直打ちでやってみます。
2. いいからサンプルコードだ!
まず、Visual Studioでコンソールアプリケーションプロジェクトを新規作成し、"Program.cs"に以下をコピペします。参照の追加とかは不要ですが、名前空間名は最初に作ったプロジェクト名に合わせて修正してください。
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); //そのまま再生 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); } }
この次の操作は必須というわけでは無いんですが念の為に。メニューバーの「ビルド」から「構成マネージャー」を選び、「アクティブソリューションプラットフォーム」で「新規作成」を選んで「x86」を選択し、OKとします。
これは場合によっては不要な手順ですが、やっておいた方が安全です。
最後にAquesTalk評価版である"aqtk1-win-eva-1xx.zip"(公式ページの「AquesTalk」でversionが1.x系のもの)をダウンロードし、解凍したフォルダ"aqtk1-win-eva"全体をC#の実行ファイルのディレクトリ(..\bin\x86\Debug)に配置します。配置で重要なのは、実行ファイルから見た"AquesTalk.dll"のパスが、上記コードの"dllName"の値と一致していることです。
以上の準備を終えたら実行すれば、適当にひらがなを入力すると読み上げが行われるのを確認できるはずです。注意ですが、カタカナや漢字を適当に入力すると読み上げが行われません。詳しいことは"aqtk1-win-eva"フォルダ以下の"siyo_onseikigou.pdf"を読んでください。
3. で、どういう仕組みでしょうか
仕組みというかDllImport属性とextern修飾子を組み合わせて、実行時にライブラリを呼び出す必要があるというだけです。上記のコードなら一番下のコイツらですね。
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);
このように
- 読み込み先のdllの名前
- "extern static"の宣言
- dll内で定義されているはずの関数名、引数、戻り値による関数宣言
を行えば、C#の関数と同じ感覚で関数が呼び出せます。重要な事として関数の名前、引数、戻り値の型については事前によく下調べしないとダメです。AquesTalkの場合なら評価版に同梱の"aqtk1_win_man.pdf"というドキュメンテーションを読み、"AquesTalk_Synth"という関数があることなどを知るのが本来の手順です。
またC#とC言語間での型名の読み替えも必要です。コツとしては
- ポインタはIntPtrで受け渡しして、後からMarshalクラスの関数で他の型に変換すると簡単
- 引数の参照渡し(ポインタ渡し)をC#で行うにはref修飾子を使う
- 文字列(const char*)渡しはstringでOK
こんなところでしょうか。今回の場合、ドキュメンテーションを読むと"AquesTalk_Synthe"関数の戻り値は「wavファイルに相当するバイト配列の先頭アドレス」だという事前知識が得られるので、その事前知識をもとにIntPtrをバイト配列に変換し、SoundPlayerで音として再生しています。もちろんファイルに保存するとか再生速度を変えるといった複雑な操作をしても構いません。
4. 注意と補足
まず注意を二つ。
第一に、手順紹介のところで「ビルド構成マネージャーでx86を選べ」的なことを書きましたが、ナゼそうした方がいいかというと"AquesTalk.dll"自体がx86をターゲットとしてビルドされているからです。実際、ターゲットを"x64"にして実行すると、「同一プロセスの中でターゲットとするCPUアーキテクチャが混在している」という主旨の例外が飛んできます(BadImageFormatException)ので、結局C#側をAquesTalkに合わせることになります。この制限は中規模~大規模のプログラムにおいて厄介者になりますが、仕方ないと諦めるか、またはプロセスを複数立てて対処するなどしましょう。
第二に、言うまでも無いですがAquesTalkの二次利用については制限があります。AquesTalkの規約については公式ページの方に色々書いてあるので、個人的に使う以上の事をする場合はライセンスを良く読んで下さい。
そして補足ですが、実はアクエスト社が公式サイトで評価版を配布しているAquesTalkとAquesTalk2はいずれも世間的に「ゆっくりボイス」と呼ばれるものではありません。ニコ動等でデファクトスタンダードとして使われているのはSofTalkなどに同梱された古いライブラリセットなので、評価版の声質を聞いて「なんか違う…」と思った方はSofTalkに同梱のAquesTalk.dllを代わりに使って試してみましょう。ちなみにSofTalkに同梱されたAquesTalkは現在アクエスト社が公式ページで配っているAquesTalkとライセンスが違うので、そこも要注意ということで。
5. 次回予告
今回のは凡庸すぎる内容だったので、次回はよりデスクトップマスコットらしい処理として、声 + 口パクの連動機能を実装する方法を紹介します。