1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
|
/*
Copyright (C) 2011 Devin Anderson
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <cassert>
#include <memory>
#include "JackCoreMidiInputPort.h"
#include "JackMidiUtil.h"
#include "JackError.h"
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):
JackCoreMidiPort(time_ratio)
{
thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages);
std::auto_ptr<JackMidiAsyncQueue> thread_queue_ptr(thread_queue);
write_queue = new JackMidiBufferWriteQueue();
std::auto_ptr<JackMidiBufferWriteQueue> write_queue_ptr(write_queue);
sysex_buffer = new jack_midi_data_t[max_bytes];
write_queue_ptr.release();
thread_queue_ptr.release();
jack_event = 0;
running_status_buf[0] = 0;
}
JackCoreMidiInputPort::~JackCoreMidiInputPort()
{
delete thread_queue;
delete write_queue;
delete[] sysex_buffer;
}
jack_nframes_t
JackCoreMidiInputPort::GetFramesFromTimeStamp(MIDITimeStamp timestamp)
{
return GetFramesFromTime((jack_time_t) (timestamp * time_ratio));
}
void
JackCoreMidiInputPort::Initialize(const char *alias_name,
const char *client_name,
const char *driver_name, int index,
MIDIEndpointRef endpoint)
{
JackCoreMidiPort::Initialize(alias_name, client_name, driver_name, index, endpoint, false);
}
void
JackCoreMidiInputPort::ProcessCoreMidi(const MIDIPacketList *packet_list)
{
set_threaded_log_function();
// TODO: maybe parsing should be done by JackMidiRawInputWriteQueue instead
unsigned int packet_count = packet_list->numPackets;
assert(packet_count);
MIDIPacket *packet = (MIDIPacket *) packet_list->packet;
for (unsigned int i = 0; i < packet_count; i++) {
jack_midi_data_t *data = packet->data;
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.
if (sysex_bytes_sent) {
if (data[0] & 0x80) {
jack_error("JackCoreMidiInputPort::ProcessCoreMidi - System "
"exclusive message aborted.");
sysex_bytes_sent = 0;
goto parse_event;
}
buffer_sysex_bytes:
if ((sysex_bytes_sent + size) <= sizeof(sysex_buffer)) {
memcpy(sysex_buffer + sysex_bytes_sent, packet,
size * sizeof(jack_midi_data_t));
}
sysex_bytes_sent += size;
if (data[size - 1] == 0xf7) {
if (sysex_bytes_sent > sizeof(sysex_buffer)) {
jack_error("JackCoreMidiInputPort::ProcessCoreMidi - "
"Could not buffer a %d-byte system exclusive "
"message. Discarding message.",
sysex_bytes_sent);
sysex_bytes_sent = 0;
goto get_next_packet;
}
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[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;
}
}
// 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;
}
}
k += eventSize;
send_event:
event.time = GetFramesFromTimeStamp(packet->timeStamp);
switch (thread_queue->EnqueueEvent(&event)) {
case JackMidiWriteQueue::BUFFER_FULL:
jack_error("JackCoreMidiInputPort::ProcessCoreMidi - The thread "
"queue buffer is full. Dropping event.");
break;
case JackMidiWriteQueue::BUFFER_TOO_SMALL:
jack_error("JackCoreMidiInputPort::ProcessCoreMidi - The thread "
"queue couldn't enqueue a %d-byte packet. Dropping "
"event.", event.size);
break;
default:
;
}
if (k < size) goto parse_event;
get_next_packet:
packet = MIDIPacketNext(packet);
assert(packet);
}
}
void
JackCoreMidiInputPort::ProcessJack(JackMidiBuffer *port_buffer,
jack_nframes_t frames)
{
write_queue->ResetMidiBuffer(port_buffer, frames);
if (! jack_event) {
jack_event = thread_queue->DequeueEvent();
}
for (; jack_event; jack_event = thread_queue->DequeueEvent()) {
// Add 'frames' to MIDI events to align with audio.
switch (write_queue->EnqueueEvent(jack_event, frames)) {
case JackMidiWriteQueue::BUFFER_TOO_SMALL:
jack_error("JackCoreMidiInputPort::ProcessJack - The write queue "
"couldn't enqueue a %d-byte event. Dropping event.",
jack_event->size);
// Fallthrough on purpose
case JackMidiWriteQueue::OK:
continue;
default:
;
}
break;
}
}
bool
JackCoreMidiInputPort::Start()
{
// Hack: Get rid of any messages that might have come in before starting
// the engine.
while (thread_queue->DequeueEvent());
sysex_bytes_sent = 0;
running_status_buf[0] = 0;
return true;
}
bool
JackCoreMidiInputPort::Stop()
{
return true;
}
|