jack1 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.

334 lines
9.5KB

  1. /*
  2. Copyright (C) 2004-2006 Ian Esten
  3. Copyright (C) 2006 Dave Robillard
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU Lesser General Public License as published by
  6. the Free Software Foundation; either version 2.1 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  15. */
  16. #include <assert.h>
  17. #include <stdio.h>
  18. #include <string.h>
  19. #include <errno.h>
  20. #include <jack/jack.h>
  21. #include <jack/midiport.h>
  22. #include "port.h"
  23. enum { MIDI_INLINE_MAX = 4 }; /* 4 bytes for default event size */
  24. typedef struct _jack_midi_port_info_private {
  25. jack_nframes_t nframes; /**< Number of frames in buffer */
  26. uint32_t buffer_size; /**< Size of buffer in bytes */
  27. uint32_t event_count; /**< Number of events stored in this buffer */
  28. jack_nframes_t last_write_loc; /**< Used for both writing and mixdown */
  29. uint32_t events_lost; /**< Number of events lost in this buffer */
  30. } POST_PACKED_STRUCTURE jack_midi_port_info_private_t;
  31. typedef struct _jack_midi_port_internal_event {
  32. uint16_t time; /* offset within buffer limit to 64k */
  33. uint16_t size; /* event size limited to 64k */
  34. union {
  35. jack_shmsize_t byte_offset;
  36. jack_midi_data_t inline_data[MIDI_INLINE_MAX];
  37. } POST_PACKED_STRUCTURE;
  38. } POST_PACKED_STRUCTURE jack_midi_port_internal_event_t;
  39. size_t
  40. jack_midi_internal_event_size ()
  41. {
  42. return sizeof(jack_midi_port_internal_event_t);
  43. }
  44. static inline jack_midi_data_t*
  45. jack_midi_event_data (void* port_buffer,
  46. const jack_midi_port_internal_event_t* event)
  47. {
  48. if (event->size <= MIDI_INLINE_MAX) {
  49. return (jack_midi_data_t*)event->inline_data;
  50. } else {
  51. return ((jack_midi_data_t*)port_buffer) + event->byte_offset;
  52. }
  53. }
  54. /* jack_midi_port_functions.buffer_init */
  55. static void
  56. jack_midi_buffer_init (void *port_buffer,
  57. size_t buffer_size,
  58. jack_nframes_t nframes)
  59. {
  60. jack_midi_port_info_private_t *info =
  61. (jack_midi_port_info_private_t*)port_buffer;
  62. /* We can also add some magic field to midi buffer to validate client calls */
  63. info->nframes = nframes;
  64. info->buffer_size = buffer_size;
  65. info->event_count = 0;
  66. info->last_write_loc = 0;
  67. info->events_lost = 0;
  68. }
  69. uint32_t
  70. jack_midi_get_event_count (void *port_buffer)
  71. {
  72. jack_midi_port_info_private_t *info =
  73. (jack_midi_port_info_private_t*)port_buffer;
  74. return info->event_count;
  75. }
  76. int
  77. jack_midi_event_get (jack_midi_event_t *event,
  78. void *port_buffer,
  79. uint32_t event_idx)
  80. {
  81. jack_midi_port_internal_event_t *port_event;
  82. jack_midi_port_info_private_t *info =
  83. (jack_midi_port_info_private_t*)port_buffer;
  84. if (event_idx >= info->event_count)
  85. #ifdef ENODATA
  86. { return ENODATA; }
  87. #else
  88. { return ENOMSG; }
  89. #endif
  90. port_event = (jack_midi_port_internal_event_t*)(info + 1);
  91. port_event += event_idx;
  92. event->time = port_event->time;
  93. event->size = port_event->size;
  94. event->buffer = jack_midi_event_data (port_buffer, port_event);
  95. return 0;
  96. }
  97. size_t
  98. jack_midi_max_event_size (void *port_buffer)
  99. {
  100. jack_midi_port_info_private_t *info =
  101. (jack_midi_port_info_private_t*)port_buffer;
  102. size_t buffer_size =
  103. info->buffer_size;
  104. /* (event_count + 1) below accounts for jack_midi_port_internal_event_t
  105. * which would be needed to store the next event */
  106. size_t used_size = sizeof(jack_midi_port_info_private_t)
  107. + info->last_write_loc
  108. + ((info->event_count + 1)
  109. * sizeof(jack_midi_port_internal_event_t));
  110. if (used_size > buffer_size) {
  111. return 0;
  112. } else if ((buffer_size - used_size) < MIDI_INLINE_MAX) {
  113. return MIDI_INLINE_MAX;
  114. } else {
  115. return buffer_size - used_size;
  116. }
  117. }
  118. jack_midi_data_t*
  119. jack_midi_event_reserve (void *port_buffer,
  120. jack_nframes_t time,
  121. size_t data_size)
  122. {
  123. jack_midi_data_t *retbuf = (jack_midi_data_t*)port_buffer;
  124. jack_midi_port_info_private_t *info =
  125. (jack_midi_port_info_private_t*)port_buffer;
  126. jack_midi_port_internal_event_t *event_buffer =
  127. (jack_midi_port_internal_event_t*)(info + 1);
  128. size_t buffer_size =
  129. info->buffer_size;
  130. if (time < 0 || time >= info->nframes) {
  131. goto failed;
  132. }
  133. if (info->event_count > 0 && time < event_buffer[info->event_count - 1].time) {
  134. goto failed;
  135. }
  136. /* Check if data_size is >0 and there is enough space in the buffer for the event. */
  137. if (data_size <= 0) {
  138. goto failed; // return NULL?
  139. } else if (jack_midi_max_event_size (port_buffer) < data_size) {
  140. goto failed;
  141. } else {
  142. jack_midi_port_internal_event_t *event = &event_buffer[info->event_count];
  143. event->time = time;
  144. event->size = data_size;
  145. if (data_size <= MIDI_INLINE_MAX) {
  146. retbuf = event->inline_data;
  147. } else {
  148. info->last_write_loc += data_size;
  149. retbuf = &retbuf[buffer_size - 1 - info->last_write_loc];
  150. event->byte_offset =
  151. buffer_size - 1 - info->last_write_loc;
  152. }
  153. info->event_count += 1;
  154. return retbuf;
  155. }
  156. failed:
  157. info->events_lost++;
  158. return NULL;
  159. }
  160. int
  161. jack_midi_event_write (void *port_buffer,
  162. jack_nframes_t time,
  163. const jack_midi_data_t *data,
  164. size_t data_size)
  165. {
  166. jack_midi_data_t *retbuf =
  167. jack_midi_event_reserve (port_buffer, time, data_size);
  168. if (retbuf) {
  169. memcpy (retbuf, data, data_size);
  170. return 0;
  171. } else {
  172. return ENOBUFS;
  173. }
  174. }
  175. /* Can't check to make sure this port is an output anymore. If this gets
  176. * called on an input port, all clients after the client that calls it
  177. * will think there are no events in the buffer as the event count has
  178. * been reset.
  179. */
  180. void
  181. jack_midi_clear_buffer (void *port_buffer)
  182. {
  183. jack_midi_port_info_private_t *info =
  184. (jack_midi_port_info_private_t*)port_buffer;
  185. info->event_count = 0;
  186. info->last_write_loc = 0;
  187. info->events_lost = 0;
  188. }
  189. /* jack_midi_port_functions.mixdown */
  190. static void
  191. jack_midi_port_mixdown (jack_port_t *port, jack_nframes_t nframes)
  192. {
  193. JSList *node;
  194. jack_port_t *input;
  195. jack_nframes_t num_events = 0;
  196. jack_nframes_t i = 0;
  197. int err = 0;
  198. jack_nframes_t lost_events = 0;
  199. /* The next (single) event to mix in to the buffer */
  200. jack_midi_port_info_private_t *earliest_info;
  201. jack_midi_port_internal_event_t *earliest_event;
  202. jack_midi_data_t *earliest_buffer;
  203. jack_midi_port_info_private_t *in_info; /* For finding next event */
  204. jack_midi_port_internal_event_t *in_events; /* Corresponds to in_info */
  205. jack_midi_port_info_private_t *out_info; /* Output 'buffer' */
  206. jack_midi_clear_buffer (port->mix_buffer);
  207. out_info = (jack_midi_port_info_private_t*)port->mix_buffer;
  208. /* This function uses jack_midi_port_info_private_t.last_write_loc of the
  209. * source ports to store indices of the last event read from that buffer
  210. * so far. This is OK because last_write_loc is used when writing events
  211. * to a buffer, which at this stage is already complete so the value
  212. * can be safely smashed. */
  213. /* Iterate through all connections to see how many events we need to mix,
  214. * and initialise their 'last event read' (last_write_loc) to 0 */
  215. for (node = port->connections; node; node = jack_slist_next (node)) {
  216. input = (jack_port_t*)node->data;
  217. in_info =
  218. (jack_midi_port_info_private_t*)jack_output_port_buffer (input);
  219. num_events += in_info->event_count;
  220. lost_events += in_info->events_lost;
  221. in_info->last_write_loc = 0;
  222. }
  223. /* Write the events in the order of their timestamps */
  224. for (i = 0; i < num_events; ++i) {
  225. earliest_info = NULL;
  226. earliest_event = NULL;
  227. earliest_buffer = NULL;
  228. /* Find the earliest unread event, to mix next
  229. * (search for an event earlier than earliest_event) */
  230. for (node = port->connections; node; node = jack_slist_next (node)) {
  231. in_info = (jack_midi_port_info_private_t*)
  232. jack_output_port_buffer (((jack_port_t*)node->data));
  233. in_events = (jack_midi_port_internal_event_t*)(in_info + 1);
  234. /* If there are unread events left in this port.. */
  235. if (in_info->event_count > in_info->last_write_loc) {
  236. /* .. and this event is the new earliest .. */
  237. /* NOTE: that's why we compare time with <, not <= */
  238. if (earliest_info == NULL
  239. || in_events[in_info->last_write_loc].time
  240. < earliest_event->time) {
  241. /* .. then set this event as the next earliest */
  242. earliest_info = in_info;
  243. earliest_event = (jack_midi_port_internal_event_t*)
  244. (&in_events[in_info->last_write_loc]);
  245. }
  246. }
  247. }
  248. if (earliest_info && earliest_event) {
  249. earliest_buffer = (jack_midi_data_t*)earliest_info;
  250. /* Write event to output */
  251. err = jack_midi_event_write (
  252. jack_port_buffer (port),
  253. earliest_event->time,
  254. jack_midi_event_data (earliest_buffer, earliest_event),
  255. earliest_event->size);
  256. earliest_info->last_write_loc++;
  257. if (err) {
  258. out_info->events_lost = num_events - i;
  259. break;
  260. }
  261. }
  262. }
  263. assert (out_info->event_count == num_events - out_info->events_lost);
  264. // inherit total lost events count from all connected ports.
  265. out_info->events_lost += lost_events;
  266. }
  267. uint32_t
  268. jack_midi_get_lost_event_count (void *port_buffer)
  269. {
  270. return ((jack_midi_port_info_private_t*)port_buffer)->events_lost;
  271. }
  272. jack_port_functions_t jack_builtin_midi_functions = {
  273. .buffer_init = jack_midi_buffer_init,
  274. .mixdown = jack_midi_port_mixdown,
  275. };