X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/blobdiff_plain/e03eb69edbcd171f32edf369af2c766c383e4c16..8ab2aa9fd51a89e06d92a4f7c3792aaa4a08cc71:/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,
};
/*