From cde1ee8fa147dfd15dc5c5b43093cd8c8a402b74 Mon Sep 17 00:00:00 2001 From: Miller Puckette Date: Wed, 16 Jan 2008 21:54:11 +0000 Subject: 0.41-0 test 11 svn path=/trunk/; revision=9147 --- pd/portmidi/pm_linux/README_LINUX.txt | 15 ++++- pd/portmidi/pm_linux/pmlinux.c | 9 +++ pd/portmidi/pm_linux/pmlinuxalsa.c | 100 +++++++++++++++++++++++++++------- 3 files changed, 101 insertions(+), 23 deletions(-) (limited to 'pd/portmidi/pm_linux') diff --git a/pd/portmidi/pm_linux/README_LINUX.txt b/pd/portmidi/pm_linux/README_LINUX.txt index e8a4332f..d3adf5a9 100644 --- a/pd/portmidi/pm_linux/README_LINUX.txt +++ b/pd/portmidi/pm_linux/README_LINUX.txt @@ -1,9 +1,14 @@ README_LINUX.txt for PortMidi Roger Dannenberg -8 June 2004 +29 Aug 2006 To make PortMidi and PortTime, go back up to the portmidi -directory and type make. +directory and type + +make -f pm_linux/Makefile + +(You can also copy pm_linux/Makefile to the portmidi +directory and just type "make".) The Makefile will build all test programs and the portmidi library. You may want to modify the Makefile to remove the @@ -21,6 +26,12 @@ all test programs in pm_test seem to run properly. CHANGELOG +29-aug-2006 Roger B. Dannenberg + Fixed PortTime to join with time thread for clean exit. + +28-aug-2006 Roger B. Dannenberg + Updated this documentation. + 08-Jun-2004 Roger B. Dannenberg Updated code to use new system abstraction. diff --git a/pd/portmidi/pm_linux/pmlinux.c b/pd/portmidi/pm_linux/pmlinux.c index 8c70319f..dcb2b8b2 100644 --- a/pd/portmidi/pm_linux/pmlinux.c +++ b/pd/portmidi/pm_linux/pmlinux.c @@ -5,6 +5,9 @@ be separate from the main portmidi.c file because it is system dependent, and it is separate from, pmlinuxalsa.c, because it might need to register non-alsa devices as well. + + NOTE: if you add non-ALSA support, you need to fix :alsa_poll() + in pmlinuxalsa.c, which assumes all input devices are ALSA. */ #include "stdlib.h" @@ -19,12 +22,18 @@ PmError pm_init() { + /* Note: it is not an error for PMALSA to fail to initialize. + * It may be a design error that the client cannot query what subsystems + * are working properly other than by looking at the list of available + * devices. + */ #ifdef PMALSA pm_linuxalsa_init(); #endif #ifdef PMNULL pm_linuxnull_init(); #endif + return pmNoError; } void pm_term(void) diff --git a/pd/portmidi/pm_linux/pmlinuxalsa.c b/pd/portmidi/pm_linux/pmlinuxalsa.c index 9b0eee75..6132e090 100644 --- a/pd/portmidi/pm_linux/pmlinuxalsa.c +++ b/pd/portmidi/pm_linux/pmlinuxalsa.c @@ -9,6 +9,9 @@ #include "stdlib.h" #include "portmidi.h" +#ifdef NEWBUFFER +#include "pmutil.h" +#endif #include "pminternal.h" #include "pmlinuxalsa.h" #include "string.h" @@ -41,7 +44,8 @@ extern pm_fns_node pm_linuxalsa_in_dictionary; extern pm_fns_node pm_linuxalsa_out_dictionary; -static snd_seq_t *seq; // all input comes here, output queue allocated on seq +static snd_seq_t *seq = NULL; // all input comes here, + // output queue allocated on seq static int queue, queue_used; /* one for all ports, reference counted */ typedef struct alsa_descriptor_struct { @@ -208,7 +212,7 @@ static PmError alsa_write_byte(PmInternal *midi, unsigned char byte, when = (when - now) + midi->latency; if (when < 0) when = 0; VERBOSE printf("timestamp %d now %d latency %d, ", - timestamp, now, midi->latency); + (int) timestamp, (int) now, midi->latency); VERBOSE printf("scheduling event after %d\n", when); /* message is sent in relative ticks, where 1 tick = 1 ms */ snd_seq_ev_schedule_tick(&ev, queue, 1, when); @@ -238,7 +242,6 @@ static PmError alsa_write_byte(PmInternal *midi, unsigned char byte, static PmError alsa_out_close(PmInternal *midi) { - int err; alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor; if (!desc) return pmBadPtr; @@ -252,6 +255,7 @@ static PmError alsa_out_close(PmInternal *midi) } if (midi->latency > 0) alsa_unuse_queue(); snd_midi_event_free(desc->parser); + midi->descriptor = NULL; /* destroy the pointer to signify "closed" */ pm_free(desc); if (pm_hosterror) { get_alsa_error_text(pm_hosterror_text, PM_HOST_ERROR_MSG_LEN, @@ -329,7 +333,6 @@ static PmError alsa_in_open(PmInternal *midi, void *driverInfo) static PmError alsa_in_close(PmInternal *midi) { - int err; alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor; if (!desc) return pmBadPtr; if (pm_hosterror = snd_seq_disconnect_from(seq, desc->this_port, @@ -351,8 +354,30 @@ static PmError alsa_in_close(PmInternal *midi) static PmError alsa_abort(PmInternal *midi) { + /* NOTE: ALSA documentation is vague. This is supposed to + * remove any pending output messages. If you can test and + * confirm this code is correct, please update this comment. -RBD + */ + /* Unfortunately, I can't even compile it -- my ALSA version + * does not implement snd_seq_remove_events_t, so this does + * not compile. I'll try again, but it looks like I'll need to + * upgrade my entire Linux OS -RBD + */ + /* alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor; - /* This is supposed to flush any pending output. */ + snd_seq_remove_events_t info; + snd_seq_addr_t addr; + addr.client = desc->client; + addr.port = desc->port; + snd_seq_remove_events_set_dest(&info, &addr); + snd_seq_remove_events_set_condition(&info, SND_SEQ_REMOVE_DEST); + pm_hosterror = snd_seq_remove_events(seq, &info); + if (pm_hosterror) { + get_alsa_error_text(pm_hosterror_text, PM_HOST_ERROR_MSG_LEN, + pm_hosterror); + return pmHostError; + } + */ printf("WARNING: alsa_abort not implemented\n"); return pmNoError; } @@ -408,10 +433,10 @@ static PmError alsa_write(PmInternal *midi, PmEvent *buffer, long length) #endif -static PmError alsa_write_flush(PmInternal *midi) +static PmError alsa_write_flush(PmInternal *midi, PmTimestamp timestamp) { alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor; - VERBOSE printf("snd_seq_drain_output: 0x%x\n", seq); + VERBOSE printf("snd_seq_drain_output: 0x%x\n", (unsigned int) seq); desc->error = snd_seq_drain_output(seq); if (desc->error < 0) return pmHostError; @@ -583,25 +608,42 @@ static void handle_event(snd_seq_event_t *ev) break; case SND_SEQ_EVENT_SYSEX: { const BYTE *ptr = (const BYTE *) ev->data.ext.ptr; - int i; - long msg = 0; - int shift = 0; - if (!(midi->filters & PM_FILT_SYSEX)) { - for (i = 0; i < ev->data.ext.len; i++) { - pm_read_byte(midi, *ptr++, timestamp); - } - } + /* assume there is one sysex byte to process */ + pm_read_bytes(midi, ptr, ev->data.ext.len, timestamp); break; } } } + static PmError alsa_poll(PmInternal *midi) { snd_seq_event_t *ev; - while (snd_seq_event_input(seq, &ev) >= 0) { - if (ev) { - handle_event(ev); + /* expensive check for input data, gets data from device: */ + while (snd_seq_event_input_pending(seq, TRUE) > 0) { + /* cheap check on local input buffer */ + while (snd_seq_event_input_pending(seq, FALSE) > 0) { + /* check for and ignore errors, e.g. input overflow */ + /* note: if there's overflow, this should be reported + * all the way through to client. Since input from all + * devices is merged, we need to find all input devices + * and set all to the overflow state. + * NOTE: this assumes every input is ALSA based. + */ + int rslt = snd_seq_event_input(seq, &ev); + if (rslt >= 0) { + handle_event(ev); + } else if (rslt == -ENOSPC) { + int i; + for (i = 0; i < pm_descriptor_index; i++) { + if (descriptors[i].pub.input) { + PmInternal *midi = (PmInternal *) + descriptors[i].internalDescriptor; + /* careful, device may not be open! */ + if (midi) Pm_SetOverflow(midi->queue); + } + } + } } } return pmNoError; @@ -677,8 +719,17 @@ PmError pm_linuxalsa_init( void ) snd_seq_port_info_t *pinfo; unsigned int caps; - err = snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK); - if (err < 0) return; + /* Previously, the last parameter was SND_SEQ_NONBLOCK, but this + * would cause messages to be dropped if the ALSA buffer fills up. + * The correct behavior is for writes to block until there is + * room to send all the data. The client should normally allocate + * a large enough buffer to avoid blocking on output. + * Now that blocking is enabled, the seq_event_input() will block + * if there is no input data. This is not what we want, so must + * call seq_event_input_pending() to avoid blocking. + */ + err = snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, 0); + if (err < 0) return err; snd_seq_client_info_alloca(&cinfo); snd_seq_port_info_alloca(&pinfo); @@ -715,10 +766,17 @@ PmError pm_linuxalsa_init( void ) } } } + return pmNoError; } void pm_linuxalsa_term(void) { - snd_seq_close(seq); + if (seq) { + snd_seq_close(seq); + pm_free(descriptors); + descriptors = NULL; + pm_descriptor_index = 0; + pm_descriptor_max = 0; + } } -- cgit v1.2.1