|
- /*
- Copyright (C) 2004-2006 Ian Esten
- Copyright (C) 2006 Dave Robillard
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
- #include <assert.h>
- #include <stdio.h>
- #include <string.h>
- #include <errno.h>
-
- #include <jack/jack.h>
- #include <jack/midiport.h>
-
- #include "port.h"
-
- enum { MIDI_INLINE_MAX = 4 }; /* 4 bytes for default event size */
-
- typedef struct _jack_midi_port_info_private {
- jack_nframes_t nframes; /**< Number of frames in buffer */
- uint32_t buffer_size; /**< Size of buffer in bytes */
- uint32_t event_count; /**< Number of events stored in this buffer */
- jack_nframes_t last_write_loc; /**< Used for both writing and mixdown */
- uint32_t events_lost; /**< Number of events lost in this buffer */
- } POST_PACKED_STRUCTURE jack_midi_port_info_private_t;
-
- typedef struct _jack_midi_port_internal_event {
- uint16_t time; /* offset within buffer limit to 64k */
- uint16_t size; /* event size limited to 64k */
- union {
- jack_shmsize_t byte_offset;
- jack_midi_data_t inline_data[MIDI_INLINE_MAX];
- } POST_PACKED_STRUCTURE;
- } POST_PACKED_STRUCTURE jack_midi_port_internal_event_t;
-
- size_t
- jack_midi_internal_event_size ()
- {
- return sizeof (jack_midi_port_internal_event_t);
- }
-
- static inline jack_midi_data_t*
- jack_midi_event_data(void* port_buffer,
- const jack_midi_port_internal_event_t* event)
- {
- if (event->size <= MIDI_INLINE_MAX)
- return (jack_midi_data_t*) event->inline_data;
- else
- return ((jack_midi_data_t *)port_buffer) + event->byte_offset;
- }
-
-
- /* jack_midi_port_functions.buffer_init */
- static void
- jack_midi_buffer_init(void *port_buffer,
- size_t buffer_size,
- jack_nframes_t nframes)
- {
- jack_midi_port_info_private_t *info =
- (jack_midi_port_info_private_t *) port_buffer;
- /* We can also add some magic field to midi buffer to validate client calls */
- info->nframes = nframes;
- info->buffer_size = buffer_size;
- info->event_count = 0;
- info->last_write_loc = 0;
- info->events_lost = 0;
- }
-
-
- uint32_t
- jack_midi_get_event_count(void *port_buffer)
- {
- jack_midi_port_info_private_t *info =
- (jack_midi_port_info_private_t *) port_buffer;
- return info->event_count;
- }
-
-
- int
- jack_midi_event_get(jack_midi_event_t *event,
- void *port_buffer,
- uint32_t event_idx)
- {
- jack_midi_port_internal_event_t *port_event;
- jack_midi_port_info_private_t *info =
- (jack_midi_port_info_private_t *) port_buffer;
-
- if (event_idx >= info->event_count)
- #ifdef ENODATA
- return ENODATA;
- #else
- return ENOMSG;
- #endif
-
- port_event = (jack_midi_port_internal_event_t *) (info + 1);
- port_event += event_idx;
- event->time = port_event->time;
- event->size = port_event->size;
- event->buffer = jack_midi_event_data(port_buffer, port_event);
-
- return 0;
- }
-
-
- size_t
- jack_midi_max_event_size(void *port_buffer)
- {
- jack_midi_port_info_private_t *info =
- (jack_midi_port_info_private_t *) port_buffer;
- size_t buffer_size =
- info->buffer_size;
-
- /* (event_count + 1) below accounts for jack_midi_port_internal_event_t
- * which would be needed to store the next event */
- size_t used_size = sizeof(jack_midi_port_info_private_t)
- + info->last_write_loc
- + ((info->event_count + 1)
- * sizeof(jack_midi_port_internal_event_t));
-
- if (used_size > buffer_size)
- return 0;
- else if ((buffer_size - used_size) < MIDI_INLINE_MAX)
- return MIDI_INLINE_MAX;
- else
- return (buffer_size - used_size);
- }
-
-
- jack_midi_data_t*
- jack_midi_event_reserve(void *port_buffer,
- jack_nframes_t time,
- size_t data_size)
- {
- jack_midi_data_t *retbuf = (jack_midi_data_t *) port_buffer;
-
- jack_midi_port_info_private_t *info =
- (jack_midi_port_info_private_t *) port_buffer;
- jack_midi_port_internal_event_t *event_buffer =
- (jack_midi_port_internal_event_t *) (info + 1);
- size_t buffer_size =
- info->buffer_size;
-
- if (time < 0 || time >= info->nframes)
- goto failed;
-
- if (info->event_count > 0 && time < event_buffer[info->event_count-1].time)
- goto failed;
-
- /* Check if data_size is >0 and there is enough space in the buffer for the event. */
- if (data_size <=0) {
- goto failed; // return NULL?
- } else if (jack_midi_max_event_size (port_buffer) < data_size) {
- goto failed;
- } else {
- jack_midi_port_internal_event_t *event = &event_buffer[info->event_count];
-
- event->time = time;
- event->size = data_size;
- if (data_size <= MIDI_INLINE_MAX) {
- retbuf = event->inline_data;
- } else {
- info->last_write_loc += data_size;
- retbuf = &retbuf[buffer_size - 1 - info->last_write_loc];
- event->byte_offset =
- buffer_size - 1 - info->last_write_loc;
- }
- info->event_count += 1;
- return retbuf;
- }
- failed:
- info->events_lost++;
- return NULL;
- }
-
-
- int
- jack_midi_event_write(void *port_buffer,
- jack_nframes_t time,
- const jack_midi_data_t *data,
- size_t data_size)
- {
- jack_midi_data_t *retbuf =
- jack_midi_event_reserve(port_buffer, time, data_size);
-
- if (retbuf) {
- memcpy(retbuf, data, data_size);
- return 0;
- } else {
- return ENOBUFS;
- }
- }
-
-
- /* Can't check to make sure this port is an output anymore. If this gets
- * called on an input port, all clients after the client that calls it
- * will think there are no events in the buffer as the event count has
- * been reset.
- */
- void
- jack_midi_clear_buffer(void *port_buffer)
- {
- jack_midi_port_info_private_t *info =
- (jack_midi_port_info_private_t *) port_buffer;
-
- info->event_count = 0;
- info->last_write_loc = 0;
- info->events_lost = 0;
- }
-
-
- /* jack_midi_port_functions.mixdown */
- static void
- jack_midi_port_mixdown(jack_port_t *port, jack_nframes_t nframes)
- {
- JSList *node;
- jack_port_t *input;
- jack_nframes_t num_events = 0;
- jack_nframes_t i = 0;
- int err = 0;
- jack_nframes_t lost_events = 0;
-
- /* The next (single) event to mix in to the buffer */
- jack_midi_port_info_private_t *earliest_info;
- jack_midi_port_internal_event_t *earliest_event;
- jack_midi_data_t *earliest_buffer;
-
- jack_midi_port_info_private_t *in_info; /* For finding next event */
- jack_midi_port_internal_event_t *in_events; /* Corresponds to in_info */
- jack_midi_port_info_private_t *out_info; /* Output 'buffer' */
-
- jack_midi_clear_buffer(port->mix_buffer);
-
- out_info = (jack_midi_port_info_private_t *) port->mix_buffer;
-
- /* This function uses jack_midi_port_info_private_t.last_write_loc of the
- * source ports to store indices of the last event read from that buffer
- * so far. This is OK because last_write_loc is used when writing events
- * to a buffer, which at this stage is already complete so the value
- * can be safely smashed. */
-
- /* Iterate through all connections to see how many events we need to mix,
- * and initialise their 'last event read' (last_write_loc) to 0 */
- for (node = port->connections; node; node = jack_slist_next(node)) {
- input = (jack_port_t *) node->data;
- in_info =
- (jack_midi_port_info_private_t *) jack_output_port_buffer(input);
- num_events += in_info->event_count;
- lost_events += in_info->events_lost;
- in_info->last_write_loc = 0;
- }
-
- /* Write the events in the order of their timestamps */
- for (i = 0; i < num_events; ++i) {
- earliest_info = NULL;
- earliest_event = NULL;
- earliest_buffer = NULL;
-
- /* Find the earliest unread event, to mix next
- * (search for an event earlier than earliest_event) */
- for (node = port->connections; node; node = jack_slist_next(node)) {
- in_info = (jack_midi_port_info_private_t *)
- jack_output_port_buffer(((jack_port_t *) node->data));
- in_events = (jack_midi_port_internal_event_t *) (in_info + 1);
-
- /* If there are unread events left in this port.. */
- if (in_info->event_count > in_info->last_write_loc) {
- /* .. and this event is the new earliest .. */
- /* NOTE: that's why we compare time with <, not <= */
- if (earliest_info == NULL
- || in_events[in_info->last_write_loc].time
- < earliest_event->time) {
- /* .. then set this event as the next earliest */
- earliest_info = in_info;
- earliest_event = (jack_midi_port_internal_event_t *)
- (&in_events[in_info->last_write_loc]);
- }
- }
- }
-
- if (earliest_info && earliest_event) {
- earliest_buffer = (jack_midi_data_t *) earliest_info;
-
- /* Write event to output */
- err = jack_midi_event_write(
- jack_port_buffer(port),
- earliest_event->time,
- jack_midi_event_data(earliest_buffer, earliest_event),
- earliest_event->size);
-
- earliest_info->last_write_loc++;
-
- if (err) {
- out_info->events_lost = num_events - i;
- break;
- }
- }
- }
- assert(out_info->event_count == num_events - out_info->events_lost);
-
- // inherit total lost events count from all connected ports.
- out_info->events_lost += lost_events;
- }
-
-
- uint32_t
- jack_midi_get_lost_event_count(void *port_buffer)
- {
- return ((jack_midi_port_info_private_t *) port_buffer)->events_lost;
- }
-
- jack_port_functions_t jack_builtin_midi_functions = {
- .buffer_init = jack_midi_buffer_init,
- .mixdown = jack_midi_port_mixdown,
- };
|