diff --git a/Makefile.am b/Makefile.am index db767bd..5fab3ef 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,5 +1,11 @@ MAINTAINERCLEANFILES = Makefile.in +if HAVE_ALSA_MIDI +ALSA_MIDI_DIR = alsa_midi +else +ALSA_MIDI_DIR = +endif + if HAVE_READLINE JACK_TRANSPORT = jack_transport dist-check-readline: @@ -160,4 +166,7 @@ alsa_out_LDADD = $(top_builddir)/libjack/libjack.la endif #HAVE_ALSA endif #HAVE_SAMPLERATE +SUBDIRS = $(ALSA_MIDI_DIR) +DIST_SUBDIRS = alsa_midi + # XXX ? dist-hook: dist-check-sndfile dist-check-samplerate diff --git a/alsa_midi/Makefile.am b/alsa_midi/Makefile.am new file mode 100644 index 0000000..55950f4 --- /dev/null +++ b/alsa_midi/Makefile.am @@ -0,0 +1,13 @@ +MAINTAINERCLEANFILES = Makefile.in + +# +# in-process ALSA/JACK MIDI bridge client +# + +alsa_mididir = $(ADDON_DIR) + +alsa_midi_LTLIBRARIES = alsa_midi.la +alsa_midi_la_LDFLAGS = -module -avoid-version @OS_LDFLAGS@ +alsa_midi_la_SOURCES = alsa_midi.c port.c port_hash.c port_thread.c list.c + +noinst_HEADERS = a2j.h list.h port.h port_hash.h port_thread.h diff --git a/alsa_midi/a2j.h b/alsa_midi/a2j.h new file mode 100644 index 0000000..87453ae --- /dev/null +++ b/alsa_midi/a2j.h @@ -0,0 +1,126 @@ +/* -*- Mode: C ; c-basic-offset: 2 -*- */ +/* + * ALSA SEQ < - > JACK MIDI bridge + * + * Copyright (c) 2006,2007 Dmitry S. Baikov + * Copyright (c) 2007,2008,2009 Nedko Arnaudov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU 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 + */ + +#ifndef STRUCTS_H__FD2CC895_411F_4ADE_9200_50FE395EDB72__INCLUDED +#define STRUCTS_H__FD2CC895_411F_4ADE_9200_50FE395EDB72__INCLUDED + +#include +#include +#include + +#define JACK_INVALID_PORT NULL + +#define MAX_PORTS 2048 +#define MAX_EVENT_SIZE 1024 + +#define PORT_HASH_BITS 4 +#define PORT_HASH_SIZE (1 << PORT_HASH_BITS) + +/* Beside enum use, these are indeces for (struct a2j).stream array */ +#define A2J_PORT_CAPTURE 0 // ALSA playback port -> JACK capture port +#define A2J_PORT_PLAYBACK 1 // JACK playback port -> ALSA capture port + +typedef struct a2j_port * a2j_port_hash_t[PORT_HASH_SIZE]; + +struct a2j; + +struct a2j_port +{ + struct a2j_port * next; /* hash - jack */ + struct list_head siblings; /* list - main loop */ + struct a2j * a2j_ptr; + bool is_dead; + char name[64]; + snd_seq_addr_t remote; + jack_port_t * jack_port; + + jack_ringbuffer_t * inbound_events; // alsa_midi_event_t + data + int64_t last_out_time; + + void * jack_buf; +}; + +struct a2j_stream +{ + snd_midi_event_t *codec; + + jack_ringbuffer_t *new_ports; + + a2j_port_hash_t port_hash; + struct list_head list; +}; + +struct a2j +{ + jack_client_t * jack_client; + + snd_seq_t *seq; + pthread_t alsa_input_thread; + pthread_t alsa_output_thread; + int client_id; + int port_id; + int queue; + bool freewheeling; + bool running; + bool finishing; + + jack_ringbuffer_t* port_add; // snd_seq_addr_t + jack_ringbuffer_t* port_del; // struct a2j_port* + jack_ringbuffer_t* outbound_events; // struct a2j_delivery_event + jack_nframes_t cycle_start; + + sem_t output_semaphore; + + struct a2j_stream stream[2]; +}; + +#define NSEC_PER_SEC ((int64_t)1000*1000*1000) + +struct a2j_alsa_midi_event +{ + int64_t time; + int size; +}; + +#define MAX_JACKMIDI_EV_SIZE 16 + +struct a2j_delivery_event +{ + struct list_head siblings; + + /* a jack MIDI event, plus the port its destined for: everything + the ALSA output thread needs to deliver the event. time is + part of the jack_event. + */ + jack_midi_event_t jack_event; + jack_nframes_t time; /* realtime, not offset time */ + struct a2j_port* port; + char midistring[MAX_JACKMIDI_EV_SIZE]; +}; + +void a2j_info (const char* fmt, ...); +void a2j_error (const char* fmt, ...); +void a2j_debug (const char* fmt, ...); +void a2j_warning (const char* fmt, ...); + + + +#endif /* #ifndef STRUCTS_H__FD2CC895_411F_4ADE_9200_50FE395EDB72__INCLUDED */ diff --git a/alsa_midi/a2j_in.la b/alsa_midi/a2j_in.la new file mode 100644 index 0000000..f37ad36 --- /dev/null +++ b/alsa_midi/a2j_in.la @@ -0,0 +1,41 @@ +# a2j_in.la - a libtool library file +# Generated by ltmain.sh (GNU libtool) 2.2.6b +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='a2j_in.so' + +# Names of this library. +library_names='a2j_in.so a2j_in.so a2j_in.so' + +# The name of the static archive. +old_library='' + +# Linker flags that can not go in dependency_libs. +inherited_linker_flags='' + +# Libraries that this one depends upon. +dependency_libs=' -lrt -lm -lpthread -ldl -luuid' + +# Names of additional weak libraries provided by this library +weak_library_names='' + +# Version information for a2j_in. +current=0 +age=0 +revision=0 + +# Is this an already installed library? +installed=no + +# Should we warn about portability when linking against -modules? +shouldnotlink=yes + +# Files to dlopen/dlpreopen +dlopen='' +dlpreopen='' + +# Directory that this library needs to be installed in: +libdir='/usr/lib64/jack' diff --git a/alsa_midi/alsa_midi.c b/alsa_midi/alsa_midi.c new file mode 100644 index 0000000..ad06b1a --- /dev/null +++ b/alsa_midi/alsa_midi.c @@ -0,0 +1,878 @@ +/* -*- Mode: C ; c-basic-offset: 2 -*- */ +/* + * ALSA SEQ < - > JACK MIDI bridge + * + * Copyright (c) 2006,2007 Dmitry S. Baikov + * Copyright (c) 2007,2008,2009 Nedko Arnaudov + * Copyright (c) 2009,2010 Paul Davis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU 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 +#include +#include +#include +#include +#include +#include +#include + +#include "list.h" +#include "a2j.h" +#include "port_hash.h" +#include "port.h" +#include "port_thread.h" + +bool g_stop_request = false; + +void +a2j_info (const char* fmt, ...) +{ + va_list ap; + va_start (ap, fmt); + vfprintf (stdout, fmt, ap); + fputc ('\n', stdout); +} + +void +a2j_error (const char* fmt, ...) +{ + va_list ap; + va_start (ap, fmt); + vfprintf (stdout, fmt, ap); + fputc ('\n', stdout); +} + + +void +a2j_debug (const char* fmt, ...) +{ + va_list ap; + va_start (ap, fmt); + vfprintf (stderr, fmt, ap); + fputc ('\n', stdout); +} + +void +a2j_warning (const char* fmt, ...) +{ + va_list ap; + va_start (ap, fmt); + vfprintf (stdout, fmt, ap); + fputc ('\n', stdout); +} + +static bool +a2j_stream_init(struct a2j * self, int which) +{ + struct a2j_stream *str = &self->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 +a2j_stream_detach (struct a2j_stream * stream_ptr) +{ + struct a2j_port * port_ptr; + struct list_head * node_ptr; + + while (!list_empty (&stream_ptr->list)) { + node_ptr = stream_ptr->list.next; + list_del (node_ptr); + port_ptr = list_entry (node_ptr, struct a2j_port, siblings); + a2j_info ("port deleted: %s", port_ptr->name); + a2j_port_free (port_ptr); + } +} + +static +void +a2j_stream_close (struct a2j * self, int which) +{ + struct a2j_stream *str = &self->stream[which]; + + if (str->codec) + snd_midi_event_free (str->codec); + if (str->new_ports) + jack_ringbuffer_free (str->new_ports); +} + +static void +stop_threads (struct a2j* self) +{ + if (self->running) { + void* thread_status; + + self->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 (self->seq, self->port_id, SND_SEQ_CLIENT_SYSTEM, SND_SEQ_PORT_SYSTEM_ANNOUNCE); + a2j_debug ("wait for ALSA input thread\n"); + pthread_join (self->alsa_input_thread, &thread_status); + a2j_debug ("input thread done\n"); + + /* wake output thread and join */ + sem_post(&self->output_semaphore); + pthread_join(self->alsa_output_thread, &thread_status); + a2j_debug ("output thread done\n"); + } +} + +/* + * =================== Input/output port handling ========================= + */ + +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); + a2j_port_insert (str->port_hash, port_ptr); + } +} + +static +void +a2j_port_event (struct a2j * self, snd_seq_event_t * ev) +{ + const snd_seq_addr_t addr = ev->data.addr; + + if (addr.client == self->client_id) + return; + + if (ev->type == SND_SEQ_EVENT_PORT_START || ev->type == SND_SEQ_EVENT_PORT_CHANGE) { + if (jack_ringbuffer_write_space(self->port_add) >= sizeof(addr)) { + a2j_debug("port_event: add/change %d:%d", addr.client, addr.port); + jack_ringbuffer_write(self->port_add, (char*)&addr, sizeof(addr)); + } else { + a2j_error("dropping port_event: add/change %d:%d", addr.client, addr.port); + } + } else if (ev->type == SND_SEQ_EVENT_PORT_EXIT) { + a2j_debug("port_event: del %d:%d", addr.client, addr.port); + a2j_port_setdead(self->stream[A2J_PORT_CAPTURE].port_hash, addr); + a2j_port_setdead(self->stream[A2J_PORT_PLAYBACK].port_hash, addr); + } +} + +/* --- INBOUND FROM ALSA TO JACK ---- */ + +static void +a2j_input_event (struct a2j * self, snd_seq_event_t * alsa_event) +{ + jack_midi_data_t data[MAX_EVENT_SIZE]; + struct a2j_stream *str = &self->stream[A2J_PORT_CAPTURE]; + long size; + struct a2j_port *port; + jack_nframes_t now; + + now = jack_frame_time (self->jack_client); + + if ((port = a2j_port_get(str->port_hash, alsa_event->source)) == NULL) { + return; + } + + /* + * 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) { + return; + } + + // fixup NoteOn with vel 0 + if ((data[0] & 0xF0) == 0x90 && data[2] == 0x00) { + data[0] = 0x80 + (data[0] & 0x0F); + data[2] = 0x40; + } + + 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)) { + struct a2j_alsa_midi_event 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 ); + 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; + } + if (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; + 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 ); + } else { + a2j_error ("MIDI data lost (incoming event buffer full): %ld bytes lost", size); + } + +} + +static int +a2j_process_incoming (struct a2j* self, 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 (self->jack_client); + + while (jack_ringbuffer_peek (port->inbound_events, (char*)&ev, sizeof(ev) ) == sizeof(ev) ) { + + jack_midi_data_t* buf; + jack_nframes_t offset; + + if (ev.time >= self->cycle_start) { + 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 = self->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* +alsa_input_thread (void* arg) +{ + struct a2j * self = arg; + int npfd; + struct pollfd * pfd; + snd_seq_addr_t addr; + snd_seq_client_info_t * client_info; + snd_seq_port_info_t * port_info; + bool initial; + snd_seq_event_t * event; + int ret; + + npfd = snd_seq_poll_descriptors_count(self->seq, POLLIN); + pfd = (struct pollfd *)alloca(npfd * sizeof(struct pollfd)); + snd_seq_poll_descriptors(self->seq, pfd, npfd, POLLIN); + + initial = true; + + while (self->running) { + if ((ret = poll(pfd, npfd, 1000)) > 0) { + + while (snd_seq_event_input (self->seq, &event) > 0) { + if (initial) { + snd_seq_client_info_alloca(&client_info); + snd_seq_port_info_alloca(&port_info); + snd_seq_client_info_set_client(client_info, -1); + while (snd_seq_query_next_client(self->seq, client_info) >= 0) { + addr.client = snd_seq_client_info_get_client(client_info); + if (addr.client == SND_SEQ_CLIENT_SYSTEM || addr.client == self->client_id) { + continue; + } + snd_seq_port_info_set_client(port_info, addr.client); + snd_seq_port_info_set_port(port_info, -1); + while (snd_seq_query_next_port(self->seq, port_info) >= 0) { + addr.port = snd_seq_port_info_get_port(port_info); + a2j_update_port(self, addr, port_info); + } + } + + initial = false; + } + + if (event->source.client == SND_SEQ_CLIENT_SYSTEM) { + a2j_port_event(self, event); + } else { + a2j_input_event(self, event); + } + + snd_seq_free_event (event); + } + } + } + + return (void*) 0; +} + +/* --- OUTBOUND FROM JACK TO ALSA ---- */ + +int +a2j_process_outgoing ( + struct a2j * self, + 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 (self->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); + + 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 (self->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; +} + +static void* +alsa_output_thread(void * arg) +{ + struct a2j * self = (struct a2j*) arg; + struct a2j_stream *str = &self->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 err; + int limit; + + while (self->running) { + /* first, make a list of all events in the outbound_events FIFO */ + + INIT_LIST_HEAD(&evlist); + + jack_ringbuffer_get_read_vector (self->outbound_events, vec); + + a2j_debug ("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 ("output thread: wait for events"); + sem_wait (&self->output_semaphore); + a2j_debug ("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 (self->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)) + { + continue; // invalid event + } + + snd_seq_ev_set_source(&alsa_event, self->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 (self->jack_client); + + ev->time += self->cycle_start; + + a2j_debug ("@ %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 ("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 */ + err = snd_seq_event_output(self->seq, &alsa_event); + snd_seq_drain_output (self->seq); + now = jack_frame_time (self->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 (self->outbound_events, vec[0].len + vec[1].len); + + /* and head back for more */ + } + + return (void*) 0; +} + +/** CORE JACK PROCESSING */ + + +/* ALSA */ + +static void +a2j_jack_process_internal (struct a2j * self, 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 = &self->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 (self, port_ptr, nframes); + } else { + nevents += a2j_process_outgoing (self, port_ptr); + } + + } else if (jack_ringbuffer_write_space (self->port_del) >= sizeof(port_ptr)) { + + a2j_debug("jack: removed port %s", port_ptr->name); + *port_ptr_ptr = port_ptr->next; + jack_ringbuffer_write(self->port_del, (char*)&port_ptr, sizeof(port_ptr)); + 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 (&self->output_semaphore, &sv); + sem_post (&self->output_semaphore); + } +} + +static int +a2j_process(jack_nframes_t nframes, void * arg) +{ + struct a2j* self = (struct a2j *) arg; + + if (self->freewheeling) { + return 0; + } + + self->cycle_start = jack_last_frame_time (self->jack_client); + + a2j_jack_process_internal (self, A2J_PORT_CAPTURE, nframes); + a2j_jack_process_internal (self, A2J_PORT_PLAYBACK, nframes); + + return 0; +} + +/* --- */ + +static +void +a2j_freewheel(int starting, void * arg) +{ + struct a2j* self = (struct a2j*) arg; + self->freewheeling = starting; +} + +static +void +a2j_shutdown (void * arg) +{ + struct a2j* self = (struct a2j*) self; + a2j_warning ("JACK server shutdown notification received."); + stop_threads (self); +} + +int +connect_to_alsa (struct a2j* self) +{ + int error; + void* thread_status; + + self->port_add = jack_ringbuffer_create (2 * MAX_PORTS * sizeof(snd_seq_addr_t)); + + if (self->port_add == NULL) { + goto free_self; + } + + self->port_del = jack_ringbuffer_create(2 * MAX_PORTS * sizeof(struct a2j_port *)); + if (self->port_del == NULL) { + goto free_ringbuffer_add; + } + + self->outbound_events = jack_ringbuffer_create (MAX_EVENT_SIZE * 16 * sizeof(struct a2j_delivery_event)); + if (self->outbound_events == NULL) { + goto free_ringbuffer_del; + } + + if (!a2j_stream_init (self, A2J_PORT_CAPTURE)) { + goto free_ringbuffer_outbound; + } + + if (!a2j_stream_init (self, A2J_PORT_PLAYBACK)) { + goto close_capture_stream; + } + + if ((error = snd_seq_open(&self->seq, "hw", SND_SEQ_OPEN_DUPLEX, 0)) < 0) { + a2j_error("failed to open alsa seq"); + goto close_playback_stream; + } + + if ((error = snd_seq_set_client_name(self->seq, "jackmidi")) < 0) { + a2j_error("snd_seq_set_client_name() failed"); + goto close_seq_client; + } + + if ((self->port_id = snd_seq_create_simple_port( + self->seq, + "port", + SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_WRITE +#ifndef DEBUG + |SND_SEQ_PORT_CAP_NO_EXPORT +#endif + ,SND_SEQ_PORT_TYPE_APPLICATION)) < 0) { + + a2j_error("snd_seq_create_simple_port() failed"); + goto close_seq_client; + } + + if ((self->client_id = snd_seq_client_id(self->seq)) < 0) { + a2j_error("snd_seq_client_id() failed"); + goto close_seq_client; + } + + if ((self->queue = snd_seq_alloc_queue(self->seq)) < 0) { + a2j_error("snd_seq_alloc_queue() failed"); + goto close_seq_client; + } + + snd_seq_start_queue (self->seq, self->queue, 0); + + if ((error = snd_seq_nonblock(self->seq, 1)) < 0) { + a2j_error("snd_seq_nonblock() failed"); + goto close_seq_client; + } + + snd_seq_drop_input (self->seq); + + a2j_add_ports(&self->stream[A2J_PORT_CAPTURE]); + a2j_add_ports(&self->stream[A2J_PORT_PLAYBACK]); + + if (sem_init(&self->output_semaphore, 0, 0) < 0) { + a2j_error("can't create IO semaphore"); + goto close_jack_client; + } + + self->running = true; + + if (pthread_create(&self->alsa_input_thread, NULL, alsa_input_thread, self) < 0) { + a2j_error("cannot start ALSA input thread"); + goto sem_destroy; + } + + /* wake the poll loop in the alsa input thread so initial ports are fetched */ + if ((error = snd_seq_connect_from (self->seq, self->port_id, SND_SEQ_CLIENT_SYSTEM, SND_SEQ_PORT_SYSTEM_ANNOUNCE)) < 0) { + a2j_error("snd_seq_connect_from() failed"); + goto join_input_thread; + } + + if (pthread_create(&self->alsa_output_thread, NULL, alsa_output_thread, self) < 0) { + a2j_error("cannot start ALSA input thread"); + goto sem_destroy; + } + + return 0; + + /* error handling */ + + self->running = false; /* tell alsa threads to stop */ + self->finishing = false; + + snd_seq_disconnect_from(self->seq, self->port_id, SND_SEQ_CLIENT_SYSTEM, SND_SEQ_PORT_SYSTEM_ANNOUNCE); + join_input_thread: + pthread_join (self->alsa_input_thread, &thread_status); + sem_destroy: + sem_destroy (&self->output_semaphore); + close_jack_client: + if ((error = jack_client_close(self->jack_client)) < 0) { + a2j_error("Cannot close jack client"); + } + close_seq_client: + snd_seq_close(self->seq); + close_playback_stream: + a2j_stream_close(self, A2J_PORT_PLAYBACK); + close_capture_stream: + a2j_stream_close(self, A2J_PORT_CAPTURE); + free_ringbuffer_outbound: + jack_ringbuffer_free(self->outbound_events); + free_ringbuffer_del: + jack_ringbuffer_free(self->port_del); + free_ringbuffer_add: + jack_ringbuffer_free(self->port_add); + free_self: + free(self); + return -1; +} + +/* JACK internal client API: 2 entry points + */ + +int +jack_initialize (jack_client_t *client, const char* load_init) +{ + struct a2j* self = calloc(1, sizeof(struct a2j)); + + if (!self) { + return -1; + } + + self->jack_client = client; + + if (load_init) { + char* args = strdup (load_init); + char* token; + char* ptr = args; + char* savep; + + while (1) { + if ((token = strtok_r (ptr, ", ", &savep)) == NULL) { + break; + } +#if 0 + /* example of how to use tokens */ + + if (strncasecmp (token, "in", 2) == 0) { + self->input = 1; + } +#endif + + ptr = NULL; + } + + free (args); + } + + if (connect_to_alsa (self)) { + free (self); + return -1; + } + + jack_set_process_callback (client, a2j_process, self); + jack_set_freewheel_callback (client, a2j_freewheel, self); + jack_on_shutdown (client, a2j_shutdown, self); + + jack_activate (client); + + return 0; +} + +void +jack_finish (void *arg) +{ + struct a2j* self = (struct a2j*) arg; + + self->finishing = true; + + stop_threads (self); + sem_destroy(&self->output_semaphore); + jack_ringbuffer_reset (self->port_add); + a2j_stream_detach (&self->stream[A2J_PORT_CAPTURE]); + a2j_stream_detach (&self->stream[A2J_PORT_PLAYBACK]); + snd_seq_close(self->seq); + self->seq = NULL; + a2j_stream_close (self, A2J_PORT_CAPTURE); + a2j_stream_close (self, A2J_PORT_PLAYBACK); + jack_ringbuffer_free(self->outbound_events); + jack_ringbuffer_free(self->port_add); + jack_ringbuffer_free(self->port_del); + + free (self); +} diff --git a/alsa_midi/list.c b/alsa_midi/list.c new file mode 100644 index 0000000..438066c --- /dev/null +++ b/alsa_midi/list.c @@ -0,0 +1,147 @@ +/* -*- Mode: C ; c-basic-offset: 2 -*- */ +/***************************************************************************** + * + * list_sort() adapted from linux kernel. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + *****************************************************************************/ + +#include + +#include "list.h" + +/* list sort from Mark J Roberts (mjr@znex.org) */ +void +__list_sort( + struct list_head *head, + int member_offset, + int (*cmp)(void * a, void * b)) +{ + struct list_head *p, *q, *e, *list, *tail, *oldhead; + int insize, nmerges, psize, qsize, i; + + list = head->next; + list_del(head); + insize = 1; + for (;;) { + p = oldhead = list; + list = tail = NULL; + nmerges = 0; + + while (p) { + nmerges++; + q = p; + psize = 0; + for (i = 0; i < insize; i++) { + psize++; + q = q->next == oldhead ? NULL : q->next; + if (!q) + break; + } + + qsize = insize; + while (psize > 0 || (qsize > 0 && q)) { + if (!psize) { + e = q; + q = q->next; + qsize--; + if (q == oldhead) + q = NULL; + } else if (!qsize || !q) { + e = p; + p = p->next; + psize--; + if (p == oldhead) + p = NULL; + } else if (cmp((void *)p - member_offset, (void *)q - member_offset) <= 0) { + e = p; + p = p->next; + psize--; + if (p == oldhead) + p = NULL; + } else { + e = q; + q = q->next; + qsize--; + if (q == oldhead) + q = NULL; + } + if (tail) + tail->next = e; + else + list = e; + e->prev = tail; + tail = e; + } + p = q; + } + + tail->next = list; + list->prev = tail; + + if (nmerges <= 1) + break; + + insize *= 2; + } + + head->next = list; + head->prev = list->prev; + list->prev->next = head; + list->prev = head; +} + +struct test_list_el { + int value; + struct list_head test_list_node; +}; + +int test_list_sort_comparator(struct test_list_el * e1, struct test_list_el * e2) +{ + return e1->value - e2->value; +} + +void test_list_sort(void) +{ + struct list_head test_list; + struct test_list_el *el, *next; + struct test_list_el te1 = {.value = 1}; + struct test_list_el te2 = {.value = 2}; + struct test_list_el te3 = {.value = 3}; + struct test_list_el te4 = {.value = 4}; + struct test_list_el te5 = {.value = 5}; + struct test_list_el te6 = {.value = 6}; + struct test_list_el te7 = {.value = 7}; + + const int expected[] = {1, 2, 3, 4, 5, 6, 7}; + int i; + + INIT_LIST_HEAD(&test_list); + list_add_tail(&te2.test_list_node, &test_list); + list_add_tail(&te6.test_list_node, &test_list); + list_add_tail(&te4.test_list_node, &test_list); + list_add_tail(&te5.test_list_node, &test_list); + list_add_tail(&te7.test_list_node, &test_list); + list_add_tail(&te1.test_list_node, &test_list); + list_add_tail(&te3.test_list_node, &test_list); + + list_sort(&test_list, struct test_list_el, test_list_node, test_list_sort_comparator); + + i = 0; + list_for_each_entry_safe(el, next, &test_list, test_list_node) { + assert(el->value == expected[i]); + i++; + } +} diff --git a/alsa_midi/list.h b/alsa_midi/list.h new file mode 100644 index 0000000..5b7f4d4 --- /dev/null +++ b/alsa_midi/list.h @@ -0,0 +1,903 @@ +/* -*- Mode: C ; c-basic-offset: 2 -*- */ +/***************************************************************************** + * + * Linux kernel header adapted for user-mode + * The 2.6.17-rt1 version was used. + * + * Original copyright holders of this code are unknown, they were not + * mentioned in the original file. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + *****************************************************************************/ + +#ifndef _LINUX_LIST_H +#define _LINUX_LIST_H + +#include + +#if !defined(offsetof) +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +/** + * container_of - cast a member of a structure out to the containing structure + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + * + */ +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +#define prefetch(x) (x = x) + +/* + * These are non-NULL pointers that will result in page faults + * under normal circumstances, used to verify that nobody uses + * non-initialized list entries. + */ +#define LIST_POISON1 ((void *) 0x00100100) +#define LIST_POISON2 ((void *) 0x00200200) + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + list->next = list; + list->prev = list; +} + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add_rcu(struct list_head * new, + struct list_head * prev, struct list_head * next) +{ + new->next = next; + new->prev = prev; +// smp_wmb(); + next->prev = new; + prev->next = new; +} + +/** + * list_add_rcu - add a new entry to rcu-protected list + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + * + * The caller must take whatever precautions are necessary + * (such as holding appropriate locks) to avoid racing + * with another list-mutation primitive, such as list_add_rcu() + * or list_del_rcu(), running on this same list. + * However, it is perfectly legal to run concurrently with + * the _rcu list-traversal primitives, such as + * list_for_each_entry_rcu(). + */ +static inline void list_add_rcu(struct list_head *new, struct list_head *head) +{ + __list_add_rcu(new, head, head->next); +} + +/** + * list_add_tail_rcu - add a new entry to rcu-protected list + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + * + * The caller must take whatever precautions are necessary + * (such as holding appropriate locks) to avoid racing + * with another list-mutation primitive, such as list_add_tail_rcu() + * or list_del_rcu(), running on this same list. + * However, it is perfectly legal to run concurrently with + * the _rcu list-traversal primitives, such as + * list_for_each_entry_rcu(). + */ +static inline void list_add_tail_rcu(struct list_head *new, + struct list_head *head) +{ + __list_add_rcu(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = LIST_POISON1; + entry->prev = LIST_POISON2; +} + +/** + * list_del_rcu - deletes entry from list without re-initialization + * @entry: the element to delete from the list. + * + * Note: list_empty on entry does not return true after this, + * the entry is in an undefined state. It is useful for RCU based + * lockfree traversal. + * + * In particular, it means that we can not poison the forward + * pointers that may still be used for walking the list. + * + * The caller must take whatever precautions are necessary + * (such as holding appropriate locks) to avoid racing + * with another list-mutation primitive, such as list_del_rcu() + * or list_add_rcu(), running on this same list. + * However, it is perfectly legal to run concurrently with + * the _rcu list-traversal primitives, such as + * list_for_each_entry_rcu(). + * + * Note that the caller is not permitted to immediately free + * the newly deleted entry. Instead, either synchronize_rcu() + * or call_rcu() must be used to defer freeing until an RCU + * grace period has elapsed. + */ +static inline void list_del_rcu(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->prev = LIST_POISON2; +} + +/* + * list_replace_rcu - replace old entry by new one + * @old : the element to be replaced + * @new : the new element to insert + * + * The old entry will be replaced with the new entry atomically. + */ +static inline void list_replace_rcu(struct list_head *old, + struct list_head *new) +{ + new->next = old->next; + new->prev = old->prev; +// smp_wmb(); + new->next->prev = new; + new->prev->next = new; + old->prev = LIST_POISON2; +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static inline void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/** + * list_move - delete from one list and add as another's head + * @list: the entry to move + * @head: the head that will precede our entry + */ +static inline void list_move(struct list_head *list, struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add(list, head); +} + +/** + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add_tail(list, head); +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +/** + * list_empty_careful - tests whether a list is + * empty _and_ checks that no other CPU might be + * in the process of still modifying either member + * + * NOTE: using list_empty_careful() without synchronization + * can only be safe if the only activity that can happen + * to the list entry is list_del_init(). Eg. it cannot be used + * if another CPU could re-list_add() it. + * + * @head: the list to test. + */ +static inline int list_empty_careful(const struct list_head *head) +{ + struct list_head *next = head->next; + return (next == head) && (next == head->prev); +} + +static inline void __list_splice(struct list_head *list, + struct list_head *head) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; +} + +/** + * list_splice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice(struct list_head *list, struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head); +} + +/** + * list_splice_init - join two lists and reinitialise the emptied list. + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * The list at @list is reinitialised + */ +static inline void list_splice_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head); + INIT_LIST_HEAD(list); + } +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; prefetch(pos->next), pos != (head); \ + pos = pos->next) + +/** + * __list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + * + * This variant differs from list_for_each() in that it's the + * simplest possible list iteration code, no prefetching is done. + * Use this for code that knows the list to be very short (empty + * or 1 entry) most of the time. + */ +#define __list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_prev - iterate over a list backwards + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \ + pos = pos->prev) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop counter. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + prefetch(pos->member.next), &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_reverse - iterate backwards over list of given type. + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_reverse(pos, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member); \ + prefetch(pos->member.prev), &pos->member != (head); \ + pos = list_entry(pos->member.prev, typeof(*pos), member)) + +/** + * list_prepare_entry - prepare a pos entry for use as a start point in + * list_for_each_entry_continue + * @pos: the type * to use as a start point + * @head: the head of the list + * @member: the name of the list_struct within the struct. + */ +#define list_prepare_entry(pos, head, member) \ + ((pos) ? : list_entry(head, typeof(*pos), member)) + +/** + * list_for_each_entry_continue - iterate over list of given type + * continuing after existing point + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_continue(pos, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member); \ + prefetch(pos->member.next), &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_from - iterate over list of given type + * continuing from existing point + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_from(pos, head, member) \ + for (; prefetch(pos->member.next), &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_continue - iterate over list of given type + * continuing after existing point safe against removal of list entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe_continue(pos, n, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_from - iterate over list of given type + * from existing point safe against removal of list entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe_from(pos, n, head, member) \ + for (n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_reverse - iterate backwards over list of given type safe against + * removal of list entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe_reverse(pos, n, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member), \ + n = list_entry(pos->member.prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.prev, typeof(*n), member)) + +/** + * list_for_each_rcu - iterate over an rcu-protected list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + * + * This list-traversal primitive may safely run concurrently with + * the _rcu list-mutation primitives such as list_add_rcu() + * as long as the traversal is guarded by rcu_read_lock(). + */ +#define list_for_each_rcu(pos, head) \ + for (pos = (head)->next; \ + prefetch(rcu_dereference(pos)->next), pos != (head); \ + pos = pos->next) + +#define __list_for_each_rcu(pos, head) \ + for (pos = (head)->next; \ + rcu_dereference(pos) != (head); \ + pos = pos->next) + +/** + * list_for_each_safe_rcu - iterate over an rcu-protected list safe + * against removal of list entry + * @pos: the &struct list_head to use as a loop counter. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + * + * This list-traversal primitive may safely run concurrently with + * the _rcu list-mutation primitives such as list_add_rcu() + * as long as the traversal is guarded by rcu_read_lock(). + */ +#define list_for_each_safe_rcu(pos, n, head) \ + for (pos = (head)->next; \ + n = rcu_dereference(pos)->next, pos != (head); \ + pos = n) + +/** + * list_for_each_entry_rcu - iterate over rcu list of given type + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * This list-traversal primitive may safely run concurrently with + * the _rcu list-mutation primitives such as list_add_rcu() + * as long as the traversal is guarded by rcu_read_lock(). + */ +#define list_for_each_entry_rcu(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + prefetch(rcu_dereference(pos)->member.next), \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + + +/** + * list_for_each_continue_rcu - iterate over an rcu-protected list + * continuing after existing point. + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + * + * This list-traversal primitive may safely run concurrently with + * the _rcu list-mutation primitives such as list_add_rcu() + * as long as the traversal is guarded by rcu_read_lock(). + */ +#define list_for_each_continue_rcu(pos, head) \ + for ((pos) = (pos)->next; \ + prefetch(rcu_dereference((pos))->next), (pos) != (head); \ + (pos) = (pos)->next) + +/* + * Double linked lists with a single pointer list head. + * Mostly useful for hash tables where the two pointer list head is + * too wasteful. + * You lose the ability to access the tail in O(1). + */ + +struct hlist_head { + struct hlist_node *first; +}; + +struct hlist_node { + struct hlist_node *next, **pprev; +}; + +#define HLIST_HEAD_INIT { .first = NULL } +#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL } +#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL) +static inline void INIT_HLIST_NODE(struct hlist_node *h) +{ + h->next = NULL; + h->pprev = NULL; +} + +static inline int hlist_unhashed(const struct hlist_node *h) +{ + return !h->pprev; +} + +static inline int hlist_empty(const struct hlist_head *h) +{ + return !h->first; +} + +static inline void __hlist_del(struct hlist_node *n) +{ + struct hlist_node *next = n->next; + struct hlist_node **pprev = n->pprev; + *pprev = next; + if (next) + next->pprev = pprev; +} + +static inline void hlist_del(struct hlist_node *n) +{ + __hlist_del(n); + n->next = LIST_POISON1; + n->pprev = LIST_POISON2; +} + +/** + * hlist_del_rcu - deletes entry from hash list without re-initialization + * @n: the element to delete from the hash list. + * + * Note: list_unhashed() on entry does not return true after this, + * the entry is in an undefined state. It is useful for RCU based + * lockfree traversal. + * + * In particular, it means that we can not poison the forward + * pointers that may still be used for walking the hash list. + * + * The caller must take whatever precautions are necessary + * (such as holding appropriate locks) to avoid racing + * with another list-mutation primitive, such as hlist_add_head_rcu() + * or hlist_del_rcu(), running on this same list. + * However, it is perfectly legal to run concurrently with + * the _rcu list-traversal primitives, such as + * hlist_for_each_entry(). + */ +static inline void hlist_del_rcu(struct hlist_node *n) +{ + __hlist_del(n); + n->pprev = LIST_POISON2; +} + +static inline void hlist_del_init(struct hlist_node *n) +{ + if (!hlist_unhashed(n)) { + __hlist_del(n); + INIT_HLIST_NODE(n); + } +} + +/* + * hlist_replace_rcu - replace old entry by new one + * @old : the element to be replaced + * @new : the new element to insert + * + * The old entry will be replaced with the new entry atomically. + */ +static inline void hlist_replace_rcu(struct hlist_node *old, + struct hlist_node *new) +{ + struct hlist_node *next = old->next; + + new->next = next; + new->pprev = old->pprev; +// smp_wmb(); + if (next) + new->next->pprev = &new->next; + *new->pprev = new; + old->pprev = LIST_POISON2; +} + +static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) +{ + struct hlist_node *first = h->first; + n->next = first; + if (first) + first->pprev = &n->next; + h->first = n; + n->pprev = &h->first; +} + + +/** + * hlist_add_head_rcu - adds the specified element to the specified hlist, + * while permitting racing traversals. + * @n: the element to add to the hash list. + * @h: the list to add to. + * + * The caller must take whatever precautions are necessary + * (such as holding appropriate locks) to avoid racing + * with another list-mutation primitive, such as hlist_add_head_rcu() + * or hlist_del_rcu(), running on this same list. + * However, it is perfectly legal to run concurrently with + * the _rcu list-traversal primitives, such as + * hlist_for_each_entry_rcu(), used to prevent memory-consistency + * problems on Alpha CPUs. Regardless of the type of CPU, the + * list-traversal primitive must be guarded by rcu_read_lock(). + */ +static inline void hlist_add_head_rcu(struct hlist_node *n, + struct hlist_head *h) +{ + struct hlist_node *first = h->first; + n->next = first; + n->pprev = &h->first; +// smp_wmb(); + if (first) + first->pprev = &n->next; + h->first = n; +} + +/* next must be != NULL */ +static inline void hlist_add_before(struct hlist_node *n, + struct hlist_node *next) +{ + n->pprev = next->pprev; + n->next = next; + next->pprev = &n->next; + *(n->pprev) = n; +} + +static inline void hlist_add_after(struct hlist_node *n, + struct hlist_node *next) +{ + next->next = n->next; + n->next = next; + next->pprev = &n->next; + + if(next->next) + next->next->pprev = &next->next; +} + +/** + * hlist_add_before_rcu - adds the specified element to the specified hlist + * before the specified node while permitting racing traversals. + * @n: the new element to add to the hash list. + * @next: the existing element to add the new element before. + * + * The caller must take whatever precautions are necessary + * (such as holding appropriate locks) to avoid racing + * with another list-mutation primitive, such as hlist_add_head_rcu() + * or hlist_del_rcu(), running on this same list. + * However, it is perfectly legal to run concurrently with + * the _rcu list-traversal primitives, such as + * hlist_for_each_entry_rcu(), used to prevent memory-consistency + * problems on Alpha CPUs. + */ +static inline void hlist_add_before_rcu(struct hlist_node *n, + struct hlist_node *next) +{ + n->pprev = next->pprev; + n->next = next; +// smp_wmb(); + next->pprev = &n->next; + *(n->pprev) = n; +} + +/** + * hlist_add_after_rcu - adds the specified element to the specified hlist + * after the specified node while permitting racing traversals. + * @prev: the existing element to add the new element after. + * @n: the new element to add to the hash list. + * + * The caller must take whatever precautions are necessary + * (such as holding appropriate locks) to avoid racing + * with another list-mutation primitive, such as hlist_add_head_rcu() + * or hlist_del_rcu(), running on this same list. + * However, it is perfectly legal to run concurrently with + * the _rcu list-traversal primitives, such as + * hlist_for_each_entry_rcu(), used to prevent memory-consistency + * problems on Alpha CPUs. + */ +static inline void hlist_add_after_rcu(struct hlist_node *prev, + struct hlist_node *n) +{ + n->next = prev->next; + n->pprev = &prev->next; +// smp_wmb(); + prev->next = n; + if (n->next) + n->next->pprev = &n->next; +} + +#define hlist_entry(ptr, type, member) container_of(ptr,type,member) + +#define hlist_for_each(pos, head) \ + for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \ + pos = pos->next) + +#define hlist_for_each_safe(pos, n, head) \ + for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \ + pos = n) + +/** + * hlist_for_each_entry - iterate over list of given type + * @tpos: the type * to use as a loop counter. + * @pos: the &struct hlist_node to use as a loop counter. + * @head: the head for your list. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry(tpos, pos, head, member) \ + for (pos = (head)->first; \ + pos && ({ prefetch(pos->next); 1;}) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * hlist_for_each_entry_continue - iterate over a hlist continuing after existing point + * @tpos: the type * to use as a loop counter. + * @pos: the &struct hlist_node to use as a loop counter. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_continue(tpos, pos, member) \ + for (pos = (pos)->next; \ + pos && ({ prefetch(pos->next); 1;}) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * hlist_for_each_entry_from - iterate over a hlist continuing from existing point + * @tpos: the type * to use as a loop counter. + * @pos: the &struct hlist_node to use as a loop counter. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_from(tpos, pos, member) \ + for (; pos && ({ prefetch(pos->next); 1;}) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @tpos: the type * to use as a loop counter. + * @pos: the &struct hlist_node to use as a loop counter. + * @n: another &struct hlist_node to use as temporary storage + * @head: the head for your list. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \ + for (pos = (head)->first; \ + pos && ({ n = pos->next; 1; }) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = n) + +/** + * hlist_for_each_entry_rcu - iterate over rcu list of given type + * @tpos: the type * to use as a loop counter. + * @pos: the &struct hlist_node to use as a loop counter. + * @head: the head for your list. + * @member: the name of the hlist_node within the struct. + * + * This list-traversal primitive may safely run concurrently with + * the _rcu list-mutation primitives such as hlist_add_head_rcu() + * as long as the traversal is guarded by rcu_read_lock(). + */ +#define hlist_for_each_entry_rcu(tpos, pos, head, member) \ + for (pos = (head)->first; \ + rcu_dereference(pos) && ({ prefetch(pos->next); 1;}) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +#endif + +/** + * __list_sort - sort the list using given comparator with merge-sort algorithm + * @head: is a head of the list to be sorted + * @member_offset: is machine offset inside the list entry structure to the + * field of type struct list_head which links that entry with + * the list. + */ +extern void __list_sort(struct list_head * head, + int member_offset, + int (*comparator)(void*,void*)); + +/** + * list_sort - wrapper for __list_sort + * @head: is a head of the list to be sorted + * @type: is the type of list entry + * @member: is the name of the field inside entry that links that entry with + * other entries in the list. + * @comaprator: function comparing two entries, should return value lesser + * than 0 when the first argument is lesser than the second one. + */ +#define list_sort(head,type,member,comparator) \ + ({ \ + __list_sort(head, \ + offsetof(type, member), \ + (int (*)(void*, void*)) comparator); \ + }) + +void test_list_sort(void); diff --git a/alsa_midi/port.c b/alsa_midi/port.c new file mode 100644 index 0000000..fd35864 --- /dev/null +++ b/alsa_midi/port.c @@ -0,0 +1,217 @@ +/* + * ALSA SEQ < - > JACK MIDI bridge + * + * Copyright (c) 2006,2007 Dmitry S. Baikov + * Copyright (c) 2007,2008,2009 Nedko Arnaudov + * Copyright (c) 2009,2010 Paul Davis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU 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 +#include +#include +#include +#include +#include + +#include "list.h" +#include "a2j.h" +#include "port_hash.h" +#include "port.h" + +/* This should be part of JACK API */ +#define JACK_IS_VALID_PORT_NAME_CHAR(c) \ + (isalnum(c) || \ + (c) == '/' || \ + (c) == '_' || \ + (c) == '(' || \ + (c) == ')' || \ + (c) == '-' || \ + (c) == '[' || \ + (c) == ']') + +static +int +a2j_alsa_connect_from (struct a2j * self, int client, int port) +{ + snd_seq_port_subscribe_t* sub; + snd_seq_addr_t seq_addr; + int err; + + snd_seq_port_subscribe_alloca (&sub); + seq_addr.client = client; + seq_addr.port = port; + snd_seq_port_subscribe_set_sender (sub, &seq_addr); + seq_addr.client = self->client_id; + seq_addr.port = self->port_id; + snd_seq_port_subscribe_set_dest (sub, &seq_addr); + + snd_seq_port_subscribe_set_time_update (sub, 1); + snd_seq_port_subscribe_set_queue (sub, self->queue); + snd_seq_port_subscribe_set_time_real (sub, 1); + + if ((err = snd_seq_subscribe_port (self->seq, sub))) { + a2j_error ("can't subscribe to %d:%d - %s", client, port, snd_strerror(err)); + } + + return err; +} + +void +a2j_port_setdead (a2j_port_hash_t hash, snd_seq_addr_t addr) +{ + struct a2j_port *port = a2j_port_get(hash, addr); + + if (port) { + port->is_dead = true; // see jack_process_internal + } else { + a2j_debug("port_setdead: not found (%d:%d)", addr.client, addr.port); + } +} + +void +a2j_port_free (struct a2j_port * port) +{ + // snd_seq_disconnect_from (self->seq, self->port_id, port->remote.client, port->remote.port); + // snd_seq_disconnect_to (self->seq, self->port_id, port->remote.client, port->remote.port); + + if (port->inbound_events) { + jack_ringbuffer_free (port->inbound_events); + } + + if (port->jack_port != JACK_INVALID_PORT && !port->a2j_ptr->finishing) { + jack_port_unregister (port->a2j_ptr->jack_client, port->jack_port); + } + + free (port); +} + +void +a2j_port_fill_name (struct a2j_port * port_ptr, int dir, snd_seq_client_info_t * client_info_ptr, + const snd_seq_port_info_t * port_info_ptr, bool make_unique) +{ + char *c; + + if (make_unique) { + snprintf (port_ptr->name, + sizeof(port_ptr->name), + "%s [%d] %s %s", + snd_seq_client_info_get_name(client_info_ptr), + snd_seq_client_info_get_client(client_info_ptr), + snd_seq_port_info_get_name(port_info_ptr), + (dir == A2J_PORT_CAPTURE ? "in" : "out")); + } else { + snprintf (port_ptr->name, + sizeof(port_ptr->name), + "%s %s %s", + snd_seq_client_info_get_name(client_info_ptr), + snd_seq_port_info_get_name(port_info_ptr), + (dir == A2J_PORT_CAPTURE ? "in" : "out")); + } + + // replace all offending characters with ' ' + for (c = port_ptr->name; *c; ++c) { + if (!JACK_IS_VALID_PORT_NAME_CHAR(*c)) { + *c = ' '; + } + } +} + +struct a2j_port * +a2j_port_create (struct a2j * self, int dir, snd_seq_addr_t addr, const snd_seq_port_info_t * info) +{ + struct a2j_port *port; + int err; + int client; + snd_seq_client_info_t * client_info_ptr; + int jack_caps; + struct a2j_stream * stream_ptr; + + stream_ptr = &self->stream[dir]; + + if ((err = snd_seq_client_info_malloc (&client_info_ptr)) != 0) { + a2j_error("Failed to allocate client info"); + goto fail; + } + + client = snd_seq_port_info_get_client (info); + + err = snd_seq_get_any_client_info (self->seq, client, client_info_ptr); + if (err != 0) { + a2j_error("Failed to get client info"); + goto fail_free_client_info; + } + + a2j_debug ("client name: '%s'", snd_seq_client_info_get_name(client_info_ptr)); + a2j_debug ("port name: '%s'", snd_seq_port_info_get_name(info)); + + port = calloc (1, sizeof(struct a2j_port)); + if (!port) { + goto fail_free_client_info; + } + + port->a2j_ptr = self; + port->jack_port = JACK_INVALID_PORT; + port->remote = addr; + + a2j_port_fill_name (port, dir, client_info_ptr, info, false); + + /* Add port to list early, before registering to JACK, so map functionality is guaranteed to work during port registration */ + list_add_tail (&port->siblings, &stream_ptr->list); + + if (dir == A2J_PORT_CAPTURE) { + jack_caps = JackPortIsOutput; + } else { + jack_caps = JackPortIsInput; + } + + /* mark anything that looks like a hardware port as physical&terminal */ + if (snd_seq_port_info_get_type (info) & (SND_SEQ_PORT_TYPE_HARDWARE|SND_SEQ_PORT_TYPE_PORT|SND_SEQ_PORT_TYPE_SPECIFIC)) { + jack_caps |= JackPortIsPhysical|JackPortIsTerminal; + } + + port->jack_port = jack_port_register (self->jack_client, port->name, JACK_DEFAULT_MIDI_TYPE, jack_caps, 0); + if (port->jack_port == JACK_INVALID_PORT) { + a2j_error("jack_port_register() failed for '%s'", port->name); + goto fail_free_port; + } + + if (dir == A2J_PORT_CAPTURE) { + err = a2j_alsa_connect_from (self, port->remote.client, port->remote.port); + } else { + err = snd_seq_connect_to (self->seq, self->port_id, port->remote.client, port->remote.port); + } + + if (err) { + a2j_info("port skipped: %s", port->name); + goto fail_free_port; + } + + port->inbound_events = jack_ringbuffer_create(MAX_EVENT_SIZE*16); + + a2j_info("port created: %s", port->name); + return port; + + fail_free_port: + list_del (&port->siblings); + + a2j_port_free (port); + + fail_free_client_info: + snd_seq_client_info_free (client_info_ptr); + + fail: + return NULL; +} diff --git a/alsa_midi/port.h b/alsa_midi/port.h new file mode 100644 index 0000000..07169a3 --- /dev/null +++ b/alsa_midi/port.h @@ -0,0 +1,29 @@ +/* -*- Mode: C ; c-basic-offset: 2 -*- */ +/* + * ALSA SEQ < - > JACK MIDI bridge + * + * Copyright (c) 2006,2007 Dmitry S. Baikov + * Copyright (c) 2007,2008,2009 Nedko Arnaudov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU 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 + */ + +#ifndef PORT_H__757ADD0F_5E53_41F7_8B7F_8119C5E8A9F1__INCLUDED +#define PORT_H__757ADD0F_5E53_41F7_8B7F_8119C5E8A9F1__INCLUDED + +struct a2j_port* a2j_port_create (struct a2j * self, int dir, snd_seq_addr_t addr, const snd_seq_port_info_t * info); +void a2j_port_setdead (a2j_port_hash_t hash, snd_seq_addr_t addr); +void a2j_port_free (struct a2j_port * port); + +#endif /* #ifndef PORT_H__757ADD0F_5E53_41F7_8B7F_8119C5E8A9F1__INCLUDED */ diff --git a/alsa_midi/port_hash.c b/alsa_midi/port_hash.c new file mode 100644 index 0000000..cc9c5c6 --- /dev/null +++ b/alsa_midi/port_hash.c @@ -0,0 +1,63 @@ +/* -*- Mode: C ; c-basic-offset: 2 -*- */ +/* + * ALSA SEQ < - > JACK MIDI bridge + * + * Copyright (c) 2006,2007 Dmitry S. Baikov + * Copyright (c) 2007,2008,2009 Nedko Arnaudov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU 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 +#include +#include +#include +#include + +#include "list.h" +#include "a2j.h" +#include "port_hash.h" + +static inline +int +a2j_port_hash( + snd_seq_addr_t addr) +{ + return (addr.client + addr.port) % PORT_HASH_SIZE; +} + +struct a2j_port * +a2j_port_get( + a2j_port_hash_t hash, + snd_seq_addr_t addr) +{ + struct a2j_port **pport = &hash[a2j_port_hash(addr)]; + while (*pport) { + struct a2j_port *port = *pport; + if (port->remote.client == addr.client && port->remote.port == addr.port) + return port; + pport = &port->next; + } + return NULL; +} + +void +a2j_port_insert( + a2j_port_hash_t hash, + struct a2j_port * port) +{ + struct a2j_port **pport = &hash[a2j_port_hash(port->remote)]; + port->next = *pport; + *pport = port; +} diff --git a/alsa_midi/port_hash.h b/alsa_midi/port_hash.h new file mode 100644 index 0000000..ec21f11 --- /dev/null +++ b/alsa_midi/port_hash.h @@ -0,0 +1,35 @@ +/* -*- Mode: C ; c-basic-offset: 2 -*- */ +/* + * ALSA SEQ < - > JACK MIDI bridge + * + * Copyright (c) 2006,2007 Dmitry S. Baikov + * Copyright (c) 2007,2008,2009 Nedko Arnaudov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU 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 + */ + +#ifndef PORT_HASH_H__A44CBCD6_E075_49CB_8F73_DF9772511D55__INCLUDED +#define PORT_HASH_H__A44CBCD6_E075_49CB_8F73_DF9772511D55__INCLUDED + +void +a2j_port_insert( + a2j_port_hash_t hash, + struct a2j_port * port); + +struct a2j_port * +a2j_port_get( + a2j_port_hash_t hash, + snd_seq_addr_t addr); + +#endif /* #ifndef PORT_HASH_H__A44CBCD6_E075_49CB_8F73_DF9772511D55__INCLUDED */ diff --git a/alsa_midi/port_thread.c b/alsa_midi/port_thread.c new file mode 100644 index 0000000..98df0e2 --- /dev/null +++ b/alsa_midi/port_thread.c @@ -0,0 +1,235 @@ +/* -*- Mode: C ; c-basic-offset: 2 -*- */ +/* + * ALSA SEQ < - > JACK MIDI bridge + * + * Copyright (c) 2006,2007 Dmitry S. Baikov + * Copyright (c) 2007,2008,2009 Nedko Arnaudov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU 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 +#include +#include +#include +#include + +#include "list.h" +#include "a2j.h" +#include "port.h" +#include "port_hash.h" +#include "port_thread.h" + +struct a2j_port * +a2j_find_port_by_addr( + struct a2j_stream * stream_ptr, + snd_seq_addr_t addr) +{ + struct list_head * node_ptr; + struct a2j_port * port_ptr; + + list_for_each(node_ptr, &stream_ptr->list) + { + port_ptr = list_entry(node_ptr, struct a2j_port, siblings); + if (port_ptr->remote.client == addr.client && port_ptr->remote.port == addr.port) + { + return port_ptr; + } + } + + return NULL; +} + +struct a2j_port * +a2j_find_port_by_jack_port_name( + struct a2j_stream * stream_ptr, + const char * jack_port) +{ + struct list_head * node_ptr; + struct a2j_port * port_ptr; + + list_for_each(node_ptr, &stream_ptr->list) + { + port_ptr = list_entry(node_ptr, struct a2j_port, siblings); + if (strcmp(port_ptr->name, jack_port) == 0) + { + return port_ptr; + } + } + + return NULL; +} + +/* + * ==================== Port add/del handling thread ============================== + */ + +static +void +a2j_update_port_type (struct a2j * self, int dir, snd_seq_addr_t addr, int caps, const snd_seq_port_info_t * info) +{ + struct a2j_stream * stream_ptr; + int alsa_mask; + struct a2j_port * port_ptr; + + a2j_debug("update_port_type(%d:%d)", addr.client, addr.port); + + stream_ptr = &self->stream[dir]; + port_ptr = a2j_find_port_by_addr(stream_ptr, addr); + + if (dir == A2J_PORT_CAPTURE) { + alsa_mask = SND_SEQ_PORT_CAP_SUBS_READ; + } else { + alsa_mask = SND_SEQ_PORT_CAP_SUBS_WRITE; + } + + if (port_ptr != NULL && (caps & alsa_mask) != alsa_mask) { + a2j_debug("setdead: %s", port_ptr->name); + port_ptr->is_dead = true; + } + + if (port_ptr == NULL && (caps & alsa_mask) == alsa_mask) { + if(jack_ringbuffer_write_space(stream_ptr->new_ports) >= sizeof(port_ptr)) { + port_ptr = a2j_port_create (self, dir, addr, info); + if (port_ptr != NULL) { + jack_ringbuffer_write(stream_ptr->new_ports, (char *)&port_ptr, sizeof(port_ptr)); + } + } else { + a2j_error( "dropping new port event... increase MAX_PORTS" ); + } + } +} + +void +a2j_update_port (struct a2j * self, snd_seq_addr_t addr, const snd_seq_port_info_t * info) +{ + unsigned int port_caps = snd_seq_port_info_get_capability(info); + unsigned int port_type = snd_seq_port_info_get_type(info); + + a2j_debug("port %u:%u", addr.client, addr.port); + a2j_debug("port type: 0x%08X", port_type); + a2j_debug("port caps: 0x%08X", port_caps); + + if (port_type & SND_SEQ_PORT_TYPE_SPECIFIC) { + a2j_debug("SPECIFIC"); + } + + if (port_type & SND_SEQ_PORT_TYPE_MIDI_GENERIC) { + a2j_debug("MIDI_GENERIC"); + } + + if (port_type & SND_SEQ_PORT_TYPE_MIDI_GM) { + a2j_debug("MIDI_GM"); + } + + if (port_type & SND_SEQ_PORT_TYPE_MIDI_GS) { + a2j_debug("MIDI_GS"); + } + + if (port_type & SND_SEQ_PORT_TYPE_MIDI_XG) { + a2j_debug("MIDI_XG"); + } + + if (port_type & SND_SEQ_PORT_TYPE_MIDI_MT32) { + a2j_debug("MIDI_MT32"); + } + + if (port_type & SND_SEQ_PORT_TYPE_MIDI_GM2) { + a2j_debug("MIDI_GM2"); + } + + if (port_type & SND_SEQ_PORT_TYPE_SYNTH) { + a2j_debug("SYNTH"); + } + + if (port_type & SND_SEQ_PORT_TYPE_DIRECT_SAMPLE) { + a2j_debug("DIRECT_SAMPLE"); + } + + if (port_type & SND_SEQ_PORT_TYPE_SAMPLE) { + a2j_debug("SAMPLE"); + } + + if (port_type & SND_SEQ_PORT_TYPE_HARDWARE) { + a2j_debug("HARDWARE"); + } + + if (port_type & SND_SEQ_PORT_TYPE_SOFTWARE) { + a2j_debug("SOFTWARE"); + } + + if (port_type & SND_SEQ_PORT_TYPE_SYNTHESIZER) { + a2j_debug("SYNTHESIZER"); + } + + if (port_type & SND_SEQ_PORT_TYPE_PORT) { + a2j_debug("PORT"); + } + + if (port_type & SND_SEQ_PORT_TYPE_APPLICATION) { + a2j_debug("APPLICATION"); + } + + if (port_type == 0) { + a2j_debug("Ignoring port of type 0"); + return; + } + + if (port_caps & SND_SEQ_PORT_CAP_NO_EXPORT) { + a2j_debug("Ignoring no-export port"); + return; + } + + a2j_update_port_type (self, A2J_PORT_CAPTURE, addr, port_caps, info); + a2j_update_port_type (self, A2J_PORT_PLAYBACK, addr, port_caps, info); +} + +void +a2j_free_ports (jack_ringbuffer_t * ports) +{ + struct a2j_port *port; + int sz; + + while ((sz = jack_ringbuffer_read (ports, (char*)&port, sizeof(port)))) { + assert (sz == sizeof(port)); + a2j_info("port deleted: %s", port->name); + list_del (&port->siblings); + a2j_port_free(port); + } +} + +void +a2j_update_ports (struct a2j * self) +{ + snd_seq_addr_t addr; + int size; + + while ((size = jack_ringbuffer_read(self->port_add, (char *)&addr, sizeof(addr))) != 0) { + + snd_seq_port_info_t * info; + int err; + + snd_seq_port_info_alloca(&info); + + assert (size == sizeof(addr)); + assert (addr.client != self->client_id); + + if ((err = snd_seq_get_any_port_info(self->seq, addr.client, addr.port, info)) >= 0) { + a2j_update_port(self, addr, info); + } else { + a2j_port_setdead(self->stream[A2J_PORT_CAPTURE].port_hash, addr); + a2j_port_setdead(self->stream[A2J_PORT_PLAYBACK].port_hash, addr); + } + } +} diff --git a/alsa_midi/port_thread.h b/alsa_midi/port_thread.h new file mode 100644 index 0000000..e69af7f --- /dev/null +++ b/alsa_midi/port_thread.h @@ -0,0 +1,49 @@ +/* -*- Mode: C ; c-basic-offset: 2 -*- */ +/* + * ALSA SEQ < - > JACK MIDI bridge + * + * Copyright (c) 2006,2007 Dmitry S. Baikov + * Copyright (c) 2007,2008,2009 Nedko Arnaudov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU 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 + */ + +#ifndef PORT_THREAD_H__1C6B5065_5556_4AC6_AA9F_44C32A9648C6__INCLUDED +#define PORT_THREAD_H__1C6B5065_5556_4AC6_AA9F_44C32A9648C6__INCLUDED + +void +a2j_update_port( + struct a2j * self, + snd_seq_addr_t addr, + const snd_seq_port_info_t * info); + +void +a2j_update_ports( + struct a2j * self); + +void +a2j_free_ports( + jack_ringbuffer_t * ports); + +struct a2j_port * +a2j_find_port_by_addr( + struct a2j_stream * stream_ptr, + snd_seq_addr_t addr); + +struct a2j_port * +a2j_find_port_by_jack_port_name( + struct a2j_stream * stream_ptr, + const char * jack_port); + +#endif /* #ifndef PORT_THREAD_H__1C6B5065_5556_4AC6_AA9F_44C32A9648C6__INCLUDED */