diff options
Diffstat (limited to 'drivers/alsa_midi/alsa_midi.c')
-rw-r--r-- | drivers/alsa_midi/alsa_midi.c | 1032 |
1 files changed, 515 insertions, 517 deletions
diff --git a/drivers/alsa_midi/alsa_midi.c b/drivers/alsa_midi/alsa_midi.c index a4542b4..28a00cd 100644 --- a/drivers/alsa_midi/alsa_midi.c +++ b/drivers/alsa_midi/alsa_midi.c @@ -42,6 +42,7 @@ void _a2j_debug (const char* fmt, ...) { va_list ap; + va_start (ap, fmt); vfprintf (stderr, fmt, ap); fputc ('\n', stderr); @@ -52,25 +53,26 @@ void a2j_error (const char* fmt, ...) { va_list ap; + va_start (ap, fmt); vfprintf (stdout, fmt, ap); fputc ('\n', stdout); } static bool -a2j_stream_init(alsa_midi_driver_t* driver, int which) +a2j_stream_init (alsa_midi_driver_t* driver, int which) { - struct a2j_stream *str = &driver->stream[which]; - - str->new_ports = jack_ringbuffer_create (MAX_PORTS * sizeof(struct a2j_port *)); - if (str->new_ports == NULL) { - return false; - } - - snd_midi_event_new (MAX_EVENT_SIZE, &str->codec); - INIT_LIST_HEAD (&str->list); - - return true; + struct a2j_stream *str = &driver->stream[which]; + + str->new_ports = jack_ringbuffer_create (MAX_PORTS * sizeof(struct a2j_port *)); + if (str->new_ports == NULL) { + return false; + } + + snd_midi_event_new (MAX_EVENT_SIZE, &str->codec); + INIT_LIST_HEAD (&str->list); + + return true; } static void @@ -78,10 +80,10 @@ a2j_stream_detach (struct a2j_stream * stream_ptr) { struct a2j_port * port_ptr; struct list_head * node_ptr; - - if (!stream_ptr) { - return; - } + + if (!stream_ptr) { + return; + } while (!list_empty (&stream_ptr->list)) { node_ptr = stream_ptr->list.next; @@ -98,34 +100,36 @@ a2j_stream_close (alsa_midi_driver_t* driver, int which) { struct a2j_stream *str = &driver->stream[which]; - if (!str) { - return; - } + if (!str) { + return; + } - if (str->codec) + if (str->codec) { snd_midi_event_free (str->codec); - if (str->new_ports) + } + if (str->new_ports) { jack_ringbuffer_free (str->new_ports); + } } static void stop_threads (alsa_midi_driver_t* driver) { - if (driver->running) { - void* thread_status; - - driver->running = false; /* tell alsa io thread to stop, whenever they wake up */ - /* do something that we need to do anyway and will wake the io thread, then join */ - snd_seq_disconnect_from (driver->seq, driver->port_id, SND_SEQ_CLIENT_SYSTEM, SND_SEQ_PORT_SYSTEM_ANNOUNCE); - a2j_debug ("wait for ALSA input thread\n"); - pthread_join (driver->alsa_input_thread, &thread_status); - a2j_debug ("input thread done\n"); - - /* wake output thread and join */ - sem_post (&driver->output_semaphore); - pthread_join (driver->alsa_output_thread, &thread_status); - a2j_debug ("output thread done\n"); - } + if (driver->running) { + void* thread_status; + + driver->running = false; /* tell alsa io thread to stop, whenever they wake up */ + /* do something that we need to do anyway and will wake the io thread, then join */ + snd_seq_disconnect_from (driver->seq, driver->port_id, SND_SEQ_CLIENT_SYSTEM, SND_SEQ_PORT_SYSTEM_ANNOUNCE); + a2j_debug ("wait for ALSA input thread\n"); + pthread_join (driver->alsa_input_thread, &thread_status); + a2j_debug ("input thread done\n"); + + /* wake output thread and join */ + sem_post (&driver->output_semaphore); + pthread_join (driver->alsa_output_thread, &thread_status); + a2j_debug ("output thread done\n"); + } } /* @@ -135,8 +139,9 @@ stop_threads (alsa_midi_driver_t* driver) void a2j_add_ports (struct a2j_stream * str) { struct a2j_port * port_ptr; - while (jack_ringbuffer_read (str->new_ports, (char *)&port_ptr, sizeof(port_ptr))) { - a2j_debug("jack: inserted port %s", port_ptr->name); + + while (jack_ringbuffer_read (str->new_ports, (char*)&port_ptr, sizeof(port_ptr))) { + a2j_debug ("jack: inserted port %s", port_ptr->name); a2j_port_insert (str->port_hash, port_ptr); } } @@ -147,19 +152,20 @@ a2j_port_event (alsa_midi_driver_t* driver, snd_seq_event_t * ev) { const snd_seq_addr_t addr = ev->data.addr; - if (addr.client == driver->client_id) + if (addr.client == driver->client_id) { return; + } if (ev->type == SND_SEQ_EVENT_PORT_START) { - a2j_debug("port_event: add %d:%d", addr.client, addr.port); + a2j_debug ("port_event: add %d:%d", addr.client, addr.port); a2j_new_ports (driver, addr); } else if (ev->type == SND_SEQ_EVENT_PORT_CHANGE) { - a2j_debug("port_event: change %d:%d", addr.client, addr.port); + a2j_debug ("port_event: change %d:%d", addr.client, addr.port); a2j_update_ports (driver, addr); } else if (ev->type == SND_SEQ_EVENT_PORT_EXIT) { - a2j_debug("port_event: del %d:%d", addr.client, addr.port); - a2j_port_setdead(driver->stream[A2J_PORT_CAPTURE].port_hash, addr); - a2j_port_setdead(driver->stream[A2J_PORT_PLAYBACK].port_hash, addr); + a2j_debug ("port_event: del %d:%d", addr.client, addr.port); + a2j_port_setdead (driver->stream[A2J_PORT_CAPTURE].port_hash, addr); + a2j_port_setdead (driver->stream[A2J_PORT_PLAYBACK].port_hash, addr); } } @@ -175,8 +181,8 @@ a2j_input_event (alsa_midi_driver_t* driver, snd_seq_event_t * alsa_event) jack_nframes_t now; now = jack_frame_time (driver->jack_client); - - if ((port = a2j_port_get(str->port_hash, alsa_event->source)) == NULL) { + + if ((port = a2j_port_get (str->port_hash, alsa_event->source)) == NULL) { return; } @@ -184,8 +190,8 @@ a2j_input_event (alsa_midi_driver_t* driver, snd_seq_event_t * alsa_event) * RPNs, NRPNs, Bank Change, etc. need special handling * but seems, ALSA does it for us already. */ - snd_midi_event_reset_decode(str->codec); - if ((size = snd_midi_event_decode(str->codec, data, sizeof(data), alsa_event))<0) { + snd_midi_event_reset_decode (str->codec); + if ((size = snd_midi_event_decode (str->codec, data, sizeof(data), alsa_event)) < 0) { return; } @@ -195,47 +201,47 @@ a2j_input_event (alsa_midi_driver_t* driver, snd_seq_event_t * alsa_event) data[2] = 0x40; } - a2j_debug("input: %d bytes at event_frame=%u", (int)size, now); + a2j_debug ("input: %d bytes at event_frame=%u", (int)size, now); - if (jack_ringbuffer_write_space(port->inbound_events) >= (sizeof(struct a2j_alsa_midi_event) + size)) { + if (jack_ringbuffer_write_space (port->inbound_events) >= (sizeof(struct a2j_alsa_midi_event) + size)) { struct a2j_alsa_midi_event ev; - char *ev_charp = (char*) &ev; + char *ev_charp = (char*)&ev; size_t limit; size_t to_write = sizeof(ev); jack_ringbuffer_data_t vec[2]; - jack_ringbuffer_get_write_vector( port->inbound_events, vec ); + jack_ringbuffer_get_write_vector ( port->inbound_events, vec ); ev.time = now; ev.size = size; - + limit = (to_write > vec[0].len ? vec[0].len : to_write); if (limit) { - memcpy( vec[0].buf, ev_charp, limit ); - to_write -= limit; - ev_charp += limit; - vec[0].buf += limit; - vec[0].len -= limit; + memcpy ( vec[0].buf, ev_charp, limit ); + to_write -= limit; + ev_charp += limit; + vec[0].buf += limit; + vec[0].len -= limit; } if (to_write) { - memcpy( vec[1].buf, ev_charp, to_write ); - vec[1].buf += to_write; - vec[1].len -= to_write; + memcpy ( vec[1].buf, ev_charp, to_write ); + vec[1].buf += to_write; + vec[1].len -= to_write; } to_write = size; - ev_charp = (char *)data; + ev_charp = (char*)data; limit = (to_write > vec[0].len ? vec[0].len : to_write); if (limit) { memcpy (vec[0].buf, ev_charp, limit); - } + } to_write -= limit; ev_charp += limit; if (to_write) { memcpy (vec[1].buf, ev_charp, to_write); - } + } - jack_ringbuffer_write_advance( port->inbound_events, sizeof(ev) + size ); + jack_ringbuffer_write_advance ( port->inbound_events, sizeof(ev) + size ); } else { a2j_error ("MIDI data lost (incoming event buffer full): %ld bytes lost", size); } @@ -245,74 +251,74 @@ a2j_input_event (alsa_midi_driver_t* driver, snd_seq_event_t * alsa_event) static int a2j_process_incoming (alsa_midi_driver_t* driver, struct a2j_port* port, jack_nframes_t nframes) { - jack_nframes_t one_period; - struct a2j_alsa_midi_event ev; - char *ev_buf; - - /* grab data queued by the ALSA input thread and write it into the JACK - port buffer. it will delivered during the JACK period that this - function is called from. - */ - - /* first clear the JACK port buffer in preparation for new data - */ - - a2j_debug ("PORT: %s process input", jack_port_name (port->jack_port)); - - jack_midi_clear_buffer (port->jack_buf); - - one_period = jack_get_buffer_size (driver->jack_client); - - while (jack_ringbuffer_peek (port->inbound_events, (char*)&ev, sizeof(ev) ) == sizeof(ev) ) { - - jack_midi_data_t* buf; - jack_nframes_t offset; - - a2j_debug ("Seen inbound event from read callback\n"); - - if (ev.time >= driver->cycle_start) { - a2j_debug ("event is too late\n"); - break; - } - - //jack_ringbuffer_read_advance (port->inbound_events, sizeof (ev)); - ev_buf = (char *) alloca( sizeof(ev) + ev.size ); - - if (jack_ringbuffer_peek (port->inbound_events, ev_buf, sizeof(ev) + ev.size ) != sizeof(ev) + ev.size) { - break; - } - - offset = driver->cycle_start - ev.time; - if (offset > one_period) { - /* from a previous cycle, somehow. cram it in at the front */ - offset = 0; - } else { - /* offset from start of the current cycle */ - offset = one_period - offset; - } - - a2j_debug ("event at %d offset %d", ev.time, offset); - - /* make sure there is space for it */ - - buf = jack_midi_event_reserve (port->jack_buf, offset, ev.size); - - if (buf) { - /* grab the event */ - memcpy( buf, ev_buf + sizeof(ev), ev.size ); - } else { - /* throw it away (no space) */ - a2j_error ("threw away MIDI event - not reserved at time %d", ev.time); - } - jack_ringbuffer_read_advance (port->inbound_events, sizeof(ev) + ev.size); - - a2j_debug("input on %s: sucked %d bytes from inbound at %d", jack_port_name (port->jack_port), ev.size, ev.time); - } - - return 0; + jack_nframes_t one_period; + struct a2j_alsa_midi_event ev; + char *ev_buf; + + /* grab data queued by the ALSA input thread and write it into the JACK + port buffer. it will delivered during the JACK period that this + function is called from. + */ + + /* first clear the JACK port buffer in preparation for new data + */ + + a2j_debug ("PORT: %s process input", jack_port_name (port->jack_port)); + + jack_midi_clear_buffer (port->jack_buf); + + one_period = jack_get_buffer_size (driver->jack_client); + + while (jack_ringbuffer_peek (port->inbound_events, (char*)&ev, sizeof(ev) ) == sizeof(ev) ) { + + jack_midi_data_t* buf; + jack_nframes_t offset; + + a2j_debug ("Seen inbound event from read callback\n"); + + if (ev.time >= driver->cycle_start) { + a2j_debug ("event is too late\n"); + break; + } + + //jack_ringbuffer_read_advance (port->inbound_events, sizeof (ev)); + ev_buf = (char*)alloca ( sizeof(ev) + ev.size ); + + if (jack_ringbuffer_peek (port->inbound_events, ev_buf, sizeof(ev) + ev.size ) != sizeof(ev) + ev.size) { + break; + } + + offset = driver->cycle_start - ev.time; + if (offset > one_period) { + /* from a previous cycle, somehow. cram it in at the front */ + offset = 0; + } else { + /* offset from start of the current cycle */ + offset = one_period - offset; + } + + a2j_debug ("event at %d offset %d", ev.time, offset); + + /* make sure there is space for it */ + + buf = jack_midi_event_reserve (port->jack_buf, offset, ev.size); + + if (buf) { + /* grab the event */ + memcpy ( buf, ev_buf + sizeof(ev), ev.size ); + } else { + /* throw it away (no space) */ + a2j_error ("threw away MIDI event - not reserved at time %d", ev.time); + } + jack_ringbuffer_read_advance (port->inbound_events, sizeof(ev) + ev.size); + + a2j_debug ("input on %s: sucked %d bytes from inbound at %d", jack_port_name (port->jack_port), ev.size, ev.time); + } + + return 0; } -void* +void* alsa_input_thread (void* arg) { alsa_midi_driver_t* driver = arg; @@ -324,25 +330,25 @@ alsa_input_thread (void* arg) snd_seq_event_t * event; int ret; - npfd = snd_seq_poll_descriptors_count(driver->seq, POLLIN); - pfd = (struct pollfd *)alloca(npfd * sizeof(struct pollfd)); - snd_seq_poll_descriptors(driver->seq, pfd, npfd, POLLIN); + npfd = snd_seq_poll_descriptors_count (driver->seq, POLLIN); + pfd = (struct pollfd*)alloca (npfd * sizeof(struct pollfd)); + snd_seq_poll_descriptors (driver->seq, pfd, npfd, POLLIN); initial = true; while (driver->running) { - if ((ret = poll(pfd, npfd, 1000)) > 0) { + if ((ret = poll (pfd, npfd, 1000)) > 0) { while (snd_seq_event_input (driver->seq, &event) > 0) { if (initial) { - snd_seq_client_info_alloca(&client_info); - snd_seq_client_info_set_client(client_info, -1); - while (snd_seq_query_next_client(driver->seq, client_info) >= 0) { - addr.client = snd_seq_client_info_get_client(client_info); + snd_seq_client_info_alloca (&client_info); + snd_seq_client_info_set_client (client_info, -1); + while (snd_seq_query_next_client (driver->seq, client_info) >= 0) { + addr.client = snd_seq_client_info_get_client (client_info); if (addr.client == SND_SEQ_CLIENT_SYSTEM || addr.client == driver->client_id) { continue; } - + a2j_new_ports (driver, addr); } @@ -350,9 +356,9 @@ alsa_input_thread (void* arg) } if (event->source.client == SND_SEQ_CLIENT_SYSTEM) { - a2j_port_event(driver, event); + a2j_port_event (driver, event); } else { - a2j_input_event(driver, event); + a2j_input_event (driver, event); } snd_seq_free_event (event); @@ -360,214 +366,207 @@ alsa_input_thread (void* arg) } } - return (void*) 0; + return (void*)0; } /* --- OUTBOUND FROM JACK TO ALSA ---- */ int a2j_process_outgoing ( - alsa_midi_driver_t* driver, - struct a2j_port * port) + alsa_midi_driver_t* driver, + struct a2j_port * port) { - /* collect data from JACK port buffer and queue it for delivery by ALSA output thread */ - - int nevents; - jack_ringbuffer_data_t vec[2]; - int i; - int written = 0; - size_t limit; - struct a2j_delivery_event* dev; - size_t gap = 0; - - jack_ringbuffer_get_write_vector (driver->outbound_events, vec); - - dev = (struct a2j_delivery_event*) vec[0].buf; - limit = vec[0].len / sizeof (struct a2j_delivery_event); - nevents = jack_midi_get_event_count (port->jack_buf); - - a2j_debug ("alsa_out: port has %d events for delivery\n", nevents); - - for (i = 0; (i < nevents) && (written < limit); ++i) { - - jack_midi_event_get (&dev->jack_event, port->jack_buf, i); - if (dev->jack_event.size <= MAX_JACKMIDI_EV_SIZE) - { - dev->time = dev->jack_event.time; - dev->port = port; - memcpy( dev->midistring, dev->jack_event.buffer, dev->jack_event.size ); - written++; - ++dev; - } - } - - /* anything left? use the second part of the vector, as much as possible */ - - if (i < nevents) - { - if (vec[0].len) - { - gap = vec[0].len - written * sizeof(struct a2j_delivery_event); - } - - dev = (struct a2j_delivery_event*) vec[1].buf; - - limit += (vec[1].len / sizeof (struct a2j_delivery_event)); - - while ((i < nevents) && (written < limit)) - { - jack_midi_event_get(&dev->jack_event, port->jack_buf, i); - if (dev->jack_event.size <= MAX_JACKMIDI_EV_SIZE) - { - dev->time = dev->jack_event.time; - dev->port = port; - memcpy(dev->midistring, dev->jack_event.buffer, dev->jack_event.size); - written++; - ++dev; - } - ++i; - } - } - - a2j_debug( "done pushing events: %d ... gap: %d ", (int)written, (int)gap ); - /* clear JACK port buffer; advance ring buffer ptr */ - - jack_ringbuffer_write_advance (driver->outbound_events, written * sizeof (struct a2j_delivery_event) + gap); - - return nevents; + /* collect data from JACK port buffer and queue it for delivery by ALSA output thread */ + + int nevents; + jack_ringbuffer_data_t vec[2]; + int i; + int written = 0; + size_t limit; + struct a2j_delivery_event* dev; + size_t gap = 0; + + jack_ringbuffer_get_write_vector (driver->outbound_events, vec); + + dev = (struct a2j_delivery_event*)vec[0].buf; + limit = vec[0].len / sizeof(struct a2j_delivery_event); + nevents = jack_midi_get_event_count (port->jack_buf); + + a2j_debug ("alsa_out: port has %d events for delivery\n", nevents); + + for (i = 0; (i < nevents) && (written < limit); ++i) { + + jack_midi_event_get (&dev->jack_event, port->jack_buf, i); + if (dev->jack_event.size <= MAX_JACKMIDI_EV_SIZE) { + dev->time = dev->jack_event.time; + dev->port = port; + memcpy ( dev->midistring, dev->jack_event.buffer, dev->jack_event.size ); + written++; + ++dev; + } + } + + /* anything left? use the second part of the vector, as much as possible */ + + if (i < nevents) { + if (vec[0].len) { + gap = vec[0].len - written * sizeof(struct a2j_delivery_event); + } + + dev = (struct a2j_delivery_event*)vec[1].buf; + + limit += (vec[1].len / sizeof(struct a2j_delivery_event)); + + while ((i < nevents) && (written < limit)) { + jack_midi_event_get (&dev->jack_event, port->jack_buf, i); + if (dev->jack_event.size <= MAX_JACKMIDI_EV_SIZE) { + dev->time = dev->jack_event.time; + dev->port = port; + memcpy (dev->midistring, dev->jack_event.buffer, dev->jack_event.size); + written++; + ++dev; + } + ++i; + } + } + + a2j_debug ( "done pushing events: %d ... gap: %d ", (int)written, (int)gap ); + /* clear JACK port buffer; advance ring buffer ptr */ + + jack_ringbuffer_write_advance (driver->outbound_events, written * sizeof(struct a2j_delivery_event) + gap); + + return nevents; } static int time_sorter (struct a2j_delivery_event * a, struct a2j_delivery_event * b) { - if (a->time < b->time) { - return -1; - } else if (a->time > b->time) { - return 1; - } - return 0; + if (a->time < b->time) { + return -1; + } else if (a->time > b->time) { + return 1; + } + return 0; } -static void* -alsa_output_thread(void * arg) +static void* +alsa_output_thread (void * arg) { - alsa_midi_driver_t * driver = (alsa_midi_driver_t*) arg; - struct a2j_stream *str = &driver->stream[A2J_PORT_PLAYBACK]; - int i; - struct list_head evlist; - struct list_head * node_ptr; - jack_ringbuffer_data_t vec[2]; - snd_seq_event_t alsa_event; - struct a2j_delivery_event* ev; - float sr; - jack_nframes_t now; - int limit; - - while (driver->running) { - /* pre-first, handle port deletion requests */ - - a2j_free_ports(driver); - - /* first, make a list of all events in the outbound_events FIFO */ - - INIT_LIST_HEAD(&evlist); - - jack_ringbuffer_get_read_vector (driver->outbound_events, vec); - - a2j_debug ("alsa_out: output thread: got %d+%d events", - (vec[0].len / sizeof (struct a2j_delivery_event)), - (vec[1].len / sizeof (struct a2j_delivery_event))); - - ev = (struct a2j_delivery_event*) vec[0].buf; - limit = vec[0].len / sizeof (struct a2j_delivery_event); - for (i = 0; i < limit; ++i) { - list_add_tail(&ev->siblings, &evlist); - ev++; - } - - ev = (struct a2j_delivery_event*) vec[1].buf; - limit = vec[1].len / sizeof (struct a2j_delivery_event); - for (i = 0; i < limit; ++i) { - list_add_tail(&ev->siblings, &evlist); - ev++; - } - - if (vec[0].len < sizeof(struct a2j_delivery_event) && (vec[1].len == 0)) { - /* no events: wait for some */ - a2j_debug ("alsa_out: output thread: wait for events"); - sem_wait (&driver->output_semaphore); - a2j_debug ("alsa_out: output thread: AWAKE ... loop back for events"); - continue; - } - - /* now sort this list by time */ - - list_sort(&evlist, struct a2j_delivery_event, siblings, time_sorter); - - /* now deliver */ - - sr = jack_get_sample_rate (driver->jack_client); - - list_for_each(node_ptr, &evlist) - { - ev = list_entry(node_ptr, struct a2j_delivery_event, siblings); - - snd_seq_ev_clear(&alsa_event); - snd_midi_event_reset_encode(str->codec); - if (!snd_midi_event_encode(str->codec, (const unsigned char *)ev->midistring, ev->jack_event.size, &alsa_event)) - { - a2j_debug ("alsa_out: invalid event of size %d, ignored\n", ev->jack_event.size); - continue; // invalid event - } - - snd_seq_ev_set_source(&alsa_event, driver->port_id); - snd_seq_ev_set_dest(&alsa_event, ev->port->remote.client, ev->port->remote.port); - snd_seq_ev_set_direct (&alsa_event); - - now = jack_frame_time (driver->jack_client); - - ev->time += driver->cycle_start; - - a2j_debug ("alsa_out:@ %d, next event @ %d", now, ev->time); - - /* do we need to wait a while before delivering? */ - - if (ev->time > now) { - struct timespec nanoseconds; - jack_nframes_t sleep_frames = ev->time - now; - float seconds = sleep_frames / sr; - - /* if the gap is long enough, sleep */ - - if (seconds > 0.001) { - nanoseconds.tv_sec = (time_t) seconds; - nanoseconds.tv_nsec = (long) NSEC_PER_SEC * (seconds - nanoseconds.tv_sec); - - a2j_debug ("alsa_out: output thread sleeps for %.2f msec", ((double) nanoseconds.tv_nsec / NSEC_PER_SEC) * 1000.0); - - if (nanosleep (&nanoseconds, NULL) < 0) { - fprintf (stderr, "BAD SLEEP\n"); - /* do something ? */ - } - } - } - - /* its time to deliver */ - snd_seq_event_output(driver->seq, &alsa_event); - snd_seq_drain_output (driver->seq); - now = jack_frame_time (driver->jack_client); - a2j_debug("alsa_out: written %d bytes to %s at %d, DELTA = %d", ev->jack_event.size, ev->port->name, now, - (int32_t) (now - ev->time)); - } - - /* free up space in the FIFO */ - - jack_ringbuffer_read_advance (driver->outbound_events, vec[0].len + vec[1].len); - - /* and head back for more */ - } - - return (void*) 0; + alsa_midi_driver_t * driver = (alsa_midi_driver_t*)arg; + struct a2j_stream *str = &driver->stream[A2J_PORT_PLAYBACK]; + int i; + struct list_head evlist; + struct list_head * node_ptr; + jack_ringbuffer_data_t vec[2]; + snd_seq_event_t alsa_event; + struct a2j_delivery_event* ev; + float sr; + jack_nframes_t now; + int limit; + + while (driver->running) { + /* pre-first, handle port deletion requests */ + + a2j_free_ports (driver); + + /* first, make a list of all events in the outbound_events FIFO */ + + INIT_LIST_HEAD (&evlist); + + jack_ringbuffer_get_read_vector (driver->outbound_events, vec); + + a2j_debug ("alsa_out: output thread: got %d+%d events", + (vec[0].len / sizeof(struct a2j_delivery_event)), + (vec[1].len / sizeof(struct a2j_delivery_event))); + + ev = (struct a2j_delivery_event*)vec[0].buf; + limit = vec[0].len / sizeof(struct a2j_delivery_event); + for (i = 0; i < limit; ++i) { + list_add_tail (&ev->siblings, &evlist); + ev++; + } + + ev = (struct a2j_delivery_event*)vec[1].buf; + limit = vec[1].len / sizeof(struct a2j_delivery_event); + for (i = 0; i < limit; ++i) { + list_add_tail (&ev->siblings, &evlist); + ev++; + } + + if (vec[0].len < sizeof(struct a2j_delivery_event) && (vec[1].len == 0)) { + /* no events: wait for some */ + a2j_debug ("alsa_out: output thread: wait for events"); + sem_wait (&driver->output_semaphore); + a2j_debug ("alsa_out: output thread: AWAKE ... loop back for events"); + continue; + } + + /* now sort this list by time */ + + list_sort (&evlist, struct a2j_delivery_event, siblings, time_sorter); + + /* now deliver */ + + sr = jack_get_sample_rate (driver->jack_client); + + list_for_each (node_ptr, &evlist){ + ev = list_entry (node_ptr, struct a2j_delivery_event, siblings); + + snd_seq_ev_clear (&alsa_event); + snd_midi_event_reset_encode (str->codec); + if (!snd_midi_event_encode (str->codec, (const unsigned char*)ev->midistring, ev->jack_event.size, &alsa_event)) { + a2j_debug ("alsa_out: invalid event of size %d, ignored\n", ev->jack_event.size); + continue; // invalid event + } + + snd_seq_ev_set_source (&alsa_event, driver->port_id); + snd_seq_ev_set_dest (&alsa_event, ev->port->remote.client, ev->port->remote.port); + snd_seq_ev_set_direct (&alsa_event); + + now = jack_frame_time (driver->jack_client); + + ev->time += driver->cycle_start; + + a2j_debug ("alsa_out:@ %d, next event @ %d", now, ev->time); + + /* do we need to wait a while before delivering? */ + + if (ev->time > now) { + struct timespec nanoseconds; + jack_nframes_t sleep_frames = ev->time - now; + float seconds = sleep_frames / sr; + + /* if the gap is long enough, sleep */ + + if (seconds > 0.001) { + nanoseconds.tv_sec = (time_t)seconds; + nanoseconds.tv_nsec = (long)NSEC_PER_SEC * (seconds - nanoseconds.tv_sec); + + a2j_debug ("alsa_out: output thread sleeps for %.2f msec", ((double)nanoseconds.tv_nsec / NSEC_PER_SEC) * 1000.0); + + if (nanosleep (&nanoseconds, NULL) < 0) { + fprintf (stderr, "BAD SLEEP\n"); + /* do something ? */ + } + } + } + + /* its time to deliver */ + snd_seq_event_output (driver->seq, &alsa_event); + snd_seq_drain_output (driver->seq); + now = jack_frame_time (driver->jack_client); + a2j_debug ("alsa_out: written %d bytes to %s at %d, DELTA = %d", ev->jack_event.size, ev->port->name, now, + (int32_t)(now - ev->time)); + } + + /* free up space in the FIFO */ + + jack_ringbuffer_read_advance (driver->outbound_events, vec[0].len + vec[1].len); + + /* and head back for more */ + } + + return (void*)0; } /** CORE JACK PROCESSING */ @@ -578,56 +577,54 @@ alsa_output_thread(void * arg) static void a2j_jack_process_internal (alsa_midi_driver_t* driver, int dir, jack_nframes_t nframes) { - struct a2j_stream * stream_ptr; - int i; - struct a2j_port ** port_ptr_ptr; - struct a2j_port * port_ptr; - int nevents = 0; - - stream_ptr = &driver->stream[dir]; - a2j_add_ports(stream_ptr); - - // process ports - for (i = 0 ; i < PORT_HASH_SIZE ; i++) - { - port_ptr_ptr = &stream_ptr->port_hash[i]; - while (*port_ptr_ptr != NULL) - { - port_ptr = *port_ptr_ptr; - - if (!port_ptr->is_dead) { - port_ptr->jack_buf = jack_port_get_buffer(port_ptr->jack_port, nframes); - - if (dir == A2J_PORT_CAPTURE) { - a2j_process_incoming (driver, port_ptr, nframes); - } else { - nevents += a2j_process_outgoing (driver, port_ptr); - } - - } else if (jack_ringbuffer_write_space (driver->port_del) >= sizeof(port_ptr)) { - - a2j_debug("jack: removed port %s", port_ptr->name); - *port_ptr_ptr = port_ptr->next; - jack_ringbuffer_write(driver->port_del, (char*)&port_ptr, sizeof(port_ptr)); - nevents += 1; /* wake up output thread, see: a2j_free_ports */ - continue; - - } - - port_ptr_ptr = &port_ptr->next; - } - } - - if (dir == A2J_PORT_PLAYBACK && nevents > 0) { - int sv; - - /* if we queued up anything for output, tell the output thread in - case its waiting for us. - */ - - sem_getvalue (&driver->output_semaphore, &sv); - sem_post (&driver->output_semaphore); - } + struct a2j_stream * stream_ptr; + int i; + struct a2j_port ** port_ptr_ptr; + struct a2j_port * port_ptr; + int nevents = 0; + + stream_ptr = &driver->stream[dir]; + a2j_add_ports (stream_ptr); + + // process ports + for (i = 0; i < PORT_HASH_SIZE; i++) { + port_ptr_ptr = &stream_ptr->port_hash[i]; + while (*port_ptr_ptr != NULL) { + port_ptr = *port_ptr_ptr; + + if (!port_ptr->is_dead) { + port_ptr->jack_buf = jack_port_get_buffer (port_ptr->jack_port, nframes); + + if (dir == A2J_PORT_CAPTURE) { + a2j_process_incoming (driver, port_ptr, nframes); + } else { + nevents += a2j_process_outgoing (driver, port_ptr); + } + + } else if (jack_ringbuffer_write_space (driver->port_del) >= sizeof(port_ptr)) { + + a2j_debug ("jack: removed port %s", port_ptr->name); + *port_ptr_ptr = port_ptr->next; + jack_ringbuffer_write (driver->port_del, (char*)&port_ptr, sizeof(port_ptr)); + nevents += 1; /* wake up output thread, see: a2j_free_ports */ + continue; + + } + + port_ptr_ptr = &port_ptr->next; + } + } + + if (dir == A2J_PORT_PLAYBACK && nevents > 0) { + int sv; + + /* if we queued up anything for output, tell the output thread in + case its waiting for us. + */ + + sem_getvalue (&driver->output_semaphore, &sv); + sem_post (&driver->output_semaphore); + } } /* JACK DRIVER FUNCTIONS */ @@ -635,178 +632,178 @@ a2j_jack_process_internal (alsa_midi_driver_t* driver, int dir, jack_nframes_t n static int alsa_midi_read (alsa_midi_driver_t* driver, jack_nframes_t nframes) { - driver->cycle_start = jack_last_frame_time (driver->jack_client); - a2j_jack_process_internal (driver, A2J_PORT_CAPTURE, nframes); - return 0; + driver->cycle_start = jack_last_frame_time (driver->jack_client); + a2j_jack_process_internal (driver, A2J_PORT_CAPTURE, nframes); + return 0; } static int alsa_midi_write (alsa_midi_driver_t* driver, jack_nframes_t nframes) { - driver->cycle_start = jack_last_frame_time (driver->jack_client); - a2j_jack_process_internal (driver, A2J_PORT_PLAYBACK, nframes); - return 0; + driver->cycle_start = jack_last_frame_time (driver->jack_client); + a2j_jack_process_internal (driver, A2J_PORT_PLAYBACK, nframes); + return 0; } -static int +static int alsa_midi_start (alsa_midi_driver_t* driver) { - int error; - - snd_seq_start_queue (driver->seq, driver->queue, 0); - snd_seq_drop_input (driver->seq); - - a2j_add_ports(&driver->stream[A2J_PORT_CAPTURE]); - a2j_add_ports(&driver->stream[A2J_PORT_PLAYBACK]); - - driver->running = true; - - if (pthread_create(&driver->alsa_input_thread, NULL, alsa_input_thread, driver) < 0) { - a2j_error("cannot start ALSA input thread"); - return -1; - } - - /* wake the poll loop in the alsa input thread so initial ports are fetched */ - if ((error = snd_seq_connect_from (driver->seq, driver->port_id, SND_SEQ_CLIENT_SYSTEM, SND_SEQ_PORT_SYSTEM_ANNOUNCE)) < 0) { - a2j_error("snd_seq_connect_from() failed"); - return -1; - } - - if (pthread_create(&driver->alsa_output_thread, NULL, alsa_output_thread, driver) < 0) { - a2j_error("cannot start ALSA output thread"); - return -1; - } - - return 0; + int error; + + snd_seq_start_queue (driver->seq, driver->queue, 0); + snd_seq_drop_input (driver->seq); + + a2j_add_ports (&driver->stream[A2J_PORT_CAPTURE]); + a2j_add_ports (&driver->stream[A2J_PORT_PLAYBACK]); + + driver->running = true; + + if (pthread_create (&driver->alsa_input_thread, NULL, alsa_input_thread, driver) < 0) { + a2j_error ("cannot start ALSA input thread"); + return -1; + } + + /* wake the poll loop in the alsa input thread so initial ports are fetched */ + if ((error = snd_seq_connect_from (driver->seq, driver->port_id, SND_SEQ_CLIENT_SYSTEM, SND_SEQ_PORT_SYSTEM_ANNOUNCE)) < 0) { + a2j_error ("snd_seq_connect_from() failed"); + return -1; + } + + if (pthread_create (&driver->alsa_output_thread, NULL, alsa_output_thread, driver) < 0) { + a2j_error ("cannot start ALSA output thread"); + return -1; + } + + return 0; } static int alsa_midi_stop (alsa_midi_driver_t* driver) { - stop_threads (driver); + stop_threads (driver); - (void) snd_seq_stop_queue (driver->seq, driver->queue, 0); - return 0; + (void)snd_seq_stop_queue (driver->seq, driver->queue, 0); + return 0; } static int alsa_midi_attach (alsa_midi_driver_t* driver, jack_engine_t* engine) { - int error; - - driver->port_del = jack_ringbuffer_create(2 * MAX_PORTS * sizeof(struct a2j_port *)); - if (driver->port_del == NULL) { - return -1; - } - - driver->outbound_events = jack_ringbuffer_create (MAX_EVENT_SIZE * 16 * sizeof(struct a2j_delivery_event)); - if (driver->outbound_events == NULL) { - return -1; - } - - if (!a2j_stream_init (driver, A2J_PORT_CAPTURE)) { - return -1; - } - - if (!a2j_stream_init (driver, A2J_PORT_PLAYBACK)) { - return -1; - } - - if ((error = snd_seq_open(&driver->seq, "hw", SND_SEQ_OPEN_DUPLEX, 0)) < 0) { - a2j_error("failed to open alsa seq"); - return -1; - } - - if ((error = snd_seq_set_client_name(driver->seq, "jackmidi")) < 0) { - a2j_error("snd_seq_set_client_name() failed"); - return -1; - } - - if ((driver->port_id = snd_seq_create_simple_port( - driver->seq, - "port", - SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_WRITE + int error; + + driver->port_del = jack_ringbuffer_create (2 * MAX_PORTS * sizeof(struct a2j_port *)); + if (driver->port_del == NULL) { + return -1; + } + + driver->outbound_events = jack_ringbuffer_create (MAX_EVENT_SIZE * 16 * sizeof(struct a2j_delivery_event)); + if (driver->outbound_events == NULL) { + return -1; + } + + if (!a2j_stream_init (driver, A2J_PORT_CAPTURE)) { + return -1; + } + + if (!a2j_stream_init (driver, A2J_PORT_PLAYBACK)) { + return -1; + } + + if ((error = snd_seq_open (&driver->seq, "hw", SND_SEQ_OPEN_DUPLEX, 0)) < 0) { + a2j_error ("failed to open alsa seq"); + return -1; + } + + if ((error = snd_seq_set_client_name (driver->seq, "jackmidi")) < 0) { + a2j_error ("snd_seq_set_client_name() failed"); + return -1; + } + + if ((driver->port_id = snd_seq_create_simple_port ( + driver->seq, + "port", + SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_WRITE #ifndef DEBUG - |SND_SEQ_PORT_CAP_NO_EXPORT + | SND_SEQ_PORT_CAP_NO_EXPORT #endif - ,SND_SEQ_PORT_TYPE_APPLICATION)) < 0) { - - a2j_error("snd_seq_create_simple_port() failed"); - return -1; - } - - if ((driver->client_id = snd_seq_client_id(driver->seq)) < 0) { - a2j_error("snd_seq_client_id() failed"); - return -1; - } - - if ((driver->queue = snd_seq_alloc_queue(driver->seq)) < 0) { - a2j_error("snd_seq_alloc_queue() failed"); - return -1; - } - - if ((error = snd_seq_nonblock(driver->seq, 1)) < 0) { - a2j_error("snd_seq_nonblock() failed"); - return -1; - } - - return jack_activate (driver->jack_client); + , SND_SEQ_PORT_TYPE_APPLICATION)) < 0) { + + a2j_error ("snd_seq_create_simple_port() failed"); + return -1; + } + + if ((driver->client_id = snd_seq_client_id (driver->seq)) < 0) { + a2j_error ("snd_seq_client_id() failed"); + return -1; + } + + if ((driver->queue = snd_seq_alloc_queue (driver->seq)) < 0) { + a2j_error ("snd_seq_alloc_queue() failed"); + return -1; + } + + if ((error = snd_seq_nonblock (driver->seq, 1)) < 0) { + a2j_error ("snd_seq_nonblock() failed"); + return -1; + } + + return jack_activate (driver->jack_client); } static int alsa_midi_detach (alsa_midi_driver_t* driver, jack_engine_t* engine) { - driver->finishing = true; - - snd_seq_close (driver->seq); - driver->seq = NULL; - return 0; + driver->finishing = true; + + snd_seq_close (driver->seq); + driver->seq = NULL; + return 0; } static jack_driver_t * alsa_midi_driver_new (jack_client_t *client, const char *name) { - alsa_midi_driver_t* driver = calloc(1, sizeof(alsa_midi_driver_t)); + alsa_midi_driver_t* driver = calloc (1, sizeof(alsa_midi_driver_t)); - jack_info ("creating alsa_midi driver ..."); + jack_info ("creating alsa_midi driver ..."); if (!driver) { return NULL; } - jack_driver_init ((jack_driver_t *) driver); + jack_driver_init ((jack_driver_t*)driver); - driver->attach = (JackDriverAttachFunction) alsa_midi_attach; - driver->detach = (JackDriverDetachFunction) alsa_midi_detach; - driver->read = (JackDriverReadFunction) alsa_midi_read; - driver->write = (JackDriverWriteFunction) alsa_midi_write; - driver->start = (JackDriverStartFunction) alsa_midi_start; - driver->stop = (JackDriverStartFunction) alsa_midi_stop; + driver->attach = (JackDriverAttachFunction)alsa_midi_attach; + driver->detach = (JackDriverDetachFunction)alsa_midi_detach; + driver->read = (JackDriverReadFunction)alsa_midi_read; + driver->write = (JackDriverWriteFunction)alsa_midi_write; + driver->start = (JackDriverStartFunction)alsa_midi_start; + driver->stop = (JackDriverStartFunction)alsa_midi_stop; driver->jack_client = client; - if (sem_init(&driver->output_semaphore, 0, 0) < 0) { - a2j_error ("can't create IO semaphore"); - free (driver); - return NULL; - } + if (sem_init (&driver->output_semaphore, 0, 0) < 0) { + a2j_error ("can't create IO semaphore"); + free (driver); + return NULL; + } - return (jack_driver_t *) driver; + return (jack_driver_t*)driver; } static void alsa_midi_driver_delete (alsa_midi_driver_t* driver) { - a2j_stream_detach (&driver->stream[A2J_PORT_CAPTURE]); - a2j_stream_detach (&driver->stream[A2J_PORT_PLAYBACK]); - a2j_stream_close (driver, A2J_PORT_CAPTURE); - a2j_stream_close (driver, A2J_PORT_PLAYBACK); - - sem_destroy (&driver->output_semaphore); - - jack_ringbuffer_free (driver->outbound_events); - jack_ringbuffer_free (driver->port_del); + a2j_stream_detach (&driver->stream[A2J_PORT_CAPTURE]); + a2j_stream_detach (&driver->stream[A2J_PORT_PLAYBACK]); + a2j_stream_close (driver, A2J_PORT_CAPTURE); + a2j_stream_close (driver, A2J_PORT_PLAYBACK); + + sem_destroy (&driver->output_semaphore); + + jack_ringbuffer_free (driver->outbound_events); + jack_ringbuffer_free (driver->port_del); } /* DRIVER "PLUGIN" INTERFACE */ @@ -818,14 +815,15 @@ driver_get_descriptor () { jack_driver_desc_t * desc; jack_driver_param_desc_t * params; + //unsigned int i; - desc = calloc (1, sizeof (jack_driver_desc_t)); + desc = calloc (1, sizeof(jack_driver_desc_t)); - strcpy (desc->name,"alsa_midi"); + strcpy (desc->name, "alsa_midi"); desc->nparams = 0; - - params = calloc (desc->nparams, sizeof (jack_driver_param_desc_t)); + + params = calloc (desc->nparams, sizeof(jack_driver_param_desc_t)); desc->params = params; @@ -839,19 +837,19 @@ driver_initialize (jack_client_t *client, const JSList * params) const jack_driver_param_t * param; for (node = params; node; node = jack_slist_next (node)) { - param = (const jack_driver_param_t *) node->data; + param = (const jack_driver_param_t*)node->data; switch (param->character) { - default: - break; + default: + break; } } - + return alsa_midi_driver_new (client, NULL); } void driver_finish (jack_driver_t *driver) { - alsa_midi_driver_delete ((alsa_midi_driver_t *) driver); + alsa_midi_driver_delete ((alsa_midi_driver_t*)driver); } |