2

我正在尝试从 iPhone / iPod 音乐库中为 iPod 库上的频谱应用程序获取频率,帮助自己阅读-audio-samples-via-avassetreader以获取音频样本,然后使用-the-apple-fft-and-加速框架Apple vDSP Samples,但不知何故我在某处错了,无法计算频率。

所以一步一步:

  • 阅读音频样本
  • 汉宁窗
  • 计算 fft

这是从 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.4857571.332233作为我的第一个频率

4

1 回答 1

3

在我看来,在转换为 FFT 的复杂输入时存在问题。 vDSP_ctoz()拆分一个缓冲区,其中实部和虚部交错成两个缓冲区,一个实部,一个虚部。您对该函数的输入似乎只是已转换为的真实数据COMPLEX。这意味着您的输入缓冲区vDSP_ctoz()只有所需长度的一半,并且超出缓冲区大小的一些垃圾数据正在被转换。

您要么需要创建sampleOut长度2*n并设置所有其他值(实际部分),要么更好的是,您可以绕过vDSP_ctoz()并直接将输入数据复制到A.realp并设置A.imagp为零。 vDSP_ctoz()只有在连接到产生交错复杂数据的源时才需要。

编辑

好的,我认为我的第一个建议是错误的,因为 vDSP 文档说实到复就地 fft 的实际输入应格式化为imagp包含偶数样本和realp奇数样本的拆分复数格式。我实际上并没有使用过 vDSP 库,但我熟悉许多其他 FFT 库,但我错过了这些细节。

您应该能够A.realp在调用vDSP_zvmags(&A, 1, A.realp, 1, nOver2); At that point 之后找到峰值,它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 输出不会平滑并且经常会有很多振荡。您只是在取两个相邻样本较低的第一个样本。如果您只想找到一个峰值,最好只找到整个输出的最大值。如果你想找到多个峰值,你将不得不做一些更复杂的事情。

于 2011-05-03T14:19:05.990 回答