MusicSequence via a MIDI Virtual Source
MusicSequence via a MIDI Virtual Source
Virtual MIDI sources and destinations are a bit confusing. This is one way to use a Virtual MIDI source.
Introduction
If you want other apps to the “see” your app as a MIDI source, i.e. a producer of MIDI data, you need to set up a virtual MIDI source in your app.
Here is the virtual source I’m about to create as it appears in my MIDI monitor app – along with data it just sent. (click thumbnail for full image)
MIDI Setup
With Core MIDI, the first thing you need to do is to create a MIDIClient.
MIDIClientCreateWithBlock will save a reference in a variable of type MIDIClientRef. You can also pass in a callback that will be invoked when your MIDI setup changes. You can pass in nil to remain oblivious.
1 2 3 4 5 6 7 8 9 10 11 |
// make it an instance variable var midiClient = MIDIClientRef() var status = OSStatus(noErr) status = MIDIClientCreateWithBlock("com.rockhoppertech.MyMIDIClient", &midiClient, myNotifyCallback) if status == OSStatus(noErr) { // now you can continue } func myNotifyCallback(message:UnsafePointer<MIDINotification>) -> Void { etc. |
Then (in the success block) you can create a virtual midi source with MIDISourceCreate. The name you pass in here is the name that will appear on other apps’ source list.
1 2 3 4 5 6 |
// instance variable var virtualSourceEndpointRef = MIDIEndpointRef() status = MIDISourceCreate(midiClient, "MIDISourceOSX.VirtualSource", &virtualSourceEndpointRef) |
You can then send MIDI messages through your source (and on to other apps) with MIDIReceived. Yes at first glance it’s a weird name – you’re sending not receiving, right?
It depends on how you are looking at your MIDI Entity. So, yeah, weird.
1 |
public func MIDIReceived(src: MIDIEndpointRef, _ pktlist: UnsafePointer<MIDIPacketList>) -> OSStatus |
Here is an example of sending a Note On message:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
func noteOnReceive() { var packet = MIDIPacket() packet.timeStamp = MIDITimeStamp(AudioConvertHostTimeToNanos(AudioGetCurrentHostTime())) packet.length = 3 packet.data.0 = UInt8(0x90) packet.data.1 = UInt8(60) packet.data.2 = UInt8(100) var packetlist = MIDIPacketList(numPackets: 1, packet: packet) let status = MIDIReceived(virtualSourceEndpointRef, &packetlist) if status != noErr { print("bad status \(status) receiving msg") CheckError(status) } } |
The system will assign a unique ID to your endpoints. You retrieve it with MIDIObjectGetIntegerProperty. You can then save it to user defaults, and then read it in the next time your run your app. It will work if you skip this part, but the docs recommend doing it.
1 2 |
var id = MIDIUniqueID(0) let status = MIDIObjectGetIntegerProperty(virtualSourceEndpointRef, kMIDIPropertyUniqueID, &id) |
MusicSequence
What if you want to play a MIDI Sequence? You can create a MusicSequence on the fly or read in a standard MIDI file. Then what? Well, there is a function named MusicSequenceSetMIDIEndpoint you can use. Unfortunately, it will work with Virtual Destinations and not Virtual Sources, so the “endpoint” part of the name is a bit misleading. (What else is new?)
So, we need to create a virtual destination and set that as the endpoint of the sequence.
1 |
status = MusicSequenceSetMIDIEndpoint(musicSequence, virtualDestinationEndpointRef) |
Creating the virtual MIDI destination is almost as easy as the virtual source. You will need to provide a read block that is called when data is sent to the destination. (the unique ID chacha should be done with the destination too).
1 2 3 4 5 6 7 |
// instance variable var virtualDestinationEndpointRef = MIDIEndpointRef() status = MIDIDestinationCreateWithBlock(midiClient, "MIDISourceOSX.VirtualDest", &virtualDestinationEndpointRef, readBlock) |
So, great. When you play your MusicSequence via a MusicPlayer, your virtual destination’s read block will be called with each MIDI event.
What about the virtual source?
Have the read block forward the message (via MIDIReceived) to your virtual source!
1 2 3 |
func readBlock(packetList: UnsafePointer<MIDIPacketList>, srcConnRefCon: UnsafeMutablePointer<Void>) -> Void { MIDIReceived(virtualSourceEndpointRef, packetList) } |
Groovy, huh?
Summary
Yeah, a bit of a game of twister to play a MIDISequence, but it is what it is.
The github project is for OSX, but the same MIDI code will work with iOS.
There are other uses for virtual destinations too. You can write a MIDI Monitor with one since you’re receiving all the MIDI data in its read block. You can then display it, or write trippy animations.
Hi Gene,
this is what I was searching for!
I just didn’t get how to send something from my virtual source. If I knew it’s called receiving …
Thanks a lot!
Thank you!
I’ve been thinking about shutting down this blog after 20+ years due to lack of feedback.
Your message helps.