从iPhone音频流中获取Hz频率

14

如何在iOS上从音频流(音乐)中获取Hz频率值?苹果提供的最佳且最易用的框架是什么?谢谢。


你需要更具体一些 - 你想要处理什么样的输入?语音?音乐?单个乐器演奏单个音符?纯音? - Paul R
好的 - 那么您希望提取什么样的频率信息?只是短期功率谱,还是比那更复杂的东西? - Paul R
我只需要每个短音乐片段的赫兹平均值。片段长度小于0.2秒。 - Olga Dalton
5
像音乐这样的复杂声音包含许多不同频率的能量,没有单一的“Hz值”。能量在不同频率上的分布是连续变化的。 - Paul R
3个回答

21

这是我在iOS上使用Accelerate Framework执行FFT所使用的一些代码,这使得它相当快速。

//keep all internal stuff inside this struct
    typedef struct FFTHelperRef {
        FFTSetup fftSetup; // Accelerate opaque type that contains setup information for a given FFT transform.
        COMPLEX_SPLIT complexA; // Accelerate type for complex number
        Float32 *outFFTData; // Your fft output data
        Float32 *invertedCheckData; // This thing is to verify correctness of output. Compare it with input.
    } FFTHelperRef;

//首先,使用此函数初始化您的FFTHelperRef。

FFTHelperRef * FFTHelperCreate(long numberOfSamples) {

    FFTHelperRef *helperRef = (FFTHelperRef*) malloc(sizeof(FFTHelperRef));
    vDSP_Length log2n = log2f(numberOfSamples);    
    helperRef->fftSetup = vDSP_create_fftsetup(log2n, FFT_RADIX2);
    int nOver2 = numberOfSamples/2;
    helperRef->complexA.realp = (Float32*) malloc(nOver2*sizeof(Float32) );
    helperRef->complexA.imagp = (Float32*) malloc(nOver2*sizeof(Float32) );

    helperRef->outFFTData = (Float32 *) malloc(nOver2*sizeof(Float32) );
    memset(helperRef->outFFTData, 0, nOver2*sizeof(Float32) );

    helperRef->invertedCheckData = (Float32*) malloc(numberOfSamples*sizeof(Float32) );

    return  helperRef;
}

//在这里传入初始化的FFTHelperRef、数据和数据大小。返回numSamples/2大小的FFT数据。

Float32 * computeFFT(FFTHelperRef *fftHelperRef, Float32 *timeDomainData, long numSamples) {
    vDSP_Length log2n = log2f(numSamples);
    Float32 mFFTNormFactor = 1.0/(2*numSamples);

    //Convert float array of reals samples to COMPLEX_SPLIT array A
    vDSP_ctoz((COMPLEX*)timeDomainData, 2, &(fftHelperRef->complexA), 1, numSamples/2);

    //Perform FFT using fftSetup and A
    //Results are returned in A
    vDSP_fft_zrip(fftHelperRef->fftSetup, &(fftHelperRef->complexA), 1, log2n, FFT_FORWARD);

    //scale fft 
    vDSP_vsmul(fftHelperRef->complexA.realp, 1, &mFFTNormFactor, fftHelperRef->complexA.realp, 1, numSamples/2);
    vDSP_vsmul(fftHelperRef->complexA.imagp, 1, &mFFTNormFactor, fftHelperRef->complexA.imagp, 1, numSamples/2);

    vDSP_zvmags(&(fftHelperRef->complexA), 1, fftHelperRef->outFFTData, 1, numSamples/2);

    //to check everything =============================
    vDSP_fft_zrip(fftHelperRef->fftSetup, &(fftHelperRef->complexA), 1, log2n, FFT_INVERSE);
    vDSP_ztoc( &(fftHelperRef->complexA), 1, (COMPLEX *) fftHelperRef->invertedCheckData , 2, numSamples/2);
    //=================================================    

    return fftHelperRef->outFFTData;
}

使用方法如下:

  1. 初始化: FFTHelperCreate(TimeDomainDataLenght);

  2. 传递Float32时域数据并返回频域数据: Float32 *fftData = computeFFT (fftHelper, buffer, frameSize);

现在你有一个数组,其中索引=频率,值=幅值(平方幅值?)。 根据奈奎斯特定理,该数组中的最大可能频率是样本率的一半。也就是说,如果你的采样率=44100,则可以编码的最高频率为22050 Hz。

因此,请找到该样本率的Nyquist最大频率:const Float32 NyquistMaxFreq = SAMPLE_RATE/2.0;

找到Hz很容易:Float32 hz = ((Float32)someIndex / (Float32)fftDataSize) * NyquistMaxFreq; (fftDataSize = frameSize/2.0)

这对我有效。如果我在Audacity中生成特定频率并播放它-此代码将检测到正确的频率(最强的频率,您还需要在fftData中找到最大值才能做到这一点)。

(还有1-2%的微小不匹配。不确定为什么会发生这种情况。如果有人能解释一下为什么-那将不胜感激。)

编辑:

这种不匹配是因为我用于FFT的块太小了。使用较大的时间域数据块(16384帧)解决了这个问题。 该问题在此进行了解释: Unable to get correct frequency value on iphone

编辑: 以下是示例项目:https://github.com/krafter/DetectingAudioFrequency


由于我是新手!我有点难以找到足够的信息和代码来学习Objective-C,而且我认为GitHub上的示例项目并不是很好用。我需要最好的方法来获取正确的频率和识别播放的字符串以及调整特定字符串的音高的过程。我只是请求指导而不是代码,如果您能以任何方式帮助我,我将不胜感激,感谢您的时间和支持。 - tryKuldeepTanwar
让我们在聊天中继续这个讨论 - tryKuldeepTanwar
1
@suthar 你可以在accumulatorDataLenght中使用较小的值。请记住,值越小,频率越不准确。 - krafter
1
感谢您的出色努力! - elight
如果它允许你编写本地代码,那么你可以使用它。 - krafter
显示剩余17条评论

15

这样的问题在SO上经常被问到。(我在这里回答了一个类似的问题),所以我写了一个小教程并提供了代码,即使在商业和闭源应用中也可以使用。这不一定是最好的方法,但很多人都能理解。您将根据“每个短音乐片段的Hz平均值”所指的内容进行修改。例如,您是指基本音高还是频率重心。

您可能希望像另一个答案建议的那样使用Apple的加速框架中的FFT。

希望能有所帮助。

http://blog.bjornroche.com/2012/07/frequency-detection-using-fft-aka-pitch.html


1
这不是stackoverflow的工作方式。链接到外部网站不是答案。 - Stefan Kendall
1
我已经提供了一个链接,其中包含一个更完整的类似问题的答案。 - Bjorn Roche

5

苹果没有提供频率或音高估算的框架。然而,iOS Accelerate框架包括FFT和自相关例程,可用作更复杂的频率和音高识别或估算算法的组成部分。

除了几乎没有噪音的单一长连续恒定频率纯正弦波之外,没有既简单又最佳的方法,其中一个插值幅度峰可能适用于长窗口FFT。对于语音和音乐,这种简单的方法往往根本不起作用。但搜索音高检测或估算方法将出现许多关于更合适算法的研究论文。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接