chiark / gitweb /
"make distcheck" now passes
[disorder] / server / speaker-command.c
CommitLineData
1c3f1e73 1/*
2 * This file is part of DisOrder
3 * Copyright (C) 2005, 2006, 2007 Richard Kettlewell
313acc77 4 * Portions (C) 2007 Mark Wooding
1c3f1e73 5 *
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 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19 * USA
20 */
21/** @file server/speaker-command.c
22 * @brief Support for @ref BACKEND_COMMAND */
23
05b75f8d 24#include "common.h"
1c3f1e73 25
26#include <unistd.h>
27#include <poll.h>
6d2d327c 28#include <errno.h>
1c3f1e73 29
30#include "configuration.h"
31#include "syscalls.h"
32#include "log.h"
33#include "speaker-protocol.h"
34#include "speaker.h"
35
36/** @brief Pipe to subprocess
37 *
38 * This is the file descriptor to write to for @ref BACKEND_COMMAND.
39 */
40static int cmdfd = -1;
41
42/** @brief poll array slot for @ref cmdfd
43 *
44 * Set by command_beforepoll().
45 */
46static int cmdfd_slot;
47
48/** @brief Start the subprocess for @ref BACKEND_COMMAND */
49static void fork_cmd(void) {
50 pid_t cmdpid;
51 int pfd[2];
52 if(cmdfd != -1) close(cmdfd);
53 xpipe(pfd);
54 cmdpid = xfork();
55 if(!cmdpid) {
6d2d327c 56 exitfn = _exit;
1c3f1e73 57 signal(SIGPIPE, SIG_DFL);
58 xdup2(pfd[0], 0);
59 close(pfd[0]);
60 close(pfd[1]);
61 execl("/bin/sh", "sh", "-c", config->speaker_command, (char *)0);
62 fatal(errno, "error execing /bin/sh");
63 }
64 close(pfd[0]);
65 cmdfd = pfd[1];
66 D(("forked cmd %d, fd = %d", cmdpid, cmdfd));
67}
68
69/** @brief Command backend initialization */
70static void command_init(void) {
71 info("selected command backend");
72 fork_cmd();
73}
74
75/** @brief Play to a subprocess */
76static size_t command_play(size_t frames) {
6d2d327c 77 size_t bytes = frames * bpf;
1c3f1e73 78 int written_bytes;
79
80 written_bytes = write(cmdfd, playing->buffer + playing->start, bytes);
81 D(("actually play %zu bytes, wrote %d",
82 bytes, written_bytes));
83 if(written_bytes < 0) {
84 switch(errno) {
85 case EPIPE:
86 error(0, "hmm, command died; trying another");
87 fork_cmd();
88 return 0;
89 case EAGAIN:
90 return 0;
91 default:
92 fatal(errno, "error writing to subprocess");
93 }
94 } else
6d2d327c 95 return written_bytes / bpf;
1c3f1e73 96}
97
98/** @brief Update poll array for writing to subprocess */
e84fb5f0 99static void command_beforepoll(int attribute((unused)) *timeoutp) {
1c3f1e73 100 /* We send sample data to the subprocess as fast as it can accept it.
101 * This isn't ideal as pause latency can be very high as a result. */
102 if(cmdfd >= 0)
103 cmdfd_slot = addfd(cmdfd, POLLOUT);
104}
105
106/** @brief Process poll() results for subprocess play */
107static int command_ready(void) {
108 if(fds[cmdfd_slot].revents & (POLLOUT | POLLERR))
109 return 1;
110 else
111 return 0;
112}
113
114const struct speaker_backend command_backend = {
115 BACKEND_COMMAND,
6d2d327c 116 0,
1c3f1e73 117 command_init,
118 0, /* activate */
119 command_play,
120 0, /* deactivate */
121 command_beforepoll,
122 command_ready
123};
124
125/*
126Local Variables:
127c-basic-offset:2
128comment-column:40
129fill-column:79
130indent-tabs-mode:nil
131End:
132*/