Commit 02326225 authored by Christian Schoenebeck's avatar Christian Schoenebeck
Browse files

CoreMIDI driver fix: A MIDIPacket can contain more than 1 event.

parent 4e5e7a11
......@@ -26,6 +26,25 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
using Jack::JackCoreMidiInputPort;
/**
* Takes a MIDI status byte as argument and returns the expected size of the
* associated MIDI event. Returns -1 on invalid status bytes AND on variable
* size events (SysEx events).
*/
inline static int _expectedEventSize(const unsigned char& byte) {
if (byte < 0x80) return -1; // not a valid status byte
if (byte < 0xC0) return 3; // note on/off, note pressure, control change
if (byte < 0xE0) return 2; // program change, channel pressure
if (byte < 0xF0) return 3; // pitch wheel
if (byte == 0xF0) return -1; // sysex message (variable size)
if (byte == 0xF1) return 2; // time code per quarter frame
if (byte == 0xF2) return 3; // sys. common song position pointer
if (byte == 0xF3) return 2; // sys. common song select
if (byte == 0xF4) return -1; // sys. common undefined / reserved
if (byte == 0xF5) return -1; // sys. common undefined / reserved
return 1; // tune request, end of SysEx, system real-time events
}
JackCoreMidiInputPort::JackCoreMidiInputPort(double time_ratio,
size_t max_bytes,
size_t max_messages):
......@@ -79,6 +98,12 @@ JackCoreMidiInputPort::ProcessCoreMidi(const MIDIPacketList *packet_list)
size_t size = packet->length;
assert(size);
jack_midi_event_t event;
// In a MIDIPacket there can be more than one (non SysEx) MIDI event.
// However if the packet contains a SysEx event, it is guaranteed that
// there are no other events in the same MIDIPacket.
int k = 0; // index of the current MIDI event within current MIDIPacket
int eventSize = 0; // theoretical size of the current MIDI event
int chunkSize = 0; // actual size of the current MIDI event data consumed
// XX: There might be dragons in my spaghetti. This code is begging
// for a rewrite.
......@@ -108,41 +133,62 @@ JackCoreMidiInputPort::ProcessCoreMidi(const MIDIPacketList *packet_list)
event.buffer = sysex_buffer;
event.size = sysex_bytes_sent;
sysex_bytes_sent = 0;
k = size; // don't loop in a MIDIPacket if its a SysEx
goto send_event;
}
goto get_next_packet;
}
parse_event:
if (data[0] == 0xf0) {
if (data[k+0] == 0xf0) {
// Must actually never happen, since CoreMIDI guarantees a SysEx
// message to be alone in one MIDIPaket, but safety first. The SysEx
// buffer code is not written to handle this case, so skip packet.
if (k != 0) {
jack_error("JackCoreMidiInputPort::ProcessCoreMidi - Non "
"isolated SysEx message in one packet, discarding.");
goto get_next_packet;
}
if (data[size - 1] != 0xf7) {
goto buffer_sysex_bytes;
}
}
// regular status byte ?
if ((data[0] & 0x80) || !running_status_buf[0] ||
(size + 1) > sizeof(running_status_buf))
{ // valid status byte (or invalid "running status") ...
event.buffer = data;
event.size = size;
// store status byte for eventual "running status" in next event
if (data[0] & 0x80) {
if (data[0] < 0xf0) {
// "running status" is only allowed for channel messages
running_status_buf[0] = data[0];
} else if (data[0] < 0xf8) {
// "system common" messages (0xf0..0xf7) shall reset any running
// status, however "realtime" messages (0xf8..0xff) shall be
// ignored here
running_status_buf[0] = 0;
}
// not a regular status byte ?
if (!(data[k+0] & 0x80) && running_status_buf[0]) { // "running status" mode ...
eventSize = _expectedEventSize(running_status_buf[0]);
chunkSize = (eventSize < 0) ? size - k : eventSize - 1;
if (chunkSize <= 0) goto get_next_packet;
if (chunkSize + 1 <= sizeof(running_status_buf)) {
memcpy(&running_status_buf[1], &data[k], chunkSize);
event.buffer = running_status_buf;
event.size = chunkSize + 1;
k += chunkSize;
goto send_event;
}
}
// valid status byte (or invalid "running status") ...
eventSize = _expectedEventSize(data[k+0]);
if (eventSize < 0) eventSize = size - k;
if (eventSize <= 0) goto get_next_packet;
event.buffer = &data[k];
event.size = eventSize;
// store status byte for eventual "running status" in next event
if (data[k+0] & 0x80) {
if (data[k+0] < 0xf0) {
// "running status" is only allowed for channel messages
running_status_buf[0] = data[k+0];
} else if (data[k+0] < 0xf8) {
// "system common" messages (0xf0..0xf7) shall reset any running
// status, however "realtime" messages (0xf8..0xff) shall be
// ignored here
running_status_buf[0] = 0;
}
} else { // "running status" mode ...
memcpy(&running_status_buf[1], data, size);
event.buffer = running_status_buf;
event.size = size + 1;
}
k += eventSize;
send_event:
event.time = GetFramesFromTimeStamp(packet->timeStamp);
......@@ -159,6 +205,7 @@ JackCoreMidiInputPort::ProcessCoreMidi(const MIDIPacketList *packet_list)
default:
;
}
if (k < size) goto parse_event;
get_next_packet:
packet = MIDIPacketNext(packet);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment