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.

322 lines
11KB

  1. /*
  2. Copyright (C) 2009 Devin Anderson
  3. This program is free software; you can redistribute it and/or modify
  4. it under the terms of the GNU Lesser General Public License as published by
  5. the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
  11. You should have received a copy of the GNU Lesser General Public License
  12. along with this program; if not, write to the Free Software
  13. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  14. */
  15. #include <cassert>
  16. #include "JackError.h"
  17. #include "JackPhysicalMidiOutput.h"
  18. namespace Jack {
  19. JackPhysicalMidiOutput::JackPhysicalMidiOutput(size_t non_rt_buffer_size,
  20. size_t rt_buffer_size)
  21. {
  22. size_t datum_size = sizeof(jack_midi_data_t);
  23. assert(non_rt_buffer_size > 0);
  24. assert(rt_buffer_size > 0);
  25. output_ring = jack_ringbuffer_create((non_rt_buffer_size + 1) *
  26. datum_size);
  27. if (! output_ring) {
  28. throw std::bad_alloc();
  29. }
  30. rt_output_ring = jack_ringbuffer_create((rt_buffer_size + 1) *
  31. datum_size);
  32. if (! rt_output_ring) {
  33. jack_ringbuffer_free(output_ring);
  34. throw std::bad_alloc();
  35. }
  36. jack_ringbuffer_mlock(output_ring);
  37. jack_ringbuffer_mlock(rt_output_ring);
  38. running_status = 0;
  39. }
  40. JackPhysicalMidiOutput::~JackPhysicalMidiOutput()
  41. {
  42. jack_ringbuffer_free(output_ring);
  43. jack_ringbuffer_free(rt_output_ring);
  44. }
  45. jack_nframes_t
  46. JackPhysicalMidiOutput::Advance(jack_nframes_t frame)
  47. {
  48. return frame;
  49. }
  50. inline jack_midi_data_t
  51. JackPhysicalMidiOutput::ApplyRunningStatus(jack_midi_data_t **buffer,
  52. size_t *size)
  53. {
  54. // Stolen and modified from alsa/midi_pack.h
  55. jack_midi_data_t status = (*buffer)[0];
  56. if ((status >= 0x80) && (status < 0xf0)) {
  57. if (status == running_status) {
  58. (*buffer)++;
  59. (*size)--;
  60. } else {
  61. running_status = status;
  62. }
  63. } else if (status < 0xf8) {
  64. running_status = 0;
  65. }
  66. return status;
  67. }
  68. void
  69. JackPhysicalMidiOutput::HandleEventLoss(JackMidiEvent *event)
  70. {
  71. jack_error("%d byte MIDI event lost", event->size);
  72. }
  73. void
  74. JackPhysicalMidiOutput::Process(jack_nframes_t frames)
  75. {
  76. assert(port_buffer);
  77. jack_nframes_t current_frame = Advance(0);
  78. jack_nframes_t current_midi_event = 0;
  79. jack_midi_data_t datum;
  80. size_t datum_size = sizeof(jack_midi_data_t);
  81. JackMidiEvent *midi_event;
  82. jack_midi_data_t *midi_event_buffer;
  83. size_t midi_event_size;
  84. jack_nframes_t midi_events = port_buffer->event_count;
  85. // First, send any realtime MIDI data that's left from last cycle.
  86. if ((current_frame < frames) &&
  87. jack_ringbuffer_read_space(rt_output_ring)) {
  88. jack_log("JackPhysicalMidiOutput::Process (%d) - Sending buffered "
  89. "realtime data from last period.", current_frame);
  90. current_frame = SendBufferedData(rt_output_ring, current_frame,
  91. frames);
  92. jack_log("JackPhysicalMidiOutput::Process (%d) - Sent", current_frame);
  93. }
  94. // Iterate through the events in this cycle.
  95. for (; (current_midi_event < midi_events) && (current_frame < frames);
  96. current_midi_event++) {
  97. // Once we're inside this loop, we know that the realtime buffer
  98. // is empty. As long as we don't find a realtime message, we can
  99. // concentrate on sending non-realtime data.
  100. midi_event = &(port_buffer->events[current_midi_event]);
  101. jack_nframes_t midi_event_time = midi_event->time;
  102. midi_event_buffer = midi_event->GetData(port_buffer);
  103. midi_event_size = midi_event->size;
  104. datum = ApplyRunningStatus(&midi_event_buffer, &midi_event_size);
  105. if (current_frame < midi_event_time) {
  106. // We have time before this event is scheduled to be sent.
  107. // Send data in the non-realtime buffer.
  108. if (jack_ringbuffer_read_space(output_ring)) {
  109. jack_log("JackPhysicalMidiOutput::Process (%d) - Sending "
  110. "buffered non-realtime data from last period.",
  111. current_frame);
  112. current_frame = SendBufferedData(output_ring, current_frame,
  113. midi_event_time);
  114. jack_log("JackPhysicalMidiOutput::Process (%d) - Sent",
  115. current_frame);
  116. }
  117. if (current_frame < midi_event_time) {
  118. // We _still_ have time before this event is scheduled to
  119. // be sent. Let's send as much of this event as we can
  120. // (save for one byte, which will need to be sent at or
  121. // after its scheduled time). First though, we need to
  122. // make sure that we can buffer this data if we need to.
  123. // Otherwise, we might start sending a message that we
  124. // can't finish.
  125. if (midi_event_size > 1) {
  126. if (jack_ringbuffer_write_space(output_ring) <
  127. ((midi_event_size - 1) * datum_size)) {
  128. HandleEventLoss(midi_event);
  129. continue;
  130. }
  131. // Send as much of the event as possible (save for one
  132. // byte).
  133. do {
  134. jack_log("JackPhysicalMidiOutput::Process (%d) - "
  135. "Sending unbuffered event byte early.",
  136. current_frame);
  137. current_frame = Send(current_frame,
  138. *midi_event_buffer);
  139. jack_log("JackPhysicalMidiOutput::Process (%d) - "
  140. "Sent.", current_frame);
  141. midi_event_buffer++;
  142. midi_event_size--;
  143. if (current_frame >= midi_event_time) {
  144. // The event we're processing must be a
  145. // non-realtime event. It has more than one
  146. // byte.
  147. goto buffer_non_realtime_data;
  148. }
  149. } while (midi_event_size > 1);
  150. }
  151. jack_log("JackPhysicalMidiOutput::Process (%d) - Advancing to "
  152. ">= %d", current_frame, midi_event_time);
  153. current_frame = Advance(midi_event_time);
  154. jack_log("JackPhysicalMidiOutput::Process (%d) - Advanced.",
  155. current_frame);
  156. }
  157. }
  158. // If the event is realtime, then we'll send the event now.
  159. // Otherwise, we attempt to put the rest of the event bytes in the
  160. // non-realtime buffer.
  161. if (datum >= 0xf8) {
  162. jack_log("JackPhysicalMidiOutput::Process (%d) - Sending "
  163. "unbuffered realtime event.", current_frame);
  164. current_frame = Send(current_frame, datum);
  165. jack_log("JackPhysicalMidiOutput::Process (%d) - Sent.",
  166. current_frame);
  167. } else if (jack_ringbuffer_write_space(output_ring) >=
  168. (midi_event_size * datum_size)) {
  169. buffer_non_realtime_data:
  170. jack_log("JackPhysicalMidiOutput::Process (%d) - Buffering %d "
  171. "byte(s) of non-realtime data.", current_frame,
  172. midi_event_size);
  173. jack_ringbuffer_write(output_ring,
  174. (const char *) midi_event_buffer,
  175. midi_event_size);
  176. } else {
  177. HandleEventLoss(midi_event);
  178. }
  179. }
  180. if (current_frame < frames) {
  181. // If we have time left to send data, then we know that all of the
  182. // data in the realtime buffer has been sent, and that all of the
  183. // non-realtime messages have either been sent, or buffered. We
  184. // use whatever time is left to send data in the non-realtime
  185. // buffer.
  186. if (jack_ringbuffer_read_space(output_ring)) {
  187. jack_log("JackPhysicalMidiOutput::Process (%d) - All events "
  188. "processed. Sending buffered non-realtime data.",
  189. current_frame);
  190. current_frame = SendBufferedData(output_ring, current_frame,
  191. frames);
  192. jack_log("JackPhysicalMidiOutput::Process (%d) - Sent.",
  193. current_frame);
  194. }
  195. } else {
  196. // Since we have no time left, we need to put all remaining midi
  197. // events in their appropriate buffers, and send them next period.
  198. for (current_midi_event++; current_midi_event < midi_events;
  199. current_midi_event++) {
  200. midi_event = &(port_buffer->events[current_midi_event]);
  201. midi_event_buffer = midi_event->GetData(port_buffer);
  202. midi_event_size = midi_event->size;
  203. datum = ApplyRunningStatus(&midi_event_buffer, &midi_event_size);
  204. if (datum >= 0xf8) {
  205. // Realtime.
  206. if (jack_ringbuffer_write_space(rt_output_ring) >=
  207. datum_size) {
  208. jack_log("JackPhysicalMidiOutput::Process - Buffering "
  209. "realtime event for next period.");
  210. jack_ringbuffer_write(rt_output_ring,
  211. (const char *) &datum, datum_size);
  212. continue;
  213. }
  214. } else {
  215. // Non-realtime.
  216. if (jack_ringbuffer_write_space(output_ring) >=
  217. (midi_event_size * datum_size)) {
  218. jack_log("JackPhysicalMidiOutput::Process - Buffering "
  219. "non-realtime event for next period.");
  220. jack_ringbuffer_write(output_ring,
  221. (const char *) midi_event_buffer,
  222. midi_event_size * datum_size);
  223. continue;
  224. }
  225. }
  226. HandleEventLoss(midi_event);
  227. }
  228. }
  229. }
  230. jack_nframes_t
  231. JackPhysicalMidiOutput::SendBufferedData(jack_ringbuffer_t *buffer,
  232. jack_nframes_t current_frame,
  233. jack_nframes_t boundary)
  234. {
  235. assert(buffer);
  236. assert(current_frame < boundary);
  237. size_t datum_size = sizeof(jack_midi_data_t);
  238. size_t data_length = jack_ringbuffer_read_space(buffer) / datum_size;
  239. for (size_t i = 0; i < data_length; i++) {
  240. jack_midi_data_t datum;
  241. jack_ringbuffer_read(buffer, (char *) &datum, datum_size);
  242. current_frame = Send(current_frame, datum);
  243. if (current_frame >= boundary) {
  244. break;
  245. }
  246. }
  247. return current_frame;
  248. }
  249. }