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.
38 /** @brief Pipe to subprocess */
39 static int command_fd;
41 /** @brief Child process ID */
42 static pid_t command_pid;
44 static const char *const command_options[] = {
49 /** @brief Close pipe and wait for subprocess to terminate */
50 static void command_wait(void) {
56 while((rc = waitpid(command_pid, &w, 0) < 0 && errno == EINTR))
59 fatal(errno, "waitpid");
62 error(0, "command subprocess %s", ws);
67 /** @brief Create subprocess */
68 static void command_open(void) {
72 if(!(command = uaudio_get("command")))
73 fatal(0, "'command' not set");
75 command_pid = xfork();
78 signal(SIGPIPE, SIG_DFL);
82 /* TODO it would be nice to set some environment variables given the sample
83 * format. The original intended model is that you adapt DisOrder to the
84 * command you run but it'd be nice to support the opposite. */
85 execl("/bin/sh", "sh", "-c", command, (char *)0);
86 fatal(errno, "error executing /bin/sh");
92 /** @brief Send audio data to subprocess */
93 static size_t command_play(void *buffer, size_t nsamples) {
94 uaudio_schedule_synchronize();
95 const size_t bytes = nsamples * uaudio_sample_size;
96 int written = write(command_fd, buffer, bytes);
100 return 0; /* will retry */
102 error(0, "audio command subprocess terminated");
105 return 0; /* will retry */
107 fatal(errno, "error writing to audio command subprocess");
110 const size_t written_samples = written / uaudio_sample_size;
111 uaudio_schedule_update(written_samples);
112 return written_samples;
115 static void command_start(uaudio_callback *callback,
118 uaudio_schedule_init();
119 uaudio_thread_start(callback,
123 4096 / uaudio_sample_size);
126 static void command_stop(void) {
127 uaudio_thread_stop();
131 static void command_activate(void) {
132 uaudio_schedule_reactivated = 1;
133 uaudio_thread_activate();
136 static void command_deactivate(void) {
137 uaudio_thread_deactivate();
140 const struct uaudio uaudio_command = {
142 .options = command_options,
143 .start = command_start,
144 .stop = command_stop,
145 .activate = command_activate,
146 .deactivate = command_deactivate