高速フーリエ変換を使用してオーディオを分析する


109

私はpythonでグラフィカルスペクトラムアナライザーを作成しようとしています。

私は現在、1024ビットの16ビットデュアルチャネル44,100 Hzのサンプルレートのオーディオストリームを読み取り、2つのチャネルの振幅を一緒に平均しています。だから今私は256の署名されたショーツの配列を持っています。次に、numpyのようなモジュールを使用して、その配列にfftを実行し、その結果を使用して、グラフィカルスペクトラムアナライザーを作成します。これは、最初は32バーだけです。

高速フーリエ変換と離散フーリエ変換に関するウィキペディアの記事を読みましたが、結果の配列が何を表しているのかまだわかりません。これは、numpyを使用して配列にfftを実行した後の配列の様子です。

   [ -3.37260500e+05 +0.00000000e+00j   7.11787022e+05 +1.70667403e+04j
   4.10040193e+05 +3.28653370e+05j   9.90933073e+04 +1.60555003e+05j
   2.28787050e+05 +3.24141951e+05j   2.09781047e+04 +2.31063376e+05j
  -2.15941453e+05 +1.63773851e+05j  -7.07833051e+04 +1.52467334e+05j
  -1.37440802e+05 +6.28107674e+04j  -7.07536614e+03 +5.55634993e+03j
  -4.31009964e+04 -1.74891657e+05j   1.39384348e+05 +1.95956947e+04j
   1.73613033e+05 +1.16883207e+05j   1.15610357e+05 -2.62619884e+04j
  -2.05469722e+05 +1.71343186e+05j  -1.56779748e+04 +1.51258101e+05j
  -2.08639913e+05 +6.07372799e+04j  -2.90623668e+05 -2.79550838e+05j
  -1.68112214e+05 +4.47877871e+04j  -1.21289916e+03 +1.18397979e+05j
  -1.55779104e+05 +5.06852464e+04j   1.95309737e+05 +1.93876325e+04j
  -2.80400414e+05 +6.90079265e+04j   1.25892113e+04 -1.39293422e+05j
   3.10709174e+04 -1.35248953e+05j   1.31003438e+05 +1.90799303e+05j...

これらの数値が正確に何を表しているのか、およびこれらの数値を32本の棒のそれぞれの高さのパーセンテージにどのように変換するのかと思っています。また、2つのチャネルを一緒に平均化する必要がありますか?

回答:


209

表示している配列は、オーディオ信号のフーリエ変換係数です。これらの係数を使用して、オーディオの周波数コンテンツを取得できます。FFTは複素数値の入力関数に対して定義されているため、入力がすべて実数値であっても、得られる係数は虚数になります。各周波数の電力量を取得するには、各周波数のFFT係数の大きさを計算する必要があります。これは係数の実数成分だけではなく、その実数成分と虚数成分の二乗の合計の平方根を計算する必要があります。つまり、係数がa + b * jの場合、その大きさはsqrt(a ^ 2 + b ^ 2)になります。

各FFT係数の大きさを計算したら、各FFT係数が属するオーディオ周波数を把握する必要があります。NポイントのFFTは、0から始まるNの等間隔周波数での信号の周波数成分を提供します。サンプリング周波数は44100サンプル/秒であるためです。FFTのポイント数は256、周波数間隔は44100/256 = 172 Hz(概算)

配列の最初の係数は、周波数係数0になります。これは基本的に、すべての周波数の平均電力レベルです。残りの係数は、128に達するまで、172 Hzの倍数で0からカウントアップします。FFTでは、サンプルポイントの半分までの周波数しか測定できません。罰の大食いで理由を知る必要がある場合は、ナイキスト周波数およびナイキストシャノンサンプリング定理でこれらのリンクを読んでください。ただし、基本的な結果として、低周波数が高周波数バケットで複製またはエイリアス化されます。したがって、周波数は0から始まり、係数ごとに172 Hzずつ増加してN / 2係数になり、その後、N-1の係数になるまで172 Hzずつ減少します。

それはあなたが始めるのに十分な情報であるはずです。ウィキペディアに記載されているよりもはるかに親しみやすいFFTの紹介が必要な場合は、デジタル信号処理の理解:第2版を試すことができます。とても助かりました。

つまり、これらの数値はそれを表しています。高さのパーセンテージへの変換は、各周波数成分の大きさをすべての成分の大きさの合計でスケーリングすることによって行うことができます。ただし、これは相対的な周波数分布を表すだけで、各周波数の実際のパワーは得られません。周波数成分で可能な最大の大きさでスケーリングを試すこともできますが、それがうまく表示されるかどうかはわかりません。実行可能なスケーリング係数を見つける最も簡単な方法は、大音量とソフト音声の信号を実験して適切な設定を見つけることです。

最後に、全体としてオーディオ信号全体の周波数コンテンツを表示したい場合は、2つのチャネルを一緒に平均化する必要があります。ステレオオーディオをモノラルオーディオにミキシングして、結合された周波数を表示しています。右周波数と左周波数の2つの別々のディスプレイが必要な場合は、各チャネルで個別にフーリエ変換を実行する必要があります。


1
オンラインでFFTの非常に複雑な説明のみを見つけることができます。これは、サンプリングされた点の数がFFTの結果にどのように影響するかを説明する素晴らしい簡単な説明でした。これありがとう!
エコーロケーション

26

このスレッドは古いものですが、とても参考になりました。私はこれを見つけ、同様のものを作成しようとしている人に私の意見を伝えたかっただけです。

バーへの分割に関しては、バーの数に基づいてデータを均等に分割することにより、これはanttiの提案どおりに行われるべきではありません。最も有用なのは、データをオクターブ部分に分割することです。各オクターブは、前の周波数の2倍です。(つまり、100Hzは50Hzを1オクターブ上、つまり25Hzを1オクターブ上)。

必要なバーの数に応じて、範囲全体を1 / Xオクターブの範囲に分割します。バーの指定された中心周波数Aに基づいて、バーの上限と下限を以下から取得します。

upper limit = A * 2 ^ ( 1 / 2X )
lower limit = A / 2 ^ ( 1 / 2X )

次の隣接する中心周波数を計算するには、同様の計算を使用します。

next lower =  A / 2 ^ ( 1 / X )
next higher = A * 2 ^ ( 1 / X )

次に、これらの範囲に収まるデータを平均化して、各バーの振幅を取得します。

例:1/3オクターブの範囲に分割したいとし、1kHzの中心周波数から始めます。

Upper limit = 1000 * 2 ^ ( 1 / ( 2 * 3 ) ) = 1122.5
Lower limit = 1000 / 2 ^ ( 1 / ( 2 * 3 ) ) =  890.9

44100hzと1024のサンプル(各データポイント間の43hz)が与えられた場合、21から26までの値を平均化する必要があります(890.9 / 43 = 20.72〜21および1122.5 / 43 = 26.10〜26)

(1/3オクターブのバーを使用すると、約40 Hzから約20 kHzで約30小節になります)今までに理解できるように、数値が高くなるにつれて、より広い範囲の数値を平均化します。通常、低いバーには1つまたは少数のデータポイントのみが含まれます。より高いバーは何百ものポイントの平均である場合があります。その理由は、86hzが43hzを1オクターブ上にあるからです... 10086hzは10043hzとほとんど同じように聞こえます。


10

時間の長さが256/44100 = 0.00580499秒のサンプルがあります。これは、周波数分解能が1 / 0.00580499 = 172 Hzであることを意味します。Pythonから取得した256の値は、基本的に、86 Hzから255 * 172 + 86 Hz = 43946 Hzの周波数に対応します。取得する数値は複素数です(したがって、2番目ごとの数値の終わりにある「j」)。

編集:誤った情報を修正

sqrt(i 2 + j 2)を計算して複素数を振幅に変換する必要があります。ここで、iとjは実数部と虚数部です。

32小節が必要な場合は、私が理解している限り、連続する4つの振幅の平均を取り、256/4 = 32小節を希望どおりに取得する必要があります。


4
cが複素数の場合、sqrt(c.real 2 + c.imag 2)== abs(c)
tzot

0

FFTは、計算可能なN個の複素数値を返しますmodule=sqrt(real_part^2+imaginary_part^2)。各帯域の値を取得するには、帯域内のすべての高調波に関するモジュールを合計する必要があります。以下に、10バーのスペクトルアナライザーの例を示します。pyd pythonモジュールを取得するには、cコードをラップする必要があります。

float *samples_vett;
float *out_filters_vett;
int Nsamples;
float band_power = 0.0;
float harmonic_amplitude=0.0;
int i, out_index;

out_index=0;


for (i = 0; i < Nsamples / 2 + 1; i++)       
        {
            if (i == 1 || i == 2 || i == 4 || i == 8 || i == 17 || i == 33 || i == 66 || i == 132 || i == 264 || i == 511)
            {
                out_filters_vett[out_index] = band_power; 
                band_power = 0; 
                out_index++;  
            }

            harmonic_amplitude = sqrt(pow(ttfr_out_vett[i].r, 2) + pow(ttfr_out_vett[i].i, 2));
            band_power += harmonic_amplitude;

        }

Pythonで10個のLEDバースペクトルアナライザー全体を設計して作成しました。代わりに、nunmpyライブラリ(大きすぎてFFTを取得するには役に立たない)を使用する代わりに、FFTを取得してオーディオスペクトル全体をバンドに分割するpython pydモジュール(27KBのみ)が作成されました。

さらに、出力オーディオを読み取るために、ループバックWASapi portaudio pydモジュールが作成されました。プロジェクト(ブロック図)は、画像10BarsSpectrumAnalyzerWithWASapi.jpgで確認できます。

YouTubeチャンネルにチュートリアルビデオを追加しました:非常にスマートなPython Spectrum Analyzer 10 Ledバーを設計および作成する方法

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.