7

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 Answers2

9

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())
pbodsk
  • 6,787
  • 3
  • 21
  • 51
  • The use of a delegate is an interesting approach. However I receive the following error when attempting to enable the callback: 'Cannot find an initializer for type 'UnsafeMutablePointer' that accepts an argument list of type '(UnsafeMutablePointer).' I have looked around for how to resolve this but I am pretty stumped. These pointer types are frustrating to say the least. – Firstname_Numbers Nov 22 '15 at 19:44
  • For the purposes of debugging, I attempted to create an AURenderCallbackStruct object so that I could define the parameters individually with dot syntax. This works fine: "renderCallbackStruct.inputProcRefCon = UnsafeMutablePointer(unsafeAddressOf(self))" but "renderCallbackStruct.inputProc = renderCallback" throws the now familiar compile error "Cannot assign a value of type '(UnsafeMutablePointer, UnsafeMutablePointer, UnsafePointer, UInt32, UInt32, UnsafeMutablePointer) -> OSStatus' to a value of type 'AURenderCallback'." – Firstname_Numbers Nov 22 '15 at 20:33
  • Well that's embarrassing. I tried to compile the project you linked above, and after I received a ton of errors I noticed that I was running XCode 6.4. I upgraded to version 7 and it works like a charm. Thank you! – Firstname_Numbers Nov 22 '15 at 22:45
  • Glad it worked, and even more glad to see I'm not the only one spending hours on weird compiler messages caused by my own mistakes :-) – pbodsk Nov 23 '15 at 07:37
  • I'm struggling to get this to work. Could you post a complete source example? – Petrus Theron Jun 16 '18 at 09:51
  • 1
    Hi @PetrusTheron, oh wow, that was ages ago :) I made this for the company I worked at back then and the code is not public. However, I made this gist: https://gist.github.com/pbodsk/40bee78940385847b7e9780a70e2a937 which should contain...well...the gist (sorry!) of how this is set up and used. I removed some of the stuff from there but I hope it helps you just a little. Sorry, that is the best I can do. – pbodsk Jun 17 '18 at 10:32
  • 1
    Also @PetrusTheron, please note that AUGraph is being deprecated soonish, see this for instance: https://stackoverflow.com/questions/44952582/is-augraph-being-deprecated-on-ios-if-so-when just to say that maybe you should consider looking into the never `AVAudioEngine` if at all possible – pbodsk Jun 17 '18 at 10:33
1

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

Dharman
  • 30,962
  • 25
  • 85
  • 135
travisjayday
  • 784
  • 6
  • 16