web-dev-qa-db-ja.com

フレーム間の位相変化を使用してFFTビンから正確な周波数を抽出する

私はこの素晴らしい記事を調べてきました: http://blogs.zynaptiq.com/bernsee/pitch-shifting-using-the-ft/

素晴らしいですが、それは非常に困難で重いものです。この素材は本当に私を伸ばしています。

特定のビンの正確な頻度を計算するStefanのコードモジュールから数学を抽出しました。しかし、私は最後の計算を理解していません。最後に誰かが私に数学的構造を説明できますか?

コードを掘り下げる前に、シーンを設定しましょう。

  • FftFrameSize = 1024に設定したとすると、512 +1のビンを処理します。

  • 例として、Bin [1]の理想的な周波数はフレーム内の単一の波に適合します。 40KHzのサンプルレートでは、tOneFrame = 1024/40K秒= 1/40sであるため、Bin [1]は理想的には40Hzの信号を収集します。

  • Osamp(overSample)= 4に設定すると、入力信号に沿って256のステップで進行します。したがって、最初の分析では、バイト0から1023、次に256から1279などを調べます。各フロートが4回処理されることに注意してください。

.。

void calcBins( 
              long fftFrameSize, 
              long osamp, 
              float sampleRate, 
              float * floats, 
              BIN * bins
              )
{
    /* initialize our static arrays */
    static float gFFTworksp[2*MAX_FRAME_LENGTH];
    static float gLastPhase[MAX_FRAME_LENGTH/2+1];

    static long gInit = 0;
    if (! gInit) 
    {
        memset(gFFTworksp, 0, 2*MAX_FRAME_LENGTH*sizeof(float));
        memset(gLastPhase, 0, (MAX_FRAME_LENGTH/2+1)*sizeof(float));
        gInit = 1;
    }

    /* do windowing and re,im interleave */
    for (long k = 0; k < fftFrameSize; k++) 
    {
        double window = -.5*cos(2.*M_PI*(double)k/(double)fftFrameSize)+.5;
        gFFTworksp[2*k] = floats[k] * window;
        printf("sinValue: %f", gFFTworksp[2*k]);
        gFFTworksp[2*k+1] = 0.;
    }

    /* do transform */
    smbFft(gFFTworksp, fftFrameSize, -1);

    printf("\n");

    /* this is the analysis step */
    for (long k = 0; k <= fftFrameSize/2; k++) 
    {
        /* de-interlace FFT buffer */
        double real = gFFTworksp[2*k];
        double imag = gFFTworksp[2*k+1];

        /* compute magnitude and phase */
        double magn = 2.*sqrt(real*real + imag*imag);
        double phase = atan2(imag,real);

        /* compute phase difference */
        double phaseDiff = phase - gLastPhase[k];
        gLastPhase[k] = phase;

        /* subtract expected phase difference */
        double binPhaseOffset = M_TWOPI * (double)k / (double)osamp;
        double deltaPhase = phaseDiff - binPhaseOffset;

        /* map delta phase into [-Pi, Pi) interval */
        // better, but obfuscatory...
        //    deltaPhase -= M_TWOPI * floor(deltaPhase / M_TWOPI + .5);

        while (deltaPhase >= M_PI)
            deltaPhase -= M_TWOPI;
        while (deltaPhase < -M_PI)
            deltaPhase += M_TWOPI;

(編集:)今私が得られないビット:

        // Get deviation from bin frequency from the +/- Pi interval 
        // Compute the k-th partials' true frequency    

        // Start with bin's ideal frequency
        double bin0Freq = (double)sampleRate / (double)fftFrameSize;
        bins[k].idealFreq = (double)k * bin0Freq;

        // Add deltaFreq
        double sampleTime = 1. / (double)sampleRate;
        double samplesInStep = (double)fftFrameSize / (double)osamp;
        double stepTime = sampleTime * samplesInStep;
        double deltaTime = stepTime;        

        // Definition of frequency is rate of change of phase, i.e. f = dϕ/dt
        // double deltaPhaseUnit = deltaPhase / M_TWOPI; // range [-.5, .5)
        double freqAdjust = (1. / M_TWOPI) * deltaPhase / deltaTime; 

        // Actual freq <-- WHY ???
        bins[k].freq = bins[k].idealFreq + freqAdjust;
    }
}

顔を見つめているように見えますが、はっきり見えません。誰かがこのプロセスを最初から段階的に説明してもらえますか?

25
P i

最後に、私はこれを理解しました。本当に私はそれを最初から導き出さなければなりませんでした。私はそれを導き出す簡単な方法があることを知っていました。私の(通常の)間違いは、私自身の常識を使うのではなく、他の人の論理に従おうとすることでした。

このパズルのロックを解除するには、2つのキーが必要です。

.。

