どんぶらアニマル さんぽ道

CBR250RR(MC22)とNSR80(HCO6)とAPE50(AC16)を中心とした備忘録。

コード進行解析ツールを作れるか? その2 続フーリエ変換を思い出す?

前回の続きで、周波数分解能を上げてみる。

周波数分解能とは

周波数分解能をいくつに設定するか?FFTの周波数分解能もサンプリングによる離散化に似ているが、サンプリングは元になる連続的なアナログデータがあるが、FFTはFFT自身がデータを生成するところが違う。図のように理想的なスペクトラムがあったとした場合、フーリエ変換では、周波数分解能で分割した「点」の離散的なデータしか得られない。分解能を無限に高く(小さく)していくと理想に近づいていく性質はサンプリングと同じ。
 
周波数分解能とは
 

平均律で必要な分解能

平均律で一般的に使われる範囲の音程を認識できるようにしたいので、ありがたくも一覧にしてくれている、いなさんの周波数一覧を眺めつつ、Excel化した。
 
 
・Excel化したファイル:平均律周波数一覧.xlsx
 
Excelで、隣り合う音程の周波数差を出し、その内の最小値を調べると1.635Hzだった。これは(88鍵の)ピアノの鍵盤の一番下(左端)の音とその右となりの黒鍵の音の差だ。
 
ということは、最低でも1.635Hzの分解能は無いと上記のラ(A0)とラ#(A#0)を判別することができない。感覚的に4倍は欲しい。4倍だと1.635/4=‭0.40875‬となる。
 
 
周波数分解能 dfを0.40875Hzとする。
サンプリングレート fsは44,100Hzとする。(WaveGeneで作ったwavのサンプリングレート)
サンプル点数をBLとする。
df=fs/BLという関係性の式を変形すると、BL=fs/dfとなり、BL=44,100/0.40875=107,889.9となる。
サンプル点数 BLはFFTの性質上、2のべき乗である必要があり、107,889.9は2の16.71920029であることから2の17乗とする。(なんかでかいなぁ)
2の17乗=131,072をサンプル点数 BLとする。
 
コードのサンプル点数に相当するfftLengthを
            //1サンプルのデータ数
            int fftLength = 256;
から
            //1サンプルのデータ数
            int fftLength = 131072;
に変更する。
 

周波数分解能を上げてやってみる

まずは前回使った10kHzを入力にやってみる。
 
この結果が下記のグラフで、ピークが10kHz、12kHz、21kHz付近にある。12k、21kはなんだろ。

10kHz、12kHzの近傍のグラフが下記。


ピークの周波数は、10,000.15Hz、12,049.85Hz、21,024.15Hzだった。
 
サンプル点数が256の時には出てなかったので、サンプル点数を増やしすぎた影響なのか?半分の65,536にしてみたら21kは無くなって、12kも小さくなった。

 
 
さらに半分の32768にしたらほぼ無くなった。
 
この時の周波数分解能は、df=fs/BL=44,100/32,768=1.3458Hzなのでラ(A0)とラ#(A#0)の判別は無理そう。
 
ついでに昨日見てたラ(A4 440.000Hz)とレ(D5 587.330Hz)のサイン波を見てみる。
 
 
ちょっと、見えないのでラとレがある400~700Hzの範囲をグラフ化してみた。
・ここまでのExcelのデータ:sign-Left10khz-10sec-131072.xlsx

ラ(A4 440.000Hz)とレ(D5 587.330Hz)を検出した周波数が少しずれているが、周波数分解能以内のズレなのでこんなもんだろう。
あと、32,768の時の1つのフレームの長さ(時間窓長)が32,768/44,100=0.743秒と長すぎる。65,536だと倍の1.486秒になる。
120BPMの4分音符の長さは、60/120=0.5秒なのでこれを超えてると前後の余分な音符も交じってしまうことを意味する。8分音符、16分音符は全く識別できない。
 

課題

ということで以下の2つを解決する必要がある。
周波数分解能を上げて出てくる余分なパワー
周波数分解能を上げるとフレームが長くなって短い音符を識別できなくなる
 
下記の式から上記の2つの問題は相反する(と思う)。分解能を上げる(dfを小さくする)には、サンプル点数を上げる必要があるが、サンプル点数を上げると時間窓長が長くなってしまう。
 
周波数分解能 df=サンプリング周波数 fs/サンプル点数 BL
 
窓関数はフレームの前方、後方の振幅を0に押さえ込んでいると思うが、その際に0に押さえ込む範囲を広げてフレームの中心付近の振幅だけ残す等のFFTの前処理でフレームが長いことによる問題を解決する方法があるのではないかと思いつつ、宿題とする。
 
今回使ったvisualStudioのプロジェクトは、他の実験にも使ってて、プレーヤの再生やWAVへの変換等の余分なコードがあるが、それらは無視して「開く」ボタンでWAVファイルを指定し、「FFT」ボタンを押すとFFTの結果が実行フォルダにlog.txtのファイル名で生成されるようにしてある。コードはばっちいけど置いとく。
 
・プロジェクト:fft01.zip
 
ビルドには以下のライブラリが必要です。
MathNet.Numerics(必要無かったかも)
 
音符が出てくるまでの道のりは長いなぁ。。。次回に続く。
 
気が向いたら感想をお願いします。(ログイン不要、ボタンを押すだけです)