在iOS上从音频流(音乐)获取Hz频率值的最佳方法是什么? Apple提供了哪些最好,最简单的框架。提前谢谢。
答案 0 :(得分:16)
以下是我使用Accelerate Framework在iOS中执行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);
现在你有一个数组,其中索引=频率,值=幅度(平方幅度?)。 根据{{3}},您在该数组中的最大可能频率是采样率的一半。也就是说,如果您的采样率= 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帧)解决了这个问题。 这个问题解释了它: Nyquist theorem
修改强> 以下是示例项目:Unable to get correct frequency value on iphone
答案 1 :(得分:15)
这样的问题在SO上有很多问题。 (我已经回答了类似的here)所以我写了一个代码的小教程,即使在商业和闭源应用程序中也可以使用。这不一定是最好的方式,但它是许多人理解的方式。您必须根据“每个短音乐片段的Hz平均值”的含义对其进行修改。你的意思是基本音高或频率质心,例如。
您可能希望在加速框架中使用Apple的FFT,如另一个答案所示。
希望它有所帮助。
http://blog.bjornroche.com/2012/07/frequency-detection-using-fft-aka-pitch.html
答案 2 :(得分:4)
Apple没有提供频率或音高估算的框架。但是,iOS Accelerate框架确实包含用于FFT和自相关的例程,可用作更复杂的频率和音调识别或估计算法的组件。
除了可能在几乎零噪声中的单个长连续恒定频率纯正弦音调之外,没有办法既简单又好,其中长窗口FFT的内插幅度峰值可能是合适的。对于语音和音乐,这种简单的方法通常根本不起作用。但搜索音调检测或估算方法将会发表大量关于更合适算法的研究论文。