7
votes

I am creating an application that uses Audio Units, and while there are many examples of code in Objective-C (including Apple's own aurioTouch and others) I am attempting to code the entire thing in Swift.

I have been able to set up my AUGraph and run some audio through it, but I cannot seem to figure out the syntax for the render callbacks. I have tried a couple of methods:

Method 1: Create an AURenderCallback directly

let render : AURenderCallback = { (
    inRefCon: UnsafeMutablePointer<Void>,
    ioActionFlags: UnsafeMutablePointer<AudioUnitRenderActionFlags>,
    inTimeStamp: UnsafePointer<AudioTimeStamp>,
    inBufNumber: UInt32,
    inNumberFrames: UInt32,
    ioData: UnsafeMutablePointer<AudioBufferList>) -> OSStatus in

    return noErr
}

I do nothing in this callback besides returning noErr at this point since I am just trying to make it work. However, the compiler returns the following error:

(UnsafeMutablePointer, UnsafeMutablePointer, UnsafePointer, UInt32, UInt32, UnsafeMutablePointer) -> OSStatus' is not convertible to 'AURenderCallback

The definition of AURenderCallback in the documentation is this:

typealias AURenderCallback = (UnsafeMutablePointer<Void>,UnsafeMutablePointer<AudioUnitRenderActionFlags>,UnsafePointer<AudioTimeStamp>, UInt32, UInt32,UnsafeMutablePointer<AudioBufferList>) -> OSStatus

It seems to be the same as what I had entered, although it could be that I do not understand what the documentation is asking for.

Method 2: Create a function representing the AURenderCallback

func render(
    inRefCon: UnsafeMutablePointer<Void>,
    ioActionFlags: UnsafeMutablePointer<AudioUnitRenderActionFlags>,
    inTimeStamp: UnsafePointer<AudioTimeStamp>,
    inBufNumber: UInt32,
    inNumberFrames: UInt32,
    ioData: UnsafeMutablePointer<AudioBufferList>) -> OSStatus {

    return noErr
}

This does not give any errors as a function, but when I place it into AURenderCallbackStruct in the inputProc parameter I receive an error:

Cannot find an initializer for type 'AURenderCallbackStruct' that accepts an argument list of type '(inputProc: (UnsafeMutablePointer, UnsafeMutablePointer, UnsafePointer, UInt32, UInt32, UnsafeMutablePointer) -> OSStatus, inputProcRefCon: nil)

I have not found many examples of creating AURenderCallbacks in Swift, and there seems to be a large difference in syntax compared with Objective-C. Any help would be appreciated.

2

2 Answers

9
votes

I just found your post while trying to figure out the same (it's not easy finding sample code and examples combining CoreAudio/Audio Unit and Swift).

By looking at this repository and reading (several times :-)) Apples documentation about Using Swift with Cocoa and Objective-C I managed to piece something together. As it says in the section about Function Pointers

When calling a function that takes a function pointer argument, you can pass a top-level Swift function, a closure literal, or nil.

So. Outside of my class I have a method that looks like this:

func renderCallback(inRefCon:UnsafeMutablePointer<Void>,
ioActionFlags:UnsafeMutablePointer<AudioUnitRenderActionFlags>,
inTimeStamp:UnsafePointer<AudioTimeStamp>,
inBusNumber:UInt32,
inNumberFrames:UInt32,
ioData:UnsafeMutablePointer<AudioBufferList>) -> OSStatus {
    let delegate = unsafeBitCast(inRefCon, AURenderCallbackDelegate.self)
    let result = delegate.performRender(ioActionFlags,
        inTimeStamp: inTimeStamp,
        inBusNumber: inBusNumber,
        inNumberFrames: inNumberFrames,
        ioData: ioData)
    return result
}

As you can see, I just call a delegate here. That delegate is declared like so (also outside the class but you already knew that :-))

@objc protocol AURenderCallbackDelegate {
func performRender(ioActionFlags: UnsafeMutablePointer<AudioUnitRenderActionFlags>,
    inTimeStamp: UnsafePointer<AudioTimeStamp>,
    inBusNumber: UInt32,
    inNumberFrames: UInt32,
    ioData: UnsafeMutablePointer<AudioBufferList>) -> OSStatus
}

Doing so enables me to "get back inside my class" by conforming to the AURenderCallbackDelegate like so:

class AudioUnitGraphManager: NSObject, AURenderCallbackDelegate

