音声分析合成システムWORLDなるものを使ってみる。

- 注意

信号処理は専門外です。内容は正確でない場合があります。
開発環境はVisual C++ 2008 Express Editionです。

- 概要

"音声分析合成システム"という名前の通りWORLDとは音声を分析、合成するライブラリです。入力音声からF0(声の高さ)，非周期性指標(声のかすれ&子音)，スペクトル包絡(声色)を得る関数とそれらの分解された情報から合成音声を得る関数を持ちます。

ボカロみたいに歌声を合成するソフトとしてUTAUというものが有りその中核で利用されています。

作者は山梨大学の森勢将雅先生。

- 導入

1. World-master.zip
http://www.kki.yamanashi.ac.jp/~mmorise/world/
からダウンロードします。今回使ったのはバージョン2.0.5のC++版。

2. 解凍してmakefileまたはvisualstudio/win.slnからサンプルをコンパイルできるなら"サンプルで遊ぶ"へ飛ぶ。下図の私のように開発環境が古すぎたのでコンパイルできなかったひとは続きに進む![01.JPG](https://qiita-image-store.s3.amazonaws.com/0/118083/349ab295-fdc1-1d19-c91d-ee4979cab9ba.jpeg)

3. 新しく空のコンソールプロジェクトを作ってsourceフォルダとtestフォルダの中身をコピー。但しctest.cは無視。(恐らくtest.cppのｃ版なので要らない)
プロジェクトのディレクトリ{
　audioio.h
　stdint.h
　audioio.cpp
　cheaptrick.cpp
　common.cpp
　d4c.cpp
　dio.cpp
　fft.cpp
　matlabfunctions.cpp
　stonemask.cpp
　synthesis.cpp
　test.cpp
　worldディレクトリ{
　　cheaptrick.h
　　common.h
　　constantnumbers.h
　　d4c.h
　　dio.h
　　fft.h
　　macrodefinitions.h
　　matlabfunctions.h
　　stinemask.h
　　synthesis.h
　}
}
みたいなファイル構成になっているはず
これでコンパイルが通るならそれでいい
私のようにstdint.hが足りんとエラーが出た人は続きを読む。

4. http://www.vector.co.jp/soft/win95/prog/se432866.html
からstdint-20070624.zipをダウンロードしてstdint.hとxstdint.hをプロジェクトのディレクトリに追加。これで私の場合はコンパイルが通った。

- サンプルで遊ぶ

出てきたexe（私の場合はWORLD_0205.exe）で遊んでみる
入力wav名、出力wav名、スペクトルの伸縮率、基本周波数の伸縮率を引数にとるようだ。コマンドプロンプトからexeの周りに変換したいwavファイル(中に入っていたvaiueo2d.wavをそのまま使った)を持ってきて。
コマンドプロンプトから

>cd C:\Documents and Set...(exeのフォルダ)
>WORLD_0205.exe vaiueo2d.wav out.wav 2.0 1.3

と入力すると。vaiueo2d.wavのイケボがout.wavにボーイッシュな娘の声らしき何かに変換されていることが分かる。
vaiueo2d.wav
https://drive.google.com/file/d/0B8PHV9xzgpWdQTJUQ3g1UlR3N3c/preview

out.wav
https://drive.google.com/file/d/0B8PHV9xzgpWdZGVhem5rcklodVk/preview

- 関数の概要

このライブラリ。解説が入っていない。
doc\readme_JP.txtに
>使い方については，test.cppを参考にしてください．
>(中略)
>基本的な考え方：
>音声は，音高と音色により表現できるといわれます．
>最新版では，音声をF0，非周期性指標，スペクトル包絡で表現します．
>これは，Vocoderと同一の考え方です．
>
>(1) Dio() により音声の基本周波数を推定
>(1-1) 必要に応じてStoneMask() により基本周波数を補正
>(2) CheapTrick() により音声のスペクトル包絡を推定
>(3) D4C() により音声の非周期性指標を推定
>(4) 必要に応じて音高や音色の制御
>(5) Synthesis() により音声を合成

とだけ書いてある。信号処理に明るくないので心配だが自分なりに解釈してみる

audioio.cpp、audioio.h、test.cppがサンプル部分、それ以外がライブラリである。

ライブラリの中にも色々関数があるが

F0，非周期性指標，スペクトル包絡　←　音声信号

に各々Dio&StoneMask、D4C、CheapTrickを使い。

F0，非周期性指標，スペクトル包絡　→　音声信号

にSynthesisを使う

と覚えていれば良さそう。残りはハンバーガーにおけるレタスのような関数。

また解析関数や合成関数ではF0，非周期性指標，スペクトル包絡は一定の時間間隔毎に管理する。デフォルトでは5ミリ秒毎になっていて。その一つ一つをフレームと呼ぶらしい。

```cpp:それぞれの関数の解説
//-----------------------------------------------------------------------------
// DIO　基本周波数を推定する
// 入力:
//   x          : 入力波形
//   x_length   : 入力波形の長さ
//   fs         : 標本化周波数(Hz)
//   option     : DIO詳細構造体
// 出力
//   time_axis  : フレームの時間的位置(sec)
//   f0         : フレームの基本周波数(Hz)
//
//optionはInitializeDioOption()で初期化してから必要なところを弄って渡す。
//f0_floor、f0_ceilが基本周波数の下限上限[Hz]。
//frame_periodがフレームの間隔[msec]
//
//time_axisとf0はGetSamplesForDIO()でフレーム数を計算できるのでその分だけ確保。
//time_axisとf0は後に多用するのでこの関数が解析を先行するようだ。
//-----------------------------------------------------------------------------
void Dio(const double *x, int x_length, int fs, const DioOption *option,
  double *time_axis, double *f0);
//-----------------------------------------------------------------------------
// D4C()　非周期性指標を推定する。
// 入力:
//   x            : 入力波形
//   x_length     : 入力波形の長さ
//   fs           : 標本化周波数
//   time_axis    : フレームの時間的位置[sec]
//   f0           : フレームの基本周波数[hz]
//   f0_length    : フレームの数
//   fft_size     : 高速フーリエ変換で利用するサイズ。
// 出力
//   aperiodicity : 非周期性指標
//
//fft_sizeはGetFFTSizeForCheapTrick()で得たものを使用する。
//
//aperiodicityは多次元配列[フレーム×周波数]。
//aperiodicity = new double *[f0_length];
//for(int i=0;i<f0_length;++i)aperiodicity[i]=newdouble[fft_size/2 + 1];
//のように確保する。fft_size/2+1というのは位相情報を切り捨てていることを意味していると思われる。
//またtest.cpp内でaperiodicity[n]を内挿法で弄っている様子なので。0番目の要素は適切に外挿してくれているのだろう。
//-----------------------------------------------------------------------------
void D4C(const double *x, int x_length, int fs, const double *time_axis,
  const double *f0, int f0_length, int fft_size, const D4COption *option,
  double **aperiodicity);

//-----------------------------------------------------------------------------
// CheapTrick() スペクトル包絡を推定する
// 入力:
//   x            : 入力波形
//   x_length     : 入力波形の長さ
//   fs           : 標本化周波数
//   time_axis    : フレームの時間的位置[sec]
//   f0           : フレームの基本周波数[hz]
//   f0_length    : フレームの数
//   option       : cheaptrick詳細構造体
// 出力
//   spectrogram  : スペクトル包絡
//
//spectrogramはaperiodicityと同じように扱えばいい。
//optionはInitializeCheapTrickOptionで初期化してから弄る。
//option->f0_floorは声の基本周波数の下限
//-----------------------------------------------------------------------------
void CheapTrick(const double *x, int x_length, int fs, const double *time_axis,
  const double *f0, int f0_length, const CheapTrickOption *option,
  double **spectrogram);
//-----------------------------------------------------------------------------
// StoneMask() Dioの推定結果を修正するらしい。無くても何とかなりそうだが取り合えず使っとくのが吉。
// 入力:
//   x                      : 入力波形
//   x_length               : 入力波形の長さ
//   fs                     : 標本化周波数
//   time_axis              : フレームの時間的位置[sec]
//   f0                     : フレームの基本周波数[hz]
//   f0_length              : フレームの数
// 出力
//   refined_f0             : フレームの修正基本周波数[hz]
//-----------------------------------------------------------------------------
void StoneMask(const double *x, int x_length, int fs, const double *time_axis,
  const double *f0, int f0_length, double *refined_f0);
//-----------------------------------------------------------------------------
// Synthesis() で合成
// 入力:
//   f0                   : フレームの基本周波数[hz]
//   f0_length            : フレームの数
//   spectrogram          : スペクトル包絡 by CheapTrick
//   fft_size             : FFT長 by　GetFFTSizeForCheapTrick
//   aperiodicity         : 非周期性指標 by D4C
//   frame_period         : フレームの時間的間隔[msec] by DIO
//   fs                   : 標本化周波数
//   y_length             : 合成波形の長さ
//=int((枠の数-1)*frame_period/1000.0*fs)+1(切り上げ)
// 出力
//   y                    : 合成波形
//-----------------------------------------------------------------------------
void Synthesis(const double *f0, int f0_length, double **const spectrogram,
    double **const aperiodicity, int fft_size, double frame_period, int fs,
    int y_length, double *y);
```

WORLDをdll化してjnaでjavaに組み込んで使う予定。