Apr 052011
 
MIDISport 2x2
What: The goal of this article is to explain the naming conventions used by CoreMIDI to reference the different components of a MIDI device. 

Who: This article will interest anyone developing MIDI apps for either Mac OS X, or iOS (iPhone, iPad).

Explanation: The heart of CoreMIDI’s funcionality is encompassed in MIDIServices.h (Apple’s MIDI Services Documention). To keep things simple, our example will look at the Midiman MIDISport 2×2. (BTW, Midiman has since been purchased by M-Audio, so this device is now sold as the M-Audio 2×2). The “2×2” in the name refers to the fact that it has two MIDI In’s and two MIDI Out’s. Let’s look at an overview of how CoreMIDI sees this device:

MIDISport 2x2 MIDIObjects

The first thing to note is that CoreMIDI divides this device into three different types of objects: devices, entities, and endpoints (references as MIDIDeviceRef, MIDIEntityRef, and MIDIEndpointRef). An endpoint is an umbrella term for a source (MIDI IN) or a destination (MIDI Out). Behind the scences, all of these derive from MIDIObjectRef.
There are generally two methods that we can use to iterate through MIDI devices on our system.

  1. Iterate through the list of devices returned by MIDIGetNumberOfDevices()
  2. Iterate through the endpoints returned from MIDIGetNumberOfSources() and MIDIGetNumberOfDestinations()

METHOD ONE

The benefit of using the first method (MIDIGetNumberOfDevices()) is that you can retrieve information for devices that might be offline (not currently connected, but have been in the past). Below is a screenshot of my Audio MIDI Setup (Applications->Utilities->Audio MIDI Setup).

Audio MIDI Setup

In this screenshot you can see I have six devices:

  • SneakyMIDI” is the name I’ve given to the Inter-Application Communication Device (IAC Device) that is included in Mac OS X and allows MIDI applications to talk to one another internally. (You can name the IAC Device anything you’d like). Think of this as a generic virtual device that MIDI apps can send and receive MIDI events from.
  • Network” allows you to setup a MIDI connection over an 802.11 network.
  • USB Trigger Finger“, “Oxygen 8“, and “USB2.0-MIDI” are all devices that I use regularly but are currently not connected, so they appear ghosted and offline.
  • MIDISPORT 2×2” is the only physical device that I currently have connected.

FIRST THINGS FIRST: Once you’ve created your project, be sure to include CoreMIDI.framework.
Click Your Project -> Your Target -> Build Phases -> Link Binary With Libraries -> [ + ] (plus-sign icon)
Add CoreMIDI.framework

Let’s look at some code that gets a handle/reference to these six devices, and displays their respective object names (device, entity, endpoints) via NSLog(). For the sake of brevity, the following code does not contain any error checking.

#import <CoreMIDI/CoreMIDI.h>

NSString *getName(MIDIObjectRef object);

int main(int argc, char *argv[])
{
  // How many MIDI devices do we have?
  ItemCount deviceCount = MIDIGetNumberOfDevices();

  // Iterate through all MIDI devices
  for (ItemCount i = 0 ; i < deviceCount ; ++i) {

    // Grab a reference to current device
    MIDIDeviceRef device = MIDIGetDevice(i);
    NSLog(@"Device: %@", getName(device));

    // Is this device online? (Currently connected?)
    SInt32 isOffline = 0;
    MIDIObjectGetIntegerProperty(device, kMIDIPropertyOffline, &isOffline);
    NSLog(@"Device is online: %s", (isOffline ? "No" : "Yes"));

    // How many entities do we have?
    ItemCount entityCount = MIDIDeviceGetNumberOfEntities(device);

    // Iterate through this device's entities
    for (ItemCount j = 0 ; j < entityCount ; ++j) {

      // Grab a reference to an entity
      MIDIEntityRef entity = MIDIDeviceGetEntity(device, j);
      NSLog(@"  Entity: %@", getName(entity));

      // Iterate through this device's source endpoints (MIDI In)
      ItemCount sourceCount = MIDIEntityGetNumberOfSources(entity);
      for (ItemCount k = 0 ; k < sourceCount ; ++k) {

        // Grab a reference to a source endpoint
        MIDIEndpointRef source = MIDIEntityGetSource(entity, k);
        NSLog(@"    Source: %@", getName(source));
      }

      // Iterate through this device's destination endpoints
      ItemCount destCount = MIDIEntityGetNumberOfDestinations(entity);
      for (ItemCount k = 0 ; k < destCount ; ++k) {

        // Grab a reference to a destination endpoint
        MIDIEndpointRef dest = MIDIEntityGetDestination(entity, k);
        NSLog(@"    Destination: %@", getName(dest));
      }
    }
    NSLog(@"------");
  }
}

