Swift and C API callbacks
Swift and C API callbacks
Swift is supposed to have been designed for compatibility with existing Apple APIs, including C APIs such as CoreMIDI.
N.B.
Apple has improved Core MIDI support for Swift since this was written.
Introduction
Core MIDI is Apple’s C API for (surprise) MIDI. Apple provides no higher level bindings besides a player in AVFoundation. If you want to develop an app with MIDI capabilities, you need to use Core MIDI.
Like many C APIs, there are several callbacks:
- MIDICompletionProc
1typealias MIDICompletionProc = CFunctionPointer<((UnsafeMutablePointer<MIDISysexSendRequest>) -> Void)> - MIDINotifyProc
1typealias MIDINotifyProc = CFunctionPointer<((UnsafePointer<MIDINotification>, UnsafeMutablePointer<Void>) -> Void)> - MIDIReadProc
1typealias MIDIReadProc = CFunctionPointer<((UnsafePointer<MIDIPacketList>, UnsafeMutablePointer<Void>, UnsafeMutablePointer<Void>) -> Void)>
In context, the very first thing you’d do is to create a MIDI client. In Swift 1.2 beta, MIDIClientCreate finally works.! See my posts on the problem and the solution.
One of the parameters to MIDIClientCreate is a function pointer (MIDINotifyProc) to be called when MIDI inputs or outputs in the system have changed, such as plugging in a new device.
1 |
func MIDIClientCreate(name: CFString!, notifyProc: MIDINotifyProc, notifyRefCon: UnsafeMutablePointer<Void>, outClient: UnsafeMutablePointer<MIDIClientRef>) -> OSStatus |
Another place where callbacks are used is reading MIDI input. A MIDIReadProc is called when data appears.
Creating Function Pointers
Ok, so how do you make a CFunctionPointer?
Here is my attempt.
It looks like CFunctionPointer needs a COpaquePointer which needs an UnsafeMutablePointer or UnsafePointer.
BTW., the CFunctionPointer source code has this amusing comment:
1 2 3 |
/// Though not directly useful in Swift, `CFunctionPointer<T>` can be /// used to safely pass a C function pointer, received from one C or /// Objective-C API, to another C or Objective-C API. |
CFunctionPointer is a struct with inits that will take an UnsafeMutablePointer or an UnsafePointer.
So, it looks like we need to make an unsafe pointer, use that to create an opaque pointer, then use that to create a function pointer (and a partridge in a pear tree).
Will this work with a MIDIReadProc?
1 2 3 4 5 6 7 8 9 |
var ump = UnsafeMutablePointer<((UnsafePointer<MIDIPacketList>, UnsafeMutablePointer<Void>, UnsafeMutablePointer<Void> ) -> Void)>.alloc(1) ump.initialize(MyMIDIReadProc) let cp = COpaquePointer(ump) let fp = CFunctionPointer<((UnsafePointer<MIDIPacketList>, UnsafeMutablePointer<Void>, UnsafeMutablePointer<Void> ) -> Void)>(cp) status = MIDIDestinationCreate(midiClient, name, fp, // Yay! it takes our function pointer! etc. |
It doesn’t work though with Core MIDI.
And it doesn’t matter if MyMIDIReadProc is a func, a class func, or a global func.
Here is the love letter from Core MIDI:
1 2 3 |
thread #7: tid = 0x713b7, 0x7a1541f0, stop reason = EXC_BAD_ACCESS (code=2, address=0x7a1541f0) frame #0: 0x7a1541f0 frame #1: 0x00159295 CoreMIDI`LocalMIDIReceiverList::HandleMIDIIn(void*, OpaqueMIDIEndpoint*, void*, MIDIPacketList const*) + 117 |
Swift 2 update
They heard us. Swift 2 beta has introduced changes in CoreMIDI and function pointers in general.
Here are the new CoreMIDI callback signatures.
- MIDICompletionProc
1234typealias MIDICompletionProc = CFunctionPointer<((UnsafeMutablePointer<MIDISysexSendRequest>) -> Void)>//In Swift 2 betatypealias MIDICompletionProc = (UnsafeMutablePointer<MIDISysexSendRequest>) -> Void - MIDINotifyProc
1234typealias MIDINotifyProc = CFunctionPointer<((UnsafePointer<MIDINotification>, UnsafeMutablePointer<Void>) -> Void)>//In Swift 2 betatypealias MIDINotifyProc = (UnsafePointer<MIDINotification>, UnsafeMutablePointer<Void>) -> Void - MIDIReadProc
1234typealias MIDIReadProc = CFunctionPointer<((UnsafePointer<MIDIPacketList>, UnsafeMutablePointer<Void>, UnsafeMutablePointer<Void>) -> Void)>//In Swift 2 betatypealias MIDIReadProc = (UnsafePointer<MIDIPacketList>, UnsafeMutablePointer<Void>, UnsafeMutablePointer<Void>) -> Void
Summary
Swift support for C APIs is improving. Given this problem with function pointers though, it is far from being usable.
Hi,
great post! Keep them coming. Am running into the same problem and getting the BAD_EXEC and put up a question on StackOverflow: http://stackoverflow.com/questions/28924831/coremidi-callbacks-in-swift.
Cheers,
Tobi