for (int k = 0; k <= fftFrameSize/2; k++) 
{
    // compute magnitude and phase 
    bins[k].mag = 2.*sqrt(fftBins[k].real*fftBins[k].real + fftBins[k].imag*fftBins[k].imag);
    bins[k].phase = atan2(fftBins[k].imag, fftBins[k].real);

    // Compute phase difference Δϕ fo bin[k]
    double deltaPhase;
    {
        double measuredPhaseDiff = bins[k].phase - gLastPhase[k];
        gLastPhase[k] = bins[k].phase;

        // Subtract expected phase difference <-- FIRST KEY
        // Think of a single wave in a 1024 float frame, with osamp = 4
        //   if the first sample catches it at phase = 0, the next will 
        //   catch it at pi/2 ie 1/4 * 2pi
        double binPhaseExpectedDiscrepancy = M_TWOPI * (double)k / (double)osamp;
        deltaPhase = measuredPhaseDiff - binPhaseExpectedDiscrepancy;

        // Wrap delta phase into [-Pi, Pi) interval 
        deltaPhase -= M_TWOPI * floor(deltaPhase / M_TWOPI + .5);
    }

    // say sampleRate = 40K samps/sec, fftFrameSize = 1024 samps in FFT giving bin[0] thru bin[512]
    // then bin[1] holds one whole wave in the frame, ie 44 waves in 1s ie 44Hz ie sampleRate / fftFrameSize
    double bin0Freq = (double)sampleRate / (double)fftFrameSize;
    bins[k].idealFreq = (double)k * bin0Freq;

    // Consider Δϕ for bin[k] between Hops.
    // write as 2π / m.
    // so after m Hops, Δϕ = 2π, ie 1 extra cycle has occurred   <-- SECOND KEY
    double m = M_TWOPI / deltaPhase;

    // so, m Hops should have bin[k].idealFreq * t_mHops cycles.  plus this extra 1.
    // 
    // bin[k].idealFreq * t_mHops + 1 cycles in t_mHops seconds 
    //   => bins[k].actualFreq = bin[k].idealFreq + 1 / t_mHops
    double tFrame = fftFrameSize / sampleRate;
    double tHop = tFrame / osamp;
    double t_mHops = m * tHop;

    bins[k].freq = bins[k].idealFreq + 1. / t_mHops;
}
6
P i

基本的な原理は非常に単純です。特定のコンポーネントがビン周波数と完全に一致する場合、その位相は1つのFTから次のFTに変化しません。ただし、周波数がビン周波数と正確に対応していない場合は、連続するFT間で位相が変化します。周波数デルタは次のとおりです。

delta_freq = delta_phase / delta_time

そして、コンポーネントの周波数の洗練された見積もりは次のようになります。

freq_est = bin_freq + delta_freq
12
Paul R

私はこのアルゴリズムを Performous 自分自身に実装しました。時間オフセットで別のFFTを取得する場合、オフセットに応じて位相が変化すると予想されます。つまり、256サンプル離れて取得された2つのFFTは、信号に存在するすべての周波数で256サンプルの位相差を持つ必要があります(これは、信号自体を想定しています)安定しているため、256サンプルなどの短期間の場合に適しています。

現在、FFTから取得する実際の位相値はサンプルではなく位相角であるため、周波数によって異なります。次のコードでは、phaseStep値はビンごとに必要な変換係数です。つまり、ビンxに対応する周波数の場合、位相シフトはx * phaseStepになります。ビンの中心周波数の場合、xは整数(ビン番号)になりますが、実際に検出された周波数の場合、任意の実数になります。

const double freqPerBin = SAMPLE_RATE / FFT_N;
const double phaseStep = 2.0 * M_PI * FFT_STEP / FFT_N;

補正は、ビン内の信号がビンの中心周波数を持っていると仮定し、そのために予想される位相シフトを計算することによって機能します。この予想されるシフトは実際のシフトから差し引かれ、エラーが残ります。余り(モジュロ2 pi)が取られ(-piからpiの範囲)、最終周波数はビンの中心+補正で計算されます。

// process phase difference
double delta = phase - m_fftLastPhase[k];
m_fftLastPhase[k] = phase;
delta -= k * phaseStep;  // subtract expected phase difference
delta = remainder(delta, 2.0 * M_PI);  // map delta phase into +/- M_PI interval
delta /= phaseStep;  // calculate diff from bin center frequency
double freq = (k + delta) * freqPerBin;  // calculate the true frequency

デルタ補正はどちらの方法でも最大0.5 * FFT_N/FFT_STEPビンになる可能性があるため、隣接する多くのビンが同じ周波数に補正されることが多いことに注意してください。使用するFFT_STEPが小さいほど、補正が可能になります(ただし、これにより処理能力が向上します)不正確さによる不正確さだけでなく、必要です)。

これがお役に立てば幸いです:)

11
Tronic

これは、フェーズボコーダー法で使用される周波数推定手法です。

(固定周波数と固定振幅の)正弦波上の単一の点を時間で見ると、位相は周波数に比例した量だけ時間とともに進みます。または、その逆を行うこともできます。正弦波の位相が任意の単位時間でどの程度変化するかを測定すると、その正弦波の周波数を計算できます。

フェーズボコーダは、2つのFFTを使用して、2つのFFTウィンドウを参照して位相を推定します。2つのFFTのオフセットは、時間内の2つの位相測定値間の距離です。そこから、そのFFTビンの周波数推定値が得られます(FFTビンは、そのビン内に収まる正弦波成分またはその他の十分に狭帯域の信号を分離するための大まかなフィルターです)。

この方法が機能するためには、使用中のFFTビンの近くのスペクトルがかなり静止している必要があります。周波数などが変化しない。これは、フェーズボコーダーが必要とする仮定です。

7
hotpaw2

多分これは役立つでしょう。 FFTビンは、それぞれがビンの周波数で回転する小さな時計またはローターを指定するものと考えてください。安定した信号の場合、ローターの(理論上の)次の位置は、得られないビットの計算を使用して予測できます。この「あるべき」(理想的な)位置に対して、いくつかの有用なものを計算できます。(1)隣接するフレームのビン内の位相との差。これはフェーズボコーダーによって使用されます。ビン周波数の推定値、または(2)より一般的には位相偏差。これは、音の開始またはオーディオ内の他のイベントの肯定的な指標です。

2
Bill

ビン周波数に正確に当てはまる信号周波数は、2πの整数倍だけビン位相を進めます。 FFTの周期的な性質により、ビン周波数に対応するビン位相は2πの倍数であるため、この場合、位相変化はありません。あなたが言及する記事もこれを説明しています。

1
tahome