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.

328 lines
9.8KB

  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. /* jack_midi_port_functions.buffer_init */
  54. static void
  55. jack_midi_buffer_init(void *port_buffer,
  56. size_t buffer_size,
  57. jack_nframes_t nframes)
  58. {
  59. jack_midi_port_info_private_t *info =
  60. (jack_midi_port_info_private_t *) port_buffer;
  61. /* We can also add some magic field to midi buffer to validate client calls */
  62. info->nframes = nframes;
  63. info->buffer_size = buffer_size;
  64. info->event_count = 0;
  65. info->last_write_loc = 0;
  66. info->events_lost = 0;
  67. }
  68. uint32_t
  69. jack_midi_get_event_count(void *port_buffer)
  70. {
  71. jack_midi_port_info_private_t *info =
  72. (jack_midi_port_info_private_t *) port_buffer;
  73. return info->event_count;
  74. }
  75. int
  76. jack_midi_event_get(jack_midi_event_t *event,
  77. void *port_buffer,
  78. uint32_t event_idx)
  79. {
  80. jack_midi_port_internal_event_t *port_event;
  81. jack_midi_port_info_private_t *info =
  82. (jack_midi_port_info_private_t *) port_buffer;
  83. if (event_idx >= info->event_count)
  84. #ifdef ENODATA
  85. return ENODATA;
  86. #else
  87. return ENOMSG;
  88. #endif
  89. port_event = (jack_midi_port_internal_event_t *) (info + 1);
  90. port_event += event_idx;
  91. event->time = port_event->time;
  92. event->size = port_event->size;
  93. event->buffer = jack_midi_event_data(port_buffer, port_event);
  94. return 0;
  95. }
  96. size_t
  97. jack_midi_max_event_size(void *port_buffer)
  98. {
  99. jack_midi_port_info_private_t *info =
  100. (jack_midi_port_info_private_t *) port_buffer;
  101. size_t buffer_size =
  102. info->buffer_size;
  103. /* (event_count + 1) below accounts for jack_midi_port_internal_event_t
  104. * which would be needed to store the next event */
  105. size_t used_size = sizeof(jack_midi_port_info_private_t)
  106. + info->last_write_loc
  107. + ((info->event_count + 1)
  108. * sizeof(jack_midi_port_internal_event_t));
  109. if (used_size > buffer_size)
  110. return 0;
  111. else if ((buffer_size - used_size) < MIDI_INLINE_MAX)
  112. return MIDI_INLINE_MAX;
  113. else
  114. return (buffer_size - used_size);
  115. }
  116. jack_midi_data_t*
  117. jack_midi_event_reserve(void *port_buffer,
  118. jack_nframes_t time,
  119. size_t data_size)
  120. {
  121. jack_midi_data_t *retbuf = (jack_midi_data_t *) port_buffer;
  122. jack_midi_port_info_private_t *info =
  123. (jack_midi_port_info_private_t *) port_buffer;
  124. jack_midi_port_internal_event_t *event_buffer =
  125. (jack_midi_port_internal_event_t *) (info + 1);
  126. size_t buffer_size =
  127. info->buffer_size;
  128. if (time < 0 || time >= info->nframes)
  129. goto failed;
  130. if (info->event_count > 0 && time < event_buffer[info->event_count-1].time)
  131. goto failed;
  132. /* Check if data_size is >0 and there is enough space in the buffer for the event. */
  133. if (data_size <=0) {
  134. goto failed; // return NULL?
  135. } else if (jack_midi_max_event_size (port_buffer) < data_size) {
  136. goto failed;
  137. } else {
  138. jack_midi_port_internal_event_t *event = &event_buffer[info->event_count];
  139. event->time = time;
  140. event->size = data_size;
  141. if (data_size <= MIDI_INLINE_MAX) {
  142. retbuf = event->inline_data;
  143. } else {
  144. info->last_write_loc += data_size;
  145. retbuf = &retbuf[buffer_size - 1 - info->last_write_loc];
  146. event->byte_offset =
  147. buffer_size - 1 - info->last_write_loc;
  148. }
  149. info->event_count += 1;
  150. return retbuf;
  151. }
  152. failed:
  153. info->events_lost++;
  154. return NULL;
  155. }
  156. int
  157. jack_midi_event_write(void *port_buffer,
  158. jack_nframes_t time,
  159. const jack_midi_data_t *data,
  160. size_t data_size)
  161. {
  162. jack_midi_data_t *retbuf =
  163. jack_midi_event_reserve(port_buffer, time, data_size);
  164. if (retbuf) {
  165. memcpy(retbuf, data, data_size);
  166. return 0;
  167. } else {
  168. return ENOBUFS;
  169. }
  170. }
  171. /* Can't check to make sure this port is an output anymore. If this gets
  172. * called on an input port, all clients after the client that calls it
  173. * will think there are no events in the buffer as the event count has
  174. * been reset.
  175. */
  176. void
  177. jack_midi_clear_buffer(void *port_buffer)
  178. {
  179. jack_midi_port_info_private_t *info =
  180. (jack_midi_port_info_private_t *) port_buffer;
  181. info->event_count = 0;
  182. info->last_write_loc = 0;
  183. info->events_lost = 0;
  184. }
  185. /* jack_midi_port_functions.mixdown */
  186. static void
  187. jack_midi_port_mixdown(jack_port_t *port, jack_nframes_t nframes)
  188. {
  189. JSList *node;
  190. jack_port_t *input;
  191. jack_nframes_t num_events = 0;
  192. jack_nframes_t i = 0;
  193. int err = 0;
  194. jack_nframes_t lost_events = 0;
  195. /* The next (single) event to mix in to the buffer */
  196. jack_midi_port_info_private_t *earliest_info;
  197. jack_midi_port_internal_event_t *earliest_event;
  198. jack_midi_data_t *earliest_buffer;
  199. jack_midi_port_info_private_t *in_info; /* For finding next event */
  200. jack_midi_port_internal_event_t *in_events; /* Corresponds to in_info */
  201. jack_midi_port_info_private_t *out_info; /* Output 'buffer' */
  202. jack_midi_clear_buffer(port->mix_buffer);
  203. out_info = (jack_midi_port_info_private_t *) port->mix_buffer;
  204. /* This function uses jack_midi_port_info_private_t.last_write_loc of the
  205. * source ports to store indices of the last event read from that buffer
  206. * so far. This is OK because last_write_loc is used when writing events
  207. * to a buffer, which at this stage is already complete so the value
  208. * can be safely smashed. */
  209. /* Iterate through all connections to see how many events we need to mix,
  210. * and initialise their 'last event read' (last_write_loc) to 0 */
  211. for (node = port->connections; node; node = jack_slist_next(node)) {
  212. input = (jack_port_t *) node->data;
  213. in_info =
  214. (jack_midi_port_info_private_t *) jack_output_port_buffer(input);
  215. num_events += in_info->event_count;
  216. lost_events += in_info->events_lost;
  217. in_info->last_write_loc = 0;
  218. }
  219. /* Write the events in the order of their timestamps */
  220. for (i = 0; i < num_events; ++i) {
  221. earliest_info = NULL;
  222. earliest_event = NULL;
  223. earliest_buffer = NULL;
  224. /* Find the earliest unread event, to mix next
  225. * (search for an event earlier than earliest_event) */
  226. for (node = port->connections; node; node = jack_slist_next(node)) {
  227. in_info = (jack_midi_port_info_private_t *)
  228. jack_output_port_buffer(((jack_port_t *) node->data));
  229. in_events = (jack_midi_port_internal_event_t *) (in_info + 1);
  230. /* If there are unread events left in this port.. */
  231. if (in_info->event_count > in_info->last_write_loc) {
  232. /* .. and this event is the new earliest .. */
  233. /* NOTE: that's why we compare time with <, not <= */
  234. if (earliest_info == NULL
  235. || in_events[in_info->last_write_loc].time
  236. < earliest_event->time) {
  237. /* .. then set this event as the next earliest */
  238. earliest_info = in_info;
  239. earliest_event = (jack_midi_port_internal_event_t *)
  240. (&in_events[in_info->last_write_loc]);
  241. }
  242. }
  243. }
  244. if (earliest_info && earliest_event) {
  245. earliest_buffer = (jack_midi_data_t *) earliest_info;
  246. /* Write event to output */
  247. err = jack_midi_event_write(
  248. jack_port_buffer(port),
  249. earliest_event->time,
  250. jack_midi_event_data(earliest_buffer, earliest_event),
  251. earliest_event->size);
  252. earliest_info->last_write_loc++;
  253. if (err) {
  254. out_info->events_lost = num_events - i;
  255. break;
  256. }
  257. }
  258. }
  259. assert(out_info->event_count == num_events - out_info->events_lost);
  260. // inherit total lost events count from all connected ports.
  261. out_info->events_lost += lost_events;
  262. }
  263. uint32_t
  264. jack_midi_get_lost_event_count(void *port_buffer)
  265. {
  266. return ((jack_midi_port_info_private_t *) port_buffer)->events_lost;
  267. }
  268. jack_port_functions_t jack_builtin_midi_functions = {
  269. .buffer_init = jack_midi_buffer_init,
  270. .mixdown = jack_midi_port_mixdown,
  271. };