10

FFT アルゴリズムを試してみました。インターネットからのFFTアルゴリズムの作業コードとともにNAudioを使用します。私の観察によると、結果のピッチは不正確です。

何が起こるかというと、E2 (ギターの最低音) から約 E6 までのピッチ進行を含む WAV ファイル (44.1khz、16 ビット、モノラル) に変換された MIDI (GuitarPro から生成) があります。低いノート (E2-B3 あたり) の結果は、一般的に非常に間違っています。しかし、C4 に到達することは、適切な進行 (次の音は C#4、次に D4 など) が既に確認できるという点である程度正しいです。ただし、検出されたピッチが実際のピッチよりも半音低いという問題があります (たとえば、音符は C4 のはずですが、D#4 が表示されます)。

何が間違っていると思いますか?必要に応じてコードを投稿できます。どうもありがとう!私はまだ DSP の分野を把握し始めています。

編集:これは私がやっていることの大まかなスクラッチです

byte[] buffer = new byte[8192];
int bytesRead;
do
{
  bytesRead = stream16.Read(buffer, 0, buffer.Length);
} while (bytesRead != 0);

次に: (関数は float[] のみを受け入れるため、waveBuffer は単純に byte[] を float[] に変換するために存在するクラスです)

public int Read(byte[] buffer, int offset, int bytesRead)
{
  int frames = bytesRead / sizeof(float);
  float pitch = DetectPitch(waveBuffer.FloatBuffer, frames);
}

そして最後に: (Smbpitchfft は FFT アルゴを持つクラスです...何も問題はないと信じているので、ここには投稿しません)

private float DetectPitch(float[] buffer, int inFrames)
{
  Func<int, int, float> window = HammingWindow;
  if (prevBuffer == null)
  {
    prevBuffer = new float[inFrames]; //only contains zeroes
  }  

  // double frames since we are combining present and previous buffers
  int frames = inFrames * 2;
  if (fftBuffer == null)
  {
    fftBuffer = new float[frames * 2]; // times 2 because it is complex input
  }

  for (int n = 0; n < frames; n++)
  {
     if (n < inFrames)
     {
       fftBuffer[n * 2] = prevBuffer[n] * window(n, frames);
       fftBuffer[n * 2 + 1] = 0; // need to clear out as fft modifies buffer
     }
     else
     {
       fftBuffer[n * 2] = buffer[n - inFrames] * window(n, frames);
       fftBuffer[n * 2 + 1] = 0; // need to clear out as fft modifies buffer
     }
   }
   SmbPitchShift.smbFft(fftBuffer, frames, -1);
  }

そして結果を解釈するために:

float binSize = sampleRate / frames;
int minBin = (int)(82.407 / binSize); //lowest E string on the guitar
int maxBin = (int)(1244.508 / binSize); //highest E string on the guitar

float maxIntensity = 0f;
int maxBinIndex = 0;

for (int bin = minBin; bin <= maxBin; bin++)
{
    float real = fftBuffer[bin * 2];
    float imaginary = fftBuffer[bin * 2 + 1];
    float intensity = real * real + imaginary * imaginary;
    if (intensity > maxIntensity)
    {
        maxIntensity = intensity;
        maxBinIndex = bin;
    }
}

return binSize * maxBinIndex;

更新(まだ興味がある場合):

したがって、以下の回答の 1 つは、FFT からの周波数ピークが常にピッチと等しいとは限らないと述べています。という事は承知しています。でもそれなら自分でやってみたかった(周波数のピークが結果のピッチになる場合もあるという前提で)。したがって、基本的に、オーディオ信号の周波数領域を表示できる 2 つのソフトウェア (DewResearch による SpectraPLUS および FFTProperties ; それらのクレジット) を入手しました。

時間領域での周波数ピークの結果は次のとおりです。

スペクトラプラス

スペクトラプラス

および FFT プロパティ: ここに画像の説明を入力

これは、A2 (110Hz 前後) のテスト ノートを使用して行われました。画像を見ると、SpectraPLUS では 102 ~ 112 Hz、FFT プロパティでは 108 Hz の範囲に周波数のピークがあります。私のコードでは、104Hz を取得します (8192 ブロックと 44.1khz のサンプルレートを使用します ... 8192 を 2 倍にして複雑な入力にするため、最終的には、SpectraPLUS の 10Hz ビンサイズと比較して、ビンサイズが約 5Hz になります。 )。

ソフトウェアでは正しい結果が返されるように見えますが、私のコードでは常に104Hzになるため、今は少し混乱しています(使用したFFT関数をMath.Netなどの他の関数と比較したことに注意してください。正しい)。

問題は私のデータの解釈にあると思いますか? または、ソフトウェアは周波数スペクトルを表示する前に何か他のことをしますか? ありがとう!

4

4 に答える 4

11

FFT 出力の解釈に問題があるようです。いくつかのランダムなポイント:

  • FFT の解像度は有限です。各出力ビンの解像度は ですFs / N。ここFsで、 はサンプル レート、Nは FFT のサイズです。

  • 音階の低い音符の場合、連続する音符間の周波数の差は比較的小さいため、半音離れた音符を区別するには十分に大きな N が必要になります (以下の注記 1 を参照)。

  • 最初のビン (インデックス 0) には、0 Hz を中心としたエネルギーが含まれていますが、+/- Fs / 2N

  • ビンiには を中心とするエネルギーが含まれますが、この中心周波数の両側i * Fs / Nからのエネルギーが含まれます+/- Fs / 2N

  • 隣接するビンからスペクトル漏れが発生します-これがどれほど悪いかは、使用するウィンドウ関数によって異なります-ウィンドウなし(==長方形ウィンドウ)およびスペクトル漏れは非常に悪い(非常に広いピーク)-周波数推定の場合、選択したい鋭いピークを与える窓関数

  • ピッチは周波数と同じではありません - ピッチは知覚であり、周波数は物理量です - 楽器の知覚されるピッチは、楽器の種類によっては基本周波数とわずかに異なる場合があります (一部の楽器は有意な音を生成しません)。エネルギーは基本周波数にありますが、基本周波数が存在するかのようにピッチを知覚します)

