今回は音声分析手法であるケプストラム分析とLPC分析をpythonを用いてやってみます。
音声は日本声優統計学会というところからお借りしました。
https://voice-statistics.github.io/
藤東知夏さんがテキストを読み上げている10秒ほどのデータ(fujitou_normal_001.wav)を使用しました。
LPC解析(Linear Predictive Coding)
声道を音響管の連接と捉えて特徴を抽出します。
ケプストラム分析に比べてformantピークが良く抽出できるみたいです。
lpc係数を求めるプログラムを示します。
librosa.lpc関数を用います。解析する信号と次数を渡すと係数が返ってきます。
1 2 |
def calc_lpc(input, order): return librosa.lpc(np.hanning(len(input))*input, order) |
lpc係数を求めてからスペクトル包絡を求めるプログラムは以下のようになります。
scipy.signal.freqz関数を用います。
1 2 3 |
def calc_lpc_spec(input, order): lpc = librosa.lpc(np.hanning(len(input))*input, order) return 20 * np.log10(np.abs(scipy.signal.freqz(1.0, lpc, worN=1024)[1])) |
ケプストラム分析
声道の特性(低周波数で変動)と音源の特性(高周波で変動)を分離して抽出します。
信号 → FT → abs() → log → 位相アンラッピング → FT → ケプストラム(Wikipedia)
操作が簡単で、これを改善したメルケプストラムが機械学習等で多く用いられています。
ケプストラム係数を求めるプログラムを示します。
1 2 3 |
def calc_ceps(input, order): cps = np.real(np.fft.ifft(20 * np.log10(np.abs(np.fft.fft(input*np.hanning(len(input))))))) return cps[:order].tolist() |
ケプストラム係数を求めてからスペクトル包絡を求めるプログラムは以下のようになります。
1 2 3 4 5 |
def calc_ceps_spec(input, order): cps = np.real(np.fft.ifft(20 * np.log10(np.abs(np.fft.fft(input*np.hanning(len(input))))))) cps[order: len(cps) - order] = 0 ceps_spec = np.real(np.fft.ifft(cps)) return ceps_spec[:1024] |
実行結果
先ほど示した関数を用いて係数の算出を行います。
今回は、信号を区切り、順番に計算を行います。
1 2 3 4 5 6 7 8 9 10 |
if __name__ == "__main__": wave, fs = wav_read(path_to_wavfile) n_fft = 2048 hop_length = 512 order = 20 index_list = range(4, len(wave)//hop_length-8) t = librosa.frames_to_time(np.arange(len(index_list)), sr=fs) # 時間軸 order_scale = np.arange(order + 1) # 次数軸 frq_scale = np.fft.fftfreq(n_fft, d=1.0 / fs)[:n_fft//2] # 周波数軸 |
係数の算出とプロットのプログラムは以下の通りです。
1 2 3 4 5 6 7 8 9 10 11 |
lpccs = [calc_lpc(wave[i*hop_length:i*hop_length+n_fft], order) for i in index_list] ceps = [calc_ceps(wave[i*hop_length:i*hop_length+n_fft], order) for i in index_list] plt.subplot(121) plt.title("Cepstrum") plt.pcolormesh(t, order_scale, list(zip(*ceps)), cmap='jet') plt.subplot(122) plt.title("LPC") plt.pcolormesh(t, order_scale, list(zip(*lpccs)), cmap='jet') plt.show() plt.close() |
実行結果はこんな感じです。
同じ次数(order=20)で抽出をしましたが、係数の雰囲気は違います。
スペクトル包絡の算出とプロットのプログラムは以下の通りです。
1 2 3 4 5 6 7 8 9 10 11 |
lpc_spec = [calc_lpc_spec(wave[i*hop_length:i*hop_length+n_fft], order) for i in index_list] ceps_spec = [calc_ceps_spec(wave[i*hop_length:i*hop_length+n_fft], order) for i in index_list] plt.subplot(121) plt.title("Cepstrum spectrum") plt.pcolormesh(t, frq_scale, list(zip(*ceps_spec)), cmap='jet') plt.subplot(122) plt.title("LPC spectrum") plt.pcolormesh(t, frq_scale, list(zip(*lpc_spec)), cmap='jet') plt.show() plt.close() |
実行結果は下のグラフです。
異なる手法ですが似たようなスペクトル包絡が生成されています。
次はMFCCもやりたいです。
コメント
人の声を分類したいと思い、拝見しました。
音響分析には、全く知見がなく、現象を利用させてもらえればと思っています。
calc_cepsの関数の中の
input*np.hanning(len(input)
の部分が
ValueError: operands could not be broadcast together with shapes (2048,2) (2048,)
になってしまうのですが、対策があれば教えてもらえると助かります。
昨日、calc_ceps関数でエラーになるとコメントした者です。
配列が合わないためにエラーが出ていたようです。
配列が合わない理由は、wavファイルの形式がステレオだったためでした。
モノラルに変換してからcalc_ceps関数に入れると、ケプストラム分析のグラフが表示できるようになりました。
ありがとうございます。