从 AvassetReader 和 vDSP_FFT 获取 iPhone mp3 频率

发布于 2024-11-04 05:50:56 字数 2696 浏览 6 评论 0原文

我正在尝试从 iPhone / iPod 音乐库获取 iPod 库上频谱应用程序的频率,帮助自己 reading-audio-samples-via-avassetreader 获取音频样本,然后使用 使用-apple-fft-and-accelerate-frameworkApple vDSP 示例,但不知怎的,我在某个地方出错了,无法计算频率。

所以一步一步:

  • 读取音频样本
  • 汉宁窗
  • 计算 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 作为我的第一个频率

I'm trying to get frequency from iPhone / iPod music library for a spectrum app on iPod library, helping myself with reading-audio-samples-via-avassetreader to get audio samples and then with using-the-apple-fft-and-accelerate-framework and Apple vDSP Samples, but somehow I'm wrong somewhere and unable to calculate the frequency.

So step by step:

  • read audio sample
  • Hanning window
  • calculate fft

Is this the correct way to get frequencies from an iPod mp3 library?

Here is my code:

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;  
}  

I got 1.485757 and 1.332233 as my first frequencies

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

神妖 2024-11-11 05:50:56

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

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

编辑

好吧,我认为我的第一个建议是错误的,因为 vDSP 文档说实数到复数就地 fft 的实际输入应该格式化为分割复数格式,使得< code>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 输出表示从 0Fs/2 的频率。

另一件需要注意的是,我不知道你的寻峰算法是否能很好地工作,因为 FFT 输出不会平滑,并且经常会出现很多振荡。您只需选取两个相邻样本较低的第一个样本即可。如果您只想找到单个峰值,最好只找到整个输出的最大幅度。如果你想找到多个峰值,你将不得不做一些更复杂的事情。

It looks to me like there is a problem in the conversion to complex input for the FFT. vDSP_ctoz() splits a buffer where real and imaginary components are interleaved into two buffers, one real and one imaginary. Your input to that function appears to be just real data that has been casted to COMPLEX. This means that your input buffer to vDSP_ctoz() is only half as long as it needs to be and some garbage data beyond the buffer size is getting converted.

You either need to create sampleOut to be 2*n in length and set every other value (the real parts) or better yet, you can bypass the vDSP_ctoz() and directly copy your input data into A.realp and set A.imagp to zeros. vDSP_ctoz() should only be needed when interfacing to a source that produces interleaved complex data.

Edit

Ok, I think I was wrong on my first suggestion since the vDSP documentation says that the real input of the real-to-complex in-place fft should be formatted into the split complex format such that imagp contains even samples and realp contains the odd samples. I have not actually used the vDSP library, but I am familiar with a lot of other FFT libraries and I missed that detail.

You should be able to find the peaks using A.realp after the call to vDSP_zvmags(&A, 1, A.realp, 1, nOver2); At that point, A.realp should contain the magnitude squared of the FFT output, which is scalar. If you are going to do the scaling, it should be done before the mag2 operation, but it may not be needed if you are just looking for the peaks.

To get the real frequencies represented by the FFT output, use this formula:

F = (i * Fs) / N,   i=0,1,...,N/2

where

i is the index of the FFT output buffer
Fs is the audio sampling rate
N is the FFT length

so your calculation might look like this:

float frequency = (peakIndex * 44100) / n;

Keep in mind that vDSP only returns the first half of the input spectrum for real input since the second half is redundant. So the FFT output represents frequencies from 0 to Fs/2.

One other note is that I don't know if your peak finding algorithm will work very well since FFT output will not be smooth and there will often be a lot of oscillation. You are simply taking the first sample where the two adjacent samples are lower. If you just want to find a single peak, it would be better just to find the max magnitude across the entire output. If you want to find multiple peaks, you will have to do something more sophisticated.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文