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