我正在尝试为iOS显示频谱分析仪,两周后卡住了。我已经阅读了很多关于FFT和加速框架的帖子,并从Apple下载了aurioTouch2示例。
我想我理解FFT的机制(在20年前的Uni中做过)并且是一位经验丰富的iOS程序员,但我已经碰壁了。
我正在使用AudioUnit播放mp3,m4a和wav文件并使其工作得非常漂亮。我已经向AUGraph添加了一个Render Callback,我可以将Waveforms绘制成音乐。波形与音乐很吻合。
当我从渲染回调中获取数据时,该数据为浮点数形式,范围为0 .. 1,并尝试通过FFT代码(我自己的或aurioTouch2的FFTBufferManager.mm)传递数据,我得到的东西并非完全错误,但也不正确。或者这是一个440Hz的正弦波:
该峰值为-6.1306,然后是-24。 -31。, - 35。那些到最后的价值大约是-63。
“黑贝蒂”动画gif:
我从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);
我得到的结果是:
我现在渲染最后5个结果,它们是落后的褪色。
添加hann窗口后:
现在在应用hann窗口后看起来好多了(谢谢hotpaw2)。不担心镜像。
我现在的主要问题是使用真实的歌曲,它看起来不像其他频谱分析仪。无论我推动什么音乐,一切都总是在左边高高举起。在应用窗口之后,它看起来似乎更好了。
答案 0 :(得分:3)
AU渲染回调仅返回所需复杂输入的实部。要使用复杂的FFT,您需要自己用零填充相同数量的虚部,并在需要时复制实部的元素。