『ゆっくり』などのテキスト読み上げは有名なんだけど、いかんせん内部エンジンが商用ソフトだ。フリー配布されているとはいえ安心して使えないし未来における保証もない。クローズドという事はそのメーカーが消えてしまえばそれまでだからだ。
ゆえにフリーソフトウェアなものを求めていた。この種のものだと大学系の研究成果なんかかな、と思っていたら、それに近い種類と思われるものがきっちり存在した。それが今回話題のOpenJTalkだ。もちろん、さっそく使わせてもらう事にした。
インストールと簡易呼び出しスクリプトの基本は、以下のサイトの情報を参考にさせてもらった。うちの環境は Debian wheezy であるが、squeeze用でも現状ほとんど同じだ。
http://pochi.usamimi.info/linux/open_jtalk.html
なお上記サイトとうちのスクリプトの違いは、再生スクリプトをひとつにまとめた事、あとその場でコマンドライン版 alsaplayer を呼び出し再生している事の二点だ。うちは「その場で適当に読み上げてほしい」わけで、このサイトの方のように読み上げwavファイルが欲しいわけではないから、ここだけちょっと細工した。
インストール条件において MeCab の必要性を聞いた事があるが、解説しているサイトの記述によると組み込まれているわけで別途入れる必要はないとの事だ。まぁどちらにせよ、うちのPCには MeCab は入っているわけで、ない場合どうなるかの検証はまた別の機会に。
ではインストールに入る。基本的に上記サイトさんの記事のコピペであるが、私が実際にどうしたかのメモに近いので、その部分だけはあからさまに違うので要注意。
OpenJTalkは「内部で呼び出しているエンジンのインストール」→「OpenJTalk本体のインストール」→「音声ファイル他のインストール」という手順を踏むようだ。特に最初のエンジンのヘッダがないとOpenJTalkはそもそもコンパイルできない。
以下のバージョン等は 2011/5/12時点のものです。
もちろん、それぞれに最新版がある場合はそれを入手。ただしパッチの摘要などはもちろんその最新版の事情に添う必要があります。
まずこいつを入れないとOpenJTalkがコンパイルできない。
$ tar zxvf hts_engine_API-1.04.tar.gz $ cd ./hts_engine_API-1.04 $ ./configure $ make $ sudo checkinstall
参考にしたサイトさんによれば、1.03現在はパッチがあったほうがいいそうです。もちろん将来的には不明。
--- open_jtalk-1.03/jpcommon/jpcommon_label.c 2011-04-29 14:08:32.000000000 +0900 +++ jpcommon_label.c 2011-05-01 19:55:40.000000000 +0900 @@ -270,6 +270,7 @@ if (index == a) break; } + if (i > 3) i = 3; return i; } @@ -369,6 +370,7 @@ for (i = 0, index = m->next; index != NULL; index = index->next) i++; + if (i > 10) i = 10; return index_mora_in_utterance(m) + i; }
参考元サイトさんは、 jpcommon_label.diff という名のファイルを作って上をコピペ、パッチをあてれば簡単といってます。個人的にも同意なのでさっそく便乗。
$ patch < jpcommon_label.diff
さて、インストール本番だ。
$ cd ../ $ ./configure --with-charset=UTF-8 $ make $ sudo checkinstall
辞書ファイル、それから音声ファイル(追加でもらってきたやつ含めて二種類)を展開します。
辞書ファイル(open_jtalk_dic_utf_8-1.03.tar.gz) を作業用ディレクトリに置いて展開し、出来たディレクトリを /usr/local/share/open_jtalk へ配置します。なお、参考サイトと10日くらいしか離れてないけど 既に一部の記述が変わっている ので要注意。
$ tar zxvf open_jtalk_dic_utf_8-1.03.tar.gz $ sudo mkdir /usr/local/share/open_jtalk $ sudo mv ./open_jtalk_dic_utf_8-1.03 /usr/local/share/open_jtalk/
hts_voice_nitech_jp_atr503_m001-1.02.tar.gz を作業用ディレクトリに置いて展開し、 出来たディレクトリを /usr/local/share/hts_voice へ配置します。(元サイトと少しだけ違うので注意してください)
$ tar zxvf hts_voice_nitech_jp_atr503_m001-1.02.tar.gz $ sudo mkdir /usr/local/share/hts_voice $ sudo mv ./hts_voice_nitech_jp_atr503_m001-1.02 /usr/local/share/hts_voice/
MMDAgent_Example-1.0.zip を作業用ディレクトリに置いて展開し、MMDAgent_Example-1.0/Voice/mei_normal を /usr/local/share/hts_voice へ配置します。
$ unzip MMDAgent_Example-1.0.zip $ sudo mv ./MMDAgent_Example-1.0/Voice/mei_normal /usr/local/share/hts_voice/
これでインストール完了ですが、オプションの多さには私も驚きました。というわけで、動作支援スクリプトを書きます。
ただし参考にした元サイトさんのものとは微妙に違います。
シンプルな変換器としてはあちらの方がよいと思われるので、普通はあちらがおすすめです。私は、自分が今後どう使うかが決まっているので、それに添って書きます。
使い方は以下の通り。
(通常) $ openjtalk.pl こんにちは (同上) $ openjtalk.pl M:こんにちは (女性) $ openjtalk.pl F:ゆっくりしていってね
最初にF:とつけるとMMDAgentのサンプル音声を使います。M:をつけると明示的に標準を使いますが、デフォルトはこちらにしてありますので、F:指定するか無印か、でOKです。コードを流用される時にお好きなようにしてください。
なお、呼び出している alsaplayer はテキスト版( alsaplayer-text )です。モノラルのwavを再生する小さなものなら何でもいいでしょう。
#!/usr/bin/perl use strict; use warnings; use Time::Local; use File::Sync qw(fsync sync); my $path = "/usr/local"; my $audioplayer = "/usr/bin/alsaplayer --quiet"; my $hontai = $path."/bin/open_jtalk"; my $m_voice = $path.'/share/hts_voice/hts_voice_nitech_jp_atr503_m001-1.02'; my $f_voice = $path.'/share/hts_voice/mei_normal'; my $voice = $m_voice; my $sex = "M"; my $dic = $path.'/share/open_jtalk/open_jtalk_dic_utf_8-1.03'; my $tmpdir = "/tmp"; my $infile = "$tmpdir/in.txt"; my $outfile = "$tmpdir/out.wav"; my $talk = ""; my @opts; foreach(@ARGV){ $talk = $_; if($talk =~ /^(M|F):/){ $sex = $1; $talk =~ s/^(M|F)://; } if($sex eq "F"){ $voice = $f_voice; } open(OUT,">$infile"); print OUT "$talk"; close(OUT); if($sex eq "F"){ @opts = ( -x => "$dic", -td => "$voice/tree-dur.inf", -tm => "$voice/tree-mgc.inf", -tf => "$voice/tree-lf0.inf", -tl => "$voice/tree-lpf.inf", -md => "$voice/dur.pdf", -mm => "$voice/mgc.pdf", -mf => "$voice/lf0.pdf", -ml => "$voice/lpf.pdf", -dm => "$voice/mgc.win1", -dm => "$voice/mgc.win2", -dm => "$voice/mgc.win3", -df => "$voice/lf0.win1", -df => "$voice/lf0.win2", -df => "$voice/lf0.win3", -dl => "$voice/lpf.win1", -ow => "$outfile", -a => 0.075, # 高低? -u => 0.0, # 有声化・無声化? -em => "$voice/tree-gv-mgc.inf", -ef => "$voice/tree-gv-lf0.inf", -cm => "$voice/gv-mgc.pdf", -cf => "$voice/gv-lf0.pdf", -jm => 0.5, # 大小? -jf => 1.2, # 抑揚? -k => "$voice/gv-switch.inf", ); }else{ @opts = ( -x => "$dic", -td => "$voice/tree-dur.inf", -tm => "$voice/tree-mgc.inf", -tf => "$voice/tree-lf0.inf", -md => "$voice/dur.pdf", -mm => "$voice/mgc.pdf", -mf => "$voice/lf0.pdf", -dm => "$voice/mgc.win1", -dm => "$voice/mgc.win2", -dm => "$voice/mgc.win3", -df => "$voice/lf0.win1", -df => "$voice/lf0.win2", -df => "$voice/lf0.win3", -ow => "$outfile", -em => "$voice/tree-gv-mgc.inf", -ef => "$voice/tree-gv-lf0.inf", -cm => "$voice/gv-mgc.pdf", -cf => "$voice/gv-lf0.pdf", -k => "$voice/gv-switch.inf", ); } sync(); my $param = $hontai." ".join(" ",@opts)." $infile"; system($param); sync(); system("$audioplayer $outfile"); sync(); unlink($infile,$outfile); } exit;
なかなか素晴らしいOpenJTalkなのだけど、どうもメモリリークか何かしてるくさい。Linuxカーネル側がエラー吐いてる(2011/05/14現在)。今後に注目かもしれない。