Pythonで音声処理(4)音声読み込み、周波数グラフ化

IT

前回の記事では、ピー音を作成し、FFT処理後、周波数を変更して、逆FFTにより音の高さが変わるところまで確認しました。

今回は、いよいよ実際の音声の処理を実行してみようと思います。

今回用意したプログラムは以下のとおりです。

# -*- coding: utf-8 -*-
import wave
import numpy as np
import matplotlib.pyplot as plt
import struct
# Wave読み込み
def read_wav(file_path):
wf = wave.open(file_path, "rb")
buf = wf.readframes(-1) # 全部読み込む
# 2なら16bit,4なら32bitごとに10進数化
if wf.getsampwidth() == 2:
data = np.frombuffer(buf, dtype='int16')
elif wf.getsampwidth() == 4:
data = np.frombuffer(buf, dtype='int32')
# ステレオの場合,チャンネルを分離
if wf.getnchannels()==2:
data_l = data[::2]
data_r = data[1::2]
else:
data_l = data
data_r = data
wf.close()
return data_l,data_r
# wavファイルの情報を取得
def getWavInfo(file_path):
ret = {}
wf = wave.open(file_path, "rb")
ret["ch"] = wf.getnchannels()
ret["byte"] = wf.getsampwidth()
ret["fs"] = wf.getframerate()
ret["N"] = wf.getnframes()
ret["sec"] = ret["N"] / ret["fs"]
wf.close()
return ret
if __name__ == '__main__':
data_l,data_r = read_wav("arigato.wav")
# Wavの情報取得
wi = getWavInfo("wav/arigato.wav")
# FFT
F_l = np.fft.fft(data_l)
F_r = np.fft.fft(data_r)
plt.plot(F_l.imag[1:wi["N"]])
plt.grid()
plt.savefig("arigato_freq.png")
plt.gca().clear()
view raw audiotest3.py hosted with ❤ by GitHub

細かい処理内容の説明は割愛しますが、プログラムの流れとしては下記となります。

1.音声ファイルを読み込む

2.音声ファイルの情報を読み込む

3.音声ファイルをFFT処理し、周波数をグラフ表示する

読み込んでいる音声ファイルは以下です。Webのフリー素材で拾ってきたものになります。

音量にご注意ください。

このプログラムを実行して、得られた周波数グラフは以下になります。

前回の記事でピー音の周波数を確認したときと比べて、随分複雑になっていることがわかります。

これは実際の音声には複数の音波が混ざり合っていて、周波数が複数あることを意味しています。

実はリアル音声を周波数解析する上で、課題が大きく2つあります。

課題1 時間とともに音波および周波数が変化すること

課題2 周波数が複数混合しているため、周波数シフトの方法を工夫しなければならないこと

それぞれの課題について、解説していきます。

課題1 時間とともに音波および周波数が変化すること

通常の音声はピー音と違い、時間とともに変化します。

今回のプログラムで確認した音声は、3秒程度ですが、通常の音声はより長くなります。

より長いデータをそのままFFTすると音声全体の周波数をざっくりとしか解析できません。

ざっくりとした周波数をシフトした場合、逆FFTで戻した際の音質が落ちてしまいます。

こういった課題を解決するために、音声処理の世界では、音声を一定の時間で切り刻んで解析するのが一般的です。

ただし、単純に切り刻むだけだと逆FFTで戻した音声の繋がりが悪くなるため、オーバーラップ処理と窓関数処理を実施します。

オーバーラップ処理は、音声を単純に切り刻むのではなく、前後を重ねて切り刻む処理です。

イメージは以下のような感じです。①〜⑦の前後が重なったデータを処理対象とします。

窓関数は、信号の最初と最後がゼロになるように丸める処理です。実信号に掛け合わせて利用します。窓関数により、オーバーラップされた部分の繋がりが良くなります。

課題2 周波数が複数混合しているため、周波数シフトの方法を工夫しなければならないこと

ピー音では440Hzの音程のみの信号でしたが、実際の音声には複数の周波数が含まれます。

前回の記事でシフト処理を行った際、440Hzを加算して880Hzとしましたが、複数の周波数が含まれる場合は、加算によるシフトで不都合が生じます。

下記は音階に対応する周波数を表しています。

音階周波数(Hz)一つ前の音階との差分一つ前の音階との倍率
261.626
ド#277.18315.5571.0595
293.66516.4821.0595
レ#311.12717.4621.0595
329.62818.5011.0595
ファ349.22819.6001.0595
ファ#369.99420.7661.0595
391.99522.0011.0595
ソ#415.30523.3101.0595
440.00024.6951.0595
ラ#466.16426.1641.0595
493.88327.7191.0595
523.25129.3681.0595

一つ前の音階との差分は、音程が上がるにつれて増えていっているのがわかります。

一方、一つ前の音階との倍率は、一定となります。

つまり、周波数を変更する場合、加算ではなく、乗算する必要があるということです。

リアル音声処理では、これらの課題を解決していく必要があります。

次回の記事では、実際の音声処理で必要となる、オーバーラップ処理と窓関数処理を実装していきます。

コメント

タイトルとURLをコピーしました