iOS加速框架中vDSP_ctoz的数据应该是什么格式
我正试图为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: 我从渲染回调接收的格式: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
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中不完全是整数周期的正弦曲线上的其他窗口