jack2 codebase
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

258 lines
9.4KB

  1. /*
  2. Copyright (C) 2011 Devin Anderson
  3. This program is free software; you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation; either version 2 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program; if not, write to the Free Software
  13. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  14. */
  15. #include <cassert>
  16. #include <memory>
  17. #include "JackCoreMidiInputPort.h"
  18. #include "JackMidiUtil.h"
  19. #include "JackError.h"
  20. using Jack::JackCoreMidiInputPort;
  21. /**
  22. * Takes a MIDI status byte as argument and returns the expected size of the
  23. * associated MIDI event. Returns -1 on invalid status bytes AND on variable
  24. * size events (SysEx events).
  25. */
  26. inline static int _expectedEventSize(const unsigned char& byte) {
  27. if (byte < 0x80) return -1; // not a valid status byte
  28. if (byte < 0xC0) return 3; // note on/off, note pressure, control change
  29. if (byte < 0xE0) return 2; // program change, channel pressure
  30. if (byte < 0xF0) return 3; // pitch wheel
  31. if (byte == 0xF0) return -1; // sysex message (variable size)
  32. if (byte == 0xF1) return 2; // time code per quarter frame
  33. if (byte == 0xF2) return 3; // sys. common song position pointer
  34. if (byte == 0xF3) return 2; // sys. common song select
  35. if (byte == 0xF4) return -1; // sys. common undefined / reserved
  36. if (byte == 0xF5) return -1; // sys. common undefined / reserved
  37. return 1; // tune request, end of SysEx, system real-time events
  38. }
  39. JackCoreMidiInputPort::JackCoreMidiInputPort(double time_ratio,
  40. size_t max_bytes,
  41. size_t max_messages):
  42. JackCoreMidiPort(time_ratio)
  43. {
  44. thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages);
  45. std::unique_ptr<JackMidiAsyncQueue> thread_queue_ptr(thread_queue);
  46. write_queue = new JackMidiBufferWriteQueue();
  47. std::unique_ptr<JackMidiBufferWriteQueue> write_queue_ptr(write_queue);
  48. sysex_buffer = new jack_midi_data_t[max_bytes];
  49. write_queue_ptr.release();
  50. thread_queue_ptr.release();
  51. jack_event = 0;
  52. running_status_buf[0] = 0;
  53. }
  54. JackCoreMidiInputPort::~JackCoreMidiInputPort()
  55. {
  56. delete thread_queue;
  57. delete write_queue;
  58. delete[] sysex_buffer;
  59. }
  60. jack_nframes_t
  61. JackCoreMidiInputPort::GetFramesFromTimeStamp(MIDITimeStamp timestamp)
  62. {
  63. return GetFramesFromTime((jack_time_t) (timestamp * time_ratio));
  64. }
  65. void
  66. JackCoreMidiInputPort::Initialize(const char *alias_name,
  67. const char *client_name,
  68. const char *driver_name, int index,
  69. MIDIEndpointRef endpoint)
  70. {
  71. JackCoreMidiPort::Initialize(alias_name, client_name, driver_name, index, endpoint, false);
  72. }
  73. void
  74. JackCoreMidiInputPort::ProcessCoreMidi(const MIDIPacketList *packet_list)
  75. {
  76. set_threaded_log_function();
  77. // TODO: maybe parsing should be done by JackMidiRawInputWriteQueue instead
  78. unsigned int packet_count = packet_list->numPackets;
  79. assert(packet_count);
  80. MIDIPacket *packet = (MIDIPacket *) packet_list->packet;
  81. for (unsigned int i = 0; i < packet_count; i++) {
  82. jack_midi_data_t *data = packet->data;
  83. size_t size = packet->length;
  84. assert(size);
  85. jack_midi_event_t event;
  86. // In a MIDIPacket there can be more than one (non SysEx) MIDI event.
  87. // However if the packet contains a SysEx event, it is guaranteed that
  88. // there are no other events in the same MIDIPacket.
  89. int k = 0; // index of the current MIDI event within current MIDIPacket
  90. int eventSize = 0; // theoretical size of the current MIDI event
  91. int chunkSize = 0; // actual size of the current MIDI event data consumed
  92. // XX: There might be dragons in my spaghetti. This code is begging
  93. // for a rewrite.
  94. if (sysex_bytes_sent) {
  95. if (data[0] & 0x80) {
  96. jack_error("JackCoreMidiInputPort::ProcessCoreMidi - System "
  97. "exclusive message aborted.");
  98. sysex_bytes_sent = 0;
  99. goto parse_event;
  100. }
  101. buffer_sysex_bytes:
  102. if ((sysex_bytes_sent + size) <= sizeof(sysex_buffer)) {
  103. memcpy(sysex_buffer + sysex_bytes_sent, packet,
  104. size * sizeof(jack_midi_data_t));
  105. }
  106. sysex_bytes_sent += size;
  107. if (data[size - 1] == 0xf7) {
  108. if (sysex_bytes_sent > sizeof(sysex_buffer)) {
  109. jack_error("JackCoreMidiInputPort::ProcessCoreMidi - "
  110. "Could not buffer a %d-byte system exclusive "
  111. "message. Discarding message.",
  112. sysex_bytes_sent);
  113. sysex_bytes_sent = 0;
  114. goto get_next_packet;
  115. }
  116. event.buffer = sysex_buffer;
  117. event.size = sysex_bytes_sent;
  118. sysex_bytes_sent = 0;
  119. k = size; // don't loop in a MIDIPacket if its a SysEx
  120. goto send_event;
  121. }
  122. goto get_next_packet;
  123. }
  124. parse_event:
  125. if (data[k+0] == 0xf0) {
  126. // Must actually never happen, since CoreMIDI guarantees a SysEx
  127. // message to be alone in one MIDIPaket, but safety first. The SysEx
  128. // buffer code is not written to handle this case, so skip packet.
  129. if (k != 0) {
  130. jack_error("JackCoreMidiInputPort::ProcessCoreMidi - Non "
  131. "isolated SysEx message in one packet, discarding.");
  132. goto get_next_packet;
  133. }
  134. if (data[size - 1] != 0xf7) {
  135. goto buffer_sysex_bytes;
  136. }
  137. }
  138. // not a regular status byte ?
  139. if (!(data[k+0] & 0x80) && running_status_buf[0]) { // "running status" mode ...
  140. eventSize = _expectedEventSize(running_status_buf[0]);
  141. chunkSize = (eventSize < 0) ? size - k : eventSize - 1;
  142. if (chunkSize <= 0) goto get_next_packet;
  143. if (chunkSize + 1 <= sizeof(running_status_buf)) {
  144. memcpy(&running_status_buf[1], &data[k], chunkSize);
  145. event.buffer = running_status_buf;
  146. event.size = chunkSize + 1;
  147. k += chunkSize;
  148. goto send_event;
  149. }
  150. }
  151. // valid status byte (or invalid "running status") ...
  152. eventSize = _expectedEventSize(data[k+0]);
  153. if (eventSize < 0) eventSize = size - k;
  154. if (eventSize <= 0) goto get_next_packet;
  155. event.buffer = &data[k];
  156. event.size = eventSize;
  157. // store status byte for eventual "running status" in next event
  158. if (data[k+0] & 0x80) {
  159. if (data[k+0] < 0xf0) {
  160. // "running status" is only allowed for channel messages
  161. running_status_buf[0] = data[k+0];
  162. } else if (data[k+0] < 0xf8) {
  163. // "system common" messages (0xf0..0xf7) shall reset any running
  164. // status, however "realtime" messages (0xf8..0xff) shall be
  165. // ignored here
  166. running_status_buf[0] = 0;
  167. }
  168. }
  169. k += eventSize;
  170. send_event:
  171. event.time = GetFramesFromTimeStamp(packet->timeStamp);
  172. switch (thread_queue->EnqueueEvent(&event)) {
  173. case JackMidiWriteQueue::BUFFER_FULL:
  174. jack_error("JackCoreMidiInputPort::ProcessCoreMidi - The thread "
  175. "queue buffer is full. Dropping event.");
  176. break;
  177. case JackMidiWriteQueue::BUFFER_TOO_SMALL:
  178. jack_error("JackCoreMidiInputPort::ProcessCoreMidi - The thread "
  179. "queue couldn't enqueue a %d-byte packet. Dropping "
  180. "event.", event.size);
  181. break;
  182. default:
  183. ;
  184. }
  185. if (k < size) goto parse_event;
  186. get_next_packet:
  187. packet = MIDIPacketNext(packet);
  188. assert(packet);
  189. }
  190. }
  191. void
  192. JackCoreMidiInputPort::ProcessJack(JackMidiBuffer *port_buffer,
  193. jack_nframes_t frames)
  194. {
  195. write_queue->ResetMidiBuffer(port_buffer, frames);
  196. if (! jack_event) {
  197. jack_event = thread_queue->DequeueEvent();
  198. }
  199. for (; jack_event; jack_event = thread_queue->DequeueEvent()) {
  200. // Add 'frames' to MIDI events to align with audio.
  201. switch (write_queue->EnqueueEvent(jack_event, frames)) {
  202. case JackMidiWriteQueue::BUFFER_TOO_SMALL:
  203. jack_error("JackCoreMidiInputPort::ProcessJack - The write queue "
  204. "couldn't enqueue a %d-byte event. Dropping event.",
  205. jack_event->size);
  206. // Fallthrough on purpose
  207. case JackMidiWriteQueue::OK:
  208. continue;
  209. default:
  210. ;
  211. }
  212. break;
  213. }
  214. }
  215. bool
  216. JackCoreMidiInputPort::Start()
  217. {
  218. // Hack: Get rid of any messages that might have come in before starting
  219. // the engine.
  220. while (thread_queue->DequeueEvent());
  221. sysex_bytes_sent = 0;
  222. running_status_buf[0] = 0;
  223. return true;
  224. }
  225. bool
  226. JackCoreMidiInputPort::Stop()
  227. {
  228. return true;
  229. }