iOS加速框架中vDSP_ctoz的数据应该是什么格式

iOS加速框架中vDSP_ctoz的数据应该是什么格式,ios,fft,accelerate-framework,Ios,Fft,Accelerate Framework,我正试图为iOS显示一个频谱分析仪,两周后我被卡住了。我在这里读了几乎所有关于FFT和加速框架的文章,并从苹果下载了aurioTouch2示例 我想我了解FFT的机制(20年前在Uni做过),并且是一名经验丰富的iOS程序员,但我遇到了麻烦 我使用AudioUnit来播放mp3、m4a和wav文件,并使其运行良好。我已经附加了一个渲染回调到AUGraph,我可以根据音乐绘制波形。波形与音乐配合得很好 当我从范围为0的浮点形式的渲染回调中获取数据时。。1并尝试通过FFT代码(我自己的或aurio

我正试图为iOS显示一个频谱分析仪,两周后我被卡住了。我在这里读了几乎所有关于FFT和加速框架的文章,并从苹果下载了aurioTouch2示例

我想我了解FFT的机制(20年前在Uni做过),并且是一名经验丰富的iOS程序员,但我遇到了麻烦

我使用AudioUnit来播放mp3、m4a和wav文件,并使其运行良好。我已经附加了一个渲染回调到AUGraph,我可以根据音乐绘制波形。波形与音乐配合得很好

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

峰值为-6.1306,然后是-24-31., -35. 最后的数值大约是-63

“黑贝蒂”的动画gif:

我从渲染回调接收的格式:

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示例时,看起来他们正在以有符号int格式接收数据,但随后运行音频转换器将其转换为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希望采用什么格式?它被转换为(复杂*),但我在aurioTouch2代码中找不到任何地方,该代码将mAudioBuffer数据转换为(复杂*)格式。那么,是否必须以这种格式来自渲染回调

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;
}
Boolean-FFTBufferManager::computeftfloat(Float32*outftdata)
{
if(HasNewAudioData())
{
//在Hotpaw2评论之后添加。
UInt32 windowSize=mFFTLength;
Float32*window=(float*)malloc(windowSize*sizeof(float));
memset(window,0,windowSize*sizeof(float));
vDSP_hann_窗口(窗口,窗口大小,0);
vDSP_vmul(mAudioBuffer,1,窗口,1,mAudioBuffer,1,mFFTLength);
//在Hotpaw2评论之后添加。
DSPComplex*audioBufferComplex=新的DSPComplex[mFFTLength];
对于(int i=0;i
在阅读了下面的答案后,我尝试将其添加到方法的顶部:

    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);
DSPComplex*audioBufferComplex=新的DSPComplex[mFFTLength];
对于(int i=0;i
我得到的结果是:

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

添加hann窗口后:

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

我现在的主要问题是使用一首真正的歌曲,它看起来不像其他频谱分析仪。无论我演奏什么音乐,所有的东西都会被推到左边的高处。在应用窗口后,它似乎更符合节拍


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

这是有道理的,我在读了这篇文章后尝试过,结果仍然不正确。我在440Hz的声音文件中找到了镜像。因此,左边只剩下一个峰(如上图所示),左边有一个峰,右边有一个峰,中间有一个碗形。我编辑了我的答案,以显示我从AurioTouch2复制的代码。碗状结果对于严格实数输入的复杂FFT的全长结果是正确的,几乎没有高频内容。它应该是共轭对称的。因此,通常只绘制前半部分的两倍。幅度响应中的肠道形状也可能是矩形窗口的结果,而不是Von Hann或FFT的le中不完全是整数周期的正弦曲线上的其他窗口