git-svn-id: svn+ssh://jackaudio.org/trunk/jack@1035 0c269be4-1314-0410-8aa9-9f06e86f4224tags/0.109.0
| @@ -16,7 +16,7 @@ dnl micro version = incremented when implementation-only | |||
| dnl changes are made | |||
| dnl --- | |||
| JACK_MAJOR_VERSION=0 | |||
| JACK_MINOR_VERSION=104 | |||
| JACK_MINOR_VERSION=105 | |||
| JACK_MICRO_VERSION=0 | |||
| dnl --- | |||
| @@ -44,7 +44,7 @@ dnl slacker than this, and closer to those for the JACK version | |||
| dnl number. | |||
| dnl --- | |||
| JACK_API_CURRENT=0 | |||
| JACK_API_REVISION=26 | |||
| JACK_API_REVISION=27 | |||
| JACK_API_AGE=0 | |||
| AC_SUBST(JACK_MAJOR_VERSION) | |||
| @@ -8,9 +8,13 @@ plugin_LTLIBRARIES = jack_alsa.la | |||
| jack_alsa_la_LDFLAGS = -module -avoid-version | |||
| jack_alsa_la_SOURCES = alsa_driver.c generic_hw.c memops.c \ | |||
| alsa_seqmidi.c alsa_rawmidi.c \ | |||
| hammerfall.c hdsp.c ice1712.c usx2y.c | |||
| noinst_HEADERS = alsa_driver.h \ | |||
| alsa_midi.h \ | |||
| midi_pack.h \ | |||
| midi_unpack.h \ | |||
| generic.h \ | |||
| hammerfall.h \ | |||
| hdsp.h \ | |||
| @@ -1022,6 +1022,9 @@ alsa_driver_start (alsa_driver_t *driver) | |||
| malloc (sizeof (struct pollfd) * | |||
| (driver->playback_nfds + driver->capture_nfds + 2)); | |||
| if (driver->midi) | |||
| (driver->midi->start)(driver->midi); | |||
| if (driver->playback_handle) { | |||
| /* fill playback buffer with zeroes, and mark | |||
| all fragments as having data. | |||
| @@ -1126,6 +1129,9 @@ alsa_driver_stop (alsa_driver_t *driver) | |||
| driver->hw->set_input_monitor_mask (driver->hw, 0); | |||
| } | |||
| if (driver->midi) | |||
| (driver->midi->stop)(driver->midi); | |||
| return 0; | |||
| } | |||
| @@ -1553,6 +1559,9 @@ alsa_driver_read (alsa_driver_t *driver, jack_nframes_t nframes) | |||
| if (nframes > driver->frames_per_cycle) { | |||
| return -1; | |||
| } | |||
| if (driver->midi) | |||
| (driver->midi->read)(driver->midi, nframes); | |||
| nread = 0; | |||
| contiguous = 0; | |||
| @@ -1622,6 +1631,9 @@ alsa_driver_write (alsa_driver_t* driver, jack_nframes_t nframes) | |||
| return -1; | |||
| } | |||
| if (driver->midi) | |||
| (driver->midi->write)(driver->midi, nframes); | |||
| nwritten = 0; | |||
| contiguous = 0; | |||
| orig_nframes = nframes; | |||
| @@ -1805,6 +1817,12 @@ alsa_driver_attach (alsa_driver_t *driver) | |||
| } | |||
| } | |||
| if (driver->midi) { | |||
| int err = (driver->midi->attach)(driver->midi); | |||
| if (err) | |||
| jack_error("ALSA: cannot attach midi: %d", err); | |||
| } | |||
| return jack_activate (driver->client); | |||
| } | |||
| @@ -1818,6 +1836,9 @@ alsa_driver_detach (alsa_driver_t *driver) | |||
| return 0; | |||
| } | |||
| if (driver->midi) | |||
| (driver->midi->detach)(driver->midi); | |||
| for (node = driver->capture_ports; node; | |||
| node = jack_slist_next (node)) { | |||
| jack_port_unregister (driver->client, | |||
| @@ -1904,6 +1925,9 @@ alsa_driver_delete (alsa_driver_t *driver) | |||
| { | |||
| JSList *node; | |||
| if (driver->midi) | |||
| (driver->midi->destroy)(driver->midi); | |||
| for (node = driver->clock_sync_listeners; node; | |||
| node = jack_slist_next (node)) { | |||
| free (node->data); | |||
| @@ -1975,7 +1999,8 @@ alsa_driver_new (char *name, char *playback_alsa_device, | |||
| int user_playback_nchnls, | |||
| int shorts_first, | |||
| jack_nframes_t capture_latency, | |||
| jack_nframes_t playback_latency | |||
| jack_nframes_t playback_latency, | |||
| alsa_midi_t *midi_driver | |||
| ) | |||
| { | |||
| int err; | |||
| @@ -2058,6 +2083,8 @@ alsa_driver_new (char *name, char *playback_alsa_device, | |||
| driver->alsa_name_playback = strdup (playback_alsa_device); | |||
| driver->alsa_name_capture = strdup (capture_alsa_device); | |||
| driver->midi = midi_driver; | |||
| if (alsa_driver_check_card_type (driver)) { | |||
| alsa_driver_delete (driver); | |||
| return NULL; | |||
| @@ -2332,7 +2359,7 @@ driver_get_descriptor () | |||
| desc = calloc (1, sizeof (jack_driver_desc_t)); | |||
| strcpy (desc->name,"alsa"); | |||
| desc->nparams = 17; | |||
| desc->nparams = 18; | |||
| params = calloc (desc->nparams, sizeof (jack_driver_param_desc_t)); | |||
| @@ -2483,6 +2510,18 @@ driver_get_descriptor () | |||
| strcpy (params[i].short_desc, "Extra output latency (frames)"); | |||
| strcpy (params[i].long_desc, params[i].short_desc); | |||
| i++; | |||
| strcpy (params[i].name, "midi"); | |||
| params[i].character = 'X'; | |||
| params[i].type = JackDriverParamString; | |||
| strcpy (params[i].value.str, "none"); | |||
| strcpy (params[i].short_desc, "ALSA MIDI driver (seq|raw)"); | |||
| strcpy (params[i].long_desc, | |||
| "ALSA MIDI driver:\n" | |||
| " none - no MIDI driver\n" | |||
| " seq - ALSA Sequencer driver\n" | |||
| " raw - ALSA RawMIDI driver\n"); | |||
| desc->params = params; | |||
| return desc; | |||
| @@ -2508,6 +2547,8 @@ driver_initialize (jack_client_t *client, const JSList * params) | |||
| int shorts_first = FALSE; | |||
| jack_nframes_t systemic_input_latency = 0; | |||
| jack_nframes_t systemic_output_latency = 0; | |||
| char *midi_driver_name = "none"; | |||
| alsa_midi_t *midi = NULL; | |||
| const JSList * node; | |||
| const jack_driver_param_t * param; | |||
| @@ -2596,6 +2637,10 @@ driver_initialize (jack_client_t *client, const JSList * params) | |||
| systemic_output_latency = param->value.ui; | |||
| break; | |||
| case 'X': | |||
| midi_driver_name = strdup (param->value.str); | |||
| break; | |||
| } | |||
| } | |||
| @@ -2605,6 +2650,12 @@ driver_initialize (jack_client_t *client, const JSList * params) | |||
| playback = TRUE; | |||
| } | |||
| if (strcmp(midi_driver_name, "seq")==0) { | |||
| midi = alsa_seqmidi_new(client, NULL); | |||
| } else if (strcmp(midi_driver_name, "raw")==0) { | |||
| midi = alsa_rawmidi_new(client); | |||
| } | |||
| return alsa_driver_new ("alsa_pcm", playback_pcm_name, | |||
| capture_pcm_name, client, | |||
| frames_per_interrupt, | |||
| @@ -2614,7 +2665,7 @@ driver_initialize (jack_client_t *client, const JSList * params) | |||
| user_capture_nchnls, user_playback_nchnls, | |||
| shorts_first, | |||
| systemic_input_latency, | |||
| systemic_output_latency); | |||
| systemic_output_latency, midi); | |||
| } | |||
| void | |||
| @@ -39,6 +39,8 @@ | |||
| #include <jack/memops.h> | |||
| #include <jack/jack.h> | |||
| #include "./alsa_midi.h" | |||
| typedef void (*ReadCopyFunction) (jack_default_audio_sample_t *dst, char *src, | |||
| unsigned long src_bytes, | |||
| unsigned long src_skip_bytes); | |||
| @@ -141,6 +143,8 @@ typedef struct _alsa_driver { | |||
| int xrun_count; | |||
| int process_count; | |||
| alsa_midi_t *midi; | |||
| } alsa_driver_t; | |||
| static inline void | |||
| @@ -0,0 +1,38 @@ | |||
| /* | |||
| * Copyright (c) 2006 Dmitry S. Baikov | |||
| * | |||
| * 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; either version 2 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This program is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU 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 __jack_alsa_midi_h__ | |||
| #define __jack_alsa_midi_h__ | |||
| #include <jack/jack.h> | |||
| typedef struct alsa_midi_t alsa_midi_t; | |||
| struct alsa_midi_t { | |||
| void (*destroy)(alsa_midi_t *amidi); | |||
| int (*attach)(alsa_midi_t *amidi); | |||
| int (*detach)(alsa_midi_t *amidi); | |||
| int (*start)(alsa_midi_t *amidi); | |||
| int (*stop)(alsa_midi_t *amidi); | |||
| void (*read)(alsa_midi_t *amidi, jack_nframes_t nframes); | |||
| void (*write)(alsa_midi_t *amidi, jack_nframes_t nframes); | |||
| }; | |||
| alsa_midi_t* alsa_rawmidi_new(jack_client_t *jack); | |||
| alsa_midi_t* alsa_seqmidi_new(jack_client_t *jack, const char* alsa_name); | |||
| #endif /* __jack_alsa_midi_h__ */ | |||
| @@ -0,0 +1,870 @@ | |||
| /* | |||
| * ALSA SEQ < - > JACK MIDI bridge | |||
| * | |||
| * Copyright (c) 2006,2007 Dmitry S. Baikov <c0ff@konstruktiv.org> | |||
| * | |||
| * 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; either version 2 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This program is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU 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 | |||
| */ | |||
| /* | |||
| * alsa_seqmidi_read: | |||
| * add new ports | |||
| * reads queued snd_seq_event's | |||
| * if PORT_EXIT: mark port as dead | |||
| * if PORT_ADD, PORT_CHANGE: send addr to port_thread (it also may mark port as dead) | |||
| * else process input event | |||
| * remove dead ports and send them to port_thread | |||
| * | |||
| * alsa_seqmidi_write: | |||
| * remove dead ports and send them to port_thread | |||
| * add new ports | |||
| * queue output events | |||
| * | |||
| * port_thread: | |||
| * wait for port_sem | |||
| * free deleted ports | |||
| * create new ports or mark existing as dead | |||
| */ | |||
| #include <alsa/asoundlib.h> | |||
| #include <jack/jack.h> | |||
| #include <jack/midiport.h> | |||
| #include <jack/ringbuffer.h> | |||
| #include <jack/thread.h> | |||
| #include <stdlib.h> | |||
| #include <stdio.h> | |||
| #include <signal.h> | |||
| #include <semaphore.h> | |||
| #include <time.h> | |||
| #include <ctype.h> | |||
| #include "./alsa_midi.h" | |||
| #ifdef STANDALONE | |||
| #define MESSAGE(...) fprintf(stderr, __VA_ARGS__) | |||
| #else | |||
| #include <jack/messagebuffer.h> | |||
| #endif | |||
| #define info_log(...) MESSAGE(__VA_ARGS__) | |||
| #define error_log(...) MESSAGE(__VA_ARGS__) | |||
| #ifdef DEBUG | |||
| #define debug_log(...) MESSAGE(__VA_ARGS__) | |||
| #else | |||
| #define debug_log(...) | |||
| #endif | |||
| #define NSEC_PER_SEC ((int64_t)1000*1000*1000) | |||
| enum { | |||
| MAX_PORTS = 64, | |||
| MAX_EVENT_SIZE = 1024, | |||
| }; | |||
| typedef struct port_t port_t; | |||
| enum { | |||
| PORT_HASH_BITS = 4, | |||
| PORT_HASH_SIZE = 1 << PORT_HASH_BITS | |||
| }; | |||
| typedef port_t* port_hash_t[PORT_HASH_SIZE]; | |||
| struct port_t { | |||
| port_t *next; | |||
| int is_dead; | |||
| char name[64]; | |||
| snd_seq_addr_t remote; | |||
| jack_port_t *jack_port; | |||
| jack_ringbuffer_t *early_events; // alsa_midi_event_t + data | |||
| int64_t last_out_time; | |||
| void *jack_buf; | |||
| }; | |||
| typedef struct { | |||
| snd_midi_event_t *codec; | |||
| jack_ringbuffer_t *new_ports; | |||
| port_t *ports[MAX_PORTS]; | |||
| } stream_t; | |||
| typedef struct alsa_seqmidi { | |||
| alsa_midi_t ops; | |||
| jack_client_t *jack; | |||
| snd_seq_t *seq; | |||
| int client_id; | |||
| int port_id; | |||
| int queue; | |||
| int keep_walking; | |||
| pthread_t port_thread; | |||
| sem_t port_sem; | |||
| jack_ringbuffer_t *port_add; // snd_seq_addr_t | |||
| jack_ringbuffer_t *port_del; // port_t* | |||
| stream_t stream[2]; | |||
| char alsa_name[32]; | |||
| } alsa_seqmidi_t; | |||
| struct alsa_midi_event { | |||
| int64_t time; | |||
| int size; | |||
| }; | |||
| typedef struct alsa_midi_event alsa_midi_event_t; | |||
| struct process_info { | |||
| int dir; | |||
| jack_nframes_t nframes; | |||
| jack_nframes_t period_start; | |||
| jack_nframes_t sample_rate; | |||
| jack_nframes_t cur_frames; | |||
| int64_t alsa_time; | |||
| }; | |||
| enum PortType { PORT_INPUT = 0, PORT_OUTPUT = 1 }; | |||
| typedef void (*port_jack_func)(alsa_seqmidi_t *self, port_t *port,struct process_info* info); | |||
| static void do_jack_input(alsa_seqmidi_t *self, port_t *port, struct process_info* info); | |||
| static void do_jack_output(alsa_seqmidi_t *self, port_t *port, struct process_info* info); | |||
| typedef struct { | |||
| int alsa_mask; | |||
| int jack_caps; | |||
| char name[4]; | |||
| port_jack_func jack_func; | |||
| } port_type_t; | |||
| static port_type_t port_type[2] = { | |||
| { | |||
| SND_SEQ_PORT_CAP_SUBS_READ, | |||
| JackPortIsOutput|JackPortIsPhysical|JackPortIsTerminal, | |||
| "in", | |||
| do_jack_input | |||
| }, | |||
| { | |||
| SND_SEQ_PORT_CAP_SUBS_WRITE, | |||
| JackPortIsInput|JackPortIsPhysical|JackPortIsTerminal, | |||
| "out", | |||
| do_jack_output | |||
| } | |||
| }; | |||
| static void alsa_seqmidi_delete(alsa_midi_t *m); | |||
| static int alsa_seqmidi_attach(alsa_midi_t *m); | |||
| static int alsa_seqmidi_detach(alsa_midi_t *m); | |||
| static int alsa_seqmidi_start(alsa_midi_t *m); | |||
| static int alsa_seqmidi_stop(alsa_midi_t *m); | |||
| static void alsa_seqmidi_read(alsa_midi_t *m, jack_nframes_t nframes); | |||
| static void alsa_seqmidi_write(alsa_midi_t *m, jack_nframes_t nframes); | |||
| static | |||
| void stream_init(alsa_seqmidi_t *self, int dir) | |||
| { | |||
| stream_t *str = &self->stream[dir]; | |||
| str->new_ports = jack_ringbuffer_create(MAX_PORTS*sizeof(port_t*)); | |||
| snd_midi_event_new(MAX_EVENT_SIZE, &str->codec); | |||
| } | |||
| static void port_free(alsa_seqmidi_t *self, port_t *port); | |||
| static void free_ports(alsa_seqmidi_t *self, jack_ringbuffer_t *ports); | |||
| static | |||
| void stream_attach(alsa_seqmidi_t *self, int dir) | |||
| { | |||
| } | |||
| static | |||
| void stream_detach(alsa_seqmidi_t *self, int dir) | |||
| { | |||
| stream_t *str = &self->stream[dir]; | |||
| int i; | |||
| free_ports(self, str->new_ports); | |||
| // delete all ports from hash | |||
| for (i=0; i<PORT_HASH_SIZE; ++i) { | |||
| port_t *port = str->ports[i]; | |||
| while (port) { | |||
| port_t *next = port->next; | |||
| port_free(self, port); | |||
| port = next; | |||
| } | |||
| str->ports[i] = NULL; | |||
| } | |||
| } | |||
| static | |||
| void stream_close(alsa_seqmidi_t *self, int dir) | |||
| { | |||
| stream_t *str = &self->stream[dir]; | |||
| if (str->codec) | |||
| snd_midi_event_free(str->codec); | |||
| if (str->new_ports) | |||
| jack_ringbuffer_free(str->new_ports); | |||
| } | |||
| alsa_midi_t* alsa_seqmidi_new(jack_client_t *client, const char* alsa_name) | |||
| { | |||
| alsa_seqmidi_t *self = calloc(1, sizeof(alsa_seqmidi_t)); | |||
| debug_log("midi: new\n"); | |||
| if (!self) | |||
| return NULL; | |||
| self->jack = client; | |||
| if (!alsa_name) | |||
| alsa_name = "jack_midi"; | |||
| snprintf(self->alsa_name, sizeof(self->alsa_name), "%s", alsa_name); | |||
| self->port_add = jack_ringbuffer_create(2*MAX_PORTS*sizeof(snd_seq_addr_t)); | |||
| self->port_del = jack_ringbuffer_create(2*MAX_PORTS*sizeof(port_t*)); | |||
| sem_init(&self->port_sem, 0, 0); | |||
| stream_init(self, PORT_INPUT); | |||
| stream_init(self, PORT_OUTPUT); | |||
| self->ops.destroy = alsa_seqmidi_delete; | |||
| self->ops.attach = alsa_seqmidi_attach; | |||
| self->ops.detach = alsa_seqmidi_detach; | |||
| self->ops.start = alsa_seqmidi_start; | |||
| self->ops.stop = alsa_seqmidi_stop; | |||
| self->ops.read = alsa_seqmidi_read; | |||
| self->ops.write = alsa_seqmidi_write; | |||
| return &self->ops; | |||
| } | |||
| static | |||
| void alsa_seqmidi_delete(alsa_midi_t *m) | |||
| { | |||
| alsa_seqmidi_t *self = (alsa_seqmidi_t*) m; | |||
| debug_log("midi: delete\n"); | |||
| alsa_seqmidi_detach(m); | |||
| stream_close(self, PORT_OUTPUT); | |||
| stream_close(self, PORT_INPUT); | |||
| jack_ringbuffer_free(self->port_add); | |||
| jack_ringbuffer_free(self->port_del); | |||
| sem_close(&self->port_sem); | |||
| free(self); | |||
| } | |||
| static | |||
| int alsa_seqmidi_attach(alsa_midi_t *m) | |||
| { | |||
| alsa_seqmidi_t *self = (alsa_seqmidi_t*) m; | |||
| int err; | |||
| debug_log("midi: attach\n"); | |||
| if (self->seq) | |||
| return -EALREADY; | |||
| if ((err = snd_seq_open(&self->seq, "hw", SND_SEQ_OPEN_DUPLEX, 0)) < 0) { | |||
| error_log("failed to open alsa seq"); | |||
| return err; | |||
| } | |||
| snd_seq_set_client_name(self->seq, self->alsa_name); | |||
| 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); | |||
| self->client_id = snd_seq_client_id(self->seq); | |||
| self->queue = snd_seq_alloc_queue(self->seq); | |||
| snd_seq_start_queue(self->seq, self->queue, 0); | |||
| stream_attach(self, PORT_INPUT); | |||
| stream_attach(self, PORT_OUTPUT); | |||
| snd_seq_nonblock(self->seq, 1); | |||
| return 0; | |||
| } | |||
| static | |||
| int alsa_seqmidi_detach(alsa_midi_t *m) | |||
| { | |||
| alsa_seqmidi_t *self = (alsa_seqmidi_t*) m; | |||
| debug_log("midi: detach\n"); | |||
| if (!self->seq) | |||
| return -EALREADY; | |||
| alsa_seqmidi_stop(m); | |||
| jack_ringbuffer_reset(self->port_add); | |||
| free_ports(self, self->port_del); | |||
| stream_detach(self, PORT_INPUT); | |||
| stream_detach(self, PORT_OUTPUT); | |||
| snd_seq_close(self->seq); | |||
| self->seq = NULL; | |||
| return 0; | |||
| } | |||
| static void* port_thread(void *); | |||
| static void add_existing_ports(alsa_seqmidi_t *self); | |||
| static void update_ports(alsa_seqmidi_t *self); | |||
| static void add_ports(stream_t *str); | |||
| static | |||
| int alsa_seqmidi_start(alsa_midi_t *m) | |||
| { | |||
| alsa_seqmidi_t *self = (alsa_seqmidi_t*) m; | |||
| int err; | |||
| debug_log("midi: start\n"); | |||
| if (!self->seq) | |||
| return -EBADF; | |||
| if (self->keep_walking) | |||
| return -EALREADY; | |||
| snd_seq_connect_from(self->seq, self->port_id, SND_SEQ_CLIENT_SYSTEM, SND_SEQ_PORT_SYSTEM_ANNOUNCE); | |||
| snd_seq_drop_input(self->seq); | |||
| add_existing_ports(self); | |||
| update_ports(self); | |||
| add_ports(&self->stream[PORT_INPUT]); | |||
| add_ports(&self->stream[PORT_OUTPUT]); | |||
| self->keep_walking = 1; | |||
| if ((err = pthread_create(&self->port_thread, NULL, port_thread, self))) { | |||
| self->keep_walking = 0; | |||
| return -errno; | |||
| } | |||
| return 0; | |||
| } | |||
| static | |||
| int alsa_seqmidi_stop(alsa_midi_t *m) | |||
| { | |||
| alsa_seqmidi_t *self = (alsa_seqmidi_t*) m; | |||
| debug_log("midi: stop\n"); | |||
| if (!self->keep_walking) | |||
| return -EALREADY; | |||
| snd_seq_disconnect_from(self->seq, self->port_id, SND_SEQ_CLIENT_SYSTEM, SND_SEQ_PORT_SYSTEM_ANNOUNCE); | |||
| self->keep_walking = 0; | |||
| sem_post(&self->port_sem); | |||
| pthread_join(self->port_thread, NULL); | |||
| self->port_thread = 0; | |||
| return 0; | |||
| } | |||
| static | |||
| int alsa_connect_from(alsa_seqmidi_t *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))) | |||
| error_log("can't subscribe to %d:%d - %s\n", client, port, snd_strerror(err)); | |||
| return err; | |||
| } | |||
| /* | |||
| * ==================== Port routines ============================= | |||
| */ | |||
| static inline | |||
| int port_hash(snd_seq_addr_t addr) | |||
| { | |||
| return (addr.client + addr.port) % PORT_HASH_SIZE; | |||
| } | |||
| static | |||
| port_t* port_get(port_hash_t hash, snd_seq_addr_t addr) | |||
| { | |||
| port_t **pport = &hash[port_hash(addr)]; | |||
| while (*pport) { | |||
| port_t *port = *pport; | |||
| if (port->remote.client == addr.client && port->remote.port == addr.port) | |||
| return port; | |||
| pport = &port->next; | |||
| } | |||
| return NULL; | |||
| } | |||
| static | |||
| void port_insert(port_hash_t hash, port_t *port) | |||
| { | |||
| port_t **pport = &hash[port_hash(port->remote)]; | |||
| port->next = *pport; | |||
| *pport = port; | |||
| } | |||
| static | |||
| void port_setdead(port_hash_t hash, snd_seq_addr_t addr) | |||
| { | |||
| port_t *port = port_get(hash, addr); | |||
| if (port) | |||
| port->is_dead = 1; // see jack_process | |||
| else | |||
| debug_log("port_setdead: not found (%d:%d)\n", addr.client, addr.port); | |||
| } | |||
| static | |||
| void port_free(alsa_seqmidi_t *self, port_t *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->early_events) | |||
| jack_ringbuffer_free(port->early_events); | |||
| if (port->jack_port) | |||
| jack_port_unregister(self->jack, port->jack_port); | |||
| info_log("port deleted: %s\n", port->name); | |||
| free(port); | |||
| } | |||
| static | |||
| port_t* port_create(alsa_seqmidi_t *self, int type, snd_seq_addr_t addr, const snd_seq_port_info_t *info) | |||
| { | |||
| port_t *port; | |||
| char *c; | |||
| int err; | |||
| port = calloc(1, sizeof(port_t)); | |||
| if (!port) | |||
| return NULL; | |||
| port->remote = addr; | |||
| snprintf(port->name, sizeof(port->name), "%s-%d-%d-%s", | |||
| port_type[type].name, addr.client, addr.port, snd_seq_port_info_get_name(info)); | |||
| // replace all offending characters by - | |||
| for (c = port->name; *c; ++c) | |||
| if (!isalnum(*c)) | |||
| *c = '-'; | |||
| port->jack_port = jack_port_register(self->jack, | |||
| port->name, JACK_DEFAULT_MIDI_TYPE, port_type[type].jack_caps, 0); | |||
| if (!port->jack_port) | |||
| goto failed; | |||
| if (type == PORT_INPUT) | |||
| err = 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) | |||
| goto failed; | |||
| port->early_events = jack_ringbuffer_create(MAX_EVENT_SIZE*16); | |||
| info_log("port created: %s\n", port->name); | |||
| return port; | |||
| failed: | |||
| port_free(self, port); | |||
| return NULL; | |||
| } | |||
| /* | |||
| * ==================== Port add/del handling thread ============================== | |||
| */ | |||
| static | |||
| void update_port_type(alsa_seqmidi_t *self, int type, snd_seq_addr_t addr, int caps, const snd_seq_port_info_t *info) | |||
| { | |||
| stream_t *str = &self->stream[type]; | |||
| int alsa_mask = port_type[type].alsa_mask; | |||
| port_t *port = port_get(str->ports, addr); | |||
| debug_log("update_port_type(%d:%d)\n", addr.client, addr.port); | |||
| if (port && (caps & alsa_mask)!=alsa_mask) { | |||
| debug_log("setdead: %s\n", port->name); | |||
| port->is_dead = 1; | |||
| } | |||
| if (!port && (caps & alsa_mask)==alsa_mask) { | |||
| assert (jack_ringbuffer_write_space(str->new_ports) >= sizeof(port)); | |||
| port = port_create(self, type, addr, info); | |||
| if (port) | |||
| jack_ringbuffer_write(str->new_ports, (char*)&port, sizeof(port)); | |||
| } | |||
| } | |||
| static | |||
| void update_port(alsa_seqmidi_t *self, snd_seq_addr_t addr, const snd_seq_port_info_t *info) | |||
| { | |||
| unsigned int port_caps = snd_seq_port_info_get_capability(info); | |||
| if (port_caps & SND_SEQ_PORT_CAP_NO_EXPORT) | |||
| return; | |||
| update_port_type(self, PORT_INPUT, addr, port_caps, info); | |||
| update_port_type(self, PORT_OUTPUT, addr, port_caps, info); | |||
| } | |||
| static | |||
| void free_ports(alsa_seqmidi_t *self, jack_ringbuffer_t *ports) | |||
| { | |||
| port_t *port; | |||
| int sz; | |||
| while ((sz = jack_ringbuffer_read(ports, (char*)&port, sizeof(port)))) { | |||
| assert (sz == sizeof(port)); | |||
| port_free(self, port); | |||
| } | |||
| } | |||
| static | |||
| void update_ports(alsa_seqmidi_t *self) | |||
| { | |||
| snd_seq_addr_t addr; | |||
| int size; | |||
| while ((size = jack_ringbuffer_read(self->port_add, (char*)&addr, sizeof(addr)))) { | |||
| 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) { | |||
| update_port(self, addr, info); | |||
| } else { | |||
| //port_setdead(self->stream[PORT_INPUT].ports, addr); | |||
| //port_setdead(self->stream[PORT_OUTPUT].ports, addr); | |||
| } | |||
| } | |||
| } | |||
| static | |||
| void* port_thread(void *arg) | |||
| { | |||
| alsa_seqmidi_t *self = arg; | |||
| while (self->keep_walking) { | |||
| sem_wait(&self->port_sem); | |||
| free_ports(self, self->port_del); | |||
| update_ports(self); | |||
| } | |||
| debug_log("port_thread exited\n"); | |||
| return NULL; | |||
| } | |||
| static | |||
| void add_existing_ports(alsa_seqmidi_t *self) | |||
| { | |||
| snd_seq_addr_t addr; | |||
| snd_seq_client_info_t *client_info; | |||
| snd_seq_port_info_t *port_info; | |||
| 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); | |||
| update_port(self, addr, port_info); | |||
| } | |||
| } | |||
| } | |||
| /* | |||
| * =================== Input/output port handling ========================= | |||
| */ | |||
| static | |||
| void set_process_info(struct process_info *info, alsa_seqmidi_t *self, int dir, jack_nframes_t nframes) | |||
| { | |||
| const snd_seq_real_time_t* alsa_time; | |||
| snd_seq_queue_status_t *status; | |||
| snd_seq_queue_status_alloca(&status); | |||
| info->dir = dir; | |||
| info->period_start = jack_last_frame_time(self->jack); | |||
| info->nframes = nframes; | |||
| info->sample_rate = jack_get_sample_rate(self->jack); | |||
| info->cur_frames = jack_frame_time(self->jack); | |||
| // immediately get alsa'a real time (uhh, why everybody has their on 'real' time) | |||
| snd_seq_get_queue_status(self->seq, self->queue, status); | |||
| alsa_time = snd_seq_queue_status_get_real_time(status); | |||
| info->alsa_time = alsa_time->tv_sec * NSEC_PER_SEC + alsa_time->tv_nsec; | |||
| } | |||
| static | |||
| void add_ports(stream_t *str) | |||
| { | |||
| port_t *port; | |||
| while (jack_ringbuffer_read(str->new_ports, (char*)&port, sizeof(port))) { | |||
| debug_log("jack: inserted port %s\n", port->name); | |||
| port_insert(str->ports, port); | |||
| } | |||
| } | |||
| static | |||
| void jack_process(alsa_seqmidi_t *self, struct process_info *info) | |||
| { | |||
| stream_t *str = &self->stream[info->dir]; | |||
| port_jack_func process = port_type[info->dir].jack_func; | |||
| int i, del=0; | |||
| add_ports(str); | |||
| // process ports | |||
| for (i=0; i<PORT_HASH_SIZE; ++i) { | |||
| port_t **pport = &str->ports[i]; | |||
| while (*pport) { | |||
| port_t *port = *pport; | |||
| port->jack_buf = jack_port_get_buffer(port->jack_port, info->nframes); | |||
| if (info->dir == PORT_INPUT) | |||
| jack_midi_clear_buffer(port->jack_buf); | |||
| if (!port->is_dead) | |||
| (*process)(self, port, info); | |||
| else if (jack_ringbuffer_write_space(self->port_del) >= sizeof(port)) { | |||
| debug_log("jack: removed port %s\n", port->name); | |||
| *pport = port->next; | |||
| jack_ringbuffer_write(self->port_del, (char*)&port, sizeof(port)); | |||
| del++; | |||
| continue; | |||
| } | |||
| pport = &port->next; | |||
| } | |||
| } | |||
| if (del) | |||
| sem_post(&self->port_sem); | |||
| } | |||
| /* | |||
| * ============================ Input ============================== | |||
| */ | |||
| static | |||
| void do_jack_input(alsa_seqmidi_t *self, port_t *port, struct process_info *info) | |||
| { | |||
| // process port->early_events | |||
| alsa_midi_event_t ev; | |||
| while (jack_ringbuffer_read(port->early_events, (char*)&ev, sizeof(ev))) { | |||
| jack_midi_data_t* buf; | |||
| jack_nframes_t time = ev.time - info->period_start; | |||
| if (time < 0) | |||
| time = 0; | |||
| else if (time >= info->nframes) | |||
| time = info->nframes - 1; | |||
| buf = jack_midi_event_reserve(port->jack_buf, time, ev.size); | |||
| if (buf) | |||
| jack_ringbuffer_read(port->early_events, (char*)buf, ev.size); | |||
| else | |||
| jack_ringbuffer_read_advance(port->early_events, ev.size); | |||
| debug_log("input: it's time for %d bytes at %d\n", ev.size, time); | |||
| } | |||
| } | |||
| static | |||
| void port_event(alsa_seqmidi_t *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) { | |||
| assert (jack_ringbuffer_write_space(self->port_add) >= sizeof(addr)); | |||
| debug_log("port_event: add/change %d:%d\n", addr.client, addr.port); | |||
| jack_ringbuffer_write(self->port_add, (char*)&addr, sizeof(addr)); | |||
| sem_post(&self->port_sem); | |||
| } else if (ev->type == SND_SEQ_EVENT_PORT_EXIT) { | |||
| debug_log("port_event: del %d:%d\n", addr.client, addr.port); | |||
| port_setdead(self->stream[PORT_INPUT].ports, addr); | |||
| port_setdead(self->stream[PORT_OUTPUT].ports, addr); | |||
| } | |||
| } | |||
| static | |||
| void input_event(alsa_seqmidi_t *self, snd_seq_event_t *alsa_event, struct process_info* info) | |||
| { | |||
| jack_midi_data_t data[MAX_EVENT_SIZE]; | |||
| stream_t *str = &self->stream[PORT_INPUT]; | |||
| long size; | |||
| int64_t alsa_time, time_offset; | |||
| int64_t frame_offset, event_frame; | |||
| port_t *port; | |||
| port = port_get(str->ports, alsa_event->source); | |||
| if (!port) | |||
| 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] == 0x90 && data[2] == 0x00) { | |||
| data[0] = 0x80; | |||
| data[2] = 0x40; | |||
| } | |||
| alsa_time = alsa_event->time.time.tv_sec * NSEC_PER_SEC + alsa_event->time.time.tv_nsec; | |||
| time_offset = info->alsa_time - alsa_time; | |||
| frame_offset = (info->sample_rate * time_offset) / NSEC_PER_SEC; | |||
| event_frame = (int64_t)info->cur_frames - info->period_start - frame_offset + info->nframes; | |||
| debug_log("input: %d bytes at event_frame=%d\n", (int)size, (int)event_frame); | |||
| if (event_frame >= info->nframes && | |||
| jack_ringbuffer_write_space(port->early_events) >= (sizeof(alsa_midi_event_t) + size)) { | |||
| alsa_midi_event_t ev; | |||
| ev.time = event_frame + info->period_start; | |||
| ev.size = size; | |||
| jack_ringbuffer_write(port->early_events, (char*)&ev, sizeof(ev)); | |||
| jack_ringbuffer_write(port->early_events, (char*)data, size); | |||
| debug_log("postponed to next frame +%d\n", (int) (event_frame - info->nframes)); | |||
| return; | |||
| } | |||
| if (event_frame < 0) | |||
| event_frame = 0; | |||
| else if (event_frame >= info->nframes) | |||
| event_frame = info->nframes - 1; | |||
| jack_midi_event_write(port->jack_buf, event_frame, data, size); | |||
| } | |||
| static | |||
| void alsa_seqmidi_read(alsa_midi_t *m, jack_nframes_t nframes) | |||
| { | |||
| alsa_seqmidi_t *self = (alsa_seqmidi_t*) m; | |||
| int res; | |||
| snd_seq_event_t *event; | |||
| struct process_info info; | |||
| if (!self->keep_walking) | |||
| return; | |||
| set_process_info(&info, self, PORT_INPUT, nframes); | |||
| jack_process(self, &info); | |||
| while ((res = snd_seq_event_input(self->seq, &event))>0) { | |||
| if (event->source.client == SND_SEQ_CLIENT_SYSTEM) | |||
| port_event(self, event); | |||
| else | |||
| input_event(self, event, &info); | |||
| } | |||
| } | |||
| /* | |||
| * ============================ Output ============================== | |||
| */ | |||
| static | |||
| void do_jack_output(alsa_seqmidi_t *self, port_t *port, struct process_info* info) | |||
| { | |||
| stream_t *str = &self->stream[info->dir]; | |||
| int nevents = jack_midi_get_event_count(port->jack_buf); | |||
| int i; | |||
| for (i=0; i<nevents; ++i) { | |||
| jack_midi_event_t jack_event; | |||
| snd_seq_event_t alsa_event; | |||
| jack_nframes_t frame_offset; | |||
| int64_t out_time; | |||
| snd_seq_real_time_t out_rt; | |||
| int err; | |||
| jack_midi_event_get(&jack_event, port->jack_buf, i); | |||
| snd_seq_ev_clear(&alsa_event); | |||
| snd_midi_event_reset_encode(str->codec); | |||
| if (!snd_midi_event_encode(str->codec, jack_event.buffer, 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, port->remote.client, port->remote.port); | |||
| frame_offset = jack_event.time + info->period_start + info->nframes - info->cur_frames; | |||
| out_time = info->alsa_time + (frame_offset * NSEC_PER_SEC) / info->sample_rate; | |||
| // we should use absolute time to prevent reordering caused by rounding errors | |||
| if (out_time < port->last_out_time) | |||
| out_time = port->last_out_time; | |||
| else | |||
| port->last_out_time = out_time; | |||
| out_rt.tv_nsec = out_time % NSEC_PER_SEC; | |||
| out_rt.tv_sec = out_time / NSEC_PER_SEC; | |||
| snd_seq_ev_schedule_real(&alsa_event, self->queue, 0, &out_rt); | |||
| err = snd_seq_event_output(self->seq, &alsa_event); | |||
| debug_log("alsa_out: written %d bytes to %s at +%d: %d\n", jack_event.size, port->name, (int)frame_offset, err); | |||
| } | |||
| } | |||
| static | |||
| void alsa_seqmidi_write(alsa_midi_t *m, jack_nframes_t nframes) | |||
| { | |||
| alsa_seqmidi_t *self = (alsa_seqmidi_t*) m; | |||
| struct process_info info; | |||
| if (!self->keep_walking) | |||
| return; | |||
| set_process_info(&info, self, PORT_OUTPUT, nframes); | |||
| jack_process(self, &info); | |||
| snd_seq_drain_output(self->seq); | |||
| } | |||
| @@ -0,0 +1,46 @@ | |||
| /* | |||
| * Copyright (c) 2006,2007 Dmitry S. Baikov | |||
| * | |||
| * 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; either version 2 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This program is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU 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 __jack_midi_pack_h__ | |||
| #define __jack_midi_pack_h__ | |||
| typedef struct { | |||
| int running_status; | |||
| } midi_pack_t; | |||
| static inline | |||
| void midi_pack_reset(midi_pack_t *p) | |||
| { | |||
| p->running_status = 0; | |||
| } | |||
| static | |||
| void midi_pack_event(midi_pack_t *p, jack_midi_event_t *e) | |||
| { | |||
| if (e->buffer[0] >= 0x80 && e->buffer[0] < 0xF0) { // Voice Message | |||
| if (e->buffer[0] == p->running_status) { | |||
| e->buffer++; | |||
| e->size--; | |||
| } else | |||
| p->running_status = e->buffer[0]; | |||
| } else if (e->buffer[0] < 0xF8) { // not System Realtime | |||
| p->running_status = 0; | |||
| } | |||
| } | |||
| #endif /* __jack_midi_pack_h__ */ | |||
| @@ -0,0 +1,139 @@ | |||
| /* | |||
| * Copyright (c) 2006,2007 Dmitry S. Baikov | |||
| * | |||
| * 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; either version 2 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This program is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU 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 __jack_midi_unpack_h__ | |||
| #define __jack_midi_unpack_h__ | |||
| enum { | |||
| MIDI_UNPACK_MAX_MSG = 1024 | |||
| }; | |||
| typedef struct { | |||
| int pos, need, size; | |||
| unsigned char data[MIDI_UNPACK_MAX_MSG]; | |||
| } midi_unpack_t; | |||
| static inline | |||
| void midi_unpack_init(midi_unpack_t *u) | |||
| { | |||
| u->pos = 0; | |||
| u->size = sizeof(u->data); | |||
| u->need = u->size; | |||
| } | |||
| static inline | |||
| void midi_unpack_reset(midi_unpack_t *u) | |||
| { | |||
| u->pos = 0; | |||
| u->need = u->size; | |||
| } | |||
| static const unsigned char midi_voice_len[] = { | |||
| 3, /*0x80 Note Off*/ | |||
| 3, /*0x90 Note On*/ | |||
| 3, /*0xA0 Aftertouch*/ | |||
| 3, /*0xB0 Control Change*/ | |||
| 2, /*0xC0 Program Change*/ | |||
| 2, /*0xD0 Channel Pressure*/ | |||
| 3, /*0xE0 Pitch Wheel*/ | |||
| 1 /*0xF0 System*/ | |||
| }; | |||
| static const unsigned char midi_system_len[] = { | |||
| 0, /*0xF0 System Exclusive Start*/ | |||
| 2, /*0xF1 MTC Quarter Frame*/ | |||
| 3, /*0xF2 Song Postion*/ | |||
| 2, /*0xF3 Song Select*/ | |||
| 0, /*0xF4 undefined*/ | |||
| 0, /*0xF5 undefined*/ | |||
| 1, /*0xF6 Tune Request*/ | |||
| 1 /*0xF7 System Exlusive End*/ | |||
| }; | |||
| static | |||
| int midi_unpack_buf(midi_unpack_t *buf, const unsigned char *data, int len, void *jack_port_buf, jack_nframes_t time) | |||
| { | |||
| int i; | |||
| for (i=0; i<len; ++i) | |||
| { | |||
| const unsigned char byte = data[i]; | |||
| if (byte >= 0xF8) // system realtime | |||
| { | |||
| jack_midi_event_write(jack_port_buf, time, &data[i], 1); | |||
| //printf("midi_unpack: written system relatime event\n"); | |||
| //midi_input_write(in, &data[i], 1); | |||
| } | |||
| else if (byte < 0x80) // data | |||
| { | |||
| assert (buf->pos < buf->size); | |||
| buf->data[buf->pos++] = byte; | |||
| } | |||
| else if (byte < 0xF0) // voice | |||
| { | |||
| assert (byte >= 0x80 && byte < 0xF0); | |||
| //buf->need = ((byte|0x0F) == 0xCF || (byte|0x0F)==0xDF) ? 2 : 3; | |||
| buf->need = midi_voice_len[(byte-0x80)>>4]; | |||
| buf->data[0] = byte; | |||
| buf->pos = 1; | |||
| } | |||
| else if (byte == 0xF7) // sysex end | |||
| { | |||
| assert (buf->pos < buf->size); | |||
| buf->data[buf->pos++] = byte; | |||
| buf->need = buf->pos; | |||
| } | |||
| else | |||
| { | |||
| assert (byte >= 0xF0 && byte < 0xF8); | |||
| buf->pos = 1; | |||
| buf->data[0] = byte; | |||
| buf->need = midi_system_len[byte - 0xF0]; | |||
| if (!buf->need) | |||
| buf->need = buf->size; | |||
| } | |||
| if (buf->pos == buf->need) | |||
| { | |||
| // TODO: deal with big sysex'es (they are silently dropped for now) | |||
| if (buf->data[0] >= 0x80 || (buf->data[0]==0xF0 && buf->data[buf->pos-1] == 0xF7)) { | |||
| /* convert Note On with velocity 0 to Note Off */ | |||
| if ((buf->data[0] & 0xF0) == 0x90 && buf->data[2] == 0) { | |||
| // we use temp array here to keep running status in sync | |||
| jack_midi_data_t temp[3] = { 0x80, 0, 0x40 }; | |||
| temp[0] |= buf->data[0] & 0x0F; | |||
| temp[1] = buf->data[1]; | |||
| jack_midi_event_write(jack_port_buf, time, temp, 3); | |||
| } else | |||
| jack_midi_event_write(jack_port_buf, time, &buf->data[0], buf->pos); | |||
| //printf("midi_unpack: written %d-byte event\n", buf->pos); | |||
| //midi_input_write(in, &buf->data[0], buf->pos); | |||
| } | |||
| /* keep running status */ | |||
| if (buf->data[0] >= 0x80 && buf->data[0] < 0xF0) | |||
| buf->pos = 1; | |||
| else | |||
| { | |||
| buf->pos = 0; | |||
| buf->need = buf->size; | |||
| } | |||
| } | |||
| } | |||
| assert (i==len); | |||
| return i; | |||
| } | |||
| #endif /* __jack_midi_unpack_h__ */ | |||
| @@ -21,3 +21,6 @@ inprocess.la | |||
| inprocess.lo | |||
| intime.la | |||
| intime.lo | |||
| jack_alias | |||
| jack_evmon | |||
| jack_thread_wait | |||
| @@ -45,7 +45,7 @@ int process(jack_nframes_t nframes, void *arg) | |||
| int i,j; | |||
| void* port_buf = jack_port_get_buffer(output_port, nframes); | |||
| unsigned char* buffer; | |||
| jack_midi_clear_buffer(port_buf, nframes); | |||
| jack_midi_clear_buffer(port_buf); | |||
| /*memset(buffer, 0, nframes*sizeof(jack_default_audio_sample_t));*/ | |||
| for(i=0; i<nframes; i++) | |||
| @@ -54,7 +54,7 @@ int process(jack_nframes_t nframes, void *arg) | |||
| { | |||
| if(note_starts[j] == loop_index) | |||
| { | |||
| buffer = jack_midi_event_reserve(port_buf, i, 3, nframes); | |||
| buffer = jack_midi_event_reserve(port_buf, i, 3); | |||
| /* printf("wrote a note on, port buffer = 0x%x, event buffer = 0x%x\n", port_buf, buffer);*/ | |||
| buffer[2] = 64; /* velocity */ | |||
| buffer[1] = note_frqs[j]; | |||
| @@ -62,7 +62,7 @@ int process(jack_nframes_t nframes, void *arg) | |||
| } | |||
| else if(note_starts[j] + note_lengths[j] == loop_index) | |||
| { | |||
| buffer = jack_midi_event_reserve(port_buf, i, 3, nframes); | |||
| buffer = jack_midi_event_reserve(port_buf, i, 3); | |||
| /* printf("wrote a note off, port buffer = 0x%x, event buffer = 0x%x\n", port_buf, buffer);*/ | |||
| buffer[2] = 64; /* velocity */ | |||
| buffer[1] = note_frqs[j]; | |||
| @@ -49,18 +49,18 @@ int process(jack_nframes_t nframes, void *arg) | |||
| jack_default_audio_sample_t *out = (jack_default_audio_sample_t *) jack_port_get_buffer (output_port, nframes); | |||
| jack_midi_event_t in_event; | |||
| jack_nframes_t event_index = 0; | |||
| jack_nframes_t event_count = jack_midi_get_event_count(port_buf, nframes); | |||
| jack_nframes_t event_count = jack_midi_get_event_count(port_buf); | |||
| if(event_count > 1) | |||
| { | |||
| printf(" midisine: have %d events\n", event_count); | |||
| for(i=0; i<event_count; i++) | |||
| { | |||
| jack_midi_event_get(&in_event, port_buf, i, nframes); | |||
| jack_midi_event_get(&in_event, port_buf, i); | |||
| printf(" event %d time is %d. 1st byte is 0x%x\n", i, in_event.time, *(in_event.buffer)); | |||
| } | |||
| /* printf("1st byte of 1st event addr is %p\n", in_events[0].buffer);*/ | |||
| } | |||
| jack_midi_event_get(&in_event, port_buf, 0, nframes); | |||
| jack_midi_event_get(&in_event, port_buf, 0); | |||
| for(i=0; i<nframes; i++) | |||
| { | |||
| if((in_event.time == i) && (event_index < event_count)) | |||
| @@ -79,7 +79,7 @@ int process(jack_nframes_t nframes, void *arg) | |||
| } | |||
| event_index++; | |||
| if(event_index < event_count) | |||
| jack_midi_event_get(&in_event, port_buf, event_index, nframes); | |||
| jack_midi_event_get(&in_event, port_buf, event_index); | |||
| } | |||
| ramp += note_frqs[note]; | |||
| ramp = (ramp > 1.0) ? ramp - 2.0 : ramp; | |||
| @@ -45,12 +45,10 @@ typedef struct _jack_midi_event | |||
| /* Get number of events in a port buffer. | |||
| * | |||
| * @param port_buffer Port buffer from which to retrieve event. | |||
| * @param nframes Number of valid frames this cycle. | |||
| * @return number of events inside @a port_buffer | |||
| */ | |||
| jack_nframes_t | |||
| jack_midi_get_event_count(void* port_buffer, | |||
| jack_nframes_t nframes); | |||
| jack_midi_get_event_count(void* port_buffer); | |||
| /** Get a MIDI event from an event port buffer. | |||
| @@ -62,14 +60,12 @@ jack_midi_get_event_count(void* port_buffer, | |||
| * @param event Event structure to store retrieved event in. | |||
| * @param port_buffer Port buffer from which to retrieve event. | |||
| * @param event_index Index of event to retrieve. | |||
| * @param nframes Number of valid frames this cycle. | |||
| * @return 0 on success, ENODATA if buffer is empty. | |||
| */ | |||
| int | |||
| jack_midi_event_get(jack_midi_event_t *event, | |||
| void *port_buffer, | |||
| jack_nframes_t event_index, | |||
| jack_nframes_t nframes); | |||
| jack_nframes_t event_index); | |||
| /** Clear an event buffer. | |||
| @@ -79,11 +75,9 @@ jack_midi_event_get(jack_midi_event_t *event, | |||
| * function may not be called on an input port's buffer. | |||
| * | |||
| * @param port_buffer Port buffer to clear (must be an output port buffer). | |||
| * @param nframes Number of valid frames this cycle. | |||
| */ | |||
| void | |||
| jack_midi_clear_buffer(void *port_buffer, | |||
| jack_nframes_t nframes); | |||
| jack_midi_clear_buffer(void *port_buffer); | |||
| /** Get the size of the largest event that can be stored by the port. | |||
| @@ -92,10 +86,9 @@ jack_midi_clear_buffer(void *port_buffer, | |||
| * events already stored in the port. | |||
| * | |||
| * @param port_buffer Port buffer to check size of. | |||
| * @param nframes Number of valid frames this cycle. | |||
| */ | |||
| size_t | |||
| jack_midi_max_event_size(void* port_buffer, jack_nframes_t nframes); | |||
| jack_midi_max_event_size(void* port_buffer); | |||
| /** Allocate space for an event to be written to an event port buffer. | |||
| @@ -110,15 +103,13 @@ jack_midi_max_event_size(void* port_buffer, jack_nframes_t nframes); | |||
| * @param port_buffer Buffer to write event to. | |||
| * @param time Sample offset of event. | |||
| * @param data_size Length of event's raw data in bytes. | |||
| * @param nframes Number of valid frames this event. | |||
| * @return Pointer to the beginning of the reserved event's data buffer, or | |||
| * NULL on error (ie not enough space). | |||
| */ | |||
| jack_midi_data_t* | |||
| jack_midi_event_reserve(void *port_buffer, | |||
| jack_nframes_t time, | |||
| size_t data_size, | |||
| jack_nframes_t nframes); | |||
| size_t data_size); | |||
| /** Write an event into an event port buffer. | |||
| @@ -131,15 +122,13 @@ jack_midi_event_reserve(void *port_buffer, | |||
| * @param time Sample offset of event. | |||
| * @param data Message data to be written. | |||
| * @param data_size Length of @a data in bytes. | |||
| * @param nframes Number of valid frames this event. | |||
| * @return 0 on success, ENOBUFS if there's not enough space in buffer for event. | |||
| */ | |||
| int | |||
| jack_midi_event_write(void *port_buffer, | |||
| jack_nframes_t time, | |||
| const jack_midi_data_t *data, | |||
| size_t data_size, | |||
| jack_nframes_t nframes); | |||
| size_t data_size); | |||
| /** Get the number of events that could not be written to @a port_buffer. | |||
| @@ -148,12 +137,10 @@ jack_midi_event_write(void *port_buffer, | |||
| * Currently the only way this can happen is if events are lost on port mixdown. | |||
| * | |||
| * @param port_buffer Port to receive count for. | |||
| * @param nframes Number of valid frames this cycle. | |||
| * @returns Number of events that could not be written to @a port_buffer. | |||
| */ | |||
| jack_nframes_t | |||
| jack_midi_get_lost_event_count(void *port_buffer, | |||
| jack_nframes_t nframes); | |||
| jack_midi_get_lost_event_count(void *port_buffer); | |||
| #ifdef __cplusplus | |||
| @@ -120,7 +120,7 @@ typedef struct _jack_port_functions { | |||
| * A better solution is to make jack_engine_place_buffers to be type-specific, | |||
| * but this works. | |||
| */ | |||
| void (*buffer_init)(void *buffer, size_t size); | |||
| void (*buffer_init)(void *buffer, size_t size, jack_nframes_t); | |||
| /* Function to mixdown multiple inputs to a buffer. Can be NULL, | |||
| * indicating that multiple input connections are not legal for | |||
| @@ -332,7 +332,8 @@ jack_engine_place_port_buffers (jack_engine_t* engine, | |||
| jack_port_type_id_t ptid, | |||
| jack_shmsize_t one_buffer, | |||
| jack_shmsize_t size, | |||
| unsigned long nports) | |||
| unsigned long nports, | |||
| jack_nframes_t nframes) | |||
| { | |||
| jack_shmsize_t offset; /* shared memory offset */ | |||
| jack_port_buffer_info_t *bi; | |||
| @@ -408,7 +409,7 @@ jack_engine_place_port_buffers (jack_engine_t* engine, | |||
| bi = pti->info; | |||
| for (i=0; i<nports; ++i, ++bi) | |||
| pfuncs->buffer_init(shm_segment + bi->offset, one_buffer); | |||
| pfuncs->buffer_init(shm_segment + bi->offset, one_buffer, nframes); | |||
| } | |||
| pthread_mutex_unlock (&pti->lock); | |||
| @@ -466,7 +467,7 @@ jack_resize_port_segment (jack_engine_t *engine, | |||
| } | |||
| } | |||
| jack_engine_place_port_buffers (engine, ptid, one_buffer, size, nports); | |||
| jack_engine_place_port_buffers (engine, ptid, one_buffer, size, nports, engine->control->buffer_size); | |||
| #ifdef USE_MLOCK | |||
| if (engine->control->real_time) { | |||
| @@ -3475,12 +3476,19 @@ jack_port_do_register (jack_engine_t *engine, jack_request_t *req) | |||
| req->x.port_info.client_id)) | |||
| == NULL) { | |||
| jack_error ("unknown client id in port registration request"); | |||
| jack_unlock_graph (engine); | |||
| return -1; | |||
| } | |||
| if ((port = jack_get_port_by_name(engine, req->x.port_info.name)) != NULL) { | |||
| jack_error ("duplicate port name in port registration request"); | |||
| jack_unlock_graph (engine); | |||
| return -1; | |||
| } | |||
| jack_unlock_graph (engine); | |||
| if ((port_id = jack_get_free_port (engine)) == (jack_port_id_t) -1) { | |||
| jack_error ("no ports available!"); | |||
| jack_unlock_graph (engine); | |||
| return -1; | |||
| } | |||
| @@ -3501,10 +3509,11 @@ jack_port_do_register (jack_engine_t *engine, jack_request_t *req) | |||
| if (jack_port_assign_buffer (engine, port)) { | |||
| jack_error ("cannot assign buffer for port"); | |||
| jack_port_release (engine, &engine->internal_ports[port_id]); | |||
| jack_unlock_graph (engine); | |||
| return -1; | |||
| } | |||
| jack_lock_graph (engine); | |||
| client->ports = jack_slist_prepend (client->ports, port); | |||
| jack_port_registration_notify (engine, port_id, TRUE); | |||
| jack_unlock_graph (engine); | |||
| @@ -28,6 +28,7 @@ | |||
| typedef struct _jack_midi_port_info_private { | |||
| jack_nframes_t nframes; /**< Number of frames in buffer */ | |||
| size_t buffer_size; /**< Size of buffer in bytes */ | |||
| jack_nframes_t event_count; /**< Number of events stored in this buffer */ | |||
| jack_nframes_t last_write_loc; /**< Used for both writing and mixdown */ | |||
| @@ -44,11 +45,13 @@ typedef struct _jack_midi_port_internal_event { | |||
| /* jack_midi_port_functions.buffer_init */ | |||
| static void | |||
| jack_midi_buffer_init(void *port_buffer, | |||
| size_t buffer_size) | |||
| size_t buffer_size, | |||
| jack_nframes_t nframes) | |||
| { | |||
| jack_midi_port_info_private_t *info = | |||
| (jack_midi_port_info_private_t *) port_buffer; | |||
| /* We can also add some magic field to midi buffer to validate client calls */ | |||
| info->nframes = nframes; | |||
| info->buffer_size = buffer_size; | |||
| info->event_count = 0; | |||
| info->last_write_loc = 0; | |||
| @@ -57,8 +60,7 @@ jack_midi_buffer_init(void *port_buffer, | |||
| jack_nframes_t | |||
| jack_midi_get_event_count(void *port_buffer, | |||
| jack_nframes_t nframes) | |||
| jack_midi_get_event_count(void *port_buffer) | |||
| { | |||
| jack_midi_port_info_private_t *info = | |||
| (jack_midi_port_info_private_t *) port_buffer; | |||
| @@ -69,8 +71,7 @@ jack_midi_get_event_count(void *port_buffer, | |||
| int | |||
| jack_midi_event_get(jack_midi_event_t *event, | |||
| void *port_buffer, | |||
| jack_nframes_t event_idx, | |||
| jack_nframes_t nframes) | |||
| jack_nframes_t event_idx) | |||
| { | |||
| jack_midi_port_internal_event_t *port_event; | |||
| jack_midi_port_info_private_t *info = | |||
| @@ -91,8 +92,7 @@ jack_midi_event_get(jack_midi_event_t *event, | |||
| size_t | |||
| jack_midi_max_event_size(void *port_buffer, | |||
| jack_nframes_t nframes) | |||
| jack_midi_max_event_size(void *port_buffer) | |||
| { | |||
| jack_midi_port_info_private_t *info = | |||
| (jack_midi_port_info_private_t *) port_buffer; | |||
| @@ -116,8 +116,7 @@ jack_midi_max_event_size(void *port_buffer, | |||
| jack_midi_data_t* | |||
| jack_midi_event_reserve(void *port_buffer, | |||
| jack_nframes_t time, | |||
| size_t data_size, | |||
| jack_nframes_t nframes) | |||
| size_t data_size) | |||
| { | |||
| jack_midi_data_t *retbuf = (jack_midi_data_t *) port_buffer; | |||
| @@ -128,11 +127,11 @@ jack_midi_event_reserve(void *port_buffer, | |||
| size_t buffer_size = | |||
| info->buffer_size; | |||
| if (time < 0 || time >= nframes) | |||
| return NULL; | |||
| if (time < 0 || time >= info->nframes) | |||
| goto failed; | |||
| if (info->event_count > 0 && time < event_buffer[info->event_count-1].time) | |||
| return NULL; | |||
| goto failed; | |||
| /* Check if data_size is >0 and there is enough space in the buffer for the event. */ | |||
| if (data_size <=0 || | |||
| @@ -140,7 +139,7 @@ jack_midi_event_reserve(void *port_buffer, | |||
| + ((info->event_count + 1) | |||
| * sizeof(jack_midi_port_internal_event_t)) | |||
| + data_size > buffer_size) { | |||
| return NULL; | |||
| goto failed; | |||
| } else { | |||
| info->last_write_loc += data_size; | |||
| retbuf = &retbuf[buffer_size - 1 - info->last_write_loc]; | |||
| @@ -151,6 +150,9 @@ jack_midi_event_reserve(void *port_buffer, | |||
| info->event_count += 1; | |||
| return retbuf; | |||
| } | |||
| failed: | |||
| info->events_lost++; | |||
| return NULL; | |||
| } | |||
| @@ -158,11 +160,10 @@ int | |||
| jack_midi_event_write(void *port_buffer, | |||
| jack_nframes_t time, | |||
| const jack_midi_data_t *data, | |||
| size_t data_size, | |||
| jack_nframes_t nframes) | |||
| size_t data_size) | |||
| { | |||
| jack_midi_data_t *retbuf = | |||
| jack_midi_event_reserve(port_buffer, time, data_size, nframes); | |||
| jack_midi_event_reserve(port_buffer, time, data_size); | |||
| if (retbuf) { | |||
| memcpy(retbuf, data, data_size); | |||
| @@ -179,8 +180,7 @@ jack_midi_event_write(void *port_buffer, | |||
| * been reset. | |||
| */ | |||
| void | |||
| jack_midi_clear_buffer(void *port_buffer, | |||
| jack_nframes_t nframes) | |||
| jack_midi_clear_buffer(void *port_buffer) | |||
| { | |||
| jack_midi_port_info_private_t *info = | |||
| (jack_midi_port_info_private_t *) port_buffer; | |||
| @@ -193,14 +193,14 @@ jack_midi_clear_buffer(void *port_buffer, | |||
| /* jack_midi_port_functions.mixdown */ | |||
| static void | |||
| jack_midi_port_mixdown(jack_port_t *port, | |||
| jack_nframes_t nframes) | |||
| jack_midi_port_mixdown(jack_port_t *port, jack_nframes_t nframes) | |||
| { | |||
| JSList *node; | |||
| jack_port_t *input; | |||
| jack_nframes_t num_events = 0; | |||
| jack_nframes_t i = 0; | |||
| int err = 0; | |||
| jack_nframes_t lost_events = 0; | |||
| /* The next (single) event to mix in to the buffer */ | |||
| jack_midi_port_info_private_t *earliest_info; | |||
| @@ -211,7 +211,7 @@ jack_midi_port_mixdown(jack_port_t *port, | |||
| jack_midi_port_internal_event_t *in_events; /* Corresponds to in_info */ | |||
| jack_midi_port_info_private_t *out_info; /* Output 'buffer' */ | |||
| jack_midi_clear_buffer(port->mix_buffer, nframes); | |||
| jack_midi_clear_buffer(port->mix_buffer); | |||
| out_info = (jack_midi_port_info_private_t *) port->mix_buffer; | |||
| @@ -228,6 +228,7 @@ jack_midi_port_mixdown(jack_port_t *port, | |||
| in_info = | |||
| (jack_midi_port_info_private_t *) jack_output_port_buffer(input); | |||
| num_events += in_info->event_count; | |||
| lost_events += in_info->events_lost; | |||
| in_info->last_write_loc = 0; | |||
| } | |||
| @@ -267,8 +268,7 @@ jack_midi_port_mixdown(jack_port_t *port, | |||
| jack_port_buffer(port), | |||
| earliest_event->time, | |||
| &earliest_buffer[earliest_event->byte_offset], | |||
| earliest_event->size, | |||
| nframes); | |||
| earliest_event->size); | |||
| earliest_info->last_write_loc++; | |||
| @@ -279,12 +279,14 @@ jack_midi_port_mixdown(jack_port_t *port, | |||
| } | |||
| } | |||
| assert(out_info->event_count == num_events - out_info->events_lost); | |||
| // inherit total lost events count from all connected ports. | |||
| out_info->events_lost += lost_events; | |||
| } | |||
| jack_nframes_t | |||
| jack_midi_get_lost_event_count(void *port_buffer, | |||
| jack_nframes_t nframes) | |||
| jack_midi_get_lost_event_count(void *port_buffer) | |||
| { | |||
| return ((jack_midi_port_info_private_t *) port_buffer)->events_lost; | |||
| } | |||
| @@ -37,7 +37,8 @@ | |||
| #include "local.h" | |||
| static void jack_generic_buffer_init(void *port_buffer, | |||
| size_t buffer_size); | |||
| size_t buffer_size, | |||
| jack_nframes_t nframes); | |||
| static void jack_audio_port_mixdown (jack_port_t *port, | |||
| jack_nframes_t nframes); | |||
| @@ -359,8 +360,8 @@ jack_get_port_functions(jack_port_type_id_t ptid) | |||
| * Fills buffer with zeroes. For audio ports, engine->silent_buffer relies on it. | |||
| */ | |||
| static void | |||
| jack_generic_buffer_init(void *buffer, size_t size) | |||
| { | |||
| jack_generic_buffer_init(void *buffer, size_t size, jack_nframes_t nframes) | |||
| { | |||
| memset(buffer, 0, size); | |||
| } | |||
| @@ -749,7 +750,7 @@ jack_port_get_buffer (jack_port_t *port, jack_nframes_t nframes) | |||
| * sizeof (jack_default_audio_sample_t) | |||
| * nframes; | |||
| port->mix_buffer = jack_pool_alloc (buffer_size); | |||
| port->fptr.buffer_init (port->mix_buffer, buffer_size); | |||
| port->fptr.buffer_init (port->mix_buffer, buffer_size, nframes); | |||
| } | |||
| port->fptr.mixdown (port, nframes); | |||
| return (void *) port->mix_buffer; | |||