Jack2 1.9.10
|
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 }