来自AVAudioPCMBuffer的频谱图使用Swift中的Accelerate框架

时间:2015-10-01 15:10:17

标签: ios swift audio accelerate-framework avaudioengine

我试图从Swift中的AVAudioPCMBuffer生成频谱图。我在AVAudioMixerNode上安装了一个水龙头,并通过音频缓冲区接收回调。我想将缓冲区中的信号转换为[Float:Float]字典,其中键表示频率,值表示相应频率上的音频幅度。

我尝试使用Apple的Accelerate框架,但我得到的结果似乎很可疑。我确信这只是我转换信号的方式。

我查看this blog post以及其他事项作为参考。

这就是我所拥有的:

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. 如果缓冲区包含来自多个频道的音频,这会有所不同吗?当缓冲音频通道数据是否交错时,它有何不同?
  3. 如何将fft数组中的索引转换为以Hz为单位的频率?
  4. 我可能做错了什么?
  5. 更新

    感谢大家的建议。我按照接受的答案建议填写复杂数组。当我绘制值并在音叉上播放440 Hz音调时,它会精确地记录它应该在哪里。

    以下是填充数组的代码:

    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)
    }
    

2 个答案:

答案 0 :(得分:2)

  1. Hacky方式:你可以只抛出一个浮点数组。那些实力和想象值一个接一个地出现。
  2. 取决于音频是否交错。如果它是交错的(大多数情况)左右声道都在STRIDE 2的数组中
  3. 在您的情况下,最低频率是1024个样本的频率。在44100kHz的情况下,它是~23ms,频谱的最低频率将是1 /(1024/44100)(~43Hz)。下一个频率将是此频率的两倍(~86Hz),依此类推。

答案 1 :(得分:0)

4:您已在音频总线上安装了回调处理程序。这很可能通过实时线程优先级和频繁运行。你不应该做任何有可能阻塞的事情(它可能会导致优先级倒置和故障音频):

  1. 分配内存(realpimagp - [Float](.....)Array[float]的简写 - 可能是在堆上分配的。预先分配这些

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