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.
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
{
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
;
58 return ((jack_midi_data_t
*)port_buffer
) + event
->byte_offset
;
62 /* jack_midi_port_functions.buffer_init */
64 jack_midi_buffer_init(void *port_buffer
,
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;
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
;
89 jack_midi_event_get(jack_midi_event_t
*event
,
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
)
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
);
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
;
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
)
131 else if ((buffer_size
- used_size
) < MIDI_INLINE_MAX
)
132 return MIDI_INLINE_MAX
;
134 return (buffer_size
- used_size
);
139 jack_midi_event_reserve(void *port_buffer
,
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);
152 if (time
< 0 || time
>= info
->nframes
)
155 if (info
->event_count
> 0 && time
< event_buffer
[info
->event_count
-1].time
)
158 /* Check if data_size is >0 and there is enough space in the buffer for the event. */
160 goto failed
; // return NULL?
161 } else if (jack_midi_max_event_size (port_buffer
) < data_size
) {
164 jack_midi_port_internal_event_t
*event
= &event_buffer
[info
->event_count
];
167 event
->size
= data_size
;
168 if (data_size
<= MIDI_INLINE_MAX
) {
169 retbuf
= event
->inline_data
;
171 info
->last_write_loc
+= data_size
;
172 retbuf
= &retbuf
[buffer_size
- 1 - info
->last_write_loc
];
174 buffer_size
- 1 - info
->last_write_loc
;
176 info
->event_count
+= 1;
186 jack_midi_event_write(void *port_buffer
,
188 const jack_midi_data_t
*data
,
191 jack_midi_data_t
*retbuf
=
192 jack_midi_event_reserve(port_buffer
, time
, data_size
);
195 memcpy(retbuf
, data
, data_size
);
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
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 */
222 jack_midi_port_mixdown(jack_port_t
*port
, jack_nframes_t nframes
)
226 jack_nframes_t num_events
= 0;
227 jack_nframes_t i
= 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
;
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
++;
302 out_info
->events_lost
= num_events
- i
;
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
;
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
,