D言語でAquesTalk

D言語の初歩的な機能のうちの一つ、言語バインディングを確認することが目的です。
題材として個人的に気になっていたAquesTalk のラッパを行います。

はじめに

D言語はCのAPIが呼び出しやすいように設計されているそうです(Cとのインターフェイス)。
試しに規則音声合成ライブラリ、AquesTalk の関数を呼んでみましょう。
AquesTalk はANSI C言語準拠のコードで記述されているとのことです。ありがたや。
(小生浅学なため、無駄や間違った行為があれば指摘してくださると助かります...。)

ターゲット

今回は「規則音声合成ライブラリ AquesTalk Linux 評価版 Ver. 2.4」をいじってみます。
Xlib のようなものは大掛かりになってしまいますが、これぐらいならさくっとできます。
配布されているものは、共有ライブラリと関数ヘッダです。

インターフェイスづくり

まずCの関数を呼び出す部分を書きます。と言っても関数ヘッダを少しいじるだけです。

DAquesTalk.d

module DAquesTalk

extern(C){
ubyte * AquesTalk_Synthe(const char *koe, int iSpeed, int *size);
ubyte * AquesTalk_Synthe_Euc(const char *koe, int iSpeed, int *size);
ubyte * AquesTalk_Synthe_Utf8(const char *koe, int iSpeed, int *size);
ubyte * AquesTalk_Synthe_Utf16(const char *koe, int iSpeed, int *size);
ubyte * AquesTalk_Synthe_Roman(const char *koe, int iSpeed, int *size);
void AquesTalk_FreeWave(ubyte *wav);
}

D言語だとunsinged char がないので、ubyte に書き換えます。
これを通して共有ライブラリの関数にアクセスするわけです。

あと、D言語のstring とCのchar *がつながるように

char *toCString(string str){
  return (str ~ '\0').dup.ptr;
}

という関数を用意しておくと便利かもしれません。
(std.string : toStringz が用意されていますがうまく動作しなかったことがあるのでびびっています。)

アプリケーション側

配布マニュアルに含まれていたサンプルと同じようなプログラムにします。
AquesTalkの音声記号列仕様で記述された文字列を入力することでwave ファイルを出力するものです。

main.d

import std.stdio;
import std.range;
import std.array;
import DAquesTalk;

void main(){
  string str = readln;

  int size;
  ubyte* wav = AquesTalk_Synthe_Utf8(str.toCString, 100, &size);
  if(wav == null){
    stderr.writeln = "ERR";
    return;
  }
  ubyte[] data;
  foreach(i; size.iota){
    data ~= *(wav + i);
  }
	
  auto fp = File("test.wav", "wb");
  fp.rawWrite(data);
	
  AquesTalk_FreeWave(wav);
}
コンパイル
$ dmd main.d DAquesTalk.d -L-lAquesTalk -L-lstdc++
実行
$ echo "これわ、ごーせー/お'んせーです。" | ./main

これで生成されたtest.wav を再生することで、ゆっくりボイスがいただけます。

終わりに

AquesTalk を利用してみたけど、アプリから音を出すにはSDL の利用が楽かなあ(Windows版にはサウンド出力関数が用意されているみたいです)。
また、実際の開発にはライセンスに注意してください。