NSString *getName(MIDIObjectRef object)
{
  // Returns the name of a given MIDIObjectRef as an NSString
  CFStringRef name = nil;
  if (noErr != MIDIObjectGetStringProperty(object, kMIDIPropertyName, &name))
    return nil;
  return (NSString *)name;
}

Running the code above will result in the following:

Output from NSLog():

Device: Sneaky MIDI
Device is online: Yes
  Entity: SneakyPort
    Source: SneakyPort
    Destination: SneakyPort
------
Device: Network
Device is online: Yes
------
Device: USB Trigger Finger
Device is online: No
  Entity: USB Trigger Finger
    Source: USB Trigger Finger
    Destination: USB Trigger Finger
------
Device: Oxygen 8
Device is online: No
  Entity: Port 1
    Source: Port 1
    Destination: Port 1
------
Device: MIDISPORT 2x2
Device is online: Yes
  Entity: Port A
    Source: Port A
    Destination: Port A
  Entity: Port B
    Source: Port B
    Destination: Port B
------
Device: USB2.0-MIDI
Device is online: No
  Entity: Port 1
    Source: Port 1
    Destination: Port 1
  Entity: Port 2
    Destination: (null)

As you can see, this is the programmatic way of obtaining references/handles to the devices listed in Audio MIDI Setup.

Disadvantages: There are some disadvantages to using this method, namely: MIDIGetNumberOfDevices() will not include virtual sources or destinations created by other MIDI apps. If you need a reference/handle to a virtual endpoint, you’ll need to utilize method two.

METHOD TWO

The second method uses MIDIGetNumberOfSources() and MIDIGetNumberOfDestinations() which returns all the endpoints that are currently online, including virtual sources and destinations. We iterate through these endpoints and grab a reference to them using MIDIGetSource(index) and MIDIGetDestinations(index). Let’s look at some code using this method to get a handle to source and destination endpoints:

#import <CoreMIDI/CoreMIDI.h>

NSString *getDisplayName(MIDIObjectRef object);

int main(int argc, char *argv[])
{
  NSLog(@"Iterate through destinations");
  ItemCount destCount = MIDIGetNumberOfDestinations();
  for (ItemCount i = 0 ; i < destCount ; ++i) {

    // Grab a reference to a destination endpoint
    MIDIEndpointRef dest = MIDIGetDestination(i);
    if (dest != NULL) {
      NSLog(@"  Destination: %@", getDisplayName(dest));
    }
  }

  NSLog(@"Iterate through sources");
  // Virtual sources and destinations don't have entities
  ItemCount sourceCount = MIDIGetNumberOfSources();
  for (ItemCount i = 0 ; i < sourceCount ; ++i) {

    MIDIEndpointRef source = MIDIGetSource(i);
    if (source != NULL) {
      NSLog(@"  Source: %@", getDisplayName(source));
    }
  }
}

NSString *getDisplayName(MIDIObjectRef object)
{
  // Returns the display name of a given MIDIObjectRef as an NSString
  CFStringRef name = nil;
  if (noErr != MIDIObjectGetStringProperty(object, kMIDIPropertyDisplayName, &name))
    return nil;
  return (NSString *)name;
}

Running the code above will result in the following:

Output from NSLog():

Iterate through destinations
  Destination: Sneaky MIDI SneakyPort
  Destination: MIDISPORT 2x2 Port A
  Destination: MIDISPORT 2x2 Port B
  Destination: MIDI Monitor (Untitled)
Iterate through sources
  Source: Sneaky MIDI SneakyPort
  Source: MIDISPORT 2x2 Port A
  Source: MIDISPORT 2x2 Port B

Things to note:

  • This method returns a virtual destination endpoint for “MIDI Monitor (Untitled)” which is a software utility that I happen to be running (MIDI Monitor), whereas the first method did not.
  • Most CoreMIDI functions use CFStrings whenever a string is needed. Both getName() and getDisplayName() utilize something called “toll-free-bridging” where you can simply cast a CFStringRef to an NSString * without issue. Here is what Apple says on it:

    CFString is “toll-free bridged” with its Cocoa Foundation counterpart, NSString. This means that the Core Foundation type is interchangeable in function or method calls with the bridged Foundation object. Therefore, in a method where you see an NSString * parameter, you can pass in a CFStringRef, and in a function where you see a CFStringRef parameter, you can pass in an NSString instance.

Conclusion

Both of these methods for obtaining references to MIDI endpoints have their advantages and disadvantages. In a real world app, you can utilize method two since you can’t send MIDI events to endpoints that are offline. For more robust options, you can combine methods one and two and display a list of all device online, offine, virtual, and physical.

