diff options
Diffstat (limited to 'applause.c')
-rw-r--r-- | applause.c | 263 |
1 files changed, 158 insertions, 105 deletions
@@ -39,18 +39,27 @@ #define MAX(X,Y) ((X) > (Y) ? (X) : (Y)) -static jack_port_t *output_port; static jack_client_t *client = NULL; -#define DEFAULT_BUFFER_SIZE 100 /* milliseconds */ -static jack_ringbuffer_t *buffer = NULL; -static int buffer_sem; -/** - * True if a buffer underrun occurs. - * FIXME: sig_atomic_t is probably the wrong type. - * Perhaps use the Mintomic types. - */ -static sig_atomic_t buffer_xrun = 0; +#define DEFAULT_NUMBER_OF_OUTPUT_PORTS 1 +#define DEFAULT_BUFFER_SIZE 100 /* milliseconds */ + +typedef struct applause_output_port { + jack_port_t *jack_port; + jack_ringbuffer_t *buffer; + int buffer_sem; + /** + * True if a buffer underrun occurs. + * FIXME: sig_atomic_t is probably the wrong type. + * Perhaps use the Mintomic types. + */ + sig_atomic_t buffer_xrun; +} applause_output_port; + +/** List of Applause output ports */ +static applause_output_port *output_ports = NULL; +/** Number of Applause output ports */ +static int output_ports_count = DEFAULT_NUMBER_OF_OUTPUT_PORTS; static sig_atomic_t interrupted = 0; @@ -167,37 +176,38 @@ signal_handler(int signum) static int jack_process(jack_nframes_t nframes, void *arg) { - jack_default_audio_sample_t *out; - size_t len = sizeof(*out)*nframes; - size_t r; + size_t len = sizeof(jack_default_audio_sample_t)*nframes; void *midi_in; jack_nframes_t midi_events; - out = (jack_default_audio_sample_t *) - jack_port_get_buffer(output_port, nframes); + for (int i = 0; i < output_ports_count; i++) { + applause_output_port *port = output_ports + i; + jack_default_audio_sample_t *out; + size_t r; - /** - * @bug Do we have to care about more than one - * channel per frame? - */ - r = jack_ringbuffer_read(buffer, (char *)out, len); + out = (jack_default_audio_sample_t *) + jack_port_get_buffer(port->jack_port, nframes); - /* - * The semaphor value corresponds with the number of - * writable bytes in buffer, i.e. the available space. - * This operation should never block and is supposed to - * be real-time safe :-) - */ - if (r > 0) - svsem_op(buffer_sem, r); + r = jack_ringbuffer_read(port->buffer, (char *)out, len); - /* - * Here we're assuming that memset() is realtime-capable. - * It might not be on every UNIX!? - */ - memset((char *)out + r, 0, len - r); - buffer_xrun |= len - r > 0; + /* + * The semaphor value corresponds with the number of + * writable bytes in buffer, i.e. the available space. + * This operation should never block and is supposed to + * be real-time safe :-) + */ + if (r > 0) + svsem_op(port->buffer_sem, r); + + /* + * Add silence for missing output samples. + * Here we're assuming that memset() is realtime-capable. + * It might not be on every UNIX!? + */ + memset((char *)out + r, 0, len - r); + port->buffer_xrun |= len - r > 0; + } /* * MIDI processing. @@ -284,28 +294,76 @@ init_audio(int buffer_size) fprintf(stderr, "unique name `%s' assigned\n", client_name); } - /* tell the JACK server to call `process()' whenever - there is work to be done. - */ - - jack_set_process_callback(client, jack_process, 0); + /* + * Tell the JACK server to call `jack_process()' whenever + * there is work to be done. + * This will fill all output buffers and handle MIDI events. + */ + jack_set_process_callback(client, jack_process, NULL); - /* tell the JACK server to call `jack_shutdown()' if - it ever shuts down, either entirely, or if it - just decides to stop calling us. - */ + /* + * Tell the JACK server to call `jack_shutdown()' if + * it ever shuts down, either entirely, or if it + * just decides to stop calling us. + */ + jack_on_shutdown(client, jack_shutdown, NULL); - jack_on_shutdown (client, jack_shutdown, 0); + /* + * Calculate the buffer size in bytes given the `buffer_size` + * in milliseconds. + * Make sure it is at least the Jack server's buffer size, + * else jack_process() is very likely not able to provide + * enough bytes. + * FIXME: The Jack server's sample rate and buffer size can + * theoretically change at runtime but currently, + * the buffer size is not adapted + * which means it could be too small after the change. + */ + buffer_bytes = sizeof(jack_default_audio_sample_t)* + MAX(jack_get_sample_rate(client)*buffer_size/1000, + jack_get_buffer_size(client)); /* * Create output ports */ - output_port = jack_port_register (client, "output", - JACK_DEFAULT_AUDIO_TYPE, - JackPortIsOutput, 0); - if (output_port == NULL) { - fprintf(stderr, "no more JACK ports available\n"); - return 1; + free(output_ports); + output_ports = calloc(output_ports_count, sizeof(*output_ports)); + + for (int i = 0; i < output_ports_count; i++) { + applause_output_port *port = output_ports + i; + char name[256]; + + /* + * NOTE: Port names must be unique. + * FIXME: Make the names configurable. + */ + snprintf(name, sizeof(name), "output_%d", i+1); + + port->jack_port = jack_port_register(client, name, + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, 0); + if (!port->jack_port) { + fprintf(stderr, "No more JACK ports available\n"); + return 1; + } + + /* + * Initialize ring buffer of samples. + * The semaphore is initialized with the same size + * since it represents the available bytes in the ring + * buffer. + */ + port->buffer = jack_ringbuffer_create(buffer_bytes); + if (!port->buffer) { + fprintf(stderr, "cannot create ringbuffer\n"); + return 1; + } + + port->buffer_sem = svsem_init(buffer_bytes); + if (port->buffer_sem < 0) { + fprintf(stderr, "error initializing semaphore\n"); + return 1; + } } /* @@ -331,39 +389,6 @@ init_audio(int buffer_size) pthread_mutex_init(&midi_mutex, &prioinherit); - /* - * Calculate the buffer size in bytes given the `buffer_size` - * in milliseconds. - * Make sure it is at least the Jack server's buffer size, - * else jack_process() is very likely not able to provide - * enough bytes. - * FIXME: The Jack server's sample rate and buffer size can - * theoretically change at runtime but currently, - * the buffer size is not adapted - * which means it could be too small after the change. - */ - buffer_bytes = sizeof(jack_default_audio_sample_t)* - MAX(jack_get_sample_rate(client)*buffer_size/1000, - jack_get_buffer_size(client)); - - /* - * Initialize ring buffer of samples. - * The semaphore is initialized with the same size - * since it represents the available bytes in the ring - * buffer. - */ - buffer = jack_ringbuffer_create(buffer_bytes); - if (!buffer) { - fprintf(stderr, "cannot create ringbuffer\n"); - return 1; - } - - buffer_sem = svsem_init(buffer_bytes); - if (buffer_sem < 0) { - fprintf(stderr, "error initializing semaphore\n"); - return 1; - } - /* Tell the JACK server that we are ready to roll. Our * process() callback will start running now. */ @@ -379,19 +404,24 @@ init_audio(int buffer_size) * "input" to the backend, and capture ports are "output" from * it. */ - - ports = jack_get_ports (client, NULL, NULL, - JackPortIsPhysical|JackPortIsInput); + ports = jack_get_ports(client, NULL, NULL, + JackPortIsPhysical | JackPortIsInput); if (ports == NULL) { fprintf(stderr, "no physical playback ports\n"); - return 1; + return 0; } - if (jack_connect (client, jack_port_name (output_port), ports[0])) { - fprintf (stderr, "cannot connect output ports\n"); + for (int i = 0; i < output_ports_count && ports[i]; i++) { + if (jack_connect(client, jack_port_name(output_ports[i].jack_port), + ports[i])) { + fprintf(stderr, "Cannot connect port %s to %s\n", + jack_port_name(output_ports[i].jack_port), + ports[i]); + } } - free (ports); + jack_free(ports); + return 0; } @@ -512,7 +542,8 @@ applause_midi_cc_getvalue(int control, int channel) enum applause_audio_state { APPLAUSE_AUDIO_OK = 0, APPLAUSE_AUDIO_INTERRUPTED, - APPLAUSE_AUDIO_XRUN + APPLAUSE_AUDIO_XRUN, + APPLAUSE_AUDIO_INVALID_PORT }; /** @@ -521,8 +552,10 @@ enum applause_audio_state { * This function should be called from the Stream:play() * implementation, which is faster than calling Lua functions * from a C implementation of Stream:play() using the Lua C API - * (supposedly, I could not reproduce this). + * (supposedly - I could not reproduce this). * + * @param output_port_id The number of the output port. + * The first one being 1. * @param sample_double The audio sample as a double (compatible * with lua_Number). * This is speed relevant since otherwise @@ -530,11 +563,26 @@ enum applause_audio_state { * float type. */ enum applause_audio_state -applause_push_sample(double sample_double) +applause_push_sample(int output_port_id, double sample_double) { + applause_output_port *port; jack_default_audio_sample_t sample = (jack_default_audio_sample_t)sample_double; + if (interrupted) { + interrupted = 0; + return APPLAUSE_AUDIO_INTERRUPTED; + } + + /* + * NOTE: The alternative to reporting invalid port Ids here + * would be exporting output_ports_count, so the Lua code can + * check it and assert()ing here instead. + */ + if (output_port_id < 1 || output_port_id > output_ports_count) + return APPLAUSE_AUDIO_INVALID_PORT; + port = output_ports + output_port_id - 1; + /* * We are about to "consume" one free sample in the buffer. * This can block when the buffer is full. @@ -542,17 +590,13 @@ applause_push_sample(double sample_double) * in buffer since jack_process() will only read from the * buffer. */ - svsem_op(buffer_sem, -(int)sizeof(sample)); + svsem_op(port->buffer_sem, -(int)sizeof(sample)); - jack_ringbuffer_write(buffer, (const char *)&sample, + jack_ringbuffer_write(port->buffer, (const char *)&sample, sizeof(sample)); - if (interrupted) { - interrupted = 0; - return APPLAUSE_AUDIO_INTERRUPTED; - } - if (buffer_xrun) { - buffer_xrun = 0; + if (port->buffer_xrun) { + port->buffer_xrun = 0; return APPLAUSE_AUDIO_XRUN; } @@ -588,6 +632,7 @@ l_Stream_fork(lua_State *L) if (child_pid != 0) { /* in the parent process */ //jack_activate(client); + /* FIXME: Pass on the real buffer size */ init_audio(DEFAULT_BUFFER_SIZE); /* call Client:new(child_pid) */ @@ -603,6 +648,7 @@ l_Stream_fork(lua_State *L) //jack_client_close(client); + /* FIXME: Pass on the real buffer size */ init_audio(DEFAULT_BUFFER_SIZE); for (;;) { @@ -861,7 +907,9 @@ main(int argc, char **argv) * FIXME: Support --help */ if (argc > 1) - buffer_size = atoi(argv[1]); + output_ports_count = atoi(argv[1]); + if (argc > 2) + buffer_size = atoi(argv[2]); /* * Register sigint_handler() as the SIGINT handler. @@ -904,7 +952,9 @@ main(int argc, char **argv) /* remove traceback function */ lua_remove(L, -1); - init_audio(buffer_size); + if (init_audio(buffer_size)) + /* error has already been printed */ + return EXIT_FAILURE; /* * Register native C functions. @@ -962,9 +1012,12 @@ main(int argc, char **argv) /* * FIXME: Shut down connection server. + * FIXME: Clean up properly. */ - - svsem_free(buffer_sem); + for (int i = 0; i < output_ports_count; i++) { + svsem_free(output_ports[i].buffer_sem); + } + free(output_ports); lua_close(L); |