libopenjtalk

python-jtalk

since 2014-01-16

NVDA日本語版を Open JTalk 1.07 に移行させるにあたり、新しく python-jtalk リポジトリを作り、libopenjtalk と htsengineapi のレポジトリは、python-jtalk のサブモジュールとして参照されるかたちにする。

http://sourceforge.jp/ticket/browse.php?group_id=4221&tid=30704

過去の記事

Open JTalk配付サイト)の派生版を作る。

  • nvdajp_jtalk の miscdep のリポジトリとアーカイブの一部として libopenjtalk.dll を配布
  • libopenjtalk を叩くラッパー : nishimotz 実装の _jtalk_core.py (since 2010-12-04)
    • python 2.7 Windows, 32bit 対応。ctypes の宣言が 32bit ポインタに依存。Windows API に依存していないはず。。
    • 話者モデル、辞書、libmecab が別途必要
    • ほぼそのまま http://tts.rcpt.cc 日本語TTSテストサイト で使用 (2011-01-04)
  • shlib によるpythonへの対応。
  • 現在 nvdajp に組み込まれている libopenjtalk.dll は cygwin の MinGW 互換 gcc-3 でコンパイルされている。
  • Open JTalk の一部だった mecab は libopenjtalk.dll に含まれていない
  • 中の人によると「Open JTalkはHTS Engine APIのサンプルアプリケーションという位置づけ」らしい。確かに HTS Engine API は(勝手にリポジトリを作ってみたものの)いじらなくてもよさそうだ。

パッチを作る

since 2012-07-12

いまさらながら Open JTalk 1.05 から NVDA 版ライブラリへのパッチを作る。

追加されたファイルは github を参照のこと。

htsengineapi も必要。

diff --git b/jpcommon/jpcommon_label.c a/jpcommon/jpcommon_label.c
index 97a8899..a9dcef0 100644
--- b/jpcommon/jpcommon_label.c
+++ a/jpcommon/jpcommon_label.c
@@ -105,7 +105,7 @@ static void JPCommonLabelPhoneme_convert_unvoice(JPCommonLabelPhoneme * p)
       }
    }
 
-   fprintf(stderr,
+   HTS_error(0,
            "WARNING: JPCommonLabelPhoneme_convert_unvoice() in jpcommon_label.c: %s cannot be unvoiced.\n",
            p->phoneme);
 }
@@ -148,7 +148,7 @@ static void JPCommonLabelWord_initialize(JPCommonLabelWord * w, const char *pron
       }
    }
    if (find == 0) {
-      fprintf(stderr,
+      HTS_error(0,
               "WARNING: JPCommonLabelWord_initializel() in jpcommon_label.c: %s is unknown POS.\n",
               pos);
       i = 0;
@@ -161,7 +161,7 @@ static void JPCommonLabelWord_initialize(JPCommonLabelWord * w, const char *pron
       }
    }
    if (find == 0) {
-      fprintf(stderr,
+      HTS_error(0,
               "WARNING: JPCommonLabelWord_initializel() in jpcommon_label.c: %s is unknown conjugation type.\n",
               ctype);
       i = 0;
@@ -174,7 +174,7 @@ static void JPCommonLabelWord_initialize(JPCommonLabelWord * w, const char *pron
       }
    }
    if (find == 0) {
-      fprintf(stderr,
+      HTS_error(0,
               "WARNING: JPCommonLabelWord_initializel() in jpcommon_label.c: %s is unknown conjugation form .\n",
               cform);
       i = 0;
@@ -270,6 +270,7 @@ static int index_accent_phrase_in_breath_group(JPCommonLabelAccentPhrase * a)
       if (index == a)
          break;
    }
+   if (i > 3) i = 3;
    return i;
 }
 
@@ -369,6 +370,7 @@ static int count_mora_in_utterance(JPCommonLabelMora * m)
 
    for (i = 0, index = m->next; index != NULL; index = index->next)
       i++;
+   if (i > 10) i = 10;
    return index_mora_in_utterance(m) + i;
 }
 
@@ -393,8 +395,8 @@ static void JPCommonLabel_insert_pause(JPCommonLabel * label)
    if (label->short_pause_flag == 1) {
       if (label->phoneme_tail != NULL) {
          if (strcmp(label->phoneme_tail->phoneme, JPCOMMON_PHONEME_SHORT_PAUSE) == 0) {
-            fprintf(stderr,
-                    "WARNING: JPCommonLabel_insert_word() in jpcommon_label.c: Short pause should not be chained.\n");
+            HTS_error(0,
+                    "WARNING: JPCommonLabel_push_word() in jpcommon_label.c: Short pause should not be chained.\n");
             return;
          }
          label->phoneme_tail->next =
@@ -403,8 +405,8 @@ static void JPCommonLabel_insert_pause(JPCommonLabel * label)
                                          label->phoneme_tail, NULL, NULL);
          label->phoneme_tail = label->phoneme_tail->next;
       } else {
-         fprintf(stderr,
-                 "WARNING: JPCommonLabel_insert_word() in jpcommon_label.c: First mora should not be short pause.\n");
+         HTS_error(0,
+                 "WARNING: JPCommonLabel_push_word() in jpcommon_label.c: First mora should not be short pause.\n");
       }
       label->short_pause_flag = 0;
    }