And then implementing the renderCallback method in my AudioUnitGraphManager class

func performRender(ioActionFlags: UnsafeMutablePointer<AudioUnitRenderActionFlags>, inTimeStamp: UnsafePointer<AudioTimeStamp>, inBusNumber: UInt32, inNumberFrames: UInt32, ioData: UnsafeMutablePointer<AudioBufferList>) -> OSStatus {
    print("Hello there!")
    return noErr
}

The final piece of the puzzle is to actually enable the render notify callback which I do like so:

AudioUnitAddRenderNotify(mixerUnit, renderCallback, UnsafeMutablePointer(unsafeAddressOf(self)))

Hopefully this gives you something to continue the struggle with.

Changes in Swift 3

In Swift 3 the declaration for AURenderCallback has changed to this:

typealias AURenderCallback = (UnsafeMutableRawPointer, UnsafeMutablePointer<AudioUnitRenderActionFlags>, UnsafePointer<AudioTimeStamp>, UInt32, UInt32, UnsafeMutablePointer<AudioBufferList>?) -> OSStatus

Notice the last parameter is now UnsafeMutablePointer<AudioBufferList>? compared to UnsafeMutablePointer<AudioBufferList> before (it is an optional now).

This means that the code now looks like this.

The renderCallback function

func renderCallback(inRefCon:UnsafeMutablePointer<Void>,
ioActionFlags:UnsafeMutablePointer<AudioUnitRenderActionFlags>,
inTimeStamp:UnsafePointer<AudioTimeStamp>,
inBusNumber:UInt32,
inNumberFrames:UInt32,
ioData:UnsafeMutablePointer<AudioBufferList>?) -> OSStatus {
    let delegate = unsafeBitCast(inRefCon, AURenderCallbackDelegate.self)
    let result = delegate.performRender(ioActionFlags,
        inTimeStamp: inTimeStamp,
        inBusNumber: inBusNumber,
        inNumberFrames: inNumberFrames,
        ioData: ioData)
    return result
}

The AURenderCallbackDelegate protocol

@objc protocol AURenderCallbackDelegate {
func performRender(ioActionFlags: UnsafeMutablePointer<AudioUnitRenderActionFlags>,
    inTimeStamp: UnsafePointer<AudioTimeStamp>,
    inBusNumber: UInt32,
    inNumberFrames: UInt32,
    ioData: UnsafeMutablePointer<AudioBufferList>?) -> OSStatus
}

The actual implementation of performRender

    func performRender(ioActionFlags: UnsafeMutablePointer<AudioUnitRenderActionFlags>, inTimeStamp: UnsafePointer<AudioTimeStamp>, inBusNumber: UInt32, inNumberFrames: UInt32, ioData: UnsafeMutablePointer<AudioBufferList>?) -> OSStatus {
    print("Hello there!")
    return noErr
}

Enabling the render notify callback

AudioUnitAddRenderNotify(mixerUnit!, renderCallback, Unmanaged.passUnretained(self).toOpaque())
1
votes

I'm a bit late to the party but I found a way that might be more straightforward than the above answer. We can make use of the inRefCon pointer that you can set in the callbackStruct. inRefCon gets passed to your callback function, so if you set inRefCon to your class reference, you can directly "find your way back into the class" without needing to go loopdyloop with delegates. Here's how I did it:

/* below code is inside some function in MyClass */ 

var callbackStruct = AURenderCallbackStruct()

// set inRefCon to reference to self by casting to pointer
callbackStruct.inputProcRefCon = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque())

// create our C closure
callbackStruct.inputProc = {
             (inRefCon : UnsafeMutableRawPointer,
              ioActionFlags : UnsafeMutablePointer<AudioUnitRenderActionFlags>,
              inTimeStamp : UnsafePointer<AudioTimeStamp>,
              inBusNumber : UInt32,
              inNumberFrames : UInt32,
              ioData : UnsafeMutablePointer<AudioBufferList>?) -> OSStatus in
            
            // get reference to my class by de-referencing inRefCon
            let _self = Unmanaged<MyClass>.fromOpaque(inRefCon).takeUnretainedValue()
            // time to profit with reference to our class _self
            // ...
            return 0
}

// set callback
AudioUnitSetProperty(audioUnit!,
                     kAudioUnitProperty_SetRenderCallback,
                     kAudioUnitScope_Global,
                     0,
                     &callbackStruct,
                     UInt32(MemoryLayout.size(ofValue: callbackStruct)))