こんにちは!ところたんだポン! この記事は,rogy Advent Calendar 2016 の23日目の記事に選ばれたポン! またお前かって?そんな細かいこと気にするなポン! 日付が過ぎてるって?それも気にしちゃだめポン.
前回のあらすじ
前回の記事はこちら- 早く美少女になりたい
- おっさん声を美少女化したい
- ピッチ推定・フォルマント推定で声の性質を抽出した
- 上記の性質に基づいて音声合成してみた
- この方法のにゃーんみ(限界)を感じた
ということで,今回はTD-PSOLAというアルゴリズムを使って前回を超えるクオリティの美少女ボイス化を達成したいと思います. 果たしてできるのか…!
TD-PSOLA
前回は音声信号の特徴量であるピッチ・フォルマントを抽出し,これらの値を男声から女声に変換してから音声を再合成するという方法を取っていました. 今回はそれとは打って変わって,おっさん声の信号をそのまま利用してピッチ・フォルマントの変換をやってしまおうという方針で進めていきます.
TD-PSOLA(Time-Domain Pitch Synchronous Overlap and Add: 時間領域ピッチ同期重畳加算)はこれに丁度良いアルゴリズムで,時間領域の信号(つまり録ったままの音声信号)を切り貼りすることでピッチが変換された音声を作る手法です.
このページの動画がとても参考になります.
ざっくりとアルゴリズムの中身を解説すると以下のようになります.
- ピッチに対応した波形の1周期(Pitch Period)を見つける
- Pitch Periodを切り出す
- 切り出したパーツを使って音声を再合成する
ピッチを変えずに再生時間を長く/短くしたい場合は,切り出したパーツを複製/削除して再度くっつけることで目的が達成できます. また,ピッチを変えたい場合は,切り出したパーツの間隔をずらして再配置することで,ピッチを高くしたり低くしたりすることができます(ただし音の長さが変わります). このとき,どちらの処理を施してもフォルマントが変化しないというのがこの手法のミソです. したがって,たとえばフォルマントと再生時間を維持したままピッチを2倍に変更したい場合,パーツを複製してつなげることで2倍の長さの音声を作って,間隔を詰めて再配置することでピッチを2倍にすれば良いということになります.
じゃあフォルマントをいじりたい場合はどうすればいいのでしょうか? 勘が良い方はお気づきかもしれませんが,再生速度を変えればよいことになります. 再生速度を変えると,同時にピッチと再生時間も変化しますから,先ほど説明した方法と併用することで最終的な目標を達成することになります.
具体的な方法についてはフリーのボイスチェンジャーソフトである『恋声』のウェブページに軽く説明がされています.
実装してみる
TD-PSOLAはフランス・米国特許により保護されているため,念のためソースは配布しないことにします. 代わりに,ここまでで解説してきたアルゴリズムが実際にどのように動くのか,MATLABの超便利なプロット機能を駆使して見ていきましょう.
使用する音声は例によって「にゃーん」で,目標はピッチを2倍,フォルマントを1.5倍にした音声を作ることです.
Step 1. Pitch Period の検出
原典では,音声の波形の中でピークが立つ部分を検出して,そこを1周期として認識することでPitch Periodを生成しています. なんとなくそれはいいかげんではないかと思うところもあるので,今回は前回作ったピッチ推定のプログラムを活用してPitch Periodを作ることにします.
男声のピッチの下限を80Hz程度とざっくり見積もると,少なくとも1/40秒分のデータを処理対象にしなければなりません. 今回は音声データは44100Hzでサンプリングされているので,1500サンプル間隔(だいたい30Hz)で処理をします.
ピッチ推定で得られたピッチに対応する正弦波を音声信号と一緒にプロットしてみると以下のようになります.
これでPitch Periodを検出することができました.
Step 2. 音声パーツの切り出し
今回は簡単に先ほどの正弦波の周期…位相0の点を区切れ目として音声を切り出すことにしましょう.
まず単純に計算した1周期のデータを重ねてプロットしてみます.
いい感じですね(ちょっとずつ位相がズレていますが,おそらくケプストラムによるピッチ推定の分解能の影響です). これで波形をバラバラに分解できたので,あとはこれを並べれば… と思うかもしれませんがちょっとまってください.
このままのものを並べると,たとえばピッチを低くしたい時に困りますし,なによりつなぎ目で音がプツプツになります. ということで,前者の問題を2周期分を1周期ずらして切り出すことで解決し,後者の問題は窓関数をかけることで対処します.
2周期分を1パーツとして並べるとこんな感じになります.
窓関数をかけるとこうなります.今回は三角窓を窓関数として採用しています(が,何を採用しても耳で聴く分にはあまり変化はないです).
元の音声とパーツを可算して合成したものを比較してみます.
元の音声がばっちり復元できていますね. 次のステップからはここで作ったパーツを切り貼りして別の音声信号を生成していきます.
Step 3. パーツの削除/繰り返し
今回はピッチを高くしたいので,次のステップでパーツの間隔を詰めて配置することになります. その際に再生時間が短くなるのを防ぐために,パーツを繰り返して利用することを考えます.
特に今回は2倍のピッチにするので,元の長さを維持するためには2倍の数のパーツが必要になります.
たとえばパーツがMATLABのセル配列に縦向きに格納されているとすると,こんな感じで処理できます.
parts = repmat(parts, 1, 2)'; % 複製して転置をとる |
簡単なベクトルの例で見ると何をやっているかがわかると思います.
>> x = [1;2;3;4]; |
再生時間を維持したままピッチだけ低くしたい場合には,再配置によって再生時間がもとより長くなるため,複製ではなくパーツを間引く処理が必要になります. 今回はその必要がないのでこのステップはこれで完了です.
Step 4. パーツの再配置・重畳可算
さて,それではパーツの間隔を詰めて再配置することでピッチを上げましょう. っと,その前に,今回は同時にフォルマントを1.5倍にするわけですから,そこも考慮に入れる必要があります. 『恋声』のページによると,ピッチ倍率 \(a\), フォルマント倍率 \(b\) としたときに,ピッチのみをまず \(a/b\) 倍してから再生時間のみを \(b\) 倍にし,最後に \(b\) 倍速で再生することでこれが達成できると記されています.
ややこしいので結論を先に話すと,もとの \(a\) 倍の数のパーツでもとの \(b\) 倍の長さの信号を作って,それを最後に \(b\) 倍速すればこれと同じ処理をやったことになります. \(a\) 倍の数のパーツで \(b\) 倍の長さの信号を作ると,ピッチは \(a/b\) 倍になります. これを \(b\) 倍速で再生すると,ピッチは \(a\) 倍かつフォルマントは \(b\) 倍になり,再生時間はもとの音声と同じになるというからくりです. あーややこしい.
ともかくこれを実装します.試しに再生時間を変えずにピッチだけ2倍にしてみます. 結果をプロットして比較してみましょう.
上のプロットがもとの音声,下がTD-PSOLAでピッチを2倍にしたものです. 確かに周期が1/2になっていますし,波形の形状も維持されています.これは期待できますね.
音声はこんな感じになります.
き,きもい… けど狙いが達成できていることがよくわかる音声ですね.
で,同様の方針で処理をしていきます. 2倍の数のパーツは前のステップで作成できていますから,これを最終的な音声が元の1.5倍の長さになるようにずらして配置していきます.
最終的な波形はこんな感じになります.
また,音声はこうなります.
ちょっとピッチが高くなって,再生時間が長くなりましたね.
Step 5. 後処理
さて,これであとは再生速度だけを変えれば最終的な音声が出来上がります! 再生速度を1.5倍にする処理は,2/3倍のサンプリングレートで再サンプルするのと同じことですから,resample関数を使えば一発です.
converted_voice = resample(result_tdpsola, 2, 3); |
さあ,ドキドキしてきましたね…!
結果
すごい!美少女感が出てる!! 考えてみてくださいよ,元はこんな声の信号だったんですよ.
20代も半ば,人生への疲れが見え始め,自らが選択した将来に対して夢も希望も大して感じられない,成人男性の声ですね. そんなくたびれた声を切り貼りするだけで美少女ボイスができちゃうなんてすごいことです! これこそテクノロジーのあるべき方向ですよね!嬉しいのでもう一度はっておきます.
ちょっとは美少女に近づけたのでは!
ということで,今回の目標は見事に達成です.
じゃあこの良い流れで別の音声を処理してみたらどうなるのでしょうか!?やってみましょう!
音声はこれです.有名なATR音素バランス503文の第1文ですね. 『あらゆる現実をすべて自分の方へねじ曲げたのだ』
これを変換すると…!
無声音や無音部分の処理が甘いせいでぐちゃぐちゃになっていました…
やはり美少女への道のりは険しい.
To be continued…?