@@ -433,7 +435,7 @@ void JPCommonLabel_push_word(JPCommonLabel * label, char *pron, char *pos, char
                label->phoneme_tail->up->up->up->emotion = strdup(JPCOMMON_FLAG_QUESTION);
          }
       } else {
-         fprintf(stderr,
+         HTS_error(0,
                  "WARNING: JPCommonLabel_push_word() in jpcommon_label.c: First mora should not be question flag.\n");
       }
       return;
@@ -458,7 +460,7 @@ void JPCommonLabel_push_word(JPCommonLabel * label, char *pron, char *pos, char
             label->mora_tail = label->mora_tail->next;
             label->word_tail->tail = label->mora_tail;
          } else {
-            fprintf(stderr,
+            HTS_error(0,
                     "WARNING: JPCommonLabel_push_word() in jpcommon_label.c: First mora should not be long vowel symbol.\n");
          }
          pron += find;
@@ -469,7 +471,7 @@ void JPCommonLabel_push_word(JPCommonLabel * label, char *pron, char *pos, char
             if (label->phoneme_tail != NULL && is_first_word != 1)
                JPCommonLabelPhoneme_convert_unvoice(label->phoneme_tail);
             else
-               fprintf(stderr,
+               HTS_error(0,
                        "WARNING: JPCommonLabel_push_word() in jpcommon_label.c: First mora should not be unvoice flag.\n");
             pron += find;
          } else {
@@ -550,7 +552,7 @@ void JPCommonLabel_push_word(JPCommonLabel * label, char *pron, char *pos, char
                }
                pron += find;
             } else {
-               fprintf(stderr,
+               HTS_error(0,
                        "WARNING: JPCommonLabel_push_word() in jpcommon_label.c: %s is wrong mora list.\n",
                        pron);
                break;
@@ -627,7 +629,7 @@ void JPCommonLabel_make(JPCommonLabel * label)
    for (p = label->phoneme_head, label->size = 0; p != NULL; p = p->next)
       label->size++;
    if (label->size < 1) {
-      fprintf(stderr, "WARNING: JPCommonLabel_make() in jcomon_label.c: No phoneme.\n");
+      HTS_error(0, "WARNING: JPCommonLabel_make() in jcomon_label.c: No phoneme.\n");
       return;
    }
    label->size += 2;
diff --git b/njd2jpcommon/njd2jpcommon.c a/njd2jpcommon/njd2jpcommon.c
index 8f7798c..85f6d50 100644
--- b/njd2jpcommon/njd2jpcommon.c
+++ a/njd2jpcommon/njd2jpcommon.c
@@ -83,7 +83,7 @@ static void convert_pos(char *buff, char *pos, char *pos_group1, char *pos_group
          return;
       }
    }
-   fprintf(stderr,
+   HTS_error(0,
            "WARING: convert_pos() in njd2jpcommon.c: %s %s %s %s are not appropriate POS.\n", pos,
            pos_group1, pos_group2, pos_group3);
    strcpy(buff, njd2jpcommon_pos_list[4]);
@@ -99,7 +99,7 @@ static void convert_ctype(char *buff, char *ctype)
          return;
       }
    }
-   fprintf(stderr,
+   HTS_error(0,
            "WARING: convert_ctype() in njd2jpcommon.c: %s is not appropriate conjugation type.\n",
            ctype);
    strcpy(buff, njd2jpcommon_ctype_list[1]);
@@ -115,7 +115,7 @@ static void convert_cform(char *buff, char *cform)
          return;
       }
    }
-   fprintf(stderr,
+   HTS_error(0,
            "WARING: convert_cform() in njd2jpcommon.c: %s is not appropriate conjugation form.\n",
            cform);
    strcpy(buff, njd2jpcommon_cform_list[1]);
diff --git b/njd_set_unvoiced_vowel/njd_set_unvoiced_vowel.c a/njd_set_unvoiced_vowel/njd_set_unvoiced_vowel.c
index 1dd01b0..c23153b 100644
--- b/njd_set_unvoiced_vowel/njd_set_unvoiced_vowel.c
+++ a/njd_set_unvoiced_vowel/njd_set_unvoiced_vowel.c
@@ -136,7 +136,7 @@ static int strcat_skip(char *buff, char *str, int *last_unvoiced_flag, int *mora
       *last_unvoiced_flag = 1;
       return strlen(NJD_SET_UNVOICED_VOWEL_QUOTATION);
    }
-   fprintf(stderr, "WARNING: strcat_voiced() in njd_set_unvoiced_vowel.c: Wrong pron.");
+   HTS_error(0, "WARNING: strcat_voiced() in njd_set_unvoiced_vowel.c: Wrong pron.");
    return 1;
 }

ライブラリ版のための makefile の修正:

diff --git b/Makefile.mak a/Makefile.mak
index 0f16444..33b9e9e 100644
--- b/Makefile.mak
+++ a/Makefile.mak
@@ -5,9 +5,9 @@ all:
        cd text2mecab
        nmake /f Makefile.mak
        cd ..
-       cd mecab
-       nmake /f Makefile.mak
-       cd ..
+       rem cd mecab
+       rem nmake /f Makefile.mak
+       rem cd ..
        cd mecab2njd
        nmake /f Makefile.mak
        cd ..
@@ -38,12 +38,15 @@ all:
        cd jpcommon
        nmake /f Makefile.mak
        cd ..
-       cd bin
-       nmake /f Makefile.mak
-       cd ..
-       cd mecab-naist-jdic
+       cd lib
        nmake /f Makefile.mak
        cd ..
+       rem cd bin
+       rem nmake /f Makefile.mak
+       rem cd ..
+       rem cd mecab-naist-jdic
+       rem nmake /f Makefile.mak
+       rem cd ..
 
 clean:
        cd text2mecab
@@ -85,7 +88,10 @@ clean:
        cd bin
        nmake /f Makefile.mak clean
        cd ..
-       cd mecab-naist-jdic
+       rem cd mecab-naist-jdic
+       rem nmake /f Makefile.mak clean
+       rem cd ..
+       cd lib
        nmake /f Makefile.mak clean
        cd ..

Windows版をつくる

執筆:2010-08-21

更新:2011-02-15

Open JTalk 1.02 対応。

Windows XP SP3 (32bit) + Cygwin 1.7.7 を使用。

github には ssh key 登録済みとするがダウンロードするだけなら http 経由でよい。

gcc-3 による mingw クロスコンパイルをやってみる。

$ /usr/bin/set-gcc-default-3.sh
$ cd c:/work/github 
$ git clone git@github.com:nishimotz/htsengineapi.git
$ git clone git@github.com:nishimotz/libopenjtalk.git

HTS Engine API

まず HTS Engine API から。

configure.ac の 69 行 AC_HAVE_LIBRARY([winmm],,AC_MSG_ERROR(No winmm)) をコメントアウト。その直後に

AC_DEFINE(AUDIO_PLAY_NONE)

を追加。

$ cd htsengineapi
$ export CC=gcc-3
$ export CFLAGS=-mno-cygwin
$ export LDFLAGS=-mno-cygwin
$ ./configure
$ make

./bin と ./lib にファイルができる。

libopenjtalk

つぎに libopenjtalk を。やろうとしているのは「オーディオ出力をしない」「mecab辞書をコンパイルしない」「mecab関連ディレクトリをコンパイルしない」「binの中の実行ファイルを作らない」という作業。

$ cd ..
$ cd libopenjtalk

configure.ac の 268-275 行の先頭に # をつけてコメントアウト。

Makefile.am の SUBDIRS = につづく mecab bin mecab-naist-jdic という行をコメントアウト。

mecab/src/Makefile.am の MAINTAINERCLEANFILES から mecab-dict-index を消す。 下記をコメントアウトする。

#noinst_PROGRAMS = mecab-dict-index
#mecab_dict_index_SOURCES = mecab-dict-index.cpp
#mecab_dict_index_LDADD = libmecab.a

ビルド手順:

$ autoreconf
$ autoheader
$ aclocal
$ automake
$ autoconf
$ bash do_configure_mingw32.sh
$ make
$ cd lib
$ make -f Makefile.mingw32
$ strip libopenjtalk.dll

補足: do_configure_mingw32.sh の中身はだいたい下記の通り。

export CXX='g++ -mno-cygwin'
export CC='gcc -mno-cygwin'
./configure --with-hts-engine-header-path=/cygdrive/c/work/github/htsengineapi/include \
  --with-hts-engine-library-path=/cygdrive/c/work/github/htsengineapi/lib \
  --build=i686-pc-mingw32 --with-charset=shift_jis 

作業場所が c:/work/github でない場合は修正してください。

lib の中に jtalk.py がある(かも知れない)が、廃止予定。

nmake 対応

since 2011-10-15

nvda のビルドシステムへの統合 = scons 対応の一歩手前として、nmake でコンパイルできるようにした。

すでに元ファイルに Makefile.mak が含まれているので、若干の修正のみ。

https://github.com/nishimotz/libopenjtalk/commit/8f06316be72c4a412f1db4f299c2c4d24a246c71

https://github.com/nishimotz/htsengineapi/commit/5e2f3e7cae43498bdf648a61e91d0c22c36d0546

Windows SDK v7 コマンドシェルの初期状態では x64 をビルドしてしまうので、以下のように:

> setenv /x86
> cd htsengineapi
> nmake -f Makefile.mak
> cd ..
> cd libopenjtalk
> nmake -f Makefile.mak

DLL は libopenjtalk/lib の中に作られる。

Ubuntu 9.04 32bit

since 2010-12-10

github の htsengineapi を。

$ cd htsengineapi
$ ./configure
$ make
$ cd ..

github の libopenjtalk は ubuntu_sjis ブランチで作業をしている。

$ git clone --origin github git@github.com:nishimotz/libopenjtalk.git
$ cd libopenjtalk
$ git checkout -b github/ubuntu_sjis
$ autoreconf
$ automake
$ autoconf
$ sh do_configure_ubuntu.sh
$ make
$ cd lib
$ make -f Makefile.ubuntu
$ ls .libs/
libopenjtalk.a	 libopenjtalk.lai  libopenjtalk.so    libopenjtalk.so.0.0.0
libopenjtalk.la  libopenjtalk.o    libopenjtalk.so.0
$ cd ../../..

無駄を承知で launchpad.net の nvdajp のレポジトリを取ってくる:

$ mkdir launchpad-nvdajp
$ cd launchpad-nvdajp
$ bzr branch lp:~nishimotz/nvdajp/with_jtalk
$ cd with_jtalk
$ cd source/synthDrivers

_jtalk_core.py の "if _ _ name _ _ == '_ _ main _ _':" の数行あと:

	# MECAB_DLL = "jtalk" + os.sep + "libmecab.dll"
	# JT_DLL = "jtalk" + os.sep + "libopenjtalk.dll"
	MECAB_DLL = "/usr/lib/libmecab.so.1"
	JT_DLL = "../../../../github/libopenjtalk/lib/.libs/libopenjtalk.so"

これで python _jtalk_core.py を実行すると _test.wav ができている。

play _test.wav すると音声が聞こえる。

リビジョン 3566 にて確認した。(2010-12-17)

ojt-python

2010-12-24

ojt-python は nvdajp の JTalk ドライバの土台のつもりだったが、このレポジトリを続けるかどうか検討中。

Python 2.7 および下記のライブラリを使う:

  • comtypes-0.6.*.win32.exe
  • pywin32-***.win32-py2.7.exe

mecab の準備

  • http://sourceforge.net/projects/mecab/files/ から mecab-0.98.exe を入手してインストール。
  • 辞書は Shift-JIS を選ぶ(実際にはmecab添付の辞書は libopenjtalk からは使わない)
  • インストールディレクトリは C:\MeCab にしておく。

c:\work\jtalk に必要なファイルを揃える

  • dic : Open JTalk の SJIS 辞書
  • voice : Open JTalk の話者 m001
  • libmecab.dll : mecab から。
  • mecabrc : mecab から。ただしこのファイルは存在していれば中身はコメントだけでよい。存在しないとレジストリを見に行ってしまうため。
> type c:\work\jtalk\mecabrc
;
; Configuration file of MeCab
;
; $Id: mecabrc.in,v 1.3 2006/05/29 15:36:08 taku-ku Exp $;
;
dicdir =  $(rcpath)\..\dic\ipadic

; userdic = /home/foo/bar/user.dic

; output-format-type = wakati
; input-buffer-size = 8192

; node-format = %m\n
; bos-format = %S\n
; eos-format = EOS\n
 c:\work\jtalk\dic のディレクトリ

2010/09/03  00:05    <DIR>          .
2010/09/03  00:05    <DIR>          ..
2009/12/02  15:08           262,496 char.bin
2009/12/02  15:14             4,349 COPYING
2010/08/28  23:40                74 dicrc
2009/12/02  15:08            58,225 left-id.def
2009/12/02  15:08         3,792,262 matrix.bin
2009/12/02  15:08             1,477 pos-id.def
2009/12/02  15:08             6,241 rewrite.def
2009/12/02  15:08            58,225 right-id.def
2009/12/02  15:08        49,207,063 sys.dic
2009/12/02  15:08             5,409 unk.dic

 c:\work\jtalk\voice のディレクトリ

2010/09/03  00:05    <DIR>          .
2010/09/03  00:05    <DIR>          ..
2010/05/13  05:30             6,221 COPYING
2010/05/13  05:30            11,616 dur.pdf
2010/05/13  05:30                40 gv-lf0.pdf
2010/05/13  05:30               416 gv-mgc.pdf
2010/05/13  05:30                91 gv-switch.inf
2010/05/13  05:30               959 INSTALL
2010/05/13  05:30           105,104 lf0.pdf
2010/05/13  05:30                 6 lf0.win1
2010/05/13  05:30                15 lf0.win2
2010/05/13  05:30                15 lf0.win3
2010/05/13  05:30           561,632 mgc.pdf
2010/05/13  05:30                 6 mgc.win1
2010/05/13  05:30                15 mgc.win2
2010/05/13  05:30                15 mgc.win3
2010/05/13  05:30             6,794 README
2010/05/13  05:30            37,333 tree-dur.inf
2010/05/13  05:30               287 tree-gv-lf0.inf
2010/05/13  05:30               178 tree-gv-mgc.inf
2010/05/13  05:30           231,813 tree-lf0.inf
2010/05/13  05:30            95,250 tree-mgc.inf

実行は cygwin でなく Python for Win32 で行う:

> cd ojt-python
> python jtalk_mingw32.py
speaking
text:  こんにちは。
text2mecab:  こんにちは。
Mecab_print size: 2
こんにちは,感動詞,*,*,*,*,*,こんにちは,コンニチハ,コンニチワ,5/5,C1
。,記号,句点,*,*,*,*,。,。,。,*/*,*

試行錯誤の記録

以下は cygwin (mingw32) 32bit DLLを作るためのコンパイル方法の覚え書き。

$ cd libopenjtalk
$ ./configure --with-hts-engine-header-path=../htsengineapi/include/ \
  --with-hts-engine-library-path=../htsengineapi/lib/ \
  --with-charset=UTF-8 --build=i686-pc-mingw32

失敗。sys/mman.h と sys/times.hでwarningが出る。そしてwinmmがないというエラー。 その他で気になるのは iconv_open がない、というあたりか。

とりあえず winmm だけ削って先に進んでみることにした。

configure.acのしくみは資料を参考にした。

configure.ac の 268-275 行の先頭に # をつけてコメントアウト。

$ autoheader
$ aclocal
$ automake
$ autoconf
$ ./configure --with-hts-engine-header-path=../htsengineapi/include/ \
  --with-hts-engine-library-path=../htsengineapi/lib/ \
  --with-charset=UTF-8 --build=i686-pc-mingw32
$ make

しばらくすすむのだが、mecab が内部で g++ を呼び出しているらしく、失敗。

疲れてきたので、荒っぽい方法を選ぶ。

$ sh /usr/bin/set-gcc-default-3.sh
$ g++ -v
Reading specs from /usr/lib/gcc/i686-pc-cygwin/3.4.4/specs
Configured with: /managed/gcc-build/final-v3-bootstrap/gcc-3.4.4-999/configure 
--verbose --program-suffix=-3 --prefix=/usr --exec-prefix=/usr --sysconfdir=/etc
--libdir=/usr/lib --libexecdir=/usr/lib --mandir=/usr/share/man --infodir=/usr/share/info 
--enable-languages=c,ada,c++,d,f77,pascal,java,objc --enable-nls --without-included-gettext 
--enable-version-specific-runtime-libs --without-x --enable-libgcj --disable-java-awt 
--with-system-zlib --enable-interpreter --disable-libgcj-debug --enable-threads=posix 
--enable-java-gc=boehm --disable-win32-registry --enable-sjlj-exceptions 
--enable-hash-synchronization --enable-libstdcxx-debug
Thread model: posix
gcc version 3.4.4 (cygming special, gdc 0.12, using dmd 0.125)  
$ make clean
$ make

mecab/src の中で失敗する。

mecab-dict-index を作れなくてこまっているようなので、また荒っぽいことをやる。

mecab/src/Makefile.am

MAINTAINERCLEANFILES から mecab-dict-index を消す。
下記をコメントアウトする。
#noinst_PROGRAMS = mecab-dict-index
#mecab_dict_index_SOURCES = mecab-dict-index.cpp
#mecab_dict_index_LDADD = libmecab.a

トップディレクトリで make clean; autoconf する。

さきほどの configure をやりなおす。

こんどは HTS_engine.h がないと怒られてとまる。

そうか、configure は相対パスじゃだめなのか。。。

$ ./configure --with-hts-engine-header-path=/cygdrive/c/work/github/htsengineapi/include \
  --with-hts-engine-library-path=/cygdrive/c/work/github/htsengineapi/lib \
  --with-charset=UTF-8 --build=i686-pc-mingw32

こんどは waveXXX がないと言われる。どうやらさきほどコンパイルした libHTSEngine.a が使ってしまったらしい。

/cygdrive/c/work/github/htsengineapi/lib/libHTSEngine.a(HTS_audio.o):
HTS_audio.c:(.text+0xfe): undefined reference to `_waveOutWrite@12'

やっぱり winmm なしの libHTSEngine を作ることにする。

$ cd ../htsengineapi

configure.ac の 69 行 AC_HAVE_LIBRARY([winmm],,AC_MSG_ERROR(No winmm)) をコメントアウト。 その直後に

AC_DEFINE(AUDIO_PLAY_NONE)

を追加。

$ make clean
$ autoconf
$ ./configure --build=i686-pc-mingw32
$ make

うまくいったらしい。

$ nm lib/libHTSEngine.a  | grep _wave

何も出てこない。

$ cd ../libopenjtalk

また怒られた。__getreent がないなど。

http://www.mail-archive.com/cygwin@cygwin.com/msg36709.html

なるほど。サブディレクトリで -mno-cygwin が外れてしまったらしい。

$ export CXX='g++ -mno-cygwin'
$ export CC='gcc -mno-cygwin'
$ make distclean
$ ./configure --with-hts-engine-header-path=/cygdrive/c/work/github/htsengineapi/include \
  --with-hts-engine-library-path=/cygdrive/c/work/github/htsengineapi/lib \
  --with-charset=UTF-8 --build=i686-pc-mingw32
$ make

エラー。 mecab-naist-jdic が作れない。さきほど mecab-dict-index を削った影響。 どうせ辞書は別途ダウンロードできるはずなのでこれも削る。

Makefile.am の SUBDIRS = につづく mecab bin mecab-naist-jdic という行をコメントアウト

$ automake
$ autoconf
$ ./configure オプションは上と同じ

やっとコンパイルが通った。

project/libopenjtalk.txt · 最終更新: 2014/01/16 16:00 by Takuya Nishimoto
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0