Iphone 使用Apple FFT和Accelerate框架

Iphone 使用Apple FFT和Accelerate框架,iphone,audio,signal-processing,fft,accelerate-framework,Iphone,Audio,Signal Processing,Fft,Accelerate Framework,有没有人在iPhone应用程序中使用了Apple FFT,或者知道在哪里可以找到示例应用程序来使用它?我知道苹果有一些发布的代码,但是我不太确定如何将它实现到一个实际的项目。 < P>这里是一个真实的例子:一个C++的片段,使用加速的VDSP FFT例程对远程IO音频单元的输入进行自相关。使用这个框架相当复杂,但文档也不太糟糕 OSStatus DSPCore::初始化(双采样器,uint16\u t\u缓冲大小){ 采样器=_采样器; bufferSize=\u bufferSize; pea

有没有人在iPhone应用程序中使用了
Apple FFT
,或者知道在哪里可以找到示例应用程序来使用它?我知道苹果有一些发布的代码,但是我不太确定如何将它实现到一个实际的项目。

< P>这里是一个真实的例子:一个C++的片段,使用加速的VDSP FFT例程对远程IO音频单元的输入进行自相关。使用这个框架相当复杂,但文档也不太糟糕

OSStatus DSPCore::初始化(双采样器,uint16\u t\u缓冲大小){
采样器=_采样器;
bufferSize=\u bufferSize;
peakIndex=0;
频率=0.f;
uint32_t maxFrames=getMaxFramesPerSlice();
displayData=(float*)malloc(maxFrames*sizeof(float));
bzero(显示数据,最大帧*sizeof(浮点));
log2n=log2f(最大帧);
n=1 mBuffers[0].mData,2,&A,1,numFrames/2);
memset(ioData->mBuffers[0].mData,0,ioData->mBuffers[0].mDataByteSize);
//快速傅里叶变换
vDSP_fft_zrip(fft设置和A,1,ln,fft_前向);
//绝对平方(等于mag^2)
vDSP_zvmag(&A,1,A.realp,1,numFrames/2);
bzero(A.imagp,(numFrames/2)*sizeof(float));
//逆FFT
vDSP_fft_zrip(fft设置,&A,1,ln,fft_逆);
//将复杂拆分转换为实拆分
vDSP_ztoc(&A,1,(复杂*)显示数据,2,numFrames/2);
//正常化
浮动比例=1.f/显示数据[0];
vDSP_vsmul(显示数据,1和比例,显示数据,1,numFrames);
//朴素峰值拾取:找到第一个局部最大值
peakIndex=0;
对于(尺寸ii=1;iidisplayData[ii-1])&(displayData[ii]>displayData[ii+1])){
peakIndex=ii;
打破
}
}
//计算频率
频率=采样器/peakIndex+四次插值(&displayData[peakIndex-1]);
bufferSize=numFrames;
对于(int ii=0;iimNumberBuffers;++ii){
bzero(ioData->mBuffers[ii].mData,ioData->mBuffers[ii].mDataByteSize);
}
}

我刚得到一个iPhone项目的FFT代码:

  • 创建新项目
  • 删除除main.m和xxx_info.plist之外的所有文件
  • 转到“项目设置”并搜索pch,阻止它尝试加载.pch(因为我们刚刚删除了它)
  • 将代码示例复制粘贴到main.m中的任何内容上
  • 拆下包含碳的管路。碳用于OSX
  • 删除所有框架,并添加加速框架
您可能还需要从info.plist中删除一个通知项目加载xib的条目,但我90%确定您不必为此费心

注意:程序输出到控制台,结果显示为0.000,这不是一个错误-只是速度非常快

这段代码真是太晦涩了;这是慷慨的评论,但评论实际上并没有使生活变得更容易

基本上,它的核心是:

vDSP_fft_zrip(setupReal, &A, stride, log2n, FFT_FORWARD);
vDSP_fft_zrip(setupReal, &A, stride, log2n, FFT_INVERSE);
对n个实浮点进行FFT,然后反向返回到我们开始的位置。 ip代表就地,这意味着&A被覆盖 这就是所有这些特殊打包错误的原因——这样我们就可以将返回值压缩到与发送值相同的空间中

