Jack2  1.9.10
JackCoreMidiInputPort.cpp
00001 /*
00002 Copyright (C) 2011 Devin Anderson
00003 
00004 This program is free software; you can redistribute it and/or modify
00005 it under the terms of the GNU General Public License as published by
00006 the Free Software Foundation; either version 2 of the License, or
00007 (at your option) any later version.
00008 
00009 This program is distributed in the hope that it will be useful,
00010 but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 GNU General Public License for more details.
00013 
00014 You should have received a copy of the GNU General Public License
00015 along with this program; if not, write to the Free Software
00016 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00017 
00018 */
00019 
00020 #include <cassert>
00021 #include <memory>
00022 
00023 #include "JackCoreMidiInputPort.h"
00024 #include "JackMidiUtil.h"
00025 #include "JackError.h"
00026 
00027 using Jack::JackCoreMidiInputPort;
00028 
00034 inline static int _expectedEventSize(const unsigned char& byte) {
00035     if (byte < 0x80) return -1; // not a valid status byte
00036     if (byte < 0xC0) return 3; // note on/off, note pressure, control change
00037     if (byte < 0xE0) return 2; // program change, channel pressure
00038     if (byte < 0xF0) return 3; // pitch wheel
00039     if (byte == 0xF0) return -1; // sysex message (variable size)
00040     if (byte == 0xF1) return 2; // time code per quarter frame
00041     if (byte == 0xF2) return 3; // sys. common song position pointer
00042     if (byte == 0xF3) return 2; // sys. common song select
00043     if (byte == 0xF4) return -1; // sys. common undefined / reserved
00044     if (byte == 0xF5) return -1; // sys. common undefined / reserved
00045     return 1; // tune request, end of SysEx, system real-time events
00046 }
00047 
00048 JackCoreMidiInputPort::JackCoreMidiInputPort(double time_ratio,
00049                                              size_t max_bytes,
00050                                              size_t max_messages):
00051     JackCoreMidiPort(time_ratio)
00052 {
00053     thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages);
00054     std::auto_ptr<JackMidiAsyncQueue> thread_queue_ptr(thread_queue);
00055     write_queue = new JackMidiBufferWriteQueue();
00056     std::auto_ptr<JackMidiBufferWriteQueue> write_queue_ptr(write_queue);
00057     sysex_buffer = new jack_midi_data_t[max_bytes];
00058     write_queue_ptr.release();
00059     thread_queue_ptr.release();
00060     jack_event = 0;
00061     running_status_buf[0] = 0;
00062 }
00063 
00064 JackCoreMidiInputPort::~JackCoreMidiInputPort()
00065 {
00066     delete thread_queue;
00067     delete write_queue;
00068     delete[] sysex_buffer;
00069 }
00070 
00071 jack_nframes_t
00072 JackCoreMidiInputPort::GetFramesFromTimeStamp(MIDITimeStamp timestamp)
00073 {
00074     return GetFramesFromTime((jack_time_t) (timestamp * time_ratio));
00075 }
00076 
00077 void
00078 JackCoreMidiInputPort::Initialize(const char *alias_name,
00079                                   const char *client_name,
00080                                   const char *driver_name, int index,
00081                                   MIDIEndpointRef endpoint)
00082 {
00083     JackCoreMidiPort::Initialize(alias_name, client_name, driver_name, index, endpoint, false);
00084 }
00085 
00086 void
00087 JackCoreMidiInputPort::ProcessCoreMidi(const MIDIPacketList *packet_list)
00088 {
00089     set_threaded_log_function();
00090 
00091     // TODO: maybe parsing should be done by JackMidiRawInputWriteQueue instead
00092 
00093     unsigned int packet_count = packet_list->numPackets;
00094     assert(packet_count);
00095     MIDIPacket *packet = (MIDIPacket *) packet_list->packet;
00096     for (unsigned int i = 0; i < packet_count; i++) {
00097         jack_midi_data_t *data = packet->data;
00098         size_t size = packet->length;
00099         assert(size);
00100         jack_midi_event_t event;
00101         // In a MIDIPacket there can be more than one (non SysEx) MIDI event.
00102         // However if the packet contains a SysEx event, it is guaranteed that
00103         // there are no other events in the same MIDIPacket.
00104         int k = 0; // index of the current MIDI event within current MIDIPacket
00105         int eventSize = 0; // theoretical size of the current MIDI event
00106         int chunkSize = 0; // actual size of the current MIDI event data consumed
00107 
00108         // XX: There might be dragons in my spaghetti.  This code is begging
00109         // for a rewrite.
00110 
00111         if (sysex_bytes_sent) {
00112             if (data[0] & 0x80) {
00113                 jack_error("JackCoreMidiInputPort::ProcessCoreMidi - System "
00114                            "exclusive message aborted.");
00115                 sysex_bytes_sent = 0;
00116                 goto parse_event;
00117             }
00118         buffer_sysex_bytes:
00119             if ((sysex_bytes_sent + size) <= sizeof(sysex_buffer)) {
00120                 memcpy(sysex_buffer + sysex_bytes_sent, packet,
00121                        size * sizeof(jack_midi_data_t));
00122             }
00123             sysex_bytes_sent += size;
00124             if (data[size - 1] == 0xf7) {
00125                 if (sysex_bytes_sent > sizeof(sysex_buffer)) {
00126                     jack_error("JackCoreMidiInputPort::ProcessCoreMidi - "
00127                                "Could not buffer a %d-byte system exclusive "
00128                                "message.  Discarding message.",
00129                                sysex_bytes_sent);
00130                     sysex_bytes_sent = 0;
00131                     goto get_next_packet;
00132                 }
00133                 event.buffer = sysex_buffer;
00134                 event.size = sysex_bytes_sent;
00135                 sysex_bytes_sent = 0;
00136                 k = size; // don't loop in a MIDIPacket if its a SysEx
00137                 goto send_event;
00138             }
00139             goto get_next_packet;
00140         }
00141 
00142     parse_event:
00143         if (data[k+0] == 0xf0) {
00144             // Must actually never happen, since CoreMIDI guarantees a SysEx
00145             // message to be alone in one MIDIPaket, but safety first. The SysEx
00146             // buffer code is not written to handle this case, so skip packet.
00147             if (k != 0) {
00148                 jack_error("JackCoreMidiInputPort::ProcessCoreMidi - Non "
00149                            "isolated SysEx message in one packet, discarding.");
00150                 goto get_next_packet;
00151             }
00152 
00153             if (data[size - 1] != 0xf7) {
00154                 goto buffer_sysex_bytes;
00155             }
00156         }
00157 
00158         // not a regular status byte ?
00159         if (!(data[k+0] & 0x80) && running_status_buf[0]) { // "running status" mode ...
00160             eventSize = _expectedEventSize(running_status_buf[0]);
00161             chunkSize = (eventSize < 0) ? size - k : eventSize - 1;
00162             if (chunkSize <= 0) goto get_next_packet;
00163             if (chunkSize + 1 <= sizeof(running_status_buf)) {
00164                 memcpy(&running_status_buf[1], &data[k], chunkSize);
00165                 event.buffer = running_status_buf;
00166                 event.size = chunkSize + 1;
00167                 k += chunkSize;
00168                 goto send_event;
00169             }
00170         }
00171 
00172         // valid status byte (or invalid "running status") ...
00173 
00174         eventSize = _expectedEventSize(data[k+0]);
00175         if (eventSize < 0) eventSize = size - k;
00176         if (eventSize <= 0) goto get_next_packet;
00177         event.buffer = &data[k];
00178         event.size = eventSize;
00179         // store status byte for eventual "running status" in next event
00180         if (data[k+0] & 0x80) {
00181             if (data[k+0] < 0xf0) {
00182                 // "running status" is only allowed for channel messages
00183                 running_status_buf[0] = data[k+0];
00184             } else if (data[k+0] < 0xf8) {
00185                 // "system common" messages (0xf0..0xf7) shall reset any running
00186                 // status, however "realtime" messages (0xf8..0xff) shall be
00187                 // ignored here
00188                 running_status_buf[0] = 0;
00189             }
00190         }
00191         k += eventSize;
00192 
00193     send_event:
00194         event.time = GetFramesFromTimeStamp(packet->timeStamp);
00195         switch (thread_queue->EnqueueEvent(&event)) {
00196         case JackMidiWriteQueue::BUFFER_FULL:
00197             jack_error("JackCoreMidiInputPort::ProcessCoreMidi - The thread "
00198                        "queue buffer is full.  Dropping event.");
00199             break;
00200         case JackMidiWriteQueue::BUFFER_TOO_SMALL:
00201             jack_error("JackCoreMidiInputPort::ProcessCoreMidi - The thread "
00202                        "queue couldn't enqueue a %d-byte packet.  Dropping "
00203                        "event.", event.size);
00204             break;
00205         default:
00206             ;
00207         }
00208         if (k < size) goto parse_event;
00209 
00210     get_next_packet:
00211         packet = MIDIPacketNext(packet);
00212         assert(packet);
00213     }
00214 }
00215 
00216 void
00217 JackCoreMidiInputPort::ProcessJack(JackMidiBuffer *port_buffer,
00218                                    jack_nframes_t frames)
00219 {
00220     write_queue->ResetMidiBuffer(port_buffer, frames);
00221     if (! jack_event) {
00222         jack_event = thread_queue->DequeueEvent();
00223     }
00224 
00225     for (; jack_event; jack_event = thread_queue->DequeueEvent()) {
00226         // Add 'frames' to MIDI events to align with audio.
00227         switch (write_queue->EnqueueEvent(jack_event, frames)) {
00228         case JackMidiWriteQueue::BUFFER_TOO_SMALL:
00229             jack_error("JackCoreMidiInputPort::ProcessJack - The write queue "
00230                        "couldn't enqueue a %d-byte event. Dropping event.",
00231                        jack_event->size);
00232             // Fallthrough on purpose
00233         case JackMidiWriteQueue::OK:
00234             continue;
00235         default:
00236             ;
00237         }
00238         break;
00239     }
00240 }
00241 
00242 bool
00243 JackCoreMidiInputPort::Start()
00244 {
00245     // Hack: Get rid of any messages that might have come in before starting
00246     // the engine.
00247     while (thread_queue->DequeueEvent());
00248     sysex_bytes_sent = 0;
00249     running_status_buf[0] = 0;
00250     return true;
00251 }
00252 
00253 bool
00254 JackCoreMidiInputPort::Stop()
00255 {
00256     return true;
00257 }