New file |
| | |
| | | --- audacity-Audacity-2.3.2/lib-src/portmidi/porttime/ptlinux.c |
| | | +++ audacity-Audacity-2.3.2/lib-src/portmidi/porttime/ptlinux.c |
| | | @@ -31,14 +31,13 @@ CHANGE LOG |
| | | #include "porttime.h" |
| | | #include "sys/time.h" |
| | | #include "sys/resource.h" |
| | | -#include "sys/timeb.h" |
| | | #include "pthread.h" |
| | | |
| | | #define TRUE 1 |
| | | #define FALSE 0 |
| | | |
| | | static int time_started_flag = FALSE; |
| | | -static struct timeb time_offset = {0, 0, 0, 0}; |
| | | +static struct timespec time_offset = {0, 0}; |
| | | static pthread_t pt_thread_pid; |
| | | static int pt_thread_created = FALSE; |
| | | |
| | | @@ -79,7 +78,7 @@ static void *Pt_CallbackProc(void *p) |
| | | PtError Pt_Start(int resolution, PtCallback *callback, void *userData) |
| | | { |
| | | if (time_started_flag) return ptNoError; |
| | | - ftime(&time_offset); /* need this set before process runs */ |
| | | + clock_gettime(CLOCK_MONOTONIC, &time_offset); /* need this set before process runs */ |
| | | if (callback) { |
| | | int res; |
| | | pt_callback_parameters *parms = (pt_callback_parameters *) |
| | | @@ -120,12 +119,12 @@ int Pt_Started() |
| | | |
| | | PtTimestamp Pt_Time() |
| | | { |
| | | - long seconds, milliseconds; |
| | | - struct timeb now; |
| | | - ftime(&now); |
| | | - seconds = now.time - time_offset.time; |
| | | - milliseconds = now.millitm - time_offset.millitm; |
| | | - return seconds * 1000 + milliseconds; |
| | | + long seconds, nanoseconds; |
| | | + struct timespec now; |
| | | + clock_gettime(CLOCK_MONOTONIC, &now); |
| | | + seconds = now.tv_sec - time_offset.tv_sec; |
| | | + nanoseconds = now.tv_nsec - time_offset.tv_nsec; |
| | | + return seconds * 1000 + nanoseconds / 1000000; |
| | | } |
| | | |
| | | |
| | | --- audacity-Audacity-2.3.2/lib-src/portmidi/Makefile.am.~1~ 2019-05-04 13:38:57.000000000 +0000 |
| | | +++ audacity-Audacity-2.3.2/lib-src/portmidi/Makefile.am 2019-08-13 14:04:41.370862823 +0000 |
| | | @@ -13,9 +13,7 @@ |
| | | libportmidi_s_a_SOURCES = pm_common/portmidi.c \ |
| | | pm_common/pmutil.c \ |
| | | porttime/ptlinux.c \ |
| | | - pm_linux/pmlinuxalsa.c \ |
| | | - pm_linux/pmlinux.c \ |
| | | - pm_linux/finddefault.c |
| | | + pm_linux/pmlinuxalsa.c |
| | | |
| | | # and the header files for the library. At the moment these go into the include |
| | | # directory directly, it would be much better to have them in a subdirectory |
| | | --- audacity-Audacity-2.3.2/lib-src/portmidi/pm_linux/pmlinuxalsa.c.~1~ 2019-05-04 13:38:57.000000000 +0000 |
| | | +++ audacity-Audacity-2.3.2/lib-src/portmidi/pm_linux/pmlinuxalsa.c 2019-08-13 14:18:53.991304730 +0000 |
| | | @@ -16,693 +16,71 @@ |
| | | #include "porttime.h" |
| | | #include "pmlinux.h" |
| | | |
| | | -#include <alsa/asoundlib.h> |
| | | - |
| | | -/* I used many print statements to debug this code. I left them in the |
| | | - * source, and you can turn them on by changing false to true below: |
| | | - */ |
| | | -#define VERBOSE_ON 0 |
| | | -#define VERBOSE if (VERBOSE_ON) |
| | | - |
| | | -#define MIDI_SYSEX 0xf0 |
| | | -#define MIDI_EOX 0xf7 |
| | | - |
| | | -#if SND_LIB_MAJOR == 0 && SND_LIB_MINOR < 9 |
| | | -#error needs ALSA 0.9.0 or later |
| | | -#endif |
| | | - |
| | | -/* to store client/port in the device descriptor */ |
| | | -#define MAKE_DESCRIPTOR(client, port) ((void*)(((client) << 8) | (port))) |
| | | -#define GET_DESCRIPTOR_CLIENT(info) ((((int)(info)) >> 8) & 0xff) |
| | | -#define GET_DESCRIPTOR_PORT(info) (((int)(info)) & 0xff) |
| | | - |
| | | -#define BYTE unsigned char |
| | | +PmDeviceID pm_default_input_device_id = -1; |
| | | +PmDeviceID pm_default_output_device_id = -1; |
| | | |
| | | extern pm_fns_node pm_linuxalsa_in_dictionary; |
| | | extern pm_fns_node pm_linuxalsa_out_dictionary; |
| | | |
| | | -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 { |
| | | - int client; |
| | | - int port; |
| | | - int this_port; |
| | | - int in_sysex; |
| | | - snd_midi_event_t *parser; |
| | | - int error; /* host error code */ |
| | | -} alsa_descriptor_node, *alsa_descriptor_type; |
| | | - |
| | | - |
| | | -/* get_alsa_error_text -- copy error text to potentially short string */ |
| | | -/**/ |
| | | -static void get_alsa_error_text(char *msg, int len, int err) |
| | | -{ |
| | | - int errlen = strlen(snd_strerror(err)); |
| | | - if (errlen < len) { |
| | | - strcpy(msg, snd_strerror(err)); |
| | | - } else if (len > 20) { |
| | | - sprintf(msg, "Alsa error %d", err); |
| | | - } else if (len > 4) { |
| | | - strcpy(msg, "Alsa"); |
| | | - } else { |
| | | - msg[0] = 0; |
| | | - } |
| | | -} |
| | | - |
| | | - |
| | | -/* queue is shared by both input and output, reference counted */ |
| | | -static PmError alsa_use_queue(void) |
| | | -{ |
| | | - if (queue_used == 0) { |
| | | - snd_seq_queue_tempo_t *tempo; |
| | | - |
| | | - queue = snd_seq_alloc_queue(seq); |
| | | - if (queue < 0) { |
| | | - pm_hosterror = queue; |
| | | - return pmHostError; |
| | | - } |
| | | - snd_seq_queue_tempo_alloca(&tempo); |
| | | - snd_seq_queue_tempo_set_tempo(tempo, 480000); |
| | | - snd_seq_queue_tempo_set_ppq(tempo, 480); |
| | | - pm_hosterror = snd_seq_set_queue_tempo(seq, queue, tempo); |
| | | - if (pm_hosterror < 0) |
| | | - return pmHostError; |
| | | - |
| | | - snd_seq_start_queue(seq, queue, NULL); |
| | | - snd_seq_drain_output(seq); |
| | | - } |
| | | - ++queue_used; |
| | | - return pmNoError; |
| | | -} |
| | | - |
| | | - |
| | | -static void alsa_unuse_queue(void) |
| | | +void pm_init() |
| | | { |
| | | - if (--queue_used == 0) { |
| | | - snd_seq_stop_queue(seq, queue, NULL); |
| | | - snd_seq_drain_output(seq); |
| | | - snd_seq_free_queue(seq, queue); |
| | | - VERBOSE printf("queue freed\n"); |
| | | - } |
| | | } |
| | | |
| | | - |
| | | -/* midi_message_length -- how many bytes in a message? */ |
| | | -static int midi_message_length(PmMessage message) |
| | | +void pm_term(void) |
| | | { |
| | | - message &= 0xff; |
| | | - if (message < 0x80) { |
| | | - return 0; |
| | | - } else if (message < 0xf0) { |
| | | - static const int length[] = {3, 3, 3, 3, 2, 2, 3}; |
| | | - return length[(message - 0x80) >> 4]; |
| | | - } else { |
| | | - static const int length[] = { |
| | | - -1, 2, 3, 2, 0, 0, 1, -1, 1, 0, 1, 1, 1, 0, 1, 1}; |
| | | - return length[message - 0xf0]; |
| | | - } |
| | | } |
| | | |
| | | - |
| | | -static PmError alsa_out_open(PmInternal *midi, void *driverInfo) |
| | | -{ |
| | | - void *client_port = descriptors[midi->device_id].descriptor; |
| | | - alsa_descriptor_type desc = (alsa_descriptor_type) |
| | | - pm_alloc(sizeof(alsa_descriptor_node)); |
| | | - snd_seq_port_info_t *info; |
| | | - int err; |
| | | - |
| | | - if (!desc) return pmInsufficientMemory; |
| | | - |
| | | - snd_seq_port_info_alloca(&info); |
| | | - snd_seq_port_info_set_port(info, midi->device_id); |
| | | - snd_seq_port_info_set_capability(info, SND_SEQ_PORT_CAP_WRITE | |
| | | - SND_SEQ_PORT_CAP_READ); |
| | | - snd_seq_port_info_set_type(info, SND_SEQ_PORT_TYPE_MIDI_GENERIC | |
| | | - SND_SEQ_PORT_TYPE_APPLICATION); |
| | | - snd_seq_port_info_set_port_specified(info, 1); |
| | | - err = snd_seq_create_port(seq, info); |
| | | - if (err < 0) goto free_desc; |
| | | - |
| | | - /* fill in fields of desc, which is passed to pm_write routines */ |
| | | - midi->descriptor = desc; |
| | | - desc->client = GET_DESCRIPTOR_CLIENT(client_port); |
| | | - desc->port = GET_DESCRIPTOR_PORT(client_port); |
| | | - desc->this_port = midi->device_id; |
| | | - desc->in_sysex = 0; |
| | | - |
| | | - desc->error = 0; |
| | | - |
| | | - err = snd_midi_event_new(PM_DEFAULT_SYSEX_BUFFER_SIZE, &desc->parser); |
| | | - if (err < 0) goto free_this_port; |
| | | - |
| | | - if (midi->latency > 0) { /* must delay output using a queue */ |
| | | - err = alsa_use_queue(); |
| | | - if (err < 0) goto free_parser; |
| | | - |
| | | - err = snd_seq_connect_to(seq, desc->this_port, desc->client, desc->port); |
| | | - if (err < 0) goto unuse_queue; /* clean up and return on error */ |
| | | - } else { |
| | | - err = snd_seq_connect_to(seq, desc->this_port, desc->client, desc->port); |
| | | - if (err < 0) goto free_parser; /* clean up and return on error */ |
| | | - } |
| | | - return pmNoError; |
| | | - |
| | | - unuse_queue: |
| | | - alsa_unuse_queue(); |
| | | - free_parser: |
| | | - snd_midi_event_free(desc->parser); |
| | | - free_this_port: |
| | | - snd_seq_delete_port(seq, desc->this_port); |
| | | - free_desc: |
| | | - pm_free(desc); |
| | | - pm_hosterror = err; |
| | | - if (err < 0) { |
| | | - get_alsa_error_text(pm_hosterror_text, PM_HOST_ERROR_MSG_LEN, err); |
| | | - } |
| | | - return pmHostError; |
| | | +PmDeviceID Pm_GetDefaultInputDeviceID() { |
| | | + Pm_Initialize(); |
| | | + return pm_default_input_device_id; |
| | | } |
| | | - |
| | | |
| | | -static PmError alsa_write_byte(PmInternal *midi, unsigned char byte, |
| | | - PmTimestamp timestamp) |
| | | -{ |
| | | - alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor; |
| | | - snd_seq_event_t ev; |
| | | - int err; |
| | | - |
| | | - snd_seq_ev_clear(&ev); |
| | | - if (snd_midi_event_encode_byte(desc->parser, byte, &ev) == 1) { |
| | | - snd_seq_ev_set_dest(&ev, desc->client, desc->port); |
| | | - snd_seq_ev_set_source(&ev, desc->this_port); |
| | | - if (midi->latency > 0) { |
| | | - /* compute relative time of event = timestamp - now + latency */ |
| | | - PmTimestamp now = (midi->time_proc ? |
| | | - midi->time_proc(midi->time_info) : |
| | | - Pt_Time(NULL)); |
| | | - int when = timestamp; |
| | | - /* if timestamp is zero, send immediately */ |
| | | - /* otherwise compute time delay and use delay if positive */ |
| | | - if (when == 0) when = now; |
| | | - when = (when - now) + midi->latency; |
| | | - if (when < 0) when = 0; |
| | | - VERBOSE printf("timestamp %d now %d latency %d, ", |
| | | - (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); |
| | | - /* NOTE: for cases where the user does not supply a time function, |
| | | - we could optimize the code by not starting Pt_Time and using |
| | | - the alsa tick time instead. I didn't do this because it would |
| | | - entail changing the queue management to start the queue tick |
| | | - count when PortMidi is initialized and keep it running until |
| | | - PortMidi is terminated. (This should be simple, but it's not |
| | | - how the code works now.) -RBD */ |
| | | - } else { /* send event out without queueing */ |
| | | - VERBOSE printf("direct\n"); |
| | | - /* ev.queue = SND_SEQ_QUEUE_DIRECT; |
| | | - ev.dest.client = SND_SEQ_ADDRESS_SUBSCRIBERS; */ |
| | | - snd_seq_ev_set_direct(&ev); |
| | | - } |
| | | - VERBOSE printf("sending event\n"); |
| | | - err = snd_seq_event_output(seq, &ev); |
| | | - if (err < 0) { |
| | | - desc->error = err; |
| | | - return pmHostError; |
| | | - } |
| | | - } |
| | | - return pmNoError; |
| | | +PmDeviceID Pm_GetDefaultOutputDeviceID() { |
| | | + Pm_Initialize(); |
| | | + return pm_default_output_device_id; |
| | | } |
| | | |
| | | - |
| | | -static PmError alsa_out_close(PmInternal *midi) |
| | | -{ |
| | | - alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor; |
| | | - if (!desc) return pmBadPtr; |
| | | - |
| | | - if ((pm_hosterror = snd_seq_disconnect_to(seq, desc->this_port, |
| | | - desc->client, desc->port))) { |
| | | - // if there's an error, try to delete the port anyway, but don't |
| | | - // change the pm_hosterror value so we retain the first error |
| | | - snd_seq_delete_port(seq, desc->this_port); |
| | | - } else { // if there's no error, delete the port and retain any error |
| | | - pm_hosterror = snd_seq_delete_port(seq, desc->this_port); |
| | | - } |
| | | - 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, |
| | | - pm_hosterror); |
| | | - return pmHostError; |
| | | - } |
| | | +static PmError none_out_open(PmInternal *midi, void *driverInfo) { |
| | | return pmNoError; |
| | | } |
| | | |
| | | - |
| | | -static PmError alsa_in_open(PmInternal *midi, void *driverInfo) |
| | | +static PmError none_abort(PmInternal *midi) |
| | | { |
| | | - void *client_port = descriptors[midi->device_id].descriptor; |
| | | - alsa_descriptor_type desc = (alsa_descriptor_type) |
| | | - pm_alloc(sizeof(alsa_descriptor_node)); |
| | | - snd_seq_port_info_t *info; |
| | | - snd_seq_port_subscribe_t *sub; |
| | | - snd_seq_addr_t addr; |
| | | - int err; |
| | | - |
| | | - if (!desc) return pmInsufficientMemory; |
| | | - |
| | | - err = alsa_use_queue(); |
| | | - if (err < 0) goto free_desc; |
| | | - |
| | | - snd_seq_port_info_alloca(&info); |
| | | - snd_seq_port_info_set_port(info, midi->device_id); |
| | | - snd_seq_port_info_set_capability(info, SND_SEQ_PORT_CAP_WRITE | |
| | | - SND_SEQ_PORT_CAP_READ); |
| | | - snd_seq_port_info_set_type(info, SND_SEQ_PORT_TYPE_MIDI_GENERIC | |
| | | - SND_SEQ_PORT_TYPE_APPLICATION); |
| | | - snd_seq_port_info_set_port_specified(info, 1); |
| | | - err = snd_seq_create_port(seq, info); |
| | | - if (err < 0) goto free_queue; |
| | | - |
| | | - /* fill in fields of desc, which is passed to pm_write routines */ |
| | | - midi->descriptor = desc; |
| | | - desc->client = GET_DESCRIPTOR_CLIENT(client_port); |
| | | - desc->port = GET_DESCRIPTOR_PORT(client_port); |
| | | - desc->this_port = midi->device_id; |
| | | - desc->in_sysex = 0; |
| | | - |
| | | - desc->error = 0; |
| | | - |
| | | - VERBOSE printf("snd_seq_connect_from: %d %d %d\n", |
| | | - desc->this_port, desc->client, desc->port); |
| | | - snd_seq_port_subscribe_alloca(&sub); |
| | | - addr.client = snd_seq_client_id(seq); |
| | | - addr.port = desc->this_port; |
| | | - snd_seq_port_subscribe_set_dest(sub, &addr); |
| | | - addr.client = desc->client; |
| | | - addr.port = desc->port; |
| | | - snd_seq_port_subscribe_set_sender(sub, &addr); |
| | | - snd_seq_port_subscribe_set_time_update(sub, 1); |
| | | - /* this doesn't seem to work: messages come in with real timestamps */ |
| | | - snd_seq_port_subscribe_set_time_real(sub, 0); |
| | | - err = snd_seq_subscribe_port(seq, sub); |
| | | - /* err = |
| | | - snd_seq_connect_from(seq, desc->this_port, desc->client, desc->port); */ |
| | | - if (err < 0) goto free_this_port; /* clean up and return on error */ |
| | | return pmNoError; |
| | | - |
| | | - free_this_port: |
| | | - snd_seq_delete_port(seq, desc->this_port); |
| | | - free_queue: |
| | | - alsa_unuse_queue(); |
| | | - free_desc: |
| | | - pm_free(desc); |
| | | - pm_hosterror = err; |
| | | - if (err < 0) { |
| | | - get_alsa_error_text(pm_hosterror_text, PM_HOST_ERROR_MSG_LEN, err); |
| | | - } |
| | | - return pmHostError; |
| | | } |
| | | |
| | | -static PmError alsa_in_close(PmInternal *midi) |
| | | +static PmError none_out_close(PmInternal *midi) |
| | | { |
| | | - alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor; |
| | | - if (!desc) return pmBadPtr; |
| | | - if ((pm_hosterror = snd_seq_disconnect_from(seq, desc->this_port, |
| | | - desc->client, desc->port))) { |
| | | - snd_seq_delete_port(seq, desc->this_port); /* try to close port */ |
| | | - } else { |
| | | - pm_hosterror = snd_seq_delete_port(seq, desc->this_port); |
| | | - } |
| | | - alsa_unuse_queue(); |
| | | - midi->descriptor = NULL; |
| | | - pm_free(desc); |
| | | - if (pm_hosterror) { |
| | | - get_alsa_error_text(pm_hosterror_text, PM_HOST_ERROR_MSG_LEN, |
| | | - pm_hosterror); |
| | | - return pmHostError; |
| | | - } |
| | | return pmNoError; |
| | | } |
| | | - |
| | | |
| | | -static PmError alsa_abort(PmInternal *midi) |
| | | +static unsigned int none_has_host_error(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; |
| | | - 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; |
| | | + return 0; |
| | | } |
| | | - |
| | | - |
| | | -#ifdef GARBAGE |
| | | -This is old code here temporarily for reference |
| | | -static PmError alsa_write(PmInternal *midi, PmEvent *buffer, int32_t length) |
| | | +static void none_get_host_error(PmInternal *midi, char *msg, unsigned int len) |
| | | { |
| | | - alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor; |
| | | - int i, bytes; |
| | | - unsigned char byte; |
| | | - PmMessage msg; |
| | | - |
| | | - desc->error = 0; |
| | | - for (; length > 0; length--, buffer++) { |
| | | - VERBOSE printf("message 0x%x\n", buffer->message); |
| | | - if (Pm_MessageStatus(buffer->message) == MIDI_SYSEX) |
| | | - desc->in_sysex = TRUE; |
| | | - if (desc->in_sysex) { |
| | | - msg = buffer->message; |
| | | - for (i = 0; i < 4; i++) { |
| | | - byte = msg; /* extract next byte to send */ |
| | | - alsa_write_byte(midi, byte, buffer->timestamp); |
| | | - if (byte == MIDI_EOX) { |
| | | - desc->in_sysex = FALSE; |
| | | - break; |
| | | - } |
| | | - if (desc->error < 0) break; |
| | | - msg >>= 8; /* shift next byte into position */ |
| | | - } |
| | | - } else { |
| | | - bytes = midi_message_length(buffer->message); |
| | | - msg = buffer->message; |
| | | - for (i = 0; i < bytes; i++) { |
| | | - byte = msg; /* extract next byte to send */ |
| | | - VERBOSE printf("sending 0x%x\n", byte); |
| | | - alsa_write_byte(midi, byte, buffer->timestamp); |
| | | - if (desc->error < 0) break; |
| | | - msg >>= 8; /* shift next byte into position */ |
| | | - } |
| | | - } |
| | | - } |
| | | - if (desc->error < 0) return pmHostError; |
| | | - |
| | | - 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; |
| | | - |
| | | - desc->error = pmNoError; |
| | | - return pmNoError; |
| | | } |
| | | -#endif |
| | | - |
| | | - |
| | | -static PmError alsa_write_flush(PmInternal *midi, PmTimestamp timestamp) |
| | | -{ |
| | | - alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor; |
| | | - if (!desc) return pmBadPtr; |
| | | - 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; |
| | | |
| | | - desc->error = pmNoError; |
| | | - return pmNoError; |
| | | -} |
| | | +void *pm_alloc(size_t s) { return malloc(s); } |
| | | |
| | | +void pm_free(void *ptr) { free(ptr); } |
| | | |
| | | -static PmError alsa_write_short(PmInternal *midi, PmEvent *event) |
| | | -{ |
| | | - int bytes = midi_message_length(event->message); |
| | | - PmMessage msg = event->message; |
| | | - int i; |
| | | - alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor; |
| | | - if (!desc) return pmBadPtr; |
| | | - for (i = 0; i < bytes; i++) { |
| | | - unsigned char byte = msg; |
| | | - VERBOSE printf("sending 0x%x\n", byte); |
| | | - alsa_write_byte(midi, byte, event->timestamp); |
| | | - if (desc->error < 0) break; |
| | | - msg >>= 8; /* shift next byte into position */ |
| | | - } |
| | | - if (desc->error < 0) return pmHostError; |
| | | - desc->error = pmNoError; |
| | | - return pmNoError; |
| | | -} |
| | | |
| | | - |
| | | -/* alsa_sysex -- implements begin_sysex and end_sysex */ |
| | | -PmError alsa_sysex(PmInternal *midi, PmTimestamp timestamp) { |
| | | - return pmNoError; |
| | | -} |
| | | - |
| | | - |
| | | -static PmTimestamp alsa_synchronize(PmInternal *midi) |
| | | -{ |
| | | - return 0; /* linux implementation does not use this synchronize function */ |
| | | - /* Apparently, Alsa data is relative to the time you send it, and there |
| | | - is no reference. If this is true, this is a serious shortcoming of |
| | | - Alsa. If not true, then PortMidi has a serious shortcoming -- it |
| | | - should be scheduling relative to Alsa's time reference. */ |
| | | -} |
| | | - |
| | | - |
| | | -static void handle_event(snd_seq_event_t *ev) |
| | | -{ |
| | | - int device_id = ev->dest.port; |
| | | - PmInternal *midi = descriptors[device_id].internalDescriptor; |
| | | - // There is a race condition when closing a device and |
| | | - // continuing to poll other open devices. The closed device may |
| | | - // have outstanding events from before the close operation. |
| | | - if (!midi) { |
| | | - return; |
| | | - } |
| | | - PmEvent pm_ev; |
| | | - PmTimeProcPtr time_proc = midi->time_proc; |
| | | - PmTimestamp timestamp; |
| | | - |
| | | - /* time stamp should be in ticks, using our queue where 1 tick = 1ms */ |
| | | - assert((ev->flags & SND_SEQ_TIME_STAMP_MASK) == SND_SEQ_TIME_STAMP_TICK); |
| | | - |
| | | - /* if no time_proc, just return "native" ticks (ms) */ |
| | | - if (time_proc == NULL) { |
| | | - timestamp = ev->time.tick; |
| | | - } else { /* translate time to time_proc basis */ |
| | | - snd_seq_queue_status_t *queue_status; |
| | | - snd_seq_queue_status_alloca(&queue_status); |
| | | - snd_seq_get_queue_status(seq, queue, queue_status); |
| | | - /* return (now - alsa_now) + alsa_timestamp */ |
| | | - timestamp = (*time_proc)(midi->time_info) + ev->time.tick - |
| | | - snd_seq_queue_status_get_tick_time(queue_status); |
| | | - } |
| | | - pm_ev.timestamp = timestamp; |
| | | - switch (ev->type) { |
| | | - case SND_SEQ_EVENT_NOTEON: |
| | | - pm_ev.message = Pm_Message(0x90 | ev->data.note.channel, |
| | | - ev->data.note.note & 0x7f, |
| | | - ev->data.note.velocity & 0x7f); |
| | | - pm_read_short(midi, &pm_ev); |
| | | - break; |
| | | - case SND_SEQ_EVENT_NOTEOFF: |
| | | - pm_ev.message = Pm_Message(0x80 | ev->data.note.channel, |
| | | - ev->data.note.note & 0x7f, |
| | | - ev->data.note.velocity & 0x7f); |
| | | - pm_read_short(midi, &pm_ev); |
| | | - break; |
| | | - case SND_SEQ_EVENT_KEYPRESS: |
| | | - pm_ev.message = Pm_Message(0xa0 | ev->data.note.channel, |
| | | - ev->data.note.note & 0x7f, |
| | | - ev->data.note.velocity & 0x7f); |
| | | - pm_read_short(midi, &pm_ev); |
| | | - break; |
| | | - case SND_SEQ_EVENT_CONTROLLER: |
| | | - pm_ev.message = Pm_Message(0xb0 | ev->data.note.channel, |
| | | - ev->data.control.param & 0x7f, |
| | | - ev->data.control.value & 0x7f); |
| | | - pm_read_short(midi, &pm_ev); |
| | | - break; |
| | | - case SND_SEQ_EVENT_PGMCHANGE: |
| | | - pm_ev.message = Pm_Message(0xc0 | ev->data.note.channel, |
| | | - ev->data.control.value & 0x7f, 0); |
| | | - pm_read_short(midi, &pm_ev); |
| | | - break; |
| | | - case SND_SEQ_EVENT_CHANPRESS: |
| | | - pm_ev.message = Pm_Message(0xd0 | ev->data.note.channel, |
| | | - ev->data.control.value & 0x7f, 0); |
| | | - pm_read_short(midi, &pm_ev); |
| | | - break; |
| | | - case SND_SEQ_EVENT_PITCHBEND: |
| | | - pm_ev.message = Pm_Message(0xe0 | ev->data.note.channel, |
| | | - (ev->data.control.value + 0x2000) & 0x7f, |
| | | - ((ev->data.control.value + 0x2000) >> 7) & 0x7f); |
| | | - pm_read_short(midi, &pm_ev); |
| | | - break; |
| | | - case SND_SEQ_EVENT_CONTROL14: |
| | | - if (ev->data.control.param < 0x20) { |
| | | - pm_ev.message = Pm_Message(0xb0 | ev->data.note.channel, |
| | | - ev->data.control.param, |
| | | - (ev->data.control.value >> 7) & 0x7f); |
| | | - pm_read_short(midi, &pm_ev); |
| | | - pm_ev.message = Pm_Message(0xb0 | ev->data.note.channel, |
| | | - ev->data.control.param + 0x20, |
| | | - ev->data.control.value & 0x7f); |
| | | - pm_read_short(midi, &pm_ev); |
| | | - } else { |
| | | - pm_ev.message = Pm_Message(0xb0 | ev->data.note.channel, |
| | | - ev->data.control.param & 0x7f, |
| | | - ev->data.control.value & 0x7f); |
| | | - |
| | | - pm_read_short(midi, &pm_ev); |
| | | - } |
| | | - break; |
| | | - case SND_SEQ_EVENT_SONGPOS: |
| | | - pm_ev.message = Pm_Message(0xf2, |
| | | - ev->data.control.value & 0x7f, |
| | | - (ev->data.control.value >> 7) & 0x7f); |
| | | - pm_read_short(midi, &pm_ev); |
| | | - break; |
| | | - case SND_SEQ_EVENT_SONGSEL: |
| | | - pm_ev.message = Pm_Message(0xf3, |
| | | - ev->data.control.value & 0x7f, 0); |
| | | - pm_read_short(midi, &pm_ev); |
| | | - break; |
| | | - case SND_SEQ_EVENT_QFRAME: |
| | | - pm_ev.message = Pm_Message(0xf1, |
| | | - ev->data.control.value & 0x7f, 0); |
| | | - pm_read_short(midi, &pm_ev); |
| | | - break; |
| | | - case SND_SEQ_EVENT_START: |
| | | - pm_ev.message = Pm_Message(0xfa, 0, 0); |
| | | - pm_read_short(midi, &pm_ev); |
| | | - break; |
| | | - case SND_SEQ_EVENT_CONTINUE: |
| | | - pm_ev.message = Pm_Message(0xfb, 0, 0); |
| | | - pm_read_short(midi, &pm_ev); |
| | | - break; |
| | | - case SND_SEQ_EVENT_STOP: |
| | | - pm_ev.message = Pm_Message(0xfc, 0, 0); |
| | | - pm_read_short(midi, &pm_ev); |
| | | - break; |
| | | - case SND_SEQ_EVENT_CLOCK: |
| | | - pm_ev.message = Pm_Message(0xf8, 0, 0); |
| | | - pm_read_short(midi, &pm_ev); |
| | | - break; |
| | | - case SND_SEQ_EVENT_TUNE_REQUEST: |
| | | - pm_ev.message = Pm_Message(0xf6, 0, 0); |
| | | - pm_read_short(midi, &pm_ev); |
| | | - break; |
| | | - case SND_SEQ_EVENT_RESET: |
| | | - pm_ev.message = Pm_Message(0xff, 0, 0); |
| | | - pm_read_short(midi, &pm_ev); |
| | | - break; |
| | | - case SND_SEQ_EVENT_SENSING: |
| | | - pm_ev.message = Pm_Message(0xfe, 0, 0); |
| | | - pm_read_short(midi, &pm_ev); |
| | | - break; |
| | | - case SND_SEQ_EVENT_SYSEX: { |
| | | - const BYTE *ptr = (const BYTE *) ev->data.ext.ptr; |
| | | - /* 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; |
| | | - /* 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; |
| | | -} |
| | | - |
| | | - |
| | | -static unsigned int alsa_has_host_error(PmInternal *midi) |
| | | -{ |
| | | - alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor; |
| | | - if (!desc) return 0; |
| | | - return desc->error; |
| | | -} |
| | | - |
| | | - |
| | | -static void alsa_get_host_error(PmInternal *midi, char *msg, unsigned int len) |
| | | -{ |
| | | - alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor; |
| | | - if (!desc) return; |
| | | - int err = (pm_hosterror || desc->error); |
| | | - get_alsa_error_text(msg, len, err); |
| | | -} |
| | | - |
| | | - |
| | | -pm_fns_node pm_linuxalsa_in_dictionary = { |
| | | +pm_fns_node pm_linuxalsa_out_dictionary = { |
| | | none_write_short, |
| | | none_sysex, |
| | | none_sysex, |
| | | none_write_byte, |
| | | - none_write_short, |
| | | + none_write_short, /* short realtime message */ |
| | | none_write_flush, |
| | | - alsa_synchronize, |
| | | - alsa_in_open, |
| | | - alsa_abort, |
| | | - alsa_in_close, |
| | | - alsa_poll, |
| | | - alsa_has_host_error, |
| | | - alsa_get_host_error |
| | | -}; |
| | | - |
| | | -pm_fns_node pm_linuxalsa_out_dictionary = { |
| | | - alsa_write_short, |
| | | - alsa_sysex, |
| | | - alsa_sysex, |
| | | - alsa_write_byte, |
| | | - alsa_write_short, /* short realtime message */ |
| | | - alsa_write_flush, |
| | | - alsa_synchronize, |
| | | - alsa_out_open, |
| | | - alsa_abort, |
| | | - alsa_out_close, |
| | | + none_synchronize, |
| | | + none_out_open, |
| | | + none_abort, |
| | | + none_out_close, |
| | | none_poll, |
| | | - alsa_has_host_error, |
| | | - alsa_get_host_error |
| | | + none_has_host_error, |
| | | + none_get_host_error |
| | | }; |
| | | |
| | | |
| | | @@ -718,73 +96,3 @@ |
| | | strcpy(dup, s); |
| | | return dup; |
| | | } |
| | | - |
| | | - |
| | | -PmError pm_linuxalsa_init( void ) |
| | | -{ |
| | | - int err; |
| | | - snd_seq_client_info_t *cinfo; |
| | | - snd_seq_port_info_t *pinfo; |
| | | - unsigned int caps; |
| | | - |
| | | - /* 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); |
| | | - |
| | | - snd_seq_client_info_set_client(cinfo, -1); |
| | | - while (snd_seq_query_next_client(seq, cinfo) == 0) { |
| | | - snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo)); |
| | | - snd_seq_port_info_set_port(pinfo, -1); |
| | | - while (snd_seq_query_next_port(seq, pinfo) == 0) { |
| | | - if (snd_seq_port_info_get_client(pinfo) == SND_SEQ_CLIENT_SYSTEM) |
| | | - continue; /* ignore Timer and Announce ports on client 0 */ |
| | | - caps = snd_seq_port_info_get_capability(pinfo); |
| | | - if (!(caps & (SND_SEQ_PORT_CAP_SUBS_READ | SND_SEQ_PORT_CAP_SUBS_WRITE))) |
| | | - continue; /* ignore if you cannot read or write port */ |
| | | - if (caps & SND_SEQ_PORT_CAP_SUBS_WRITE) { |
| | | - if (pm_default_output_device_id == -1) |
| | | - pm_default_output_device_id = pm_descriptor_index; |
| | | - pm_add_device("ALSA", |
| | | - pm_strdup(snd_seq_port_info_get_name(pinfo)), |
| | | - FALSE, |
| | | - MAKE_DESCRIPTOR(snd_seq_port_info_get_client(pinfo), |
| | | - snd_seq_port_info_get_port(pinfo)), |
| | | - &pm_linuxalsa_out_dictionary); |
| | | - } |
| | | - if (caps & SND_SEQ_PORT_CAP_SUBS_READ) { |
| | | - if (pm_default_input_device_id == -1) |
| | | - pm_default_input_device_id = pm_descriptor_index; |
| | | - pm_add_device("ALSA", |
| | | - pm_strdup(snd_seq_port_info_get_name(pinfo)), |
| | | - TRUE, |
| | | - MAKE_DESCRIPTOR(snd_seq_port_info_get_client(pinfo), |
| | | - snd_seq_port_info_get_port(pinfo)), |
| | | - &pm_linuxalsa_in_dictionary); |
| | | - } |
| | | - } |
| | | - } |
| | | - return pmNoError; |
| | | -} |
| | | - |
| | | - |
| | | -void pm_linuxalsa_term(void) |
| | | -{ |
| | | - if (seq) { |
| | | - snd_seq_close(seq); |
| | | - pm_free(descriptors); |
| | | - descriptors = NULL; |
| | | - pm_descriptor_index = 0; |
| | | - pm_descriptor_max = 0; |
| | | - } |
| | | -} |