swiftcore-audiopcmaudiotoolbox

Change AudioQueueBuffer's mAudioData


I would need to set the AudioQueueBufferRef's mAudioData. I tried with copyMemory:

inBuffer.pointee.copyMemory(from: lastItemOfArray, byteCount: byteCount) // byteCount is 512

but it doesnt't work.

The AudioQueueNewOutput() queue is properly setted up to Int16 pcm format

Here is my code:

   class CustomObject {
       var pcmInt16DataArray = [UnsafeMutableRawPointer]() // this contains pcmInt16 data
   }
        
   let callback: AudioQueueOutputCallback = { (
       inUserData: UnsafeMutableRawPointer?,
       inAQ: AudioQueueRef,
       inBuffer: AudioQueueBufferRef) in
    
       guard let aqp: CustomObject = inUserData?.bindMemory(to: CustomObject.self, capacity: 1).pointee else { return }
       var numBytes: UInt32 = inBuffer.pointee.mAudioDataBytesCapacity
        
    
       /// Set inBuffer.pointee.mAudioData to pcmInt16DataArray.popLast()
       /// How can I set the mAudioData here??
    
       inBuffer.pointee.mAudioDataByteSize = numBytes
       AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, nil)
    }

From apple doc: https://developer.apple.com/documentation/audiotoolbox/audioqueuebuffer?language=objc

mAudioData:

The audio data owned the audio queue buffer. The buffer address cannot be changed.

So I guess the solution would be to set a new value to the same address Anybody who knows how to do it?

UPDATE:

The incoming audio format is "pcm" signal (Little Endian) sampled at 48kHz. Here are my settings:

var dataFormat = AudioStreamBasicDescription()
dataFormat.mSampleRate = 48000;
dataFormat.mFormatID = kAudioFormatLinearPCM
dataFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsNonInterleaved;
dataFormat.mChannelsPerFrame = 1
dataFormat.mFramesPerPacket = 1
dataFormat.mBitsPerChannel = 16
dataFormat.mBytesPerFrame = 2
dataFormat.mBytesPerPacket = 2

And I am collecting the incoming data to

var pcmData = [UnsafeMutableRawPointer]()

Solution

  • You're close!

    Try this:

    inBuffer.pointee.mAudioData.copyMemory(from: lastItemOfArray, byteCount: Int(numBytes))
    

    or this:

    memcpy(inBuffer.pointee.mAudioData, lastItemOfArray, Int(numBytes))
    

    Audio Queue Services was tough enough to work with when it was pure C. Now that we have to do so much bridging to get the API to work with Swift, it's a real pain. If you have the option, try out AVAudioEngine.


    A few other things to check:

    Make sure your AudioQueue has the same format that you've defined in your AudioStreamBasicDescription.

    var queue: AudioQueueRef?
    
    // assumes userData has already been initialized and configured
    AudioQueueNewOutput(&dataFormat, callBack, &userData, nil, nil, 0, &queue)
    

    Confirm you have allocated and primed the queue's buffers.

    let numBuffers = 3
    
    // using forced optionals here for brevity
    for _ in 0..<numBuffers {
        var buffer: AudioQueueBufferRef?
        if AudioQueueAllocateBuffer(queue!, userData.bufferByteSize, &buffer) == noErr {
            userData.mBuffers.append(buffer!)
            callBack(inUserData: &userData, inAQ: queue!, inBuffer: buffer!)
        }
    }
    

    Consider making your callback a function.

    func callBack(inUserData: UnsafeMutableRawPointer?, inAQ: AudioQueueRef, inBuffer: AudioQueueBufferRef) {
    
        let numBytes: UInt32 = inBuffer.pointee.mAudioDataBytesCapacity       
        memcpy(inBuffer.pointee.mAudioData, pcmData, Int(numBytes))
        inBuffer.pointee.mAudioDataByteSize = numBytes
    
        AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, nil)
    
     }
    

    Also, see if you can get some basic PCM data to play through your audio queue before attempting to bring in the server side data.

    var pcmData: [Int16] = []
    for i in 0..<frameCount {
        let element = Int16.random(in: Int16.min...Int16.max) // noise
        pcmData.append(Int16(element))
    
    }