やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

PyOpenJTalkをビルドする

 方法がわからなかったので調査した結果をまとめる。

成果物

概要

 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.01.0 対応API不明 -
-b f postfiltering coefficient 0.0 0.01.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.01.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()関数を使っていた。第一引数が01の違いだけ。

引数 説明 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)

 threshold0.0,1.0にしても違いが分からなかった。

 frame_period100にしたら早口になった。1000にしたら遅くなった。speedでいいやん。speed=3.0と似ている。

 sampling_frequency44100にしたら声が少し高くなった気がする。48000より小さければ高くなるし、大きければ低くなる。

 all-pass filterpostfilteringとやらについては対応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 fweight 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_synthesizeHTS_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.01.0 HTS_Engine_set_alpha tts(all_pass)
-b f postfiltering coefficient 0.0 0.01.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.01.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) -