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.

212 lines
7.1KB

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