Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I call CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer?

I'm trying to figure out how to call this AVFoundation function in Swift. I've spent a ton of time fiddling with declarations and syntax, and got this far. The compiler is mostly happy, but I'm left with one last quandary.

public func captureOutput(
    captureOutput: AVCaptureOutput!,
    didOutputSampleBuffer sampleBuffer: CMSampleBuffer!,
    fromConnection connection: AVCaptureConnection!
) {
    let samplesInBuffer = CMSampleBufferGetNumSamples(sampleBuffer)
    var audioBufferList: AudioBufferList

    var buffer: Unmanaged<CMBlockBuffer>? = nil

    CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(
        sampleBuffer,
        nil,
        &audioBufferList,
        UInt(sizeof(audioBufferList.dynamicType)),
        nil,
        nil,
        UInt32(kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment),
        &buffer
    )

    // do stuff
}

The compiler complains for the 3rd and 4th arguments:

Address of variable 'audioBufferList' taken before it is initialized

and

Variable 'audioBufferList' used before being initialized

So what am I supposed to do here?

I'm working off of this StackOverflow answer but it's Objective-C. I'm trying to translate it into Swift, but run into this problem.

Or is there possibly a better approach? I need to read the data from the buffer, one sample at a time, so I'm basically trying to get an array of the samples that I can iterate over.

like image 654
nhgrif Avatar asked Dec 12 '14 12:12

nhgrif


2 Answers

Disclaimer: I have just tried to translate the code from Reading audio samples via AVAssetReader to Swift, and verified that it compiles. I have not tested if it really works.

// Needs to be initialized somehow, even if we take only the address
var audioBufferList = AudioBufferList(mNumberBuffers: 1,
      mBuffers: AudioBuffer(mNumberChannels: 0, mDataByteSize: 0, mData: nil))

var buffer: Unmanaged<CMBlockBuffer>? = nil

CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(
    sampleBuffer,
    nil,
    &audioBufferList,
    UInt(sizeof(audioBufferList.dynamicType)),
    nil,
    nil,
    UInt32(kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment),
    &buffer
)

// Ensure that the buffer is released automatically.
let buf = buffer!.takeRetainedValue() 

// Create UnsafeBufferPointer from the variable length array starting at audioBufferList.mBuffers
let audioBuffers = UnsafeBufferPointer<AudioBuffer>(start: &audioBufferList.mBuffers,
    count: Int(audioBufferList.mNumberBuffers))

for audioBuffer in audioBuffers {
    // Create UnsafeBufferPointer<Int16> from the buffer data pointer
    var samples = UnsafeMutableBufferPointer<Int16>(start: UnsafeMutablePointer(audioBuffer.mData),
        count: Int(audioBuffer.mDataByteSize)/sizeof(Int16))

    for sample in samples {
        // ....
    }
}
like image 114
Martin R Avatar answered Sep 22 '22 17:09

Martin R


Swift3 solution:

func loopAmplitudes(audioFileUrl: URL) {

    let asset = AVAsset(url: audioFileUrl)

    let reader = try! AVAssetReader(asset: asset)

    let track = asset.tracks(withMediaType: AVMediaTypeAudio)[0]

    let settings = [
        AVFormatIDKey : kAudioFormatLinearPCM
    ]

    let readerOutput = AVAssetReaderTrackOutput(track: track, outputSettings: settings)
    reader.add(readerOutput)
    reader.startReading()

    while let buffer = readerOutput.copyNextSampleBuffer() {

        var audioBufferList = AudioBufferList(mNumberBuffers: 1, mBuffers: AudioBuffer(mNumberChannels: 0, mDataByteSize: 0, mData: nil))
        var blockBuffer: CMBlockBuffer?

        CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(
            buffer,
            nil,
            &audioBufferList,
            MemoryLayout<AudioBufferList>.size,
            nil,
            nil,
            kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment,
            &blockBuffer
        );

        let buffers = UnsafeBufferPointer<AudioBuffer>(start: &audioBufferList.mBuffers, count: Int(audioBufferList.mNumberBuffers))

        for buffer in buffers {

            let samplesCount = Int(buffer.mDataByteSize) / MemoryLayout<Int16>.size
            let samplesPointer = audioBufferList.mBuffers.mData!.bindMemory(to: Int16.self, capacity: samplesCount)
            let samples = UnsafeMutableBufferPointer<Int16>(start: samplesPointer, count: samplesCount)

            for sample in samples {

                //do something with you sample (which is Int16 amplitude value)

            }
        }
    }
}
like image 43
Matej Ukmar Avatar answered Sep 22 '22 17:09

Matej Ukmar