Как получить iOS Accelerate FFT результаты в частотных величинах?

Я использую этот код (на основе образца Apple AudioRouch):

void FFTHelper::ComputeFFT(Float32* inAudioData, Float32* outFFTData)
{
    if (inAudioData == NULL || outFFTData == NULL) return;

    // Generate a split complex vector from the real data
    vDSP_ctoz((COMPLEX *)inAudioData, 2, &mDspSplitComplex, 1, mFFTLength);

    // Take the fft and scale appropriately
    vDSP_fft_zrip(mSpectrumAnalysis, &mDspSplitComplex, 1, mLog2N, kFFTDirection_Forward);
    vDSP_vsmul(mDspSplitComplex.realp, 1, &mFFTNormFactor, mDspSplitComplex.realp, 1, mFFTLength);
    vDSP_vsmul(mDspSplitComplex.imagp, 1, &mFFTNormFactor, mDspSplitComplex.imagp, 1, mFFTLength);

    // Zero out the nyquist value
    mDspSplitComplex.imagp[0] = 0.0;

    // Complex vector magnitudes squared; single precision.
    // Calculates the squared magnitudes of complex vector A.
    vDSP_zvmags(&mDspSplitComplex, 1, outFFTData, 1, mFFTLength);

}

Чтобы вычислить БПФ на простейшем из возможных - синусоидальный сигнал 1 Гц (смещенный вверх на 1 единицу):

    Float32 waveFreq            = 1.0;
    int     samplesCount        = 1024;
    Float32 samplesPerSecond    = 1000;        //sample rate
    Float32 dt = 1 / samplesPerSecond;
    Float32 sd = M_PI * 2.0 * waveFreq;

    FFTHelper *mFFTHelper = new FFTHelper(samplesCount);

    Float32 NyquistMaxFreq  = samplesPerSecond/2.0;
    Float32 fftDataSize     = samplesCount/2.0;

    Float32 *sinusoidOriginal = (Float32 *)malloc(sizeof(Float32) * samplesCount);
    Float32 *outFFTData = (Float32 *)malloc(sizeof(Float32) * fftDataSize);

    // 2. Generate sin samples:
    for (int i = 0; i < samplesCount; i++) {

        Float32 x = dt * i;
        sinusoidOriginal[i] = sin(sd * x) + 1;
        [originalPlot addVector2D:GLVector2DMake(x, sinusoidOriginal[i])];
    }

    mFFTHelper->ComputeFFT(sinusoidOriginal, outFFTData);

    for (int i = 0; i < fftDataSize; i++) {

            Float32 hz = ((Float32)i / (Float32)fftDataSize) * NyquistMaxFreq;
            GLfloat mag = outFFTData[i];
            [fftPlot addVector2D:GLVector2DMake(hz, 0)];
            [fftPlot addVector2D:GLVector2DMake(hz, mag)];

    }

Результат, который я получаю:

Черные линии - результаты плоттера от FTT, расположенные горизонтально на их частотах. Значение DC (1-я черная линия слева) выглядит хорошо, правильно представляет y = sin(x) + 1 вертикальное смещение.

Но почему 2-ая черная линия, которая представляет единственную частоту, присутствующую в уравнении синуса, не имеет величины = 1 и точно не остается на частоте 1 Гц?

Кто-нибудь может указать мне на функцию vDSP для преобразования результатов FFT в единицы измерения из входного сигнала?

1 ответ

Решение

Короткий ответ:

Вы, кажется, используете код из моего ответа из этого места: /questions/17430068/poluchit-chastotu-gts-iz-audiopotoka-na-iphone/17430091#17430091 И это здорово, но:

Вы не можете избежать этих дополнительных частот в вашем сигнале.

Генерируемый вами сигнал (синусоида) - это бесконечный сигнал. И если вы "обрезаете его" и "используете только часть сигнала", это создает "шум отсечения" на обоих концах.

Но вы можете минимизировать шум, беря большие входные порции и используя оконное преобразование перед FFT. Accelerate Framework предоставляет несколько хороших и простых оконных функций. ( Например, функция Ханна, vDSP_hann_window) Также - используйте более крупные входные блоки. Чем больше вход, тем точнее определение частоты.

Смотрите эту статью, Google: Spectral Leakage, оконная функция.

Другие вопросы по тегам