MIDI 2.0受信プログラム

Mac + C++ + Core MIDIでMIDI 2.0 UMPを受信するプログラムです。 Macに接続されたMIDI 2.0機器からUMP(Universal MIDI Packet)を受信し、 受信したデータを16進数で表示します。

MIDI 2.0機器から送信されたMIDI 2.0のメッセージは、macOSのCore MIDIフレームワークを通してアプリケーションに渡されます。

アプリケーションは、Core MIDIからMIDIEventListという構造体を受け取ります。

MIDIEventListには、1つ以上のMIDIEventPacketが含まれています。

MIDIEventPacketには、イベントのタイムスタンプと、UMP(Universal MIDI Packet)のデータが格納されています。

UMPのデータはwords[]から取り出すことができます。 1つのMIDIEventPacketに複数のUMPが含まれる場合があります。

アプリケーションでは、words[]からUMPを取り出し、Message Type(MT)などを調べて、MIDI 2.0メッセージの内容を解析します。 このプログラムでは、MT4のメッセージをそのままバイナリでコンソールに表示しています。



#include <iostream>
#include <iomanip>
#include <CoreMIDI/CoreMIDI.h>

// MIDI 2.0受信コールバック
static void processMIDIEvent(const MIDIEventList *eventList)
{
    if (eventList == nullptr) return;

    const MIDIEventPacket *packet = &eventList->packet[0];
    // eventListの中のパケットの数だけ処理する
    for (UInt32 i = 0; i < eventList->numPackets; ++i)
    {
        // パケットの中のワードの数だけ処理する
        for (UInt32 w = 0; w < packet->wordCount; ++w)
        {
            uint32_t word = packet->words[w];

            std::cout
                << "0x"
                << std::hex
                << std::setw(8)
                << std::setfill('0')
                << word
                << " ";
        }
        std::cout
            << std::dec
            << std::endl;

        packet = MIDIEventPacketNext(packet);
    }
}

int main()
{
    MIDIClientRef client = 0;
    MIDIPortRef inputPort = 0;

    OSStatus status;

    // MIDI Clientを作成
    status = MIDIClientCreateWithBlock(
        CFSTR("UMP Receiver"),
        &client,
        nullptr);

    if (status != noErr)
    {
        std::cerr
            << "MIDIClientCreateWithBlock failed: "
            << status << std::endl;
        return 1;
    }

    // MIDI 2.0用Input Port
    status = MIDIInputPortCreateWithProtocol(
        client,
        CFSTR("UMP Input"),
        kMIDIProtocol_2_0,
        &inputPort,
        ^(const MIDIEventList *eventList,
          void *srcConnRefCon) {
          processMIDIEvent(eventList);
        });

    if (status != noErr)
    {
        std::cerr
            << "MIDIInputPortCreateWithProtocol failed: "
            << status << std::endl;
        return 1;
    }

    // MIDI Portの名前を列記する
    ItemCount sourceCount = MIDIGetNumberOfSources();

    std::cout
        << "MIDI Sources: "
        << sourceCount
        << std::endl;

    for (ItemCount i = 0; i < sourceCount; ++i)
    {

        MIDIEndpointRef source = MIDIGetSource(i);
        CFStringRef name = nullptr;

        MIDIObjectGetStringProperty(
            source,
            kMIDIPropertyName,
            &name);

        char nameBuffer[256] = {};

        if (name != nullptr)
        {
            CFStringGetCString(
                name,
                nameBuffer,
                sizeof(nameBuffer),
                kCFStringEncodingUTF8);

            CFRelease(name);
        }

        std::cout
            << "[" << i << "] "
            << nameBuffer
            << std::endl;
    }

    // Source番号を入力
    int isourcenum;

    std::cout << "Select MIDI Source: ";
    std::cin >> isourcenum;
    {
        
        // isourcenum番目のMIDI Sourceに接続
        MIDIEndpointRef source = MIDIGetSource(isourcenum);

        status = MIDIPortConnectSource(
            inputPort,
            source,
            nullptr);

        if (status != noErr)
        {
            std::cerr
                << "MIDIPortConnectSource failed: "
                << status << std::endl;
            return 0;
        }
    }

    std::cout << std::endl;
    std::cout << "Waiting for MIDI 2.0 UMP..." << std::endl;
    std::cout << "Press Ctrl+C to exit." << std::endl;

    // プロセスを終了させない
    while (true)
    {
        CFRunLoopRunInMode(
            kCFRunLoopDefaultMode,
            1.0,
            false);
    }

    return 0;
}

実行すると以下のように表示します。MIDI2.0のデバイスは「MIDI 2.0デバイスを試作」で作ったRaspberry Pi Zero 2Wです。

> ./rcvmidi2  
MIDI Sources: 5
[0] Bus1
[1] Bus2
[2] NetworkMIDI2
[3] Monosynth
[4] MIDI 2.0
Select MIDI Source: 4

Waiting for MIDI 2.0 UMP...
Press Ctrl+C to exit.
0x40902711 0xc0752345 
0x40802711 0x20752345 
0x40902811 0xc07a2345 
0x40802811 0x207a2345 
0x40902911 0xc0802345 
0x40802911 0x20802345 
0x40902a11 0xc0852345 
0x40802a11 0x20852345 
0x40902b11 0xc08a2345 
0x40802b11 0x208a2345 
0x40902c11 0xc08f2345 
0x40802c11 0x208f2345 
0x40902d11 0xc0942345 
0x40802d11 0x20942345 
0x40902e11 0xc0992345