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.

256 lines
8.7KB

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