我正试图从iPhone/iPod音乐库获得频率,用于iPod库上的频谱应用程序,帮助自己通过avassetreader读取音频样本,然后使用-the- Apple -fft-and- acceler- framework和Apple vDSP样本,但不知何故我在某个地方错了,无法计算频率。
所以一步一步:
- 读取音频样本
- 汉宁窗口
这是从iPod mp3库获得频率的正确方法吗?
下面是我的代码:static COMPLEX_SPLIT A;
static FFTSetup setupReal;
static uint32_t log2n, n, nOver2;
static int32_t stride;
static float *obtainedReal;
static float scale;
+ (void)initialize
{
log2n = 10;
n = 1 << log2n;
stride = 1;
nOver2 = n / 2;
A.realp = (float *) malloc(nOver2 * sizeof(float));
A.imagp = (float *) malloc(nOver2 * sizeof(float));
obtainedReal = (float *) malloc(n * sizeof(float));
setupReal = vDSP_create_fftsetup(log2n, FFT_RADIX2);
}
- (float) performAcceleratedFastFourierTransForAudioBuffer:(AudioBufferList)ioData
{
NSUInteger * sampleIn = (NSUInteger *)ioData.mBuffers[0].mData;
for (int i = 0; i < nOver2; i++) {
double multiplier = 0.5 * (1 - cos(2*M_PI*i/nOver2-1));
A.realp[i] = multiplier * sampleIn[i];
A.imagp[i] = 0;
}
memset(ioData.mBuffers[0].mData, 0, ioData.mBuffers[0].mDataByteSize);
vDSP_fft_zrip(setupReal, &A, stride, log2n, FFT_FORWARD);
vDSP_zvmags(&A, 1, A.realp, 1, nOver2);
scale = (float) 1.0 / (2 * n);
vDSP_vsmul(A.realp, 1, &scale, A.realp, 1, nOver2);
vDSP_vsmul(A.imagp, 1, &scale, A.imagp, 1, nOver2);
vDSP_ztoc(&A, 1, (COMPLEX *)obtainedReal, 2, nOver2);
int peakIndex = 0;
for (size_t i=1; i < nOver2-1; ++i) {
if ((obtainedReal[i] > obtainedReal[i-1]) && (obtainedReal[i] > obtainedReal[i+1]))
{
peakIndex = i;
break;
}
}
//here I don't know how to calculate frequency with my data
float frequency = obtainedReal[peakIndex-1] / 44100 / n;
vDSP_destroy_fftsetup(setupReal);
free(obtainedReal);
free(A.realp);
free(A.imagp);
return frequency;
}
我得到1.485757
和1.332233
作为我的第一个频率
在我看来,FFT转换为复杂输入时存在问题。vDSP_ctoz()
分割一个缓冲区,其中实和虚分量交织成两个缓冲区,一个实和一个虚。您对该函数的输入似乎只是转换为COMPLEX
的真实数据。这意味着到vDSP_ctoz()
的输入缓冲区只有所需长度的一半,并且一些超出缓冲区大小的垃圾数据正在被转换。
您需要将sampleOut
创建为2*n
的长度并设置所有其他值(实部),或者更好的是,您可以绕过vDSP_ctoz()
并直接将输入数据复制到A.realp
并将A.imagp
设置为零。vDSP_ctoz()
应该只在与产生交错复杂数据的源接口时才需要。
编辑
好吧,我认为我的第一个建议是错误的,因为vDSP文档说,真实到复杂的原位fft的实际输入应该格式化为分裂的复杂格式,这样imagp
包含偶数样本,realp
包含奇数样本。我实际上没有使用vDSP库,但我熟悉很多其他FFT库,我错过了那个细节。
在调用vDSP_zvmags(&A, 1, A.realp, 1, nOver2);
之后,您应该能够使用A.realp
找到峰值。此时,A.realp
应该包含FFT输出的幅度平方,这是标量。如果要进行缩放,则应该在mag2操作之前进行,但如果只是寻找峰值,则可能不需要进行缩放。
要得到FFT输出表示的真实频率,使用这个公式:
F = (i * Fs) / N, i=0,1,...,N/2
,
i
是FFT输出缓冲区的索引Fs
为音频采样率N
为FFT长度
所以你的计算结果可能是这样的:
float frequency = (peakIndex * 44100) / n;
请记住,vDSP只返回实际输入频谱的前半部分,因为后半部分是冗余的。因此FFT输出表示从0
到Fs/2
的频率。
另一个注意事项是,我不知道你的峰值查找算法是否会工作得很好,因为FFT输出不会平滑,并且经常会有很多振荡。你只是取第一个样本其中两个相邻的样本值更低。如果你只想找到一个峰值,最好是找到整个输出的最大值。如果你想找到多个峰值,你将不得不做一些更复杂的事情。