/* Copyright (C) 2009 Devin Anderson This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include "JackError.h" #include "JackPhysicalMidiInput.h" namespace Jack { JackPhysicalMidiInput::JackPhysicalMidiInput(size_t buffer_size) { size_t datum_size = sizeof(jack_midi_data_t); assert(buffer_size > 0); input_ring = jack_ringbuffer_create((buffer_size + 1) * datum_size); if (! input_ring) { throw std::bad_alloc(); } jack_ringbuffer_mlock(input_ring); Clear(); expected_data_bytes = 0; status_byte = 0; } JackPhysicalMidiInput::~JackPhysicalMidiInput() { jack_ringbuffer_free(input_ring); } void JackPhysicalMidiInput::Clear() { jack_ringbuffer_reset(input_ring); buffered_bytes = 0; unbuffered_bytes = 0; } void JackPhysicalMidiInput::HandleBufferFailure(size_t unbuffered_bytes, size_t total_bytes) { jack_error("%d MIDI byte(s) of a %d byte message could not be buffered - " "message dropped", unbuffered_bytes, total_bytes); } void JackPhysicalMidiInput::HandleIncompleteMessage(size_t bytes) { jack_error("Discarding %d MIDI byte(s) - incomplete message (cable " "unplugged?)", bytes); } void JackPhysicalMidiInput::HandleInvalidStatusByte(jack_midi_data_t status) { jack_error("Dropping invalid MIDI status byte '%x'", (unsigned int) status); } void JackPhysicalMidiInput::HandleUnexpectedSysexEnd(size_t bytes) { jack_error("Discarding %d MIDI byte(s) - received sysex end without sysex " "start (cable unplugged?)", bytes); } void JackPhysicalMidiInput::HandleWriteFailure(size_t bytes) { jack_error("Failed to write a %d byte MIDI message to the port buffer", bytes); } void JackPhysicalMidiInput::Process(jack_nframes_t frames) { assert(port_buffer); port_buffer->Reset(frames); jack_nframes_t current_frame = 0; size_t datum_size = sizeof(jack_midi_data_t); for (;;) { jack_midi_data_t datum; current_frame = Receive(&datum, current_frame, frames); if (current_frame >= frames) { break; } jack_log("JackPhysicalMidiInput::Process (%d) - Received '%x' byte", current_frame, (unsigned int) datum); if (datum >= 0xf8) { // Realtime if (datum == 0xfd) { HandleInvalidStatusByte(datum); } else { jack_log("JackPhysicalMidiInput::Process - Writing realtime " "event."); WriteByteEvent(current_frame, datum); } continue; } if (datum == 0xf7) { // Sysex end if (status_byte != 0xf0) { HandleUnexpectedSysexEnd(buffered_bytes + unbuffered_bytes); Clear(); expected_data_bytes = 0; status_byte = 0; } else { jack_log("JackPhysicalMidiInput::Process - Writing sysex " "event."); WriteBufferedSysexEvent(current_frame); } continue; } if (datum >= 0x80) { // We're handling a non-realtime status byte jack_log("JackPhysicalMidiInput::Process - Handling non-realtime " "status byte."); if (buffered_bytes || unbuffered_bytes) { HandleIncompleteMessage(buffered_bytes + unbuffered_bytes + 1); Clear(); } status_byte = datum; switch (datum & 0xf0) { case 0x80: case 0x90: case 0xa0: case 0xb0: case 0xe0: // Note On, Note Off, Aftertouch, Control Change, Pitch Wheel expected_data_bytes = 2; break; case 0xc0: case 0xd0: // Program Change, Channel Pressure expected_data_bytes = 1; break; case 0xf0: switch (datum) { case 0xf0: // Sysex message expected_data_bytes = 0; break; case 0xf1: case 0xf3: // MTC Quarter frame, Song Select expected_data_bytes = 1; break; case 0xf2: // Song Position expected_data_bytes = 2; break; case 0xf4: case 0xf5: // Undefined HandleInvalidStatusByte(datum); expected_data_bytes = 0; status_byte = 0; break; case 0xf6: // Tune Request WriteByteEvent(current_frame, datum); expected_data_bytes = 0; status_byte = 0; } break; } continue; } // We're handling a data byte jack_log("JackPhysicalMidiInput::Process - Buffering data byte."); if (jack_ringbuffer_write(input_ring, (const char *) &datum, datum_size) == datum_size) { buffered_bytes++; } else { unbuffered_bytes++; } unsigned long total_bytes = buffered_bytes + unbuffered_bytes; assert((! expected_data_bytes) || (total_bytes <= expected_data_bytes)); if (total_bytes == expected_data_bytes) { if (! unbuffered_bytes) { jack_log("JackPhysicalMidiInput::Process - Writing buffered " "event."); WriteBufferedEvent(current_frame); } else { HandleBufferFailure(unbuffered_bytes, total_bytes); Clear(); } if (status_byte >= 0xf0) { expected_data_bytes = 0; status_byte = 0; } } } } void JackPhysicalMidiInput::WriteBufferedEvent(jack_nframes_t frame) { assert(port_buffer && port_buffer->IsValid()); size_t space = jack_ringbuffer_read_space(input_ring); jack_midi_data_t *event = port_buffer->ReserveEvent(frame, space + 1); if (event) { jack_ringbuffer_data_t vector[2]; jack_ringbuffer_get_read_vector(input_ring, vector); event[0] = status_byte; size_t data_length_1 = vector[0].len; memcpy(event + 1, vector[0].buf, data_length_1); size_t data_length_2 = vector[1].len; if (data_length_2) { memcpy(event + data_length_1 + 1, vector[1].buf, data_length_2); } } else { HandleWriteFailure(space + 1); } Clear(); } void JackPhysicalMidiInput::WriteBufferedSysexEvent(jack_nframes_t frame) { assert(port_buffer && port_buffer->IsValid()); size_t space = jack_ringbuffer_read_space(input_ring); jack_midi_data_t *event = port_buffer->ReserveEvent(frame, space + 2); if (event) { jack_ringbuffer_data_t vector[2]; jack_ringbuffer_get_read_vector(input_ring, vector); event[0] = status_byte; size_t data_length_1 = vector[0].len; memcpy(event + 1, vector[0].buf, data_length_1); size_t data_length_2 = vector[1].len; if (data_length_2) { memcpy(event + data_length_1 + 1, vector[1].buf, data_length_2); } event[data_length_1 + data_length_2 + 1] = 0xf7; } else { HandleWriteFailure(space + 2); } Clear(); } void JackPhysicalMidiInput::WriteByteEvent(jack_nframes_t frame, jack_midi_data_t datum) { assert(port_buffer && port_buffer->IsValid()); jack_midi_data_t *event = port_buffer->ReserveEvent(frame, 1); if (event) { event[0] = datum; } else { HandleWriteFailure(1); } } }