X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/blobdiff_plain/94ddd3e3779fee737969ffcbb0e8f161548d4e0f..063854700ea54b4fd277e945ee256c33d103f72d:/lib/uaudio-command.c diff --git a/lib/uaudio-command.c b/lib/uaudio-command.c index 6b7ef66..f93454a 100644 --- a/lib/uaudio-command.c +++ b/lib/uaudio-command.c @@ -17,7 +17,13 @@ * along with this program. If not, see . */ /** @file lib/uaudio-command.c - * @brief Support for commmand backend */ + * @brief Support for commmand backend + * + * We use the code in @ref lib/uaudio-schedule.c to ensure that we write at + * approximately the 'real' rate. For disorder-playrtp this isn't very useful + * (thought it might reduce the size of various buffers downstream of us) but + * when run from the speaker it means that pausing stands a chance of working. + */ #include "common.h" #include @@ -28,6 +34,7 @@ #include "mem.h" #include "wstat.h" #include "uaudio.h" +#include "configuration.h" /** @brief Pipe to subprocess */ static int command_fd; @@ -35,8 +42,12 @@ static int command_fd; /** @brief Child process ID */ static pid_t command_pid; +/** @brief Whether to suspend on pause */ +static int command_suspend_on_pause; + static const char *const command_options[] = { "command", + "pause-mode", NULL }; @@ -50,10 +61,10 @@ static void command_wait(void) { while((rc = waitpid(command_pid, &w, 0) < 0 && errno == EINTR)) ; if(rc < 0) - fatal(errno, "waitpid"); + disorder_fatal(errno, "waitpid"); if(w) { ws = wstat(w); - error(0, "command subprocess %s", ws); + disorder_error(0, "command subprocess %s", ws); xfree(ws); } } @@ -63,8 +74,8 @@ static void command_open(void) { int pfd[2]; const char *command; - if(!(command = uaudio_get("command"))) - fatal(0, "'command' not set"); + if(!(command = uaudio_get("command", NULL))) + disorder_fatal(0, "'command' not set"); xpipe(pfd); command_pid = xfork(); if(!command_pid) { @@ -77,14 +88,19 @@ static void command_open(void) { * format. The original intended model is that you adapt DisOrder to the * command you run but it'd be nice to support the opposite. */ execl("/bin/sh", "sh", "-c", command, (char *)0); - fatal(errno, "error executing /bin/sh"); + disorder_fatal(errno, "error executing /bin/sh"); } close(pfd[0]); command_fd = pfd[1]; } /** @brief Send audio data to subprocess */ -static size_t command_play(void *buffer, size_t nsamples) { +static size_t command_play(void *buffer, size_t nsamples, unsigned flags) { + uaudio_schedule_sync(); + /* If we're pausing and want that to be represented by stopping writing, we + * just pretend */ + if((flags & UAUDIO_PAUSED) && command_suspend_on_pause) + return nsamples; const size_t bytes = nsamples * uaudio_sample_size; int written = write(command_fd, buffer, bytes); if(written < 0) { @@ -92,25 +108,41 @@ static size_t command_play(void *buffer, size_t nsamples) { case EINTR: return 0; /* will retry */ case EPIPE: - error(0, "audio command subprocess terminated"); + disorder_error(0, "audio command subprocess terminated"); command_wait(); command_open(); return 0; /* will retry */ default: - fatal(errno, "error writing to audio command subprocess"); + disorder_fatal(errno, "error writing to audio command subprocess"); } } - return written / uaudio_sample_size; + /* TODO what if we write a partial sample? Actually reasonably unlikely but + * not impossible. Maybe someone who actually uses this backend should sort + * it out. */ + const size_t written_samples = written / uaudio_sample_size; + uaudio_schedule_sent(written_samples); + return written_samples; } static void command_start(uaudio_callback *callback, - void *userdata) { + void *userdata) { + const char *pausemode = uaudio_get("pause-mode", "silence"); + unsigned flags = 0; + + if(!strcmp(pausemode, "silence")) + command_suspend_on_pause = 0; + else if(!strcmp(pausemode, "suspend")) + command_suspend_on_pause = 1; + else + disorder_fatal(0, "unknown pause mode '%s'", pausemode); command_open(); + uaudio_schedule_init(); uaudio_thread_start(callback, userdata, command_play, uaudio_channels, - 4096 / uaudio_sample_size); + 4096 / uaudio_sample_size, + flags); } static void command_stop(void) { @@ -118,12 +150,9 @@ static void command_stop(void) { command_wait(); } -static void command_activate(void) { - uaudio_thread_activate(); -} - -static void command_deactivate(void) { - uaudio_thread_deactivate(); +static void command_configure(void) { + uaudio_set("command", config->speaker_command); + uaudio_set("pause-mode", config->pause_mode); } const struct uaudio uaudio_command = { @@ -131,8 +160,10 @@ const struct uaudio uaudio_command = { .options = command_options, .start = command_start, .stop = command_stop, - .activate = command_activate, - .deactivate = command_deactivate + .activate = uaudio_thread_activate, + .deactivate = uaudio_thread_deactivate, + .configure = command_configure, + .flags = UAUDIO_API_CLIENT | UAUDIO_API_SERVER, }; /*