Search code examples
iosxcodeswiftaudiounit

AURenderCallback in Swift


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.


Solution

  • 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())