GWRodriguez GWRodriguez - 13 days ago 5
Swift Question

Function argument passed in MusicSequenceUserCallback

The following func is part of MusicSequence in Core Audio

func MusicSequenceSetUserCallback(_ inSequence: MusicSequence, _ inCallback: MusicSequenceUserCallback?, _ inClientData: UnsafeMutablePointer<Void>) -> OSStatus


A MusicSequenceUserCallback is defined in the API as:

typealias MusicSequenceUserCallback = (UnsafeMutablePointer<Void>, MusicSequence, MusicTrack, MusicTimeStamp, UnsafePointer<MusicEventUserData>, MusicTimeStamp, MusicTimeStamp) -> Void


I am unclear how to pass a custom func into MusicSequenceSetUserCallback. Knowing about func types in Swift, I created a custom func with the exact same arguments as
MusicSequenceUserCallback
and tried to set it like this:

var argFunc: MusicSequenceUserCallback = myCallback


But I get the error
A C function pointer can only be formed from a reference to a 'func' of a literal closure
on the above line of code.

Edit

Here's the call back I wrote:

func myCallback(inClientData: UnsafeMutablePointer<Void>, sequence: MusicSequence, track: MusicTrack, eventTime: MusicTimeStamp, data: UnsafePointer<MusicEventUserData>, startSliceBeat: MusicTimeStamp, endSliceBeat: MusicTimeStamp) -> Void {
// do something
}

Answer

You are receiving the error because the callback needs to be a free function, or a closure that doesn't any capture local context. Instance methods capture the self reference, thus you cannot use them as callbacks.

The solution is to either:

  • declare myCallBack as a global function, or
  • declare myCallback as an inner function/closure, and make sure it doesn't capture any local variables

If you need to make use of instance members of your class in your callback, you can achieve this by forwarding the callback from the free function to the instance of your class, via the inClientData argument:

class MyMusicClass {

    func myCoreAudioFunction() {

        .............

        func seqCB(inClientData: UnsafeMutablePointer<Void>, sequence: MusicSequence, track: MusicTrack, eventTime: MusicTimeStamp, data: UnsafePointer<MusicEventUserData>, startSliceBeat: MusicTimeStamp, endSliceBeat: MusicTimeStamp) -> Void {

            // we know for sure we have a MyMusicClass instance here, so the cast should be OK
            // care to be taken though to unregister the callback if the `MyMusicClass` instance gets deallocated, to avoid the zombie
            let client = unsafeBitCast(inClientData, MyMusicClass.self)
            client.musicSeqenceCallback(sequence, track: track, eventTime: eventTime, data: data, startSliceBeat: startSliceBeat, endSliceBeat: endSliceBeat)
        }

        MusicSequenceSetUserCallback(myMusicSequence,seqCB, unsafeBitCast(self, UnsafeMutablePointer<Void>.self))
    }

    func musicSeqenceCallback(sequence: MusicSequence, track: MusicTrack, eventTime: MusicTimeStamp, data: UnsafePointer<MusicEventUserData>, startSliceBeat: MusicTimeStamp, endSliceBeat: MusicTimeStamp) -> Void {
        // do anything you want here, there are no more restrictions :)
    }
}