Author: James Vanaria

  31 Responses to “How to access MIDI devices with CoreMIDI”

  1. Very cool brother.
    Nice easy explanation & example on starting to work with CoreMidi.

    Look forward to more from you ;o)

  2. Very usefull, I searched for this explanation.
    I bought a USB Uno MIDI Interface, manuf=M-Audio, model=USB Uno MIDI Interface.
    The only thing I like to do is to read incomming notes on / off to trigger light cues.

    thanks Jim

    John

    PS my site is verry old (5 years) and not representive any more.

  3. What a great web log. I spend hours on the net reading blogs, about tons of various subjects. I have to first of all give praise to whoever created your theme and second of all to you for writing what i can only describe as an fabulous article. I honestly believe there is a skill to writing articles that only very few posses and honestly you got it. The combining of demonstrative and upper-class content is by all odds super rare with the astronomic amount of blogs on the cyberspace.

  4. Thank you for the tutorial. Very well done.

    Just to be sure.. In the first example, while iterating through the source endpoints, the code does:
    MIDIEntityGetSource(entity, 0);

    Shouldn’t that be MIDIEntityGetSource(entity, k);?

    Same comment for the destination endpoints, and shouldn’t it be MIDIEntityGetDestination() instead of MIDIEntityGetSource()?

    Thanks!

  5. hi, first, I am very sorry my english is poor. I hope you can help me. In example 1, MIDIGetNumberOfDevices() return 2 and the log is follow:
    Device: IAC Driver
    Device is online: No
    Entity: Bus 1
    Source: Bus 1
    Destination: Bus 1

    and in example 2, MIDIGetNumberOfDestinations() and MIDIGetNumberOfSources() are all return 0, do you konw why? Thand you for you help.

    • Your log shows: “Device is online: No” which tells me that you need to put that IAC Driver online. Try the following:

      * Launch Applications->Utilities->AudioMIDI Setup
      * Single Click on “IAC Driver”
      * Click “Show Info”
      * Then make sure “Device is online” is checked.

      • First thank you for you help very much, but I can’t find the IAC Driver in AudioMIDI Setup app. There are two driver “Built-in Input” and “Built-in Output”, they are all no “Devic is online” checked option. My PC is MacBook Pro and OS version is 10.6.8, and not other MIDI Device. Do you know how can i find the IAC Driver? Best Regards!!

  6. Hi! 1st of all, excellent article, I’ve found it very very useful.
    Im kinda beginner on coremidi, and wanted to experiment a little bit.

    I’m trying to build a client that reads the input from a korg nanoKEY2 keyboard (done!) and bypass the midipackets to a “virtual entity”.
    So I’ve added, by code, a virtual entity to the korg device, which has an input (the one im going to send the midipackets):
    MIDIDeviceAddEntity(device, (CFStringRef)@”BDEntity test”, true, 1, 0, &virtualEntity);

    Everything works like a charm, I can see the new entity at the Audio MIDI Setup inside the korg keyboard settings, but when I try to connect to it in order to send packages later, it fails.
    Here’s the code and the error message:

    //creating the client:
    OSStatus s = MIDIClientCreate((CFStringRef)@"MIDI Client", ClientNotifyProc, self, &client);
    NSLogError(s, @"Creating MIDI client");
    //creating the outputport
    s = MIDIOutputPortCreate(client, (CFStringRef)@"Output Port", &virtualOutputPort);
    NSLogError(s, @"Creating MIDI output port");
    //creating the endpoint
    virtualOutputEndpoint = MIDIEntityGetSource(virtualEntity, 0);
    //connecting to the source
    s= MIDIPortConnectSource(virtualOutputPort, virtualOutputEndpoint, NULL);//<--fail!
    NSLogError(s, @"Create MIDI output port connect source");

    when it tries to connect to the source, it fails horribly, and this is the message:
    Error (Create MIDI output port connect source): -50:Error Domain=NSMachErrorDomain Code=-50 “The operation couldn’t be completed. (Mach error -50.)”

    The rest looks fine, or at least thats what it looks like, no null pointer on anything being created, and actually, the creation of the endpoint returns something different than null, so I can assume its working ok til that point. Do you know why could this be happening?
    Thanks in advance, and thanks again for such a wonderful article!

    Brian

    • Ok, I’ve found that it seems to be that I dont need to connect to an endpoint in order to send midipackets, so this line

      s= MIDIPortConnectSource(virtualOutputPort, virtualOutputEndpoint, NULL);//<--fail!
      NSLogError(s, @"Create MIDI output port connect source");

      is worthless.
      Now I’m resending the packages using this

      OSStatus s = MIDISend(virtualOutputPort, virtualOutputEndpoint, pktlist);
      NSLogError(s, @"Sending MIDI");

      and it wont complain, the call is successful.
      The thing is, I’m using Reason in order to listen to what I’m playing, so I’ve configured on Reason that the input keyboard is the input I had previously created by code (the one im sending the midipackets to). So, I select it on Reason, but I wont see any reaction on the Reason side. If I set again that the input keyboard is the korg keyboard, it works, and I can hear the drums.
      Any idea what could it be? Is it ok to send packages to an input entity? could it be the reason why its not working?
      Thanks in advance

      • Solved the problem using:

        OSStatus s = MIDIReceived(virtualOutputEndpoint, pktlist);
        NSLogError(s, @"Sending MIDI");

        instead of:

        OSStatus s = MIDISend(virtualOutputPort, virtualOutputEndpoint, pktlist);
        NSLogError(s, @"Sending MIDI");

        Will upload the code later in case anybody is interested in doing something like this!

  7. Hi, thanks for a great article on CoreMIDI!

    I´m just wondering if it is possible to use MIDISend(…) to send MIDI data over Bluetooth (using GameKit on iOS). I guess for this to work the Bluetooth connection must act as a MIDI endpoint in some way. Does anybody know if this is possible?

    Thanks!

    • the Bluetooth connection must act as a MIDI endpoint

      Absolutely correct. So if you had a Bluetooth MIDI device, it would show up just like any other MIDI device. This is similar to USB, we send MIDI over USB all the time, but we don’t need to access an USB-specific protocols, CoreMIDI handles that for us. So the difficult part: finding a bluetooth MIDI device, -or- creating a MIDI driver that utilizes the bluetooth bus.

  8. Hi, you said that Sneaky MIDI is a virtual device that can receive and send MIDI events.
    What happens if I send a message note_on to this device? Will It play that note?
    Is possible to use this device to play MIDI without an external device?
    If possible can you post an example where a note on message is played? (is incredible that there is no such example in the whole web)
    Thanks in advance.

    • Think of the internal MIDI bus (SneakyMIDI in this example) as a virtual MIDI cable connecting two pieces of software. The device itself does not produce any sound. So if you were to send a NoteOn message to SneakyMIDI, nothing would happen because nothing is connected “on the other side”. But imagine this scenario: you have two apps running (App-A and App-B). Set App-A’s MIDI-Out to SneakyMIDI. Set App-B’s MIDI-In to SneakyMIDI. Now you’ve made a virtual connection from App-A to App-B, so whenever you send a NoteOn message from App-A, it will appear in App-B. Make sense?

      • Yes, It make sense. Thanks for the answer.
        But if I receive a note_on MIDI message which library is the more indicated to play this note if I don’t have any external device? In other words, how can I generate the sound of the note?
        (I don’t want to use sample files.)

        • Apple used to include a “Quicktime Musical Instrument” where you could trigger sounds, but that has been replaced by AudioUnit Instruments. Sadly, I don’t really have any knowledge of programming with AudioUnits.

  9. This is a part of what I am looking for, but where do I put this code? Do I put this in the default .h or .m file or do I create a new .h or .m file?
    Thanks

    • No, you are not required to put this into a new .h/.m (class) file, though it would make sense to create a new class that handles just the MIDI aspects of your app. This code can be run from within a class that you create/call, or within “main” (default).

  10. I have been searching everywhere for MIDI information. This is just about the first example I have found. But, it doesn’t work at all for me. I am wondering if the latest versions xcode etc. have changed anything that anyone knows about?

    If I add this line of code:
    NSString *getDisplayName(MIDIObjectRef object);

    I get errors like this:

    Undefined symbols for architecture x86_64:
    “_MIDIGetNumberOfDestinations”, referenced from:
    -[AppDelegate updateUserInterface] in AppDelegate.o
    ld: symbol(s) not found for architecture x86_64
    clang: error: linker command failed with exit code 1 (use -v to see invocation)

    No clue what that is all about. Any help would be appreciated.

    • Thanks for pointing this out, looks like I failed to mention that you’ll need to link against the CoreMIDI framework. I’ve updated the tutorial with a new screenshot showing how to do this. Best of luck!

  11. Hi
    I have a MIDI pedalboard FCB1010 connected to my macbook through an Komplete Audio 6 interface. When I run the above code I discover the Komplete interface (and other interfaces I had connected but are offline) but I can not see the pedalboard. Is there a way to discover the pedalboard?

    I am trying to do an Editor for the pedalboard and I’m having a hard time trying to recognise the device.

    Thank you !!!

    • Sorry for the late reply, but I don’t believe you’ll be able to see “second level” devices (i.e. the FCB1010), only the devices directly connected (i.e. the Audio 6) to the mac, using the method I’ve outlined above. I so seem to remember being able to see those devices if you’ve set up your MIDI configuration via the “Audio MIDI Setup”.

  12. Anybody can teach me MIDI programming in Xcode.

    Thanks

 Leave a Reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

(required)

(required)