12

AVAudioPCMBufferSwiftからスペクトログラムを生成しようとしています。にタップをインストールAVAudioMixerNodeし、オーディオ バッファでコールバックを受け取ります。[Float:Float]バッファ内の信号を、キーが周波数を表し、値が対応する周波数のオーディオの大きさを表す辞書に変換したいと思います。

Apple の Accelerate フレームワークを使用してみましたが、得られる結果は疑わしいようです。私はそれが信号を変換している方法にあると確信しています。

参考のために、とりわけこのブログ投稿を見ました。

ここに私が持っているものがあります:

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.floatChannelDatatoを変換して関数UnsafePointer<DSPComplex>に渡すにはどうすればよいですか? vDSP_ctozおそらくバイパスする別の/より良い方法はありvDSP_ctozますか?
  2. バッファに複数のチャンネルからのオーディオが含まれている場合、これは異なりますか? バッファー オーディオ チャネル データがインターリーブされている場合とインターリーブされていない場合の違いは?
  3. fft配列内のインデックスを Hz 単位の周波数に変換するにはどうすればよいですか?
  4. 他に何か間違っていることがありますか?

アップデート

提案してくれてありがとう。受け入れられた回答で提案されているように、複雑な配列を埋めることになりました。値をプロットして音叉で 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)
}
4

2 に答える 2

3
  1. ハッキーな方法: float 配列をキャストするだけです。reals と imag の値が次々に移動する場所。
  2. オーディオがインターリーブされているかどうかによって異なります。インターリーブされている場合 (ほとんどの場合)、左右のチャネルは STRIDE 2 の配列にあります。
  3. あなたの場合の最低周波数は、1024サンプルの期間の周波数です。44100kHz の場合は ~23ms で、スペクトルの最低周波数は 1/(1024/44100) (~43Hz) になります。次の周波数はこれの 2 倍 (~86Hz) などです。
于 2015-10-01T16:03:10.333 に答える