CentOS6.4上でnode.jsを使ってゆっくりボイスをしゃべらせるまで

ゆっくりと言えばニコニコでよく聞くアレ。
windowsだとSoftalkMacならゆっくろいどを使うとゆっくりボイスを喋らせることができる。
調べてみると、mac上でnode.jsを使ってコマンドラインから喋らせるのはすでにやっている人がいた。

2つ目の記事にあるみたいに、色々と喋らせられるようになったら面白そうなので、試してみた。
実際にボイスが再生されるまでにいろいろあったので、その記録として。

環境

以下の環境で試してみました。

音声合成ソフトをインストール

ゆっくりボイスを合成する部分にはAquesTalkというものを使っている。
音声合成するバイナリはなく、ライブラリを通して自前で用意する必要があるらしい。
ライブラリは以下からダウンロードしました。

  1. ライブラリ:http://www.a-quest.com/download/index.html

AquesTalk2 Linux 2.3.0をダウンロードし、PDFの手順通りにライブラリを設置。
64bit環境なのでインストール場所は/usr/lib64/以下へ。

$ wget http://www.a-quest.com/download/package/aqtk2-lnx-eva_230.zip
$ unzip aqtk2-lnx-eva_230.zip
$ cd aqtk2-lnx-eva_230
$ sudo cp lib64/libAquesTalk2Eva.so.2.3 /usr/lib64/
$ sudo ln -sf /usr/lib64/libAquesTalk2Eva.so.2.3 /usr/lib64/libAquesTalk2Eva.so.2
$ sudo ln -sf /usr/lib64/libAquesTalk2Eva.so.2 /usr/lib64/libAquesTalk2Eva.so
$ sudo /sbin/ldconfig -n /usr/lib64

音声合成するにはバイナリを自前で用意する必要がある。
PDFに記載されているサンプルコードを少しいじる。

/**
 * SampleTalkUtf8.c
 */

#include <stdio.h>
#include <memory.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "AquesTalk2.h" // AquestTalk クラスのヘッダ

void * file_load(const char * file, int * psize);

int main(int argc, char **argv){
	int size;
	char message[1024];

	// 音声記号列を入力
	if(fgets(message, 1024-1, stdin) == 0) return 0;

	// Phont ファイルの読み込み
	void *pPhont = file_load("aq_yukkuri.phont", &size); // Phont ファイルをここで指定
	if(pPhont == 0) return -2;

	// メモリ上に音声データを生成
	unsigned char *wav = AquesTalk2_Synthe_Utf8(message, 100, &size, pPhont);
	if(wav == 0) {
		fprintf(stderr, "ERR %d", size); // エラー時はsize にエラーコードが返る
		return -1;
	}

	// Phont データの開放(音声合成が終わったら開放できる)
	free(pPhont);

	// 生成した音声データを保存
	fwrite(wav, 1, size, stdout);

	// Synthe()で生成した音声データは、使用後に呼び出し側で解放する
	AquesTalk2_FreeWave(wav);
	return 0;
} 

// ファイルの読み込み
void * file_load(const char * file, int * psize){
	FILE *fp;
	char *data;
	struct stat st;
	*psize = 0;

	if(stat(file, &st) != 0) return NULL;

	if((data = (char *)malloc(st.st_size)) == NULL){
		fprintf(stderr, "can not alloc memory(file_load)\n");
		return NULL;
	}

	if((fp = fopen(file, "rb")) == NULL) {
		free(data);
		perror(file);
		return NULL;
	}

	if(fread(data, 1, st.st_size, fp) < (unsigned)st.st_size) {
		fprintf(stderr, "can not read data (file_load)\n");
		free(data);
		fclose(fp);
		return NULL;
	}

	fclose(fp);

	*psize = st.st_size;

	return data;
}

utf-8で文字列を認識し、ゆっくりの声種ファイルを読み込むようにした。
こいつをコンパイルする。コンパイルにはg++ (GCC) 4.4.7 20120313 (Red Hat 4.4.7-3)を使用。
評価版をダウンロードした場合、ライブラリを「AquesTalk2Eva」と指定することに注意。
これで30分くらいコンパイルが通らずに悩んだ。。。

$ g++ -o yukkuriTest SampleTalkUtf8.c -lAquesTalk2Eva
$ sudo mkdir /usr/local/yukkuriTest-0.0
$ sudo cp yukkuriTest /usr/local/yukkuriTest-0.0/
$ sudo cp ../phont/aq_yukkuri.phont /usr/local/yukkuriTest-0.0/

テスト用バイナリの設置が終わったら、音声ファイルが生成できるかテスト。

$ cd /usr/local/yukkuriTest-0.0
$ sudo sh -c "echo 'ゆっくりしていってね。' | sudo ./yukkuriTest > test.wav"
$ ls -al
合計 64
drwxr-xr-x.  2 root root  4096  5月  6 05:27 2013 .
drwxr-xr-x. 22 root root  4096  5月  6 05:10 2013 ..
-rw-r--r--.  1 root root 16124  5月  6 05:11 2013 aq_yukkuri.phont
-rw-r--r--.  1 root root 26812  5月  6 05:27 2013 test.wav
-rwxr-xr-x.  1 root root  9834  5月  6 05:25 2013 yukkuriTest

よし、生成までできた。

ゲスト側のCentOSで音声再生できるように

次は生成した音声ファイルがただしそうかを確認。
いちいち仮想マシンからscpするのも手間なので、仮想マシン上で再生できるようにする。

調べたところ、音声ファイルの再生にはaplayというコマンドでできるということがわかった。
最小構成で入れたからか、入っていないようだったのでyumで入れる。

$ sudo yum install -y alsa-utils
$ sudo aplay test.wav

・・・音が出ない(´・ω・`)
何がいけないんだろうと調べてみたら、ミュートが原因かもしれない、という記事を発見したので早速試す。

$ amixer set Front on 100%
$ sudo aplay test.wav

やっぱり出ない。今度はVirtualBoxの設定を疑う。
と、関係しそうな記事を発見。試してみる。

仮想マシン"ubuntu" のネットワークの接続タイプの変更
VBoxManage modifyvm "ubuntu" -nic1 nat

ホストOSがWindowsで音を出す場合
VBoxManage modifyvm "ubuntu" -audio dsound

仮想マシンを落とした状態で上記コマンドをWindows上のコマンドプロンプトから試し、仮想マシンをまた起動させる。
もう一度コマンドを打ったら無事再生されるようになった。

node.jsからコンソール上で音声を再生

ここまで来たらあとは組んだバイナリをnode.jsから実行するだけ。
node.jsのコード部分は次のリンクを参考にした。というかほぼコピペです。

/**
 * sayYukkuriTest.js
 */
var exec = require('child_process').exec;

var PROCESS = "/usr/local/yukkuriTest-0.0/yukkuriTest";
var APLAY = "aplay";
var FILENAME = "test.wav";

function makeWave(str, fname, callback){
	exec('echo ' + str + ' | ' + PROCESS + ' > ' + fname, function(err, stdout, stderr) {
		if (callback) callback(err);
	});
}

function sayYukkuri(fname, callback) {
	exec(APLAY + ' ' + fname, function(err, stdout, stderr) {
		if (callback) callback(err);
	});
}

makeWave('ゆっくりしていってね。', FILENAME, function() {
	sayYukkuri(FILENAME);
});

これを実行する。

# cd /usr/local/yukkuriTest-0.0
# node sayYukkuriTest.js

ゆっくりが喋った!
無事、node.jsからの確認も出来ました。

あとはこれを煮るなり焼くなりすれば面白いことができそうですね。