| @@ -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); | |||