OpenJTalkを使ってみる。

変更履歴(2011/10/30)
これは何か
参考サイト
インストール手順
準備するもの
コードの入手
hts_engine API のインストール
OpenJTalkのインストール
OpenJTalk(1.04)へのパッチ(要不要は未確認)
必要なファイル群のコピー
辞書ファイル
hts音声ファイル
MMDAgentサンプル音声
注意点

変更履歴(2011/10/30)

 前回より男声パラメータにピッチ指定が追加されています。これがないと変な声になる模様。

これは何か

 『ゆっくり』などのテキスト読み上げは有名なんだけど、いかんせん内部エンジンが商用ソフトだ。フリー配布されているとはいえ安心して使えないし未来における保証もない。クローズドという事はそのメーカーが消えてしまえばそれまでだからだ。

 ゆえにフリーソフトウェアなものを求めていた。この種のものだと大学系の研究成果なんかかな、と思っていたら、それに近い種類と思われるものがきっちり存在した。それが今回話題のOpenJTalkだ。もちろん、さっそく使わせてもらう事にした。

参考サイト

 インストールと簡易呼び出しスクリプトの基本は、以下のサイトの情報を参考にさせてもらった。Debian squeezeで試しているがWheezyでもこの記事直している時点では問題ないだろう。

http://pochi.usamimi.info/linux/open_jtalk.html

 なお上記サイトとうちのスクリプトの違いは、再生スクリプトをひとつにまとめた事、あとその場でコマンドライン版 alsaplayer を呼び出し再生している事の二点だ。うちは「その場で適当に読み上げてほしい」わけで、このサイトの方のように読み上げwavファイルが欲しいわけではないから、ここだけちょっと細工した。

 インストール条件において MeCab の必要性を聞いた事があるが、解説しているサイトの記述によると組み込まれているわけで別途入れる必要はないとの事だ。まぁどちらにせよ、うちのPCには MeCab は入っているわけで、ない場合どうなるかの検証はまた別の機会に。

 ではインストールに入る。基本的に上記サイトさんの記事のコピペであるが、私が実際にどうしたかのメモに近いので、その部分だけはあからさまに違うので要注意。

インストール手順

 OpenJTalkは「内部で呼び出しているエンジンのインストール」→「OpenJTalk本体のインストール」→「音声ファイル他のインストール」という手順を踏むようだ。特に最初のエンジンのヘッダがないとOpenJTalkはそもそもコンパイルできない。

準備するもの

コードの入手

 以下のバージョン等は 2011/5/12時点のものです。

 もちろん、それぞれに最新版がある場合はそれを入手。ただしパッチの摘要などはもちろんその最新版の事情に添う必要があります。

hts_engine API のインストール

 まずこいつを入れないとOpenJTalkがコンパイルできない。

$ tar zxvf hts_engine_API-1.05.tar.gz
$ cd ./hts_engine_API-1.05
$ ./configure
$ make
$ sudo checkinstall

OpenJTalkのインストール

 参考にしたサイトさんのパッチは1.03用ですが、該当部分は変わらないようなので念のために現在もあてています。

OpenJTalk(1.04)へのパッチ(要不要は未確認)

--- open_jtalk-1.04/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.04.tar.gz) を作業用ディレクトリに置いて展開し、出来たディレクトリを /usr/local/share/open_jtalk へ配置します。

$ tar zxvf open_jtalk_dic_utf_8-1.04.tar.gz
$ sudo mkdir /usr/local/share/open_jtalk
$ sudo mv ./open_jtalk_dic_utf_8-1.04 /usr/local/share/open_jtalk/

hts音声ファイル

 hts_voice_nitech_jp_atr503_m001-1.04.tar.gz を作業用ディレクトリに置いて展開し、 出来たディレクトリを /usr/local/share/hts_voice へ配置します。(元サイトと少しだけ違うので注意してください)

