12

オーディオ ファイルがあり、ファイルを反復処理し、各ステップで 512 個のサンプルを取得してから、それらを FFT に渡します。

ブロック 514 フロート ロング (IPP の ippsFFTFwd_RToCCS_32f_I を使用) としてデータを出力し、実数成分と虚数成分をインターリーブします。

私の問題は、これらの複素数を取得したらどうするかということです。現時点では、各値に対して行っています

const float realValue   = buffer[(y * 2) + 0];
const float imagValue   = buffer[(y * 2) + 1];
const float value       = sqrt( (realValue * realValue) + (imagValue * imagValue) );

これにより、少し使用可能なものが得られますが、値を 0 から 1 の範囲で取得する何らかの方法が必要です。上記の問題は、ピークが約 9 またはそれ以上に戻ってしまうことです。これは、オーディションのスペクトログラムでオーディオを実行すると、非常に強いように見えるという事実にもかかわらず、物事がひどく飽和し、スペクトログラムの他の部分がほとんど表示されないことを意味します. FFT によって返されるデータが何であるか 100% 確信が持てないことは十分に認めます (それ以外は、渡された 512 サンプルの長さのブロックの周波数値を表します)。特に、複素数が正確に何を表しているのかについて、私の理解が欠けています。

アドバイスや助けをいただければ幸いです。

編集:明確にするために。私の大きな問題は、返される FFT 値が、スケールが何であるかがわからないと意味がないことです。誰かがそのスケールを解決するように私に指摘できますか?

Edit2:次のようにすることで、非常に見栄えの良い結果が得られます。

size_t count2   = 0;
size_t max2     = kFFTSize + 2;
while( count2 < max2 )
{
    const float realValue   = buffer[(count2) + 0];
    const float imagValue   = buffer[(count2) + 1];
    const float value   = (log10f( sqrtf( (realValue * realValue) + (imagValue * imagValue) ) * rcpVerticalZoom ) + 1.0f) * 0.5f;
    buffer[count2 >> 1] = value;
    count2 += 2;
}

私の目には、これまでに見た他のほとんどのスペクトログラム実装よりも良く見えます。

私がやっていることに重大な問題はありますか?

4

5 に答える 5

11

すべてのFFTを表示するために行う通常のことは、大きさの対数を取ることです。

したがって、出力バッファの位置から、検出された周波数がわかります。複素数の大きさ(L2ノルム)は、検出された周波数がどれほど強いかを示し、位相(逆正接)は、音声空間よりも画像空間ではるかに重要な情報を示します。FFTは離散的であるため、周波数は0からナイキスト周波数まで実行されます。画像では、通常、最初の項(DC)が最大であるため、それが目的である場合は、正規化で使用するのに適した候補です。それがオーディオにも当てはまるかどうかはわかりません(私はそれを疑っています)

于 2009-11-05T14:28:09.073 に答える
7

512 サンプルのウィンドウごとに、FFT の大きさを計算します。各値は、信号に存在する対応する周波数の大きさを表します。

mag
 /\
 |
 |      !         !
 |      !    !    !
 +--!---!----!----!---!--> freq
 0          Fs/2      Fs

次に、周波数を計算する必要があります。

入力信号は実数値であるため、FFT は中央 (ナイキスト成分) で対称であり、最初の項は DC 成分です。信号のサンプリング周波数Fsがわかっている場合、ナイキスト周波数は Fs/2 です。したがって、インデックスkの場合、対応する頻度はk*Fs/512

したがって、長さ 512 のウィンドウごとに、指定された周波数での大きさが得られます。連続したウィンドウ上のそれらのグループがスペクトログラムを形成します。

于 2009-11-05T15:09:22.360 に答える
6

私がこの問題全体に対して多くの作業を行ったことを人々に知ってもらうためです。私が発見した主なことは、FFT を実行した後に正規化が必要だということです。

これを行うには、ウィンドウ ベクトルのすべての値を平均して、1 よりやや小さい値 (長方形のウィンドウを使用している場合は 1) を取得します。次に、その数を FFT 変換後の周波数ビンの数で割ります。

最後に、FFT によって返された実際の数値を正規化数値で割ります。振幅値は、-Inf から 1 の範囲にあるはずです。ログなどはお好みでどうぞ。あなたはまだ既知の範囲で作業しています。

于 2011-12-17T00:30:57.190 に答える
5

役立つと思うことがいくつかあります。

フォワード FT は、入力よりも出力に大きな数値を与える傾向があります。データセット全体に分散されるのではなく、特定の頻度でのすべての強度が 1 つの場所に表示されると考えることができます。これは問題ですか?ニーズに合わせていつでもデータをスケーリングできるため、おそらくそうではありません。以前、整数ベースの FFT/IFFT ペアを作成したことがあり、整数オーバーフローを防ぐために各パスで再スケーリングが必要でした。

入力である実際のデータは、ほとんど複雑なものに変換されます。結局のところ、buffer[0] と buffer[n/2] は実数で独立しています。それについての良い議論がここにあります。

入力データは、時間の経過とともに取得された等間隔の音響強度値です。それらは、適切には、時間領域にあると言われています。FT の出力は、横軸が周波数であるため、周波数領域にあると言われます。縦軸は強度のままです。入力データからは明らかではありませんが、入力にも位相情報があります。すべての音は正弦波ですが、正弦波の位相を固定するものは何もありません。この位相情報は、個々の複素数の位相として周波数領域に表示されますが、多くの場合、気にしません (気にすることもよくあります)。それはあなたが何をしているかにかかっています。計算

const float value = sqrt((realValue * realValue) + (imagValue * imagValue));

強度情報を取得しますが、位相情報を破棄します。対数を取ることは、基本的に大きなピークを減衰させるだけです。

これが役に立てば幸いです。

于 2009-11-05T16:19:22.083 に答える
1

奇妙な結果が得られた場合は、FFT ライブラリのドキュメントを参照して、出力がどのようにパックされているかを確認してください。一部のルーチンは、実数/虚数の値が交互に配置されるパック形式を使用するか、N/2 要素から開始してラップ アラウンドする場合があります。

サニティ チェックのために、Fs/2、Fs/4 (Fs = サンプル周波数) などの既知の特性を持つサンプル データを作成し、FFT ルーチンの出力を期待どおりのものと比較することをお勧めします。同じ周波数でサインとコサインの両方を作成してみてください。これらはスペクトル内で同じ大きさを持つ必要がありますが、位相が異なるためです (つまり、realValue/imagValue は異なりますが、平方和は同じである必要があります)。

ただし、FFT を使用するつもりなら、それが数学的にどのように機能するかを本当に知る必要があります。そうしないと、エイリアシングなどの他の奇妙な問題に遭遇する可能性があります。

于 2009-11-05T13:56:02.257 に答える