Spectrogram von AVAudioPCMBuffer mit dem Accelerate-Framework in Swift

Ich versuche ein Spektrogramm aus einem @ zu generierAVAudioPCMBuffer in Swift. Ich installiere einen Wasserhahn auf einemAVAudioMixerNode und erhalte einen Rückruf mit dem Audiopuffer. Ich möchte das Signal im Puffer in ein @ konvertier[Float:Float] Wörterbuch, wobei die Taste die Frequenz und der Wert die Stärke des Audios auf der entsprechenden Frequenz darstellt.

Ich habe versucht, das Apple Accelerate-Framework zu verwenden, aber die Ergebnisse, die ich erhalte, scheinen zweifelhaft zu sein. Ich bin mir sicher, dass es nur so ist, wie ich das Signal konvertiere.

Ich sahdieser Blog-Beitrag unter anderem als Referenz.

Hier ist was ich habe:

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?
})
Meine Fragen sindWie konvertiere ich dasbuffer.floatChannelData zuUnsafePointer<DSPComplex> an das @ übergebvDSP_ctoz Funktion? Gibt es einen anderen / besseren Weg, es zu tun, vielleicht sogar unter Umgehung vonvDSP_ctoz?Ist das anders, wenn der Puffer Audio von mehreren Kanälen enthält? Wie ist es anders, wenn die Daten des Puffer-Audiokanals verschachtelt sind oder nicht?Wie konvertiere ich die Indizes imfft Array zu Frequenzen in Hz? Alles andere, was ich falsch machen kann?Aktualisiere

ielen Dank für Ihre Vorschläge. Am Ende habe ich das komplexe Array wie in der akzeptierten Antwort vorgeschlagen ausgefüllt. Wenn ich die Werte zeichne und einen 440-Hz-Ton auf einer Stimmgabel spiele, registriert er genau, wo er sein sollte.

Hier ist der Code, um das Array zu füllen:

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

DaschannelSamplesas Array @ enthält dann für jeden Kanal ein separates Array von Sample

Um die Größe zu berechnen, habe ich Folgendes verwendet:

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

Antworten auf die Frage(4)

Ihre Antwort auf die Frage