为了给出一些观点(比如:为什么我们首先要使用这个函数?),让我们假设我们想要对麦克风输入执行基音检测,并且我们已经设置了它,以便每次麦克风进入1024个浮点时都会触发一些回调。假设麦克风的采样率为44.1kHz,即约44帧/秒

因此,我们的时间窗口是1024个样本的持续时间,即1/44秒

因此,我们将使用麦克风的1024个浮点数打包,设置log2n=10(2^10=1024),预先计算一些线轴(setupReal),并:

现在A将包含n/2个复数。这些代表n/2个频率箱:

  • bin[1]。idealFreq=44Hz——即我们能够可靠检测到的最低频率是该窗口内的一个完整波,即44Hz波

  • bin[2]。idealFreq=2*44Hz

  • 等等

  • bin[512]。idealFreq=512*44Hz——我们可以检测到的最高频率(称为奈奎斯特频率)是每对点代表一个波的位置,即窗口内的512个完整波,即512*44Hz,或:n/2*bin[1]。idealFreq

  • 实际上还有一个额外的Bin,Bin[0],它通常被称为“DC偏移”。恰好Bin[0]和Bin[n/2]始终具有复杂组件0,因此[0].realp用于存储Bin[0]和[0]。imagp用于存储Bin[n/2]

每个复数的大小就是围绕这个频率振动的能量

所以,正如您所看到的,它不是一个非常好的基音检测器,因为它没有足够精细的粒度。有一种巧妙的方法可以获得给定垃圾箱的精确频率

好的,现在来看看代码:

请注意,vDSP_fft_zrip中的'ip',='in-place'ie输出覆盖了A('r'表示它接受实际输入)

查看有关vDSP_fft_zrip的文档

实际数据存储在split complex中 窗体上存储奇数实数 分裂复合体的假想面 存储在 真实的一面

这可能是最难理解的事情。在整个过程中,我们一直使用相同的容器(&A)。所以在开始的时候,我们想用n个实数来填充它。在FFT之后,它将保存n/2个复数。然后我们把它放到反变换中,希望得到我们原来的n个实数

现在,函数的结构将为复杂值设置。因此,vDSP需要标准化如何将实数打包到其中

首先我们生成n个实数:1,2,…,n

for (i = 0; i < n; i++)
    originalReal[i] = (float) (i + 1);
你真的需要看看A是如何分配的,也许可以查一下复杂的
vDSP_fft_zrip(setupReal, &A, stride, log2n, FFT_FORWARD);
for (i = 0; i < n; i++)
    originalReal[i] = (float) (i + 1);
// 1. masquerades n real #s as n/2 complex #s = {1+2i, 3+4i, ...}
// 2. splits to 
//   A.realP = {1,3,...} (n/2 elts)
//   A.compP = {2,4,...} (n/2 elts)
//
vDSP_ctoz(
          (COMPLEX *) originalReal, 
          2,                            // stride 2, as each complex # is 2 floats
          &A, 
          1,                            // stride 1 in A.realP & .compP
          nOver2);                      // n/2 elts
A.realp = (float *) malloc(nOver2 * sizeof(float));
A.imagp = (float *) malloc(nOver2 * sizeof(float));
z = exp(i.theta) = cos(theta) + i.sin(theta)
// let's say log2n = 8, so n=2^8=256 samples, or 'harmonics' or 'terms'
// if we pre-calculate the 256th roots of unity (of which there are 256) 
// that will save us time later.
//
// Note that this call creates an array which will need to be released 
// later to avoid leaking
setupReal = vDSP_create_fftsetup(log2n, FFT_RADIX2);
vDSP_fft_zrip(setupReal, &A, stride, log2n, FFT_FORWARD);
vDSP_fft_zrip(setupReal, &A, stride, log2n, FFT_INVERSE);
// Need to see the documentation for this one...
// in order to optimise, different routines return values 
// that need to be scaled by different amounts in order to 
// be correct as per the math
// In this case...
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);