$ tar zxvf hts_voice_nitech_jp_atr503_m001-1.04.tar.gz
$ sudo mkdir /usr/local/share/hts_voice
$ sudo mv ./hts_voice_nitech_jp_atr503_m001-1.04 /usr/local/share/hts_voice/

MMDAgentサンプル音声

 MMDAgent_Example-1.0.zip を作業用ディレクトリに置いて展開し、MMDAgent_Example-1.0/Voice/mei_normal を /usr/local/share/hts_voice へ配置します。

$ unzip MMDAgent_Example-1.1.zip
$ sudo mv ./MMDAgent_Example-1.1/Voice/mei_normal /usr/local/share/hts_voice/

 これでインストール完了ですが、あいかわらずのオプションの多さには私も驚きました。というわけで、動作支援スクリプトを書きます。今回ちょっとだけ改良、前回のPerlものを参考にRubyの需要があったのでRubyで書きました。

 元サイトさんのものがベースになっていますが、一部のパラメータを追加しています。

 使い方は以下の通り。

(通常) $ openjtalk.rb こんにちは
(同上) $ openjtalk.rb M:こんにちは
(女性) $ openjtalk.rb F:ゆっくりしていってね

 最初にF:とつけるとMMDAgentのサンプル音声を使います。M:をつけると明示的に標準を使いますが、デフォルトはこちらにしてありますので、F:指定するか無印か、でOKです。コードを流用される時にお好きなようにしてください。

 なお、呼び出している alsaplayer はテキスト版( alsaplayer-text )です。モノラルのwavを再生する小さなものなら何でもいいでしょう。

 二つに分かれています。まずはUI部。

(openjtalk.rb)

#!/usr/bin/ruby

require 'open_jtalk.rb'

ARGV.each{|x|
  x2 = "#{x}"
  openjtalk(x2)
}

 そして、以下が本体。

(open_jtalk.rb)

#!/usr/bin/ruby
=begin
  I/F: openjtalk(sex,seq)
	sex: Male/Man/M/Female/F/Women/W/""
	seq: 文字列
=end

$ojt_audioplayer = "/usr/bin/alsaplayer --quiet"

def ojt_getParam(x)
  #
  path = "/usr/local"
  hontai = path+"/bin/open_jtalk"
  m_voice = path+'/share/hts_voice/hts_voice_nitech_jp_atr503_m001-1.04'
  f_voice = path+'/share/hts_voice/mei_normal'
  voice = m_voice
  sex = "M"
  dic = path+'/share/open_jtalk/open_jtalk_dic_utf_8-1.04'
  #dic = ENV['HOME']+'/.openjdic'
  tmpdir = "/tmp"
  infile = "#{tmpdir}/in#{$$}.txt"
  outfile = "#{tmpdir}/out#{$$}.wav"
  #
  if x =~ /(F(emale){0,1}|W(omen){0,1})/i
    sex = "F"
  end
  #
  s = []
  if sex == "M"
    s = [
        "-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",
        "-a 0.075", # 高低?
        "-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"
	]
  else
   voice = f_voice
    s = [
        "-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"
        ]
  end
  r = s.join(" ")
  [outfile,hontai + " " + r]
end

def openjtalk(x)
  if x != nil && x != ""
    sex = "M"
    if x =~ /^(F(emale){0,1}|W(omen){0,1}):/i
      sex = "F"
    end
    s = ojt_getParam(sex)
    x.gsub! /^(M|F):/,''
    system("echo \"#{x}\" | #{s[1]} 1> /dev/null 2> /dev/null")
    if File.exist?(s[0]) == true
      system("#{$ojt_audioplayer} #{s[0]} 1> /dev/null 2> /dev/null")
      File.unlink(s[0])
    end
  end
end

注意点

 1.03の頃のスクリプトをそのまま利用していると変な音声になる事があります。ピッチシフトをちゃんと指定していないとおかしくなるようなので、女声側のパラメータのコピペでもいいのでもらって指定してみてください。