2 * This file is part of DisOrder.
3 * Copyright (C) 2005, 2006, 2007, 2009 Richard Kettlewell
4 * Portions (C) 2007 Mark Wooding
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 /** @file lib/uaudio-command.c
20 * @brief Support for commmand backend
22 * We use the code in @ref lib/uaudio-schedule.c to ensure that we write at
23 * approximately the 'real' rate. For disorder-playrtp this isn't very useful
24 * (thought it might reduce the size of various buffers downstream of us) but
25 * when run from the speaker it means that pausing stands a chance of working.
37 #include "configuration.h"
39 /** @brief Pipe to subprocess */
40 static int command_fd
;
42 /** @brief Child process ID */
43 static pid_t command_pid
;
45 /** @brief Whether to suspend on pause */
46 static int command_suspend_on_pause
;
48 static const char *const command_options
[] = {
54 /** @brief Close pipe and wait for subprocess to terminate */
55 static void command_wait(void) {
61 while((rc
= waitpid(command_pid
, &w
, 0) < 0 && errno
== EINTR
))
64 fatal(errno
, "waitpid");
67 error(0, "command subprocess %s", ws
);
72 /** @brief Create subprocess */
73 static void command_open(void) {
77 if(!(command
= uaudio_get("command", NULL
)))
78 fatal(0, "'command' not set");
80 command_pid
= xfork();
83 signal(SIGPIPE
, SIG_DFL
);
87 /* TODO it would be nice to set some environment variables given the sample
88 * format. The original intended model is that you adapt DisOrder to the
89 * command you run but it'd be nice to support the opposite. */
90 execl("/bin/sh", "sh", "-c", command
, (char *)0);
91 fatal(errno
, "error executing /bin/sh");
97 /** @brief Send audio data to subprocess */
98 static size_t command_play(void *buffer
, size_t nsamples
, unsigned flags
) {
99 uaudio_schedule_sync();
100 /* If we're pausing and want that to be represented by stopping writing, we
102 if((flags
& UAUDIO_PAUSED
) && command_suspend_on_pause
)
104 const size_t bytes
= nsamples
* uaudio_sample_size
;
105 int written
= write(command_fd
, buffer
, bytes
);
109 return 0; /* will retry */
111 error(0, "audio command subprocess terminated");
114 return 0; /* will retry */
116 fatal(errno
, "error writing to audio command subprocess");
119 /* TODO what if we write a partial sample? Actually reasonably unlikely but
120 * not impossible. Maybe someone who actually uses this backend should sort
122 const size_t written_samples
= written
/ uaudio_sample_size
;
123 uaudio_schedule_sent(written_samples
);
124 return written_samples
;
127 static void command_start(uaudio_callback
*callback
,
129 const char *pausemode
= uaudio_get("pause-mode", "silence");
132 if(!strcmp(pausemode
, "silence"))
133 command_suspend_on_pause
= 0;
134 else if(!strcmp(pausemode
, "suspend"))
135 command_suspend_on_pause
= 1;
137 fatal(0, "unknown pause mode '%s'", pausemode
);
139 uaudio_schedule_init();
140 uaudio_thread_start(callback
,
144 4096 / uaudio_sample_size
,
148 static void command_stop(void) {
149 uaudio_thread_stop();
153 static void command_configure(void) {
154 uaudio_set("command", config
->speaker_command
);
155 uaudio_set("pause-mode", config
->pause_mode
);
158 const struct uaudio uaudio_command
= {
160 .options
= command_options
,
161 .start
= command_start
,
162 .stop
= command_stop
,
163 .activate
= uaudio_thread_activate
,
164 .deactivate
= uaudio_thread_deactivate
,
165 .configure
= command_configure
,