iOS Accelerate框架中vDSP_ctoz的数据应采用何种格式

时间:2014-03-23 00:06:21

标签: ios fft accelerate-framework

我正在尝试为iOS显示频谱分析仪,两周后卡住了。我已经阅读了很多关于FFT和加速框架的帖子,并从Apple下载了aurioTouch2示例。

我想我理解FFT的机制(在20年前的Uni中做过)并且是一位经验丰富的iOS程序员,但我已经碰壁了。

我正在使用AudioUnit播放mp3,m4a和wav文件并使其工作得非常漂亮。我已经向AUGraph添加了一个Render Callback,我可以将Waveforms绘制成音乐。波形与音乐很吻合。

Waveform image

当我从渲染回调中获取数据时,该数据为浮点数形式,范围为0 .. 1,并尝试通过FFT代码(我自己的或aurioTouch2的FFTBufferManager.mm)传递数据,我得到的东西并非完全错误,但也不正确。或者这是一个440Hz的正弦波:

40Khz sine wave

该峰值为-6.1306,然后是-24。 -31。, - 35。那些到最后的价值大约是-63。

“黑贝蒂”动画gif:

Animated gif for "Black Betty

我从Render回调中收到的格式:

AudioStreamBasicDescription outputFileFormat;
outputFileFormat.mSampleRate = 44100;
outputFileFormat.mFormatID = kAudioFormatLinearPCM;
outputFileFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved;
outputFileFormat.mBitsPerChannel = 32;
outputFileFormat.mChannelsPerFrame = 2;
outputFileFormat.mFramesPerPacket = 1;
outputFileFormat.mBytesPerFrame = outputFileFormat.mBitsPerChannel / 8;
outputFileFormat.mBytesPerPacket = outputFileFormat.mBytesPerFrame;

在查看aurioTouch2示例时,看起来他们正在以signed int格式接收数据,但之后运行AudioConverter将其转换为Float。他们的格式难以破译,但使用的是宏:

    drawFormat.SetAUCanonical(2, false);
    drawFormat.mSampleRate = 44100;

    XThrowIfError(AudioConverterNew(&thruFormat, &drawFormat, &audioConverter), "couldn't setup AudioConverter");

在他们的渲染回调中,他们将数据从AudioBufferList复制到mAudioBuffer(Float32 *)并将其传递给调用vDSP_ctoz的CalculateFFT方法

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

我认为这就是我的问题所在。 vDSP_ctoz期望什么格式?它被转换为(COMPLEX *),但我无法在aurioTouch2代码中找到任何将mAudioBuffer数据放入(COMPLEX *)格式的代码。所以必须以这种格式来自Render Callback吗?

typedef struct DSPComplex {
    float  real;
    float  imag;
} DSPComplex;
typedef DSPComplex                      COMPLEX;

如果此时我没有正确的格式(或理解格式),那么调试剩下的格式是没有意义的。

非常感谢任何帮助。

我正在使用的AurioTouch2代码:

Boolean FFTBufferManager::ComputeFFTFloat(Float32 *outFFTData)
{
if (HasNewAudioData())
{
    // Added after Hotpaw2 comment.
    UInt32 windowSize = mFFTLength;
    Float32 *window = (float *) malloc(windowSize * sizeof(float));

    memset(window, 0, windowSize * sizeof(float));

    vDSP_hann_window(window, windowSize, 0);

    vDSP_vmul( mAudioBuffer, 1, window, 1, mAudioBuffer, 1, mFFTLength);

    // Added after Hotpaw2 comment.
    DSPComplex *audioBufferComplex = new DSPComplex[mFFTLength];

    for (int i=0; i < mFFTLength; i++)
    {
        audioBufferComplex[i].real = mAudioBuffer[i];
        audioBufferComplex[i].imag = 0.0f;
    }

    //Generate a split complex vector from the real data
    vDSP_ctoz((COMPLEX *)audioBufferComplex, 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;

    //Convert the fft data to dB
    vDSP_zvmags(&mDspSplitComplex, 1, outFFTData, 1, mFFTLength);

    //In order to avoid taking log10 of zero, an adjusting factor is added in to make the minimum value equal -128dB
    vDSP_vsadd( outFFTData, 1, &mAdjust0DB, outFFTData, 1, mFFTLength);
    Float32 one = 1;
    vDSP_vdbcon(outFFTData, 1, &one, outFFTData, 1, mFFTLength, 0);

    free( audioBufferComplex);
    free( window);

    OSAtomicDecrement32Barrier(&mHasAudioData);
    OSAtomicIncrement32Barrier(&mNeedsAudioData);
    mAudioBufferCurrentIndex = 0;
    return true;
}
else if (mNeedsAudioData == 0)
    OSAtomicIncrement32Barrier(&mNeedsAudioData);

return false;
}

在阅读下面的答案后,我尝试将其添加到方法的顶部:

    DSPComplex *audioBufferComplex = new DSPComplex[mFFTLength];

    for (int i=0; i < mFFTLength; i++)
    {
        audioBufferComplex[i].real = mAudioBuffer[i];
        audioBufferComplex[i].imag = 0.0f;
    }

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

我得到的结果是:

After Adding above code

我现在渲染最后5个结果,它们是落后的褪色。

添加hann窗口后:

enter image description here

现在在应用hann窗口后看起来好多了(谢谢hotpaw2)。不担心镜像。

我现在的主要问题是使用真实的歌曲,它看起来不像其他频谱分析仪。无论我推动什么音乐,一切都总是在左边高高举起。在应用窗口之后,它看起来似乎更好了。

Black Betty

1 个答案:

答案 0 :(得分:3)

AU渲染回调仅返回所需复杂输入的实部。要使用复杂的FFT,您需要自己用零填充相同数量的虚部,并在需要时复制实部的元素。