cl-libworld: 音声分析合成システムWORLDのCommon Lispラッパーを書いた

WORLDはいわゆるボコーダーというやつで、音声データから周波数スペクトルや基本周波数(F0)、非周期性指標といったパラメータを取り出したり、それらのパラメータから音声を再合成したりできる。例えばF0をいじれば音のピッチを変えられるので、歌声合成に使われたりもする(UTAUなど)。
WORLDについては作者の森勢先生のこちらのパワーポイントが分かりやすい。

WORLDはC++のライブラリなのだが、以前Common Lisp RecipesでCFFIの使い方を覚えたのでCommon Lispラッパーを書いてみた。

インストール

quicklispのlocal-projects以下でgit cloneする。

cd ~/quicklisp/local-projects
git clone https://github.com/masatoi/cl-libworld.git

roswellを使っている場合は以下のようにすることで~/.roswell/local-projects以下にインストールされる(git不要!)。

ros install masatoi/cl-libworld

その後Lisp処理系から読み込み。

(ql:quickload :cl-libworld)

wavファイル読み込み

まずwavファイルから音声を読み込んでWORLD-WAV構造体を作る。

(defparameter in-wav (world:make-world-wav-from-file "/path/to/vaiueo2d.wav"))

WORLD-WAV構造体の中身は、fs(フレームレート)、nbit(量子化ビット数)、data(音声データ本体)となっている。

CL-USER> (describe in-wav)
#S(CL-LIBWORLD::WORLD-WAV..
  [structure-object]

Slots with :INSTANCE allocation:
  FS    = 22050
  NBIT  = 16
  DATA  = #(0.001678466796875d0 0.00115966796875d0 8.23974609375d-4..

分析

次に、analysis関数で読み込んだ音声データを分析する。この関数はWORLD-PARAMS構造体を返し、この中のF0スロットに基本周波数が、spectrogramスロットにスペクトルが、aperiodicityスロットに非周期性指標がそれぞれ入っている。

(defparameter params (world:analysis in-wav))
CL-USER> (describe params)
#S(CL-LIBWORLD::WORLD-PARAMS..
  [structure-object]

Slots with :INSTANCE allocation:
  FS             = 22050
  NBIT           = 16
  FRAME-PERIOD   = 5.0d0
  SPEED          = 1
  F0-FLOOR       = 71.0d0
  ALLOWED-RANGE  = 0.1d0
  Q1             = -0.15d0
  F0             = #(0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0..
  SPECTROGRAM    = #2A((9.082863453959d-6 9.081783965827358d-6 9.078627209420748d-6..
  APERIODICITY   = #2A((0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0..

analysis関数にWORLDのメタパラメータを指定することができる。ほとんどはデフォルトでいいと思うが、例えばフレーム長をデフォルトの5.0msから2.0msに、F0の下限値をデフォルトの71Hzから50Hzに変えて分析するにはこのようにする。

(defparameter params2 (world:analysis in-wav :frame-period 2d0 :f0-floor 50d0))

spectrogramとaperiodicityは同じサイズ(全フレーム数×FFTサイズ)の二次元配列なのだが、フレーム長を短くしてF0の範囲を大きくしたことによりこの配列のサイズが増えてより詳細な情報を持つようになることが分かる。

CL-USER> (array-dimensions (world::world-params-spectrogram params))
(159 513)
CL-USER> (array-dimensions (world::world-params-spectrogram params2))
(397 1025)

paramsのF0とspectrogramとaperiodicityをプロットしてみるとこうなる。



合成

analysis関数で取り出したパラメータを色々いじった後、また再合成したいときはsynthesis関数を使う。これは合成後のWORLD-WAV構造体を返す。

(defparameter out-wav (world:synthesis params))

元の音声データと合成後の音声データをプロットしてみるとこうなる。

wavファイル書き出し

output-world-wav-to-file関数でファイルに書き出せる。

(world:output-world-wav-to-file out-wav "/path/to/vaiueo2d-out.wav")