MIDI – Swift to Objective C

A month or so ago I started playing around with MIDI and Swift, and immediately became disenchanted with the way one has to deal with the C-isms of CoreMIDI. Correct me if I’m wrong, but it appears one has to allocate memory and copy the MIDI data in order to parse it in the read callback. Here’s a little example that works:

func midiTestReadProc(pktlist : UnsafePointer,
	readProcRefCon : UnsafeMutablePointer,
	srcConnRefCon : UnsafeMutablePointer)
{
	let list = pktlist.memory
	let count = list.numPackets
	Swift.print("midi rx \(count) packets")


//	THIS WORKS but I don't like the alloc part (happens every callback)
	let packet:MIDIPacket = list.packet
	var pp = UnsafeMutablePointer.alloc(1)

	pp.initialize(packet)
	
	for _ in 0 ..< count {
		let p = pp.memory
		handle(p)
		pp = MIDIPacketNext(pp)
	}
	
	pp.destroy()
}

So... I decided that the read proc should be in Objective C. Which meant I needed to set up the MIDI objects and connections in Swift, but then somehow pass the data across to an Objective C object. This took quite awhile to figure out, and I probably still don't have all the memory management parts worked out correctly, but it's good enough to blog about now so here goes...

Since this is just a test app, my app delegate is the object that finds and stores an array of available output ports. I won't bother with discussing that here -- there are many examples of this on the web. I will, however, point out that I created a very simple object to store the info about these output ports (which works for input ports too, I think) -- MIDIPort:

class MIDIPort: NSObject {
	let ref: NSNumber
	let name: NSString
	
	init(source: NSNumber, name: NSString) {
		self.ref = source
		self.name = name
		super.init()
	}
}

This probably doesn't need to be a Cocoa class, and it probably could be a Swift struct too. Currently I'm not too well versed on when Cocoa is and isn't required, so I've just been assuming the lowest common denominator. Will finesse as time goes on.

The app delegate creates the client and input port:

	func applicationDidFinishLaunching(aNotification: NSNotification) {
		
		mtClient = MTClient(name: "MTClient") // could fail, fixme
		mtInputPort = MTInputPort(client: mtClient, name: "MTInputPort")
		...

The MTClient ("MT" stands for "MIDI Test" fwiw) is pretty boring and I won't bother with showing the source code. There is an opportunity to provide a notify handler, though, so the same sort of procedure I'm outlining here for the read proc could be used for the notify handler as well.

The MTInputPort is the place where the connection between Swift and Objective C happens. I'll discuss the MTInputPort's MTInputReadHandler soon. For now just know it's property of the MTInputPort. Here is the bare-bones init routine for MTInputPort:

	init(client: MTClient, name: String) {
		self.client = client
		let err = MIDIInputPortCreate(client.ref, name, 
				readHandler.readHandler, &readHandler, &portRef)
		Swift.print("MIDIInputPortCreate returned \(err)")
		super.init()
	}

readHandler.readHandler is the routine that will be called when processing MIDI data. &readHandler is passed in as the refCon so that the read handler knows what object will be handling the data. This hopefully will become clearer soon.

The MTInputPort.readHandler property is created outside the init routine so I don't have to deal with optionals. Sooner or later I will surely have cross that bridge, but for now the property is just created when the property is assigned:

	private var readHandler = MTInputReadHandler()

OK, now for the weird part -- the MTInputReadHandler. Here's the .h file:

#import 
#include 

@interface MTInputReadHandler : NSObject
{
	MIDIReadProc	readHandler;
}

@property (assign) MIDIReadProc readHandler;

- (void)handlePktList:(const MIDIPacketList*)pktlist;

@end

Aaaaannnnd the implementation:

void midiRead(const MIDIPacketList *pktlist, void * __nullable readProcRefCon, 
		void * __nullable srcConnRefCon)
{
	MTInputReadHandler * const *mh = 
			(MTInputReadHandler * const *)readProcRefCon;
	MTInputReadHandler	*me = *mh;
	[me handlePktList:pktlist];
}

@implementation MTInputReadHandler
@synthesize readHandler;

- (id)init {
	if (nil == (self = [super init])) {
		return nil;
	}
	self.readHandler = midiRead;
	return self;
}

- (void)handlePktList:(const MIDIPacketList*)pktlist
{
	NSLog(@"handlePktList");
	// base class does nothing
}
@end

There are a few things to say about this:

  • The actual read routine isn't a method
  • The refCon passed in to MIDIInputPortCreate() as &readHandler is actually a pointer pointer (aka a 'Handle' for those of you old enough to remember Mac OSes prior to X). This means you have to dereference once to get the needed reference to the object.
  • Notice the strange "MTInputReadHandler * const *mh" casting -- if this 'const' cast isn't done the compiler will complain. It has something to do with ARC, and I don't yet understand it.

For those of you who don't know how to read "MTInputReadHandler * const *mh" without getting confused, it's best to read these sorts of things from right to left. In this case 'mh' is a pointer to a const pointer to an MTInputReadHandler. I just noticed that the line just below that throws away the const:

	MTInputReadHandler	*me = *mh;

Interesting that the compiler didn't complain. Maybe it's as confused as I am...

Leave a Reply

Your email address will not be published. Required fields are marked *