从 iPhone 上的音频流中获取 Hz 频率

Posted

技术标签:

【中文标题】从 iPhone 上的音频流中获取 Hz 频率【英文标题】:Get Hz frequency from audio stream on iPhone 【发布时间】:2012-07-26 01:29:55 【问题描述】:

ios 上的音频流(音乐)中获取 Hz 频率值的最佳方法是什么?苹果提供的最好和最简单的框架是什么?提前致谢。

【问题讨论】:

您需要更具体一些 - 您正在查看什么样的输入?演讲 ?音乐 ?一个单一的乐器演奏一个音符?纯音? OK - 那么您希望提取什么样的频率信息?只是短期功率谱,还是比这更复杂的东西? 我只需要每个短音乐片段的赫兹平均值。段长小于 0.2 s。 没有单一的“Hz 值” - 像音乐一样复杂的声音包含许多不同频率的能量,并且这种能量与频率的分布不断变化。 【参考方案1】:

这是我在 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;

像这样使用它:

    初始化它:FFTHelperCreate(TimeDomainDataLenght);

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

现在您有了一个数组,其中索引=频率,值=幅度(幅度平方?)。 根据Nyquist theorem,您在该阵列中的最大可能频率是采样率的一半。也就是说,如果您的采样率 = 44100,则可以编码的最大频率为 22050 Hz。

所以去找你的采样率的奈奎斯特最大频率:const Float32 NyquistMaxFreq = SAMPLE_RATE/2.0;

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

这对我有用。如果我在 Audacity 中生成特定频率并播放它 - 此代码检测到正确的频率(最强的频率,您还需要在 fftData 中找到 max 来执行此操作)。

(大约 1-2% 仍有一点不匹配。不知道为什么会发生这种情况。如果有人能解释我为什么 - 将不胜感激。)

编辑:

出现这种不匹配是因为我用于 FFT 的片段太小。使用更大的时域数据块(16384 帧)解决了这个问题。 这个问题解释了它: Unable to get correct frequency value on iphone

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

【讨论】:

你能发布一个示例项目吗? 太棒了...在我的 iPhone 5 上,它在这个网站上的峰值为 19K Hz:audionotch.com/app/tune。 有人用过诺卡因吗? @suthar 您可以在 accumulatorDataLenght 中使用较小的值。请记住,值越小,频率越不准确。 感谢您的努力!【参考方案2】:

这样的问题在 SO 上被问了很多。 (我已经回答了一个类似的问题here)所以我写了一个小教程,其中包含您甚至可以在商业和闭源应用程序中使用的代码。这不一定是最好的方式,但它是许多人理解的方式。您将不得不根据“每个短音乐片段的赫兹平均值”的意思对其进行修改。例如,您是指基本音高还是频率质心。

您可能希望按照另一个答案的建议在加速框架中使用 Apple 的 FFT。

希望对你有帮助。

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

【讨论】:

这不是 *** 的工作方式。指向外部网站的链接不是答案。 我提供了一个链接,指向类似问题的更完整答案。【参考方案3】:

Apple 不提供频率或音高估计框架。但是,iOS Accelerate 框架确实包含 FFT 和自相关例程,可用作更复杂的频率和音高识别或估计算法的组件。

没有既简单又最好的方法,除了可能在几乎零噪声中的单个长连续恒定频率纯正弦音调,其中长窗口 FFT 的插值幅度峰值可能是合适的。对于语音和音乐,这种简单的方法通常根本不起作用。但是对音高检测或估计方法的搜索会发现大量关于更合适算法的研究论文。

【讨论】:

以上是关于从 iPhone 上的音频流中获取 Hz 频率的主要内容,如果未能解决你的问题,请参考以下文章

iPhone应用程序仅在特定频率以上录音

核心音频,Goertzel 算法不起作用

iPhone 模拟器音频仅适用于 44100Hz、22050Hz 和 11025Hz

使用 python 从音频文件中提取 1/3 个八度音阶

libfdk_aac音频采样率和编码字节数注意

如何以编程方式将音频文件从 iPhone 的文件系统复制到资产库