来自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? })
我的问题是
- 如何将
buffer.floatChannelData
转换为UnsafePointer<DSPComplex>
传递给vDSP_ctoz
函数? 有没有不同的/更好的方法来做甚至绕过vDSP_ctoz
? - 如果缓冲区包含来自多个通道的audio,这是不同的? 当缓冲audio通道数据是或不是交错时,它有什么不同?
- 如何将
fft
数组中的索引转换为以Hz为单位的频率? - 还有什么我可能做错了?
更新
谢谢大家的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) }
- 哈克方式:你可以只投一个浮点数组。 真理和价值观正在一个接一个走向哪里。
- 这取决于audio是否交错。 如果它是交错的(大多数情况下),左右声道在带有STRIDE 2的arrays中
- 在你的情况下,最低频率是1024个采样周期的频率。 在44100kHz的情况下,它是〜23ms,频谱的最低频率将是1 /(1024/44100)(〜43Hz)。 下一个频率将是这个(〜86Hz)的两倍,依此类推。
4:您已经在audio总线上安装了callback处理程序。 这很可能会在实时线程优先级和频繁运行的情况下运行。 你不应该做任何有可能阻塞的东西(这可能会导致优先倒置和audio失真):
-
分配内存(
realp
,imagp
–[Float](.....)
是Array[float]
缩写 – 可能分配在堆上)预先分配这些 -
调用冗长的操作,如
vDSP_create_fftsetup()
– 它也分配内存并初始化它。 同样,你可以在你的函数之外分配一次。