来自AVAudioPCMBuffer的Spectrogram在Swift中使用Accelerate框架

我试图在Swift中从AVAudioPCMBuffer生成一个光谱图。 我在AVAudioMixerNode上安装一个tap,并用audio缓冲区接收一个callback。 我想将缓冲区中的信号转换为[Float:Float]字典,其中键表示频率,值表示相应频率上的audio大小。

我尝试使用苹果的加速框架,但我得到的结果似乎可疑。 我相信这只是我转换信号的方式。

我看了这篇博客文章以供参考。

这是我有什么:

 self.audioEngine.mainMixerNode.installTapOnBus(0, bufferSize: 1024, format: nil, block: { buffer, when in let bufferSize: Int = Int(buffer.frameLength) // Set up the transform let log2n = UInt(round(log2(Double(bufferSize)))) let fftSetup = vDSP_create_fftsetup(log2n, Int32(kFFTRadix2)) // Create the complex split value to hold the output of the transform var realp = [Float](count: bufferSize/2, repeatedValue: 0) var imagp = [Float](count: bufferSize/2, repeatedValue: 0) var output = DSPSplitComplex(realp: &realp, imagp: &imagp) // Now I need to convert the signal from the buffer to complex value, this is what I'm struggling to grasp. // The complexValue should be UnsafePointer<DSPComplex>. How do I generate it from the buffer's floatChannelData? vDSP_ctoz(complexValue, 2, &output, 1, UInt(bufferSize / 2)) // Do the fast Fournier forward transform vDSP_fft_zrip(fftSetup, &output, 1, log2n, Int32(FFT_FORWARD)) // Convert the complex output to magnitude var fft = [Float](count:Int(bufferSize / 2), repeatedValue:0.0) vDSP_zvmags(&output, 1, &fft, 1, vDSP_length(bufferSize / 2)) // Release the setup vDSP_destroy_fftsetup(fftsetup) // TODO: Convert fft to [Float:Float] dictionary of frequency vs magnitude. How? }) 

我的问题是

  1. 如何将buffer.floatChannelData转换为UnsafePointer<DSPComplex>传递给vDSP_ctoz函数? 有没有不同的/更好的方法来做甚至绕过vDSP_ctoz
  2. 如果缓冲区包含来自多个通道的audio,这是不同的? 当缓冲audio通道数据是或不是交错时,它有什么不同?
  3. 如何将fft数组中的索引转换为以Hz为单位的频率?
  4. 还有什么我可能做错了?

更新

谢谢大家的build议。 按照接受的答案,我最终填补了复杂的数组。 当我绘制这些值并在音叉上播放一个440赫兹的音调时,它会精确地logging它应该在的位置。

这里是填充数组的代码:

 var channelSamples: [[DSPComplex]] = [] for var i=0; i<channelCount; ++i { channelSamples.append([]) let firstSample = buffer.format.interleaved ? i : i*bufferSize for var j=firstSample; j<bufferSize; j+=buffer.stride*2 { channelSamples[i].append(DSPComplex(real: buffer.floatChannelData.memory[j], imag: buffer.floatChannelData.memory[j+buffer.stride])) } } 

channelSamples数组然后为每个通道保存独立的采样数组。

为了计算我使用的这个数值:

 var spectrum = [Float]() for var i=0; i<bufferSize/2; ++i { let imag = out.imagp[i] let real = out.realp[i] let magnitude = sqrt(pow(real,2)+pow(imag,2)) spectrum.append(magnitude) } 

  1. 哈克方式:你可以只投一个浮点数组。 真理和价值观正在一个接一个走向哪里。
  2. 这取决于audio是否交错。 如果它是交错的(大多数情况下),左右声道在带有STRIDE 2的arrays中
  3. 在你的情况下,最低频率是1024个采样周期的频率。 在44100kHz的情况下,它是〜23ms,频谱的最低频率将是1 /(1024/44100)(〜43Hz)。 下一个频率将是这个(〜86Hz)的两倍,依此类推。

4:您已经在audio总线上安装了callback处理程序。 这很可能会在实时线程优先级和频繁运行的情况下运行。 你不应该做任何有可能阻塞的东西(这可能会导致优先倒置和audio失真):

  1. 分配内存( realpimagp[Float](.....)Array[float]缩写 – 可能分配在堆上)预先分配这些

  2. 调用冗长的操作,如vDSP_create_fftsetup() – 它也分配内存并初始化它。 同样,你可以在你的函数之外分配一次。