ただし、入手可能な限られた情報からの私の最善の推測は、おそらくビンインデックスから周波数への変換のどこかで「1つずれている」か、またはFFTが小さすぎて低音に十分な解像度が得られない可能性があるということです。 Nを増やす。

また、ケプストラム分析などのいくつかの手法によって、または FFT 出力の位相成分を調べて連続する FFT と比較することによって、ピッチ推定を改善することもできます (これにより、特定の FFT サイズのビン内でより正確な周波数推定が可能になります)。 )。


ノート

(1) これにいくつかの数値を付けると、E2 は 82.4 Hz、F2 は 87.3 Hz です。したがって、ギターの最低 2 つの音を区別するには、5 Hz よりもいくらか優れた解像度が必要です (実際にたとえば、正確なチューニングを行いたい)。44.1 kHz のサンプルでは、​​十分な解像度 (44100 / 8192 = 5.4 Hz) を得るには、おそらく少なくとも N = 8192 の FFT が必要です。おそらく N = 16384 の方が良いでしょう。

于 2011-02-11T08:51:12.923 に答える
1

同様の質問がありましたが、私にとっての答えは、FFT の代わりにGoertzelを使用することでした。探しているトーンがわかっている場合 (MIDI)、Goertzel は 1 正弦波 (1 サイクル) 内でトーンを検出できます。これは、音の正弦波を生成し、「生データの上に配置」して存在するかどうかを確認することによって行われます。FFT は、大量のデータをサンプリングして、近似周波数スペクトルを提供します。

于 2011-02-11T12:48:38.160 に答える
1

音程は周波数ピークとは異なります。ピッチは、倍音などに依存する可能性のある心理的知覚現象です。人間がピッチと呼ぶものの周波数は、実際の信号スペクトルでは欠落しているか、非常に小さい可能性があります。

また、スペクトルの周波数ピークは、どの FFT ビンの中心とも異なる場合があります。FFT ビンの中心周波数は、データ内のスペクトルではなく、FFT の長さとサンプル レートのみに応じて周波数と間隔が変化します。

したがって、競合する問題が少なくとも 2 つあります。ピッチ推定の別の主題と同様に、周波数推定に関する学術論文はたくさんあります。そこから始めましょう。

于 2011-02-11T20:03:57.833 に答える