Obtaining the name of an external MIDI Device from a MIDI Endpoint

Q: In Panther, I am attempting to retrieve the name of an external MIDI device from a MIDI Object, but I get the name of the MIDI port instead. This seemed to work fine in Jaguar. How do I get the name of an external MIDI Device from a MIDI Endpoint in Panther?

A: Panther and Jaguar handle MIDI devices differently.

In Jaguar (10.2), external MIDI devices were presented to the user with only the device name. Users were presented the available MIDI devices in the system via the Audio MIDI Setup (AMS) utility, where they can describe the current configuration of MIDI devices. From within an application, the device name can be obtained from a MIDI object by using the method MIDIObjectGetStringProperty with the constant kMIDIPropertyName.

In Panther (10.3), the features of CoreMIDI were expanded to support external MIDI devices with multiple ports. A good example of a device with multiple ports is a synthesizer with more than one MIDI input. Now external MIDI devices can have multiple endpoints and entities, which requires a bit more work to find the device name. Instead of querying just the MIDI Object for the device name, MIDI endpoints, entities and external devices must all be checked to obtain the appropriate device name. This is shown in the code below .

Listing 1: Getting an endpoint name



// Obtain the name of an endpoint, following connections.
// The result should be released by the caller.
static CFStringRef ConnectedEndpointName(MIDIEndpointRef endpoint)
{
  CFMutableStringRef result = CFStringCreateMutable(NULL, 0);
  CFStringRef str;
  OSStatus err;

  // Does the endpoint have connections?
  CFDataRef connections = NULL;
  int nConnected = 0;
  bool anyStrings = false;
  err = MIDIObjectGetDataProperty(endpoint, kMIDIPropertyConnectionUniqueID, &connections);
  if (connections != NULL) {
    // It has connections, follow them
    // Concatenate the names of all connected devices
    nConnected = CFDataGetLength(connections) / sizeof(MIDIUniqueID);
    if (nConnected) {
        const SInt32 *pid = reinterpret_cast<const SInt32 *>(CFDataGetBytePtr(connections));
      for (int i = 0; i < nConnected; ++i, ++pid) {
         MIDIUniqueID id = EndianS32_BtoN(*pid);
         MIDIObjectRef connObject;
         MIDIObjectType connObjectType;
         err = MIDIObjectFindByUniqueID(id, &connObject, &connObjectType);
         if (err == noErr) {
        if (connObjectType == kMIDIObjectType_ExternalSource  ||
                                                      connObjectType == kMIDIObjectType_ExternalDestination) {
           // Connected to an external device's endpoint (10.3 and later).
           str = EndpointName(static_cast<MIDIEndpointRef>(connObject), true);
        } else {
             // Connected to an external device (10.2) (or something else, catch-all)
          str = NULL;
          MIDIObjectGetStringProperty(connObject, kMIDIPropertyName, &str);
        }
        if (str != NULL) {
          if (anyStrings)
            CFStringAppend(result, CFSTR(", "));
          else anyStrings = true;
          CFStringAppend(result, str);
          CFRelease(str);
        }
         }
      }
    }
    CFRelease(connections);
  }
  if (anyStrings)
    return result;

  // Here, either the endpoint had no connections, or we failed to obtain names for any of them.
  return EndpointName(endpoint, false);
}



//////////////////////////////////////
// Obtain the name of an endpoint without regard for whether it has connections.
// The result should be released by the caller.
static CFStringRef EndpointName(MIDIEndpointRef endpoint, bool isExternal)
{
  CFMutableStringRef result = CFStringCreateMutable(NULL, 0);
  CFStringRef str;

  // begin with the endpoint's name
  str = NULL;
  MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &str);
  if (str != NULL) {
    CFStringAppend(result, str);
    CFRelease(str);
  }

  MIDIEntityRef entity = NULL;
  MIDIEndpointGetEntity(endpoint, &entity);
  if (entity == NULL)
    // probably virtual
    return result;

  if (CFStringGetLength(result) == 0) {
    // endpoint name has zero length -- try the entity
    str = NULL;
    MIDIObjectGetStringProperty(entity, kMIDIPropertyName, &str);
    if (str != NULL) {
      CFStringAppend(result, str);
      CFRelease(str);
    }
  }
  // now consider the device's name
  MIDIDeviceRef device = NULL;
  MIDIEntityGetDevice(entity, &device);
  if (device == NULL)
    return result;

  str = NULL;
  MIDIObjectGetStringProperty(device, kMIDIPropertyName, &str);
  if (str != NULL) {
    // if an external device has only one entity, throw away
                // the endpoint name and just use the device name
    if (isExternal && MIDIDeviceGetNumberOfEntities(device) < 2) {
      CFRelease(result);
      return str;
    } else {
      // does the entity name already start with the device name?
                        // (some drivers do this though they shouldn't)
      // if so, do not prepend
      if (CFStringCompareWithOptions(str /* device name */,
                                    result /* endpoint name */,
                                         CFRangeMake(0, CFStringGetLength(str)), 0) != kCFCompareEqualTo) {
        // prepend the device name to the entity name
        if (CFStringGetLength(result) > 0)
          CFStringInsert(result, 0, CFSTR(" "));
        CFStringInsert(result, 0, str);
      }
      CFRelease(str);
    }
  }
  return result;
}

IMPORTANT: This code was taken from the CoreAudio SDK file CAMIDIEndpoints.cpp in /Developer/Examples/CoreAudio/PublicUtility. Developers are encouraged to use the CoreAudio SDK for audio and midi application development.

Important Links

*ADC Audio Developer page

*CoreAudio SDK

Back to Top 

Document Revision History

DateNotes
2004-12-01Get the names of external MIDI Devices from MIDI Endpoints.

Posted: 2004-12-01


Did this document help you?
Yes: Tell us what works for you.
It’s good, but: Report typos, inaccuracies, and so forth.
It wasn’t helpful: Tell us what would have helped.