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.

251 lines
8.5KB

  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 <cerrno>
  17. #include <cstring>
  18. #include <new>
  19. #include <stdexcept>
  20. #include "JackCoreMidiOutputPort.h"
  21. #include "JackMidiUtil.h"
  22. using Jack::JackCoreMidiOutputPort;
  23. JackCoreMidiOutputPort::JackCoreMidiOutputPort(double time_ratio,
  24. size_t max_bytes,
  25. size_t max_messages):
  26. JackCoreMidiPort(time_ratio)
  27. {
  28. read_queue = new JackMidiBufferReadQueue();
  29. std::auto_ptr<JackMidiBufferReadQueue> read_queue_ptr(read_queue);
  30. thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages);
  31. std::auto_ptr<JackMidiAsyncQueue> thread_queue_ptr(thread_queue);
  32. thread = new JackThread(this);
  33. std::auto_ptr<JackThread> thread_ptr(thread);
  34. sprintf(semaphore_name, "coremidi_%p", this);
  35. thread_queue_semaphore = sem_open(semaphore_name, O_CREAT, 0777, 0);
  36. if (thread_queue_semaphore == (sem_t *) SEM_FAILED) {
  37. throw std::runtime_error(strerror(errno));
  38. }
  39. thread_ptr.release();
  40. thread_queue_ptr.release();
  41. read_queue_ptr.release();
  42. }
  43. JackCoreMidiOutputPort::~JackCoreMidiOutputPort()
  44. {
  45. Stop();
  46. delete thread;
  47. sem_destroy(thread_queue_semaphore);
  48. sem_unlink(semaphore_name);
  49. delete read_queue;
  50. delete thread_queue;
  51. }
  52. bool
  53. JackCoreMidiOutputPort::Execute()
  54. {
  55. jack_midi_event_t *event = 0;
  56. MIDIPacketList *packet_list = (MIDIPacketList *) packet_buffer;
  57. for (;;) {
  58. MIDIPacket *packet = MIDIPacketListInit(packet_list);
  59. assert(packet);
  60. if (! event) {
  61. event = GetCoreMidiEvent(true);
  62. }
  63. jack_midi_data_t *data = event->buffer;
  64. // This is the latest time that the packet list can be sent out. We
  65. // may want to consider subtracting some frames to leave room for the
  66. // CoreMIDI driver/client to handle all of the events. There's a
  67. // property called 'kMIDIPropertyAdvanceScheduleTimeMuSec' that might
  68. // be useful in this case.
  69. jack_nframes_t send_time = event->time;
  70. size_t size = event->size;
  71. MIDITimeStamp timestamp = GetTimeStampFromFrames(send_time);
  72. packet = MIDIPacketListAdd(packet_list, PACKET_BUFFER_SIZE, packet,
  73. timestamp, size, data);
  74. if (packet) {
  75. while (GetCurrentFrame() < send_time) {
  76. event = GetCoreMidiEvent(false);
  77. if (! event) {
  78. break;
  79. }
  80. packet = MIDIPacketListAdd(packet_list, sizeof(packet_buffer),
  81. packet,
  82. GetTimeStampFromFrames(event->time),
  83. event->size, event->buffer);
  84. if (! packet) {
  85. break;
  86. }
  87. }
  88. SendPacketList(packet_list);
  89. } else {
  90. // We have a large system exclusive event. We'll have to send it
  91. // out in multiple packets.
  92. size_t bytes_sent = 0;
  93. do {
  94. packet = MIDIPacketListInit(packet_list);
  95. assert(packet);
  96. size_t num_bytes = 0;
  97. for (; bytes_sent < size; bytes_sent += num_bytes) {
  98. size_t num_bytes = size - bytes_sent;
  99. // We use 256 because the MIDIPacket struct defines the
  100. // size of the 'data' member to be 256 bytes. I believe
  101. // this prevents packets from being dynamically allocated
  102. // by 'MIDIPacketListAdd', but I might be wrong.
  103. if (num_bytes > 256) {
  104. num_bytes = 256;
  105. }
  106. packet = MIDIPacketListAdd(packet_list,
  107. sizeof(packet_buffer), packet,
  108. timestamp, num_bytes,
  109. data + bytes_sent);
  110. if (! packet) {
  111. break;
  112. }
  113. }
  114. if (! SendPacketList(packet_list)) {
  115. // An error occurred. The error message has already been
  116. // output. We lick our wounds and move along.
  117. break;
  118. }
  119. } while (bytes_sent < size);
  120. event = 0;
  121. }
  122. }
  123. return false;
  124. }
  125. jack_midi_event_t *
  126. JackCoreMidiOutputPort::GetCoreMidiEvent(bool block)
  127. {
  128. if (! block) {
  129. if (sem_trywait(thread_queue_semaphore)) {
  130. if (errno != EAGAIN) {
  131. jack_error("JackCoreMidiOutputPort::Execute - sem_trywait: %s",
  132. strerror(errno));
  133. }
  134. return 0;
  135. }
  136. } else {
  137. while (sem_wait(thread_queue_semaphore)) {
  138. if (errno != EINTR) {
  139. jack_error("JackCoreMidiOutputPort::Execute - sem_wait: %s",
  140. strerror(errno));
  141. return 0;
  142. }
  143. }
  144. }
  145. return thread_queue->DequeueEvent();
  146. }
  147. MIDITimeStamp
  148. JackCoreMidiOutputPort::GetTimeStampFromFrames(jack_nframes_t frames)
  149. {
  150. return GetTimeFromFrames(frames) / time_ratio;
  151. }
  152. bool
  153. JackCoreMidiOutputPort::Init()
  154. {
  155. set_threaded_log_function();
  156. // OSX only, values read in RT CoreMidi thread
  157. UInt64 period = 0;
  158. UInt64 computation = 250 * 1000;
  159. UInt64 constraint = 500 * 1000;
  160. thread->SetParams(period, computation, constraint);
  161. if (thread->AcquireSelfRealTime()) {
  162. jack_error("JackCoreMidiOutputPort::Init - could not acquire realtime "
  163. "scheduling. Continuing anyway.");
  164. }
  165. return true;
  166. }
  167. void
  168. JackCoreMidiOutputPort::Initialize(const char *alias_name,
  169. const char *client_name,
  170. const char *driver_name, int index,
  171. MIDIEndpointRef endpoint)
  172. {
  173. JackCoreMidiPort::Initialize(alias_name, client_name, driver_name, index,
  174. endpoint, true);
  175. }
  176. void
  177. JackCoreMidiOutputPort::ProcessJack(JackMidiBuffer *port_buffer,
  178. jack_nframes_t frames)
  179. {
  180. read_queue->ResetMidiBuffer(port_buffer);
  181. for (jack_midi_event_t *event = read_queue->DequeueEvent(); event;
  182. event = read_queue->DequeueEvent()) {
  183. switch (thread_queue->EnqueueEvent(event, frames)) {
  184. case JackMidiWriteQueue::BUFFER_FULL:
  185. jack_error("JackCoreMidiOutputPort::ProcessJack - The thread "
  186. "queue buffer is full. Dropping event.");
  187. break;
  188. case JackMidiWriteQueue::BUFFER_TOO_SMALL:
  189. jack_error("JackCoreMidiOutputPort::ProcessJack - The thread "
  190. "queue couldn't enqueue a %d-byte event. Dropping "
  191. "event.", event->size);
  192. break;
  193. default:
  194. if (sem_post(thread_queue_semaphore)) {
  195. jack_error("JackCoreMidiOutputPort::ProcessJack - unexpected "
  196. "error while posting to thread queue semaphore: %s",
  197. strerror(errno));
  198. }
  199. }
  200. }
  201. }
  202. bool
  203. JackCoreMidiOutputPort::Start()
  204. {
  205. bool result = thread->GetStatus() != JackThread::kIdle;
  206. if (! result) {
  207. result = ! thread->StartSync();
  208. if (! result) {
  209. jack_error("JackCoreMidiOutputPort::Start - failed to start MIDI "
  210. "processing thread.");
  211. }
  212. }
  213. return result;
  214. }
  215. bool
  216. JackCoreMidiOutputPort::Stop()
  217. {
  218. bool result = thread->GetStatus() == JackThread::kIdle;
  219. if (! result) {
  220. result = ! thread->Kill();
  221. if (! result) {
  222. jack_error("JackCoreMidiOutputPort::Stop - failed to stop MIDI "
  223. "processing thread.");
  224. }
  225. }
  226. return result;
  227. }