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.

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