iOS Accelerate框架中vDSP_ctoz的数据应该是啥格式
Posted
技术标签:
【中文标题】iOS Accelerate框架中vDSP_ctoz的数据应该是啥格式【英文标题】:What format should the data be for vDSP_ctoz in iOS Accelerate frameworkiOS Accelerate框架中vDSP_ctoz的数据应该是什么格式 【发布时间】:2014-03-23 00:06:20 【问题描述】:我正在尝试显示适用于 ios 的频谱分析仪,但两周后卡住了。我在这里阅读了几乎所有关于 FFT 和 Accelerate Frameworks 的文章,并从 Apple 下载了 aurioTouch2 示例。
我想我了解 FFT 的机制(20 年前在 Uni 做过),并且是一个相当有经验的 iOS 程序员,但我碰壁了。
我正在使用 AudioUnit 播放 mp3、m4a 和 wav 文件,并且效果很好。我已将渲染回调附加到 AUGraph,我可以将波形绘制到音乐中。波形与音乐配合得很好。
当我从范围为 0 .. 1 的浮点形式的渲染回调中获取数据并尝试通过 FFT 代码(我自己的或 aurioTouch2 的 FFTBufferManager.mm)传递它时,我得到了一些不完全错误的东西,但也不正确。或者例如这是一个 440Hz 正弦波:
那个峰值是-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 示例时,看起来他们正在接收签名 int 格式的数据,但随后运行 AudioConverter 将其转换为浮点数。它们的格式很难破译,但使用的是宏:
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*) 格式的任何地方。那么一定是来自这种格式的渲染回调吗?
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)。不担心镜像。
我现在的主要问题是使用与其他频谱分析器不同的真实歌曲。无论我通过什么音乐,所有东西总是被推到左边。应用窗口后,它似乎更好地适应了节拍。
【问题讨论】:
memset
之前调用vDSP_hann_window
是不需要的。
【参考方案1】:
AU 渲染回调仅返回所需的复杂输入的实部。要使用复数 FFT,您需要自己用零填充相同数量的虚部,并在需要时复制实部的元素。
【讨论】:
说的有道理,我看了之后试了一下,结果还是不对。我得到了 440Hz 声音文件的镜像。所以不是左边只有一个峰(如上图所示),我在左边有一个峰,在右边有一个峰,中间有一个碗形。我已经编辑了答案以显示我从 AurioTouch2 复制的代码。 碗形结果对于具有少量高频内容的严格实输入的复杂 FFT 的全长结果是正确的。它应该是共轭对称的。所以通常只绘制前半部分的两倍。 幅度响应中的肠形也可能是矩形窗口而不是 Von Hann 或正弦曲线上的其他窗口的结果,而不是 FFT 长度中的整数周期。 哦,我忘了给它添加一个窗口。奇怪的是它没有在 AurioTouch2 中完成。它看起来好多了。添加了新的截图。你是一个很大的帮助 hotpaw2。以上是关于iOS Accelerate框架中vDSP_ctoz的数据应该是啥格式的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 iOS Accelerate 框架正确填充 FFT 的二维数组
iOS Accelerate Framework vImage - 性能改进?