Sending MIDI using MIDISendEventList() in C/C++

489 views Asked by At

I've been working to migrate an older Core MIDI sending implementation to send MIDI 1.0 messages using Apple's newer UMP-aware MIDI Event List API methods.

I've figured out code that runs and should output MIDI clock messages, but when I send it with MIDISendEventList(...) I see nothing being output from my MIDI interface; there's also no error returned from that method to indicate what the problem is.

Here is the code I'm using:

const ByteCount clockMessageSize = 1;
const UInt32 clockMessage[clockMessageSize] = { (UInt32)0xF8 };    // MIDI clock tick 
const MIDITimeStamp timeStamp = mach_absolute_time();
MIDIEventList clockMessageEventList = {};
MIDIEventPacket* clockMessageEventListEndPacket = nullptr;

clockMessageEventListEndPacket = MIDIEventListInit(&clockMessageEventList, kMIDIProtocol_1_0);
clockMessageEventListEndPacket = MIDIEventListAdd(&clockMessageEventList, sizeof(MIDIEventList::packet), clockMessageEventListEndPacket, timeStamp, clockMessageSize, clockMessage);
                
for (NSUInteger endpointRefIndex = 0; endpointRefIndex < endPointRefsCount; ++endpointRefIndex) {
    MIDIObjectRef destinationEndpoint = endPointRefs[endpointRefIndex];
    OSStatus midiSendError = MIDISendEventList(outputPortRef, destinationEndpoint, &clockMessageEventList);
                    
    if (midiSendError != noErr) {
        printf("MIDISendEventList error: %i", (int)midiSendError);
    }
 }

Inspecting clockMessageEventList.packet after it has been configured but before it is sent shows:

(248, 0, 0, [... all zeros to index 63]) 

Does anyone know where I'm going wrong?

2

There are 2 answers

0
adamjansch On BEST ANSWER

With a bit of additional direction provided by https://stackoverflow.com/a/74248460/8653957 I managed to crack this.

For some reason, the word created for the MIDI clock message needs to be formatted slightly differently, with the first byte being 0x10, and the second byte being the MIDI clock message:

const ByteCount clockMessageSize = 1;
const UInt32 clockMessage[clockMessageSize] = { 0x10000000 | ((UInt32)message << 16) };
const MIDITimeStamp timeStamp = mach_absolute_time();
MIDIEventList clockMessageEventList = {};
MIDIEventPacket* clockMessageEventListEndPacket = nullptr;

clockMessageEventListEndPacket = MIDIEventListInit(&clockMessageEventList, kMIDIProtocol_1_0);
clockMessageEventListEndPacket = MIDIEventListAdd(&clockMessageEventList, sizeof(MIDIEventList::packet), clockMessageEventListEndPacket, timeStamp, clockMessageSize, clockMessage);

If anyone know why this is, please post it! But here you go, MIDI clock via MIDIEventList.

0
SJK On

I don't have enough reputation to reply to the reply asking why the 0x10 fix is necessary, but the reason you need 0x10 is because the event list uses a "universal MIDI packet" format instead of the standard MIDI format.

This format needs a "message type" in the first two bytes - 0x10 is classified as "MIDI 1.0 - System Real Time and System Common Messages". MIDI 1.0 also supports 0x20 (channel voice messages, such as a note on) and 0x30 (data messages for SYSEX).

CoreMIDI also provides functions to generate these 32-bit values, e.g. MIDI1UPSystemCommon would take care of the 0x10 for you, avoiding the need to manually shift bits.