マイクから入力された音の高さを検出する手順

豊田高専コンピュータ部 Advent Calendar 2019の9日目の記事です。

前置き

研究で、マイクから入力された音の高さを検出する必要がありました。そこで自分で考えた方法で実装したところ、単音の音高はうまく検知できたのですが、人間の声などの音高は誤りが多く、実用的ではありませんでした。そこでよりスマートな音高の検出方法を調べたところ、「A SMARTER WAY TO FIND PITCH」という論文を見つけました。利用する式などは論文で確認してください。具体的なソースコードは他の方が公開しているので、そちらを御覧ください。

手順

平方差分関数を使って元の波形との一致度を求める

入力された音の波形から、平方差分関数を使って、元の波形を時間軸でずらした場合にどれくらい元の波形と一致しているかを求めます。一致度が高ければ、ずらした量が元の波形の周期倍である可能性が高いと言えます。平方差分関数は高速に計算する方法があり、元の波形を高速フーリエ変換してパワースペクトルを求め、逆高速フーリエ変換をすることで求まります。 f:id:mkmokymh:20191208233033j:plain

正規化する

平方差分関数から求められる一致度ですが、値が一定ではありません。そこで-1.0~1.0までの値をとるように正規化します。

ピークピッキング

ここからは、一致度が高くなっている部分を検出します。検出は、一致度がマイナスからプラスになってからマイナスに戻るまでを1区間として、1区間での最大値をピークとします。もし小さい値が誤って検出された場合は、他の値と比べて削除します。検出したピークの中でも最も大きいピーク値を基準値として、一番最初のピークがどこで現れているかを見つけます。1.0~0.8の値を基準値にかけて、それをしきい値とします。このしきい値を超える最初のピークを見つけます。測定された波形は実際は離散的なものなので、最初のピーク直近の2つの値をつかって放物線補完を行い、より正確なピークを見つけます。 f:id:mkmokymh:20191208233059j:plain

音高計算

ピークピッキングで検出した最初のピークの位置を元の波形の周期とします。マイクのサンプリングレートを周期で割ることで周波数を求められます。

終わりに

マイクから入力された音の高さを求めるのは意外と面倒臭いですね。 単音ではない音はこのような方法でないと精度がよくないです。 実際に学校の授業で習っていたフーリエ変換を活用したので、授業の大切さを改めて実感しました。

参考文献

(PDF) A smarter way to find pitch MPMアルゴリズムのNSDFの求めかた