Skip to content

Commit

Permalink
Bug fix for sysex messages longer than 64K in CoreMIDI API.
Browse files Browse the repository at this point in the history
  • Loading branch information
garyscavone committed Aug 23, 2021
1 parent 3fe256b commit 6e4e763
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 47 deletions.
58 changes: 31 additions & 27 deletions RtMidi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1471,50 +1471,54 @@ void MidiOutCore :: sendMessage( const unsigned char *message, size_t size )
return;
}

MIDITimeStamp timeStamp = AudioGetCurrentHostTime();
CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
OSStatus result;

if ( message[0] != 0xF0 && nBytes > 3 ) {
errorString_ = "MidiOutCore::sendMessage: message format problem ... not sysex but > 3 bytes?";
error( RtMidiError::WARNING, errorString_ );
return;
}

Byte buffer[nBytes+(sizeof( MIDIPacketList ))];
MIDITimeStamp timeStamp = AudioGetCurrentHostTime();
CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
OSStatus result;

ByteCount bufsize = nBytes > 65535 ? 65535 : nBytes;
Byte buffer[bufsize+16]; // pad for other struct members
ByteCount listSize = sizeof( buffer );
MIDIPacketList *packetList = (MIDIPacketList*)buffer;
MIDIPacket *packet = MIDIPacketListInit( packetList );

ByteCount remainingBytes = nBytes;
while ( remainingBytes && packet ) {
ByteCount bytesForPacket = remainingBytes > 65535 ? 65535 : remainingBytes; // 65535 = maximum size of a MIDIPacket
while ( remainingBytes ) {
MIDIPacket *packet = MIDIPacketListInit( packetList );
// A MIDIPacketList can only contain a maximum of 64K of data, so if our message is longer,
// break it up into chunks of 64K or less and send out as a MIDIPacketList with only one
// MIDIPacket. Here, we reuse the memory allocated above on the stack for all.
ByteCount bytesForPacket = remainingBytes > 65535 ? 65535 : remainingBytes;
const Byte* dataStartPtr = (const Byte *) &message[nBytes - remainingBytes];
packet = MIDIPacketListAdd( packetList, listSize, packet, timeStamp, bytesForPacket, dataStartPtr );
remainingBytes -= bytesForPacket;
}

if ( !packet ) {
errorString_ = "MidiOutCore::sendMessage: could not allocate packet list";
error( RtMidiError::DRIVER_ERROR, errorString_ );
return;
}
if ( !packet ) {
errorString_ = "MidiOutCore::sendMessage: could not allocate packet list";
error( RtMidiError::DRIVER_ERROR, errorString_ );
return;
}

// Send to any destinations that may have connected to us.
if ( data->endpoint ) {
result = MIDIReceived( data->endpoint, packetList );
if ( result != noErr ) {
errorString_ = "MidiOutCore::sendMessage: error sending MIDI to virtual destinations.";
error( RtMidiError::WARNING, errorString_ );
// Send to any destinations that may have connected to us.
if ( data->endpoint ) {
result = MIDIReceived( data->endpoint, packetList );
if ( result != noErr ) {
errorString_ = "MidiOutCore::sendMessage: error sending MIDI to virtual destinations.";
error( RtMidiError::WARNING, errorString_ );
}
}
}

// And send to an explicit destination port if we're connected.
if ( connected_ ) {
result = MIDISend( data->port, data->destinationId, packetList );
if ( result != noErr ) {
errorString_ = "MidiOutCore::sendMessage: error sending MIDI message to port.";
error( RtMidiError::WARNING, errorString_ );
// And send to an explicit destination port if we're connected.
if ( connected_ ) {
result = MIDISend( data->port, data->destinationId, packetList );
if ( result != noErr ) {
errorString_ = "MidiOutCore::sendMessage: error sending MIDI message to port.";
error( RtMidiError::WARNING, errorString_ );
}
}
}
}
Expand Down
40 changes: 20 additions & 20 deletions tests/sysextest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

void usage( void ) {
std::cout << "\nuseage: sysextest N\n";
std::cout << " where N = length of sysex message to send / receive.\n\n";
std::cout << " where N = length of sysex data to send / receive.\n\n";
exit( 0 );
}

Expand All @@ -37,7 +37,7 @@ void mycallback( double deltatime, std::vector< unsigned char > *message, void *
for ( unsigned int i=0; i<nBytes; i++ )
std::cout << "Byte " << i << " = " << (int)message->at(i) << ", ";
if ( nBytes > 0 )
std::cout << "stamp = " << deltatime << std::endl;
std::cout << "# of bytes = " << nBytes << ", stamp = " << deltatime << std::endl;
}

int main( int argc, char *argv[] )
Expand Down Expand Up @@ -67,28 +67,28 @@ int main( int argc, char *argv[] )
try {
if ( chooseMidiPort( midiin ) == false ) goto cleanup;
if ( chooseMidiPort( midiout ) == false ) goto cleanup;
}
catch ( RtMidiError &error ) {
error.printMessage();
goto cleanup;
}

midiin->setCallback( &mycallback );

message.push_back( 0xF6 );
midiout->sendMessage( &message );
SLEEP( 500 ); // pause a little
midiin->setCallback( &mycallback );

// Create a long sysex message of numbered bytes and send it out ... twice.
for ( int n=0; n<2; n++ ) {
message.clear();
message.push_back( 240 );
for ( i=0; i<nBytes; i++ )
message.push_back( i % 128 );
message.push_back( 247 );
message.push_back( 0xF6 );
midiout->sendMessage( &message );

SLEEP( 500 ); // pause a little

// Create a long sysex message of numbered bytes and send it out ... twice.
for ( int n=0; n<2; n++ ) {
message.clear();
message.push_back( 240 );
for ( i=0; i<nBytes; i++ )
message.push_back( i % 128 );
message.push_back( 247 );
midiout->sendMessage( &message );

SLEEP( 500 ); // pause a little
}
}
catch ( RtMidiError &error ) {
error.printMessage();
goto cleanup;
}

// Clean up
Expand Down

0 comments on commit 6e4e763

Please sign in to comment.