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.

205 lines
7.0KB

  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 <new>
  17. #include "JackCoreMidiOutputPort.h"
  18. #include "JackMidiUtil.h"
  19. using Jack::JackCoreMidiOutputPort;
  20. JackCoreMidiOutputPort::JackCoreMidiOutputPort(double time_ratio,
  21. int realtime_priority,
  22. size_t max_bytes,
  23. size_t max_messages):
  24. JackCoreMidiPort(time_ratio)
  25. {
  26. read_queue = new JackMidiBufferReadQueue();
  27. std::auto_ptr<JackMidiBufferReadQueue> read_ptr(read_queue);
  28. thread_queue = new JackMidiAsyncWaitQueue(max_bytes, max_messages);
  29. std::auto_ptr<JackMidiAsyncWaitQueue> thread_ptr(thread_queue);
  30. thread = new JackThread(this);
  31. this->realtime_priority = realtime_priority;
  32. thread_ptr.release();
  33. read_ptr.release();
  34. }
  35. JackCoreMidiOutputPort::~JackCoreMidiOutputPort()
  36. {
  37. Stop();
  38. delete thread;
  39. delete read_queue;
  40. delete thread_queue;
  41. }
  42. bool
  43. JackCoreMidiOutputPort::Execute()
  44. {
  45. jack_midi_event_t *event = 0;
  46. MIDIPacketList *packet_list = (MIDIPacketList *) packet_buffer;
  47. for (;;) {
  48. MIDIPacket *packet = MIDIPacketListInit(packet_list);
  49. assert(packet);
  50. if (! event) {
  51. event = thread_queue->DequeueEvent((long) 0);
  52. }
  53. jack_midi_data_t *data = event->data;
  54. // This is the latest time that the packet list can be sent out. We
  55. // may want to consider subtracting some frames to leave room for the
  56. // CoreMIDI driver/client to handle all of the events. There's a
  57. // property called 'kMIDIPropertyAdvanceScheduleTimeMuSec' that might
  58. // be useful in this case.
  59. jack_nframes_t send_time = event->time;
  60. size_t size = event->size;
  61. MIDITimeStamp timestamp = GetTimeStampFromFrames(send_time);
  62. packet = MIDIPacketListAdd(packet_list, PACKET_BUFFER_SIZE, packet,
  63. timestamp, size, data);
  64. if (packet) {
  65. while (GetCurrentFrame() < send_time) {
  66. event = thread_queue->DequeueEvent();
  67. if (! event) {
  68. break;
  69. }
  70. packet = MIDIPacketListAdd(packet_list, midi_buffer_size,
  71. packet,
  72. GetTimeStampFromFrames(event->time),
  73. event->size, event->data);
  74. if (! packet) {
  75. break;
  76. }
  77. }
  78. SendPacketList(packet_list);
  79. } else {
  80. // We have a large system exclusive event. We'll have to send it
  81. // out in multiple packets.
  82. size_t bytes_sent = 0;
  83. do {
  84. packet = MIDIPacketListInit(packet_list);
  85. assert(packet);
  86. size_t num_bytes = 0;
  87. for (; bytes_sent < size; bytes_sent += num_bytes) {
  88. size_t num_bytes = size - bytes_sent;
  89. // We use 256 because the MIDIPacket struct defines the
  90. // size of the 'data' member to be 256 bytes. I believe
  91. // this prevents packets from being dynamically allocated
  92. // by 'MIDIPacketListAdd', but I might be wrong.
  93. if (num_bytes > 256) {
  94. num_bytes = 256;
  95. }
  96. packet = MIDIPacketListAdd(packet_list, midi_buffer_size,
  97. packet, timestamp, num_bytes,
  98. data + bytes_sent);
  99. if (! packet) {
  100. break;
  101. }
  102. }
  103. if (! SendPacketList(packet_list)) {
  104. // An error occurred. The error message has already been
  105. // output. We lick our wounds and move along.
  106. break;
  107. }
  108. } while (bytes_sent < size);
  109. event = 0;
  110. }
  111. }
  112. return false;
  113. }
  114. MIDITimeStamp
  115. JackCoreMidiOutputPort::GetTimeStampFromFrames(jack_nframes_t frames)
  116. {
  117. return GetTimeFromFrames(frames) / time_ratio;
  118. }
  119. bool
  120. JackCoreMidiOutputPort::Init()
  121. {
  122. set_threaded_log_function();
  123. if (thread->AcquireSelfRealTime(realtime_priority)) {
  124. jack_error("JackCoreMidiOutputPort::Init - could not acquire realtime "
  125. "scheduling. Continuing anyway.");
  126. }
  127. return true;
  128. }
  129. void
  130. JackCoreMidiOutputPort::Initialize(const char *alias_name,
  131. const char *client_name,
  132. const char *driver_name, int index,
  133. MIDIEndpointRef endpoint)
  134. {
  135. JackCoreMidiPort::Initialize(alias_name, client_name, driver_name, index, endpoint, true);
  136. }
  137. void
  138. JackCoreMidiOutputPort::ProcessJack(JackMidiBuffer *port_buffer,
  139. jack_nframes_t frames)
  140. {
  141. read_queue->ResetMidiBuffer(port_buffer);
  142. for (jack_midi_event_t *event = read_queue->DequeueEvent(); event;
  143. event = read_queue->DequeueEvent()) {
  144. switch (thread_queue->EnqueueEvent(event, frames)) {
  145. case JackMidiWriteQueue::BUFFER_FULL:
  146. jack_error("JackCoreMidiOutputPort::ProcessJack - The thread "
  147. "queue buffer is full. Dropping event.");
  148. continue;
  149. case JackMidiWriteQueue::BUFFER_TOO_SMALL:
  150. jack_error("JackCoreMidiOutputPort::ProcessJack - The thread "
  151. "queue couldn't enqueue a %d-byte event. Dropping "
  152. "event.", event->size);
  153. // Fallthrough on purpose
  154. default:
  155. ;
  156. }
  157. }
  158. }
  159. bool
  160. JackCoreMidiOutputPort::Start()
  161. {
  162. bool result = thread->GetStatus() != JackThread::kIdle;
  163. if (! result) {
  164. result = ! thread->StartSync();
  165. if (! result) {
  166. jack_error("JackCoreMidiOutputPort::Start - failed to start MIDI "
  167. "processing thread.");
  168. }
  169. }
  170. return result;
  171. }
  172. bool
  173. JackCoreMidiOutputPort::Stop()
  174. {
  175. bool result = thread->GetStatus() == JackThread::kIdle;
  176. if (! result) {
  177. result = ! thread->Kill();
  178. if (! result) {
  179. jack_error("JackCoreMidiOutputPort::Stop - failed to stop MIDI "
  180. "processing thread.");
  181. }
  182. }
  183. return result;
  184. }