高度な検索
Developer Connection
Member Login ログイン | ご入会 ADC連絡先

Technical Q&A QA1374
Obtaining the name of an external MIDI Device from a MIDI Endpoint

Q:Panther で、MIDI オブジェクトから外部 MIDI デバイスの名前を取得しようとしていますが、代わりに MIDI ポートの名前を受け取ります。Jaguar では問題なく動作していたように思われます。Panther において MIDI エンドポイントから外部 MIDI デバイスの名前を取得するにはどうすればいいのですか。

A:Panther と Jaguar では、MIDI デバイスの取り扱いが異なります。

Jaguar (10.2) では、外部 MIDI デバイスはデバイス名だけがユーザに提示されました。ユーザは Audio MIDI 設定(AMS)ユーティリティによってシステムで利用可能な MIDI デバイスが提示され、MIDI デバイスの現在の設定を記述することができます。アプリケーション内部からは、MIDIObjectGetStringProperty メソッドと kMIDIPropertyName 定数を使用して、MIDI オブジェクトからデバイス名を取得することができます。

Panther (10.3) では、マルチポートを装備した外部 MIDI デバイスをサポートするために、CoreMIDI の機能が拡張されました。マルチポートを装備したデバイスの好例は、複数の MIDI 入力を備えたシンセサイザです。外部 MIDI デバイスは複数のエンドポイントとエンティティを持つことができるため、デバイス名を見つけるには若干の作業が必要になります。MIDI Object に単にデバイス名を照会する方法の代わりに、適切なデバイス名を取得するには、MIDI エンドポイント、エンティティ、および外部デバイスをすべてチェックする必要があります。これを以下のコードに示します。

リスト 1:エンドポイント名の取得


// 接続をたどって、エンドポイントの名前を取得する。
// 結果は呼び出し側で解放する。
static CFStringRef ConnectedEndpointName(MIDIEndpointRef endpoint)
{
  CFMutableStringRef result = CFStringCreateMutable(NULL, 0);
  CFStringRef str;
  OSStatus err;

  // エンドポイントに接続があるか?
  CFDataRef connections = NULL;
  int nConnected = 0;
  bool anyStrings = false;
  err = MIDIObjectGetDataProperty(endpoint, kMIDIPropertyConnectionUniqueID, &connections);
  if (connections != NULL) {
    // 接続があるので、それらをたどる
    // 接続されているすべてのデバイスの名前を連結する
    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) {
           // 外部デバイスのエンドポイントに接続している(10.3 以降)。
           str = EndpointName(static_cast<MIDIEndpointRef>(connObject), true);
        } else {
             // 外部デバイス(または他の何か。他のすべての場合をキャッチ)に接続する(10.2)
          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;
  
  // エンドポイントに接続がないか、それらの名前をいずれも取得できなかった場合はここに到達
  return EndpointName(endpoint, false);
}



//////////////////////////////////////
// 接続の有無に関わらず、エンドポイントの名前を取得する。
// 結果は呼び出し側で解放する。
static CFStringRef EndpointName(MIDIEndpointRef endpoint, bool isExternal)
{
  CFMutableStringRef result = CFStringCreateMutable(NULL, 0);
  CFStringRef str;
  
  // エンドポイントの名前から始める
  str = NULL;
  MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &str);
  if (str != NULL) {
    CFStringAppend(result, str);
    CFRelease(str);
  }

  MIDIEntityRef entity = NULL;
  MIDIEndpointGetEntity(endpoint, &entity);
  if (entity == NULL)
    // おそらく仮想
    return result;
    
  if (CFStringGetLength(result) == 0) {
    // エンドポイント名の長さがゼロ -- エンティティを試す
    str = NULL;
    MIDIObjectGetStringProperty(entity, kMIDIPropertyName, &str);
    if (str != NULL) {
      CFStringAppend(result, str);
      CFRelease(str);
    }
  }
  // 今度は、デバイスの名前を検討する
  MIDIDeviceRef device = NULL;
  MIDIEntityGetDevice(entity, &device);
  if (device == NULL)
    return result;
  
  str = NULL;
  MIDIObjectGetStringProperty(device, kMIDIPropertyName, &str);
  if (str != NULL) {
    // 外部デバイスに 1 つのエンティティしかない場合は、
                // エンドポイント名を破棄して、デバイス名のみを使用する
    if (isExternal && MIDIDeviceGetNumberOfEntities(device) < 2) {
      CFRelease(result);
      return str;
    } else {
      // エンティティ名の先頭にデバイス名があるか?
                        // (そうなっているべきではないのだが、一部のドライバはそうなっている)
      // そうだとしたら、先頭に追加しない
      if (CFStringCompareWithOptions(str /* デバイス名 */,
                                    result /* エンドポイント名 */,
                                         CFRangeMake(0, CFStringGetLength(str)), 0) != kCFCompareEqualTo) {
        // エンティティ名の先頭にデバイス名を追加する
        if (CFStringGetLength(result) > 0)
          CFStringInsert(result, 0, CFSTR(" "));
        CFStringInsert(result, 0, str);
      }
      CFRelease(str);
    }
  }
  return result;
}

重要:このコードは、/Developer/Examples/CoreAudio/PublicUtility にある CoreAudio SDK ファイル「CAMIDIEndpoints.cpp」から引用しました。開発者の皆様には、オーディオおよび MIDI アプリケーションの開発に CoreAudio SDK を使用することを推奨します。

重要リンク

*ADC Audio Developer page

*CoreAudio SDK

先頭に戻る

ドキュメントの改訂履歴

日付 メモ
2004-12-01 初版

掲載日: 2004-12-01