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