Playing back audio buffer on iOS using Swift (AudioQueue)

2,699 views
Skip to first unread message

mu...@lavabit.com

unread,
Feb 27, 2015, 4:37:26 PM2/27/15
to iphonesdkd...@googlegroups.com
Hi there.

I'm writing here because I spent far too much time on a project already and there is hardly any useful documentation regarding this topic on the net.

I'm working on a iOS project where I need to output a few seconds of audio samples. Those are not from a file but generated, it is a pointer to a bunch of Double values.
I have found a framework that seems to be able to do this, AudioQueue. It seems like I got everything necessary to make it work in place, including a bit of Objective-C wrapper code that is necessary for passing the callback function which is responsible for filling the output buffer.
When I try to prime for playback or start playback, nothing happens, no sound, no error. It turns out that the callback function is never called.

Does anyone have example code I could look at or at least an idea as to what could be wrong? I know that at least part of the code might be necessary, I can post it tomorrow. I hope someone here has the competence and willingness to help me with this.

Best Regards,
Philipp

Jesse Tayler

unread,
Feb 27, 2015, 4:41:27 PM2/27/15
to iphonesdkd...@googlegroups.com
It’s really unclear what you are actually trying to do.

what do you mean by output? you want to hear the sound or write it to file?

I’d guess post your code and send a link or at least pate in enough code that we can see what the goal and the trouble might be.

good luck!


--
You received this message because you are subscribed to the Google Groups "iPhone SDK Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to iphonesdkdevelop...@googlegroups.com.
To post to this group, send email to iphonesdkd...@googlegroups.com.
Visit this group at http://groups.google.com/group/iphonesdkdevelopment.
For more options, visit https://groups.google.com/d/optout.

mu...@lavabit.com

unread,
Feb 28, 2015, 9:45:36 AM2/28/15
to iphonesdkd...@googlegroups.com
What I actually try to do is to play back a couple of samples, through the speaker. I assumed that this would be the most trivial part of the project, but so far it has been the most time consuming.

Most APIs expect an audio file, which I do not have. I found one API that looks like it can take some samples as input and play through the speaker: AudioQueue.

Below is all the relevant code. It is my very first time working with any of these things, so this is probably very ugly code.

This is part of ViewController.swift, which acts as my main class.
Important to note here is that ret.0 is a UnsafeMutablePointer<Double> that points to the samples.
 
...
       
        let ret
= syntSine(dataAsDouble, Int(pixelsWide), Int(pixelsHigh))
        samplecount
= ret.1
       
       
// Attempt using AudioQueue

       
var aq: AudioQueue = AudioQueue(input: ret.0, nSamples: samplecount)
        aq
.initialize()
       
       
var prepped: UInt32 = 0
       
var primeResult: OSStatus = AudioQueuePrime(aq.queue, 22100, &prepped)
        println
("Status: \(primeResult) Prepped: \(prepped)")
       
       
var AQError = AudioQueueStart(aq.queue, nil)
        println
("AQError: \(AQError)")

...


The AudioQueue class is intended to prepare the queue.
import Foundation

class AudioQueue {
   
   
var desc : AudioStreamBasicDescription
   
var queue : AudioQueueRef
   
var nSamples: Int       // number of samples in input
   
var bufferIndex: Int
    let spb
: Int            // samples per buffer
   
var upperBounds: Int    // current upper boundary in input
   
var input: UnsafeMutablePointer<Double>
   
    init
(input: UnsafeMutablePointer<Double>, nSamples: Int) {
        println
("init called")
       
// Does not work when the constant type is omitted. This just cost me a couple of hours. Thanks Apple. Fuck you.
       
self.desc = AudioStreamBasicDescription(
            mSampleRate
: 44100,
            mFormatID
: AudioFormatID(kAudioFormatLinearPCM),
            mFormatFlags
: AudioFormatFlags(kAudioFormatFlagIsPacked | kAudioFormatFlagIsNonInterleaved | kLinearPCMFormatFlagIsFloat | kLinearPCMFormatFlagIsPacked),
            mBytesPerPacket
: 4,
            mFramesPerPacket
: 1,
            mBytesPerFrame
: 4,
            mChannelsPerFrame
: 1,
            mBitsPerChannel
: 32,
            mReserved
: 0
       
)
       
       
self.queue = nil
       
self.nSamples = nSamples
       
self.bufferIndex = 0
       
self.spb = 22100
       
self.upperBounds = 0
       
self.input = input
   
}
   

    func audioQueueHandleBuffer
(inAQ : AudioQueueRef, inBuffer : AudioQueueBufferRef) {
        println
("AudioQueueHandleBuffer called")
       
var i = 0   // ouput index
       
var outBuffer: UnsafeMutablePointer<Double> = UnsafeMutablePointer<Double>(inBuffer.memory.mAudioData) // TODO: check whether this is correct
       
       
//fill buffer
       
if(nSamples - bufferIndex < spb) {
            upperBounds
= nSamples - bufferIndex
       
} else {
            upperBounds
= bufferIndex + spb
       
}
       
       
for(; bufferIndex < upperBounds; bufferIndex++) {
            outBuffer
[i] = input[bufferIndex]
       
}
        inBuffer
.memory.mAudioData = UnsafeMutablePointer<Void>(outBuffer)
   
}
   
   
    func initialize
() {
        println
("initialize called")
       
var err = CFIAudioQueueNewOutput(&desc, audioQueueHandleBuffer, &queue)
        println
("Initialize Error: \(err)")
       
// ...
   
}
}


This Objective-C code is supposed to hand the function pointer to AudioQueueNewOutput.
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>

// from: http://stackoverflow.com/questions/25341632/pass-c-function-callback-in-swift

void audioQueueHandleBuffer(void *ctx, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) {
   
NSCAssert(ctx != NULL, @"Cannot execute NULL context block.");
   
   
void(^block)(AudioQueueRef, AudioQueueBufferRef) = (__bridge void(^)(AudioQueueRef, AudioQueueBufferRef))ctx;
   
   
return block(inAQ, inBuffer);
}

OSStatus CFIAudioQueueNewOutput(AudioStreamBasicDescription *desc, void(^callback)(AudioQueueRef, AudioQueueBufferRef), AudioQueueRef *queue) {
   
return AudioQueueNewOutput(desc, audioQueueHandleBuffer, (__bridge_retained void *)([callback copy]), nil, nil, 0, queue);
}


That should be all the necessary code. The callback function is never called and hence cannot fill the buffer. I think the problem might be with the Ojective-C wrapper, but I really don't know and don't understand the black art that is Objective-C.



Яна Витальевна Губская

unread,
Apr 18, 2016, 12:47:57 PM4/18/16
to iPhone SDK Development
Hi, did u found any solution for swift and audio qurue services?
Reply all
Reply to author
Forward
0 new messages