方法がわからなかったので調査した結果をまとめる。
成果物
概要
PyOpenJTalkのパスと、それをインストールするパッケージの仮想環境パスを指定する。
PATH_POJT='/tmp/work/' PATH_VENV='/tmp/work/test_repo/' mkdir -p "$PATH_POJT" mkdir -p "$PATH_VENV"
PyOpenJTalkリポジトリと、そのサブモジュールを取得する。
cd "${PATH_POJT}" git clone https://github.com/r9y9/pyopenjtalk cd pyopenjtalk git submodule update -i
ソースコードを修正する。
ここは一旦省略する。まずは大本のコードをビルドする方法を確認したいから。
適当にパッケージ仮想環境をつくる。
cd "$PATH_VENV" python3 -m venv env . "${PATH_VENV}env/bin/activate" pip install numpy pip install Cython pip install six
ビルドする。
cd "${PATH_POJT}pyopenjtalk"
python3 setup.py install
数分間後、以下のように完了する。
Finished processing dependencies for pyopenjtalk==0.1.2+32df1db
コードを書き直して再ビルドし、再インストールするには一旦アンインストールせねばならない。一発で更新できるコマンドはないっぽい。python3 setup.py install --forceでもダメだった。
cd "${PATH_POJT}pyopenjtalk" pip uninstall pyopenjtalk python3 setup.py install
インポートできるかテストする。PyOpenJTalkをインストールした仮想環境を有効化する。import pyopenjtalkするコードを書いて実行する。
a.py
. "${PATH_VENV}env/bin/activate"
a.py
import pyopenjtalk
python3 a.py
エラーなく終了すれば成功。
試してみる
追加したパラメータを試してみた。
-jfを試してみたが、反映されないことがある。期待どおりなら抑揚がなくなったりするはずだった。原因はhtsvoiceファイルやOpenJTalkのバージョン差異が関係しているっぽい。nitech_jp_atr503_m001.htsvoiceなら-jf 0.1で抑揚をなくせた。しかしmei_normal.htsvoiceでは変化がなかった。詳細不明。
open_jtalk
usage:
open_jtalk [ options ] [ infile ]
options: [ def][ min-- max]
-x dir : dictionary directory [ N/A]
-m htsvoice : HTS voice files [ N/A]
-ow s : filename of output wav audio (generated speech) [ N/A]
-ot s : filename of output trace information [ N/A]
-s i : sampling frequency [ auto][ 1-- ]
-p i : frame period (point) [ auto][ 1-- ]
-a f : all-pass constant [ auto][ 0.0-- 1.0]
-b f : postfiltering coefficient [ 0.0][ 0.0-- 1.0]
-r f : speech speed rate [ 1.0][ 0.0-- ]
-fm f : additional half-tone [ 0.0][ -- ]
-u f : voiced/unvoiced threshold [ 0.5][ 0.0-- 1.0]
-jm f : weight of GV for spectrum [ 1.0][ 0.0-- ]
-jf f : weight of GV for log F0 [ 1.0][ 0.0-- ]
-g f : volume (dB) [ 0.0][ -- ]
-z i : audio buffer size (if i==0, turn off) [ 0][ 0-- ]
infile:
text file [stdin]
| options | comment | 既存 | init | range | API | 追加実装 |
|---|---|---|---|---|---|---|
-x dir |
dictionary directory |
○ | - | - | OpenJTalk(dn_mecab=dn_mecab.encode('ascii')) |
- |
-m htsvoice |
HTS voice files |
○ | - | - | HTSEngine(htsvoice.encode('ascii')) |
- |
-ow s |
filename of output wav audio (generated speech) |
☓ | - | - | HTS_Engine_save_generated_speech | FILE*でエラーになったため断念 |
-ot s |
filename of output trace information |
☓ | - | - | HTS_Engine_save_information HTS_Engine_save_label HTS_Engine_save_generated_parameter HTS_Engine_save_riff |
FILE*でエラーになったため断念 |
-s i |
sampling frequency |
☓ | auto |
1〜 |
HTS_Engine_set_sampling_frequency | tts(sampling_frequency) |
-p i |
frame period (point) |
☓ | auto |
1〜 |
HTS_Engine_set_fperiod | tts(frame_period) |
-a f |
all-pass constant |
? | auto |
0.0〜1.0 |
対応API不明 | - |
-b f |
postfiltering coefficient |
? | 0.0 |
0.0〜1.0 |
対応API不明 | - |
-r f |
speech speed rate |
○ | 1.0 |
0.0〜 |
pyopenjtalk.tts(speed=1.0) |
- |
-fm f |
additional half-tone |
○ | 0.0 |
- | pyopenjtalk.tts(half_tone=0.0) |
- |
-u f |
voiced/unvoiced threshold |
☓ | 0.5 |
0.0〜1.0 |
HTS_Engine_set_msd_threshold | tts(threshold) |
-jm f |
weight of GV for spectrum |
☓ | 1.0 |
0.0〜 |
HTS_Engine_set_gv_weight | tts(weight) |
-jf f |
weight of GV for log F0 |
☓ | 1.0 |
0.0〜 |
HTS_Engine_set_gv_interpolation_weight | tts(weight_f0) |
-g f |
volume (dB) |
☓ | 0.0 |
- | HTS_Engine_set_volume | tts(volume) |
-z i |
audio buffer size (if i==0, turn off) |
☓ | 0 |
0〜 |
HTS_Engine_set_audio_buff_size | tts(buffer_size) |
infile |
text file (default = stdin) |
○ | - | - | pyopenjtalk.tts(text) |
- |
#!/usr/bin/env python3 # coding: utf8 import pyopenjtalk import numpy import simpleaudio as sa x, sr = pyopenjtalk.tts('なにか喋ります。', volume=0.1) x, sr = pyopenjtalk.tts('なにか喋ります。', volume=1.0) x, sr = pyopenjtalk.tts('なにか喋ります。', volume=3.0) #x, sr = pyopenjtalk.tts('なにか喋ります。', threshold=0.1) #x, sr = pyopenjtalk.tts('なにか喋ります。', volume=1.2, weight=1.8) ply = sa.play_buffer(x.astype(numpy.int16), 1, 2, sr) ply.wait_done()
volume=20.0などにすると音割れする。音量かと思いきや違うっぽい。0.01のようにしてもデフォルトと変わらない。
weight, weight_f0についてはAPIがよくわからなかった。どれが何に対応しているの? 引数もよくわからんのがある。たとえば以下にあるstream_indexとかvoice_indexとか。なにそれ? 何を与えればいいのさ? どう影響するの?
# /* HTS_Engine_set_gv_weight: set GV weight */
void HTS_Engine_set_gv_weight(HTS_Engine * engine, size_t stream_index, double f)
# /* HTS_Engine_get_gv_weight: get GV weight */
double HTS_Engine_get_gv_weight(HTS_Engine * engine, size_t stream_index)
# /* HTS_Engine_set_duration_interpolation_weight: set interpolation weight for duration */
void HTS_Engine_set_duration_interpolation_weight(HTS_Engine * engine, size_t voice_index, double f)
# /* HTS_Engine_get_duration_interpolation_weight: get interpolation weight for duration */
double HTS_Engine_get_duration_interpolation_weight(HTS_Engine * engine, size_t voice_index)
# /* HTS_Engine_set_parameter_interpolation_weight: set interpolation weight for parameter */
void HTS_Engine_set_parameter_interpolation_weight(HTS_Engine * engine, size_t voice_index, size_t stream_index, double f)
# /* HTS_Engine_get_parameter_interpolation_weight: get interpolation weight for parameter */
double HTS_Engine_get_parameter_interpolation_weight(HTS_Engine * engine, size_t voice_index, size_t stream_index)
# /* HTS_Engine_set_gv_interpolation_weight: set interpolation weight for GV */
void HTS_Engine_set_gv_interpolation_weight(HTS_Engine * engine, size_t voice_index, size_t stream_index, double f)
# /* HTS_Engine_get_gv_interpolation_weight: get interpolation weight for GV */
double HTS_Engine_get_gv_interpolation_weight(HTS_Engine * engine, size_t voice_index, size_t stream_index)
HTS_Engine_set_gv_interpolation_weightを触ってみたが意味不明だった。よくわからん第1,2引数は0を渡してやった。エラーにはならないが、出力結果の違いもよくわからない。棒読みのように抑揚のない音声になってくれることを期待していたのだが……。対応APIはこれじゃないっぽい。
set_gv_interpolation_weight(0,0,weight_f0) # voice_index, stream_index
ためしに第1に1を渡してやったら実行時にSegmentation faultエラーになった。
set_gv_interpolation_weight(1,0,weight_f0) # Segmentation fault
わからん。double fだけ渡せばいいようにしてくれ。というかweight系のAPIがいくつもあるけど、どれが何なの? そこからしてわからない。コマンド版のweightは2種類あるけど、それを再現するにはどうすればいいの?
open_jtalkコマンド版のソースコードをみてみた。./open_jtalk-1.11/bin/open_jtalk.cファイル。引数-jm,-jfはそれぞれHTS_Engine_set_gv_weight()関数を使っていた。第一引数が0か1の違いだけ。
| 引数 | 説明 | API |
|---|---|---|
-jm f |
weight of GV for spectrum |
HTS_Engine_set_gv_weight(0, f) |
-jf f |
weight of GV for log F0 |
HTS_Engine_set_gv_weight(1, f) |
thresholdを0.0,1.0にしても違いが分からなかった。
frame_periodを100にしたら早口になった。1000にしたら遅くなった。speedでいいやん。speed=3.0と似ている。
sampling_frequencyを44100にしたら声が少し高くなった気がする。48000より小さければ高くなるし、大きければ低くなる。
all-pass filter、postfilteringとやらについては対応APIが不明だった。しかしコードをみてみると以下がヒントになるかもしれない。
double alpha - all-pass constant double beta - postfiltering coefficient
void HTS_Engine_set_alpha(HTS_Engine *engine,double f) void HTS_SetBeta(HTS_Engine *engine,double f)
ビンゴ。これも実装しておいた。all_pathはコロ助みたいな声になったり、オカマみたいな声になったりする。postfilteringはよくわからない。
ファイル出力系に関しては以下のとおりだった。
| API | 概要 |
|---|---|
| HTS_Engine_save_riff | wav形式ファイル出力(-owで出力される) |
| HTS_Engine_save_generated_speech | raw PCM形式 |
| HTS_Engine_save_generated_parameter | わからん |
| HTS_Engine_save_information | わからん(-otで出力される) |
| HTS_Engine_save_label | わからん(-otで出力される) |
コマンドライン版における引数-jf f(weight of GV for log F0)に対応するAPIも不明だった。ググってみると以下HTS_Vocoder_synthesizeの引数lf0がそれっぽい。
void HTS_Vocoder_synthesize(HTS_Vocoder *v, const int m, double lf0, double *spectrum, double alpha, double beta, short *rawdata)
Use: run the vocoder and synthesize waveform. Arguments: HTS_Vocoder *v - HTS_Vocoder structure pointer int m - order of spectrum coefficients double lf0 - log F0 value double *spectrum - spectrum coefficients double alpha - frequency warping parameter alpha double beta - postfiltering parameter beta short *rawdata - short pointer to store synthesized waveform
ただ、このHTS_Vocoder_synthesizeはHTS_engine.hに定義されていないっぽい。代わりにHTS_hidden.hで定義されていた。実装はHTS_vocoder.c。
これを参照すればAPIは使えそう。これを呼び出すだけでいいのかな? よくわからん。
というわけで、最終的に次のようになった。
| options | comment | 既存 | init | range | API | 追加実装 |
|---|---|---|---|---|---|---|
-x dir |
dictionary directory |
○ | - | - | OpenJTalk(dn_mecab=dn_mecab.encode('ascii')) |
- |
-m htsvoice |
HTS voice files |
○ | - | - | HTSEngine(htsvoice.encode('ascii')) |
- |
-ow s |
filename of output wav audio (generated speech) |
☓ | - | - | HTS_Engine_save_riff HTS_Engine_save_generated_speech |
FILE*でエラーになったため断念 |
-ot s |
filename of output trace information |
☓ | - | - | HTS_Engine_save_information HTS_Engine_save_label HTS_Engine_save_generated_parameter |
FILE*でエラーになったため断念 |
-s i |
sampling frequency |
☓ | auto |
1〜 |
HTS_Engine_set_sampling_frequency | tts(sampling_frequency) |
-p i |
frame period (point) |
☓ | auto |
1〜 |
HTS_Engine_set_fperiod | tts(frame_period) |
-a f |
all-pass constant |
? | auto |
0.0〜1.0 |
HTS_Engine_set_alpha | tts(all_pass) |
-b f |
postfiltering coefficient |
? | 0.0 |
0.0〜1.0 |
HTS_Engine_set_beta | tts(postfiltering) |
-r f |
speech speed rate |
○ | 1.0 |
0.0〜 |
pyopenjtalk.tts(speed=1.0) |
- |
-fm f |
additional half-tone |
○ | 0.0 |
- | pyopenjtalk.tts(half_tone=0.0) |
- |
-u f |
voiced/unvoiced threshold |
☓ | 0.5 |
0.0〜1.0 |
HTS_Engine_set_msd_threshold | tts(threshold) |
-jm f |
weight of GV for spectrum |
☓ | 1.0 |
0.0〜 |
HTS_Engine_set_gv_weight(0, f) |
tts(weight) |
-jf f |
weight of GV for log F0 |
☓ | 1.0 |
0.0〜 |
HTS_Engine_set_gv_weight(1, f) |
tts(weight_f0) |
-g f |
volume (dB) |
☓ | 0.0 |
- | HTS_Engine_set_volume | tts(volume) |
-z i |
audio buffer size (if i==0, turn off) |
☓ | 0 |
0〜 |
HTS_Engine_set_audio_buff_size | tts(buffer_size) |
infile |
text file (default = stdin) |
○ | - | - | pyopenjtalk.tts(text) |
- |