記憶:WORLDで遊んだ

これは何?

pyworldというライブラリで遊んだ時の記憶です。
未来の271108へ。
このライブラリに対するメモ書きではないのでいつか理解する日が来たらメモ書きをしてください。

WORLDって何?

多分、「PyWorldVocoder」のこと。少なくとも遊んだ時の参考サイトにはそう書いてあった。

インストール

pip install pyworld

なお、参考サイトにはこう書いてあった。

先にscipyを入れておかないとpyworldのインストールでコケることがある

今回はもともとscipy入ってたのでそのままインストールしても問題なかった。

試した

特徴量を抽出し、音声を合成

なんかすごく面白そうな記事だったのでとりあえずまねっこをしましょう。
※参考元サイトのコードをそのままやっても動かなかったので一応私が実行した形で掲載。詳しい話は参考元のサイトを見てくれ。

from scipy.io import wavfile
import pyworld as pw
import numpy as np

WAV_FILE = ファイル名

fs, data = wavfile.read(WAV_FILE)
data = data.astype(np.float)

↑見ての通り、ファイルの読み込みを行っている。

_f0, t = pw.dio(data, fs)  # 基本周波数の抽出
f0 = pw.stonemask(data, _f0, t, fs)  # 基本周波数の修正
sp = pw.cheaptrick(data, f0, t, fs)  # スペクトル包絡の抽出
ap = pw.d4c(data, f0, t, fs)  # 非周期性指標の抽出

↑見て分かんない通り、特徴量の抽出を行っている。

synthesized = pw.synthesize(f0, sp, ap, fs)

↑得られた特徴量からを音声を合成するにはsynthesize メソッドを使うそうです。

import IPython.display as display

print('original')
display.display(
    display.Audio(data, rate=fs)
)

print('synthesized')
display.display(
    display.Audio(synthesized, rate=fs)
)

↑再生。


↑オリジナル
↑synthesized

簡単ですね。

うん、私にゃぁ何にも分からなかったよ。動いたけど。
ついでに言うとdata(オリジナル音源)とsynthesizedの違いがわからなかったよ。



でも違うってよ。PC君が言うんだから間違いない。私の聴覚機能よりよっぽどか信頼できる。

音声の変換

参考サイトではとりあえず1オクターブ高い声に変換してました。オクターブ、なんとなくしか分かりませんが、まあ真似てみましょう。

high_freq = pw.synthesize(f0*2.0, sp, ap, fs)  # 周波数を2倍にすると1オクターブ上がる

print('high_freq')
display.display(
    display.Audio(high_freq, rate=fs)
)


……甲高くてやかましいですね。まあもともとが女声なので当然といえば当然です。
じゃ、逆に半分にすればええか!

high_freq = pw.synthesize(f0*0.5, sp, ap, fs)  # 周波数を2倍にすると1オクターブ上がる

print('low_freq')
display.display(
    display.Audio(high_freq, rate=fs)
)


低く……なったかな?まあどっちかっていうと容疑者を知る人へのインタビュー感がぬぐえませんが。

本命

なんでこんなのやりだしたかって、「ロボットっぽく音声を変換できる」ってのにヒットしたからです。だからわざわざCCD-0500に話させて声質変換なんてかけているんです。
まあいいや。

参考サイト通り
robot_like_f0 = np.ones_like(f0)*100  # 100は適当な数字
robot_like = pw.synthesize(robot_like_f0, sp, ap, fs)

print('robot_like')
display.display(
    display.Audio(robot_like, rate=fs)
)


声低いなぁ。

ちょっと改善?

「f0とおんなじ形の1しか入っていない配列に100を掛けたもの」だから低いんですよね。
じゃあ高くしてみましょう。

f0ってのはこんな中身をしています。3秒で考えた案は3つ。

  1. とりあえず平均値でどうよ?
  2. いや、中央値のほうがいいのでは?
  3. 0に引っ張られて中央値も小さくならない?非0の中央値はどう?

じゃ、やってみましょ。

robot_like_f0_1 = np.ones_like(f0)*f0.mean()
robot_like_1 = pw.synthesize(robot_like_f0_1, sp, ap, fs)
print('f0.mean()')
display.display(display.Audio(robot_like_1, rate=fs))
write('f0.mean.wav', fs, robot_like_1)

robot_like_f0_2 = np.ones_like(f0)*np.median(f0)
robot_like_2 = pw.synthesize(robot_like_f0_2, sp, ap, fs)
print('median(f0)')
display.display(display.Audio(robot_like_2, rate=fs))
write('median(f0).wav', fs, robot_like_2)

robot_like_f0_3 = np.ones_like(f0)*np.median(np.nonzero(f0))
robot_like_3 = pw.synthesize(robot_like_f0_3, sp, ap, fs)
print('非零中央値')
display.display(display.Audio(robot_like_3, rate=fs))
write('非零中央値.wav', fs, robot_like_3)

あ、この基本周波数、f0ってもしかして抑揚の根幹なの?
あー……

とは思ったものの

いざ抑揚を0にさせると、それでもロボットっぽさは出ない。

この違いってなんなんすかね?


でも、

what_is_the_elements_of_robots = (np.ones_like(f0)*np.median(np.nonzero(f0)) + f0)/3.5
what_is_the_elements_of_robots_test = pw.synthesize(what_is_the_elements_of_robots, sp, ap, fs)
display.display(display.Audio(what_is_the_elements_of_robots_test, rate=fs))


こうするとロボットっぽさあるでしょ?

……ない?

次回予定

もう少し、様々な声を作れるのかもしれないようなのでさらに触ってみる。

参考元

qiita.com

余談

朝に突然謎の動画の連騰をしましたが全部この記憶貯蔵のためです。別に私がおかしくなったとかそんなことはございません。