Blob


1 /*
2 * Copyright (c) 2023 Omar Polo <op@omarpolo.com>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
17 #include "config.h"
19 #include <sys/socket.h>
21 #include <ao/ao.h>
22 #include <limits.h>
23 #include <poll.h>
24 #include <pthread.h>
25 #include <string.h>
27 #include "amused.h"
28 #include "log.h"
30 static void (*onmove_cb)(void *, int);
31 static int sp[2]; /* main, audio thread */
32 static pthread_t at;
34 static int bpf;
35 static ao_sample_format fmt;
36 static char buf[BUFSIZ];
37 static size_t buflen;
39 static void *
40 aworker(void *d)
41 {
42 ao_sample_format f;
43 ao_device *device = NULL;
44 ssize_t r;
45 int sock = sp[1];
46 char ch;
48 memset(&f, 0, sizeof(f));
50 log_info("%s: starting", __func__);
52 for (;;) {
53 ch = 1;
54 if ((r = write(sock, &ch, 1)) == -1)
55 fatal("write");
56 if (r == 0)
57 break;
59 if ((r = read(sock, &ch, 1)) == -1)
60 fatal("read");
61 if (r == 0)
62 break;
64 if (memcmp(&fmt, &f, sizeof(f)) != 0) {
65 if (device != NULL)
66 ao_close(device);
67 device = ao_open_live(ao_default_driver_id(),
68 &fmt, NULL);
69 if (device == NULL) {
70 switch (errno) {
71 case AO_ENODRIVER:
72 log_warnx("ao: no driver found");
73 break;
74 case AO_ENOTLIVE:
75 log_warnx("ao: not a live device");
76 break;
77 case AO_EBADOPTION:
78 log_warnx("ao: bad option(s)");
79 break;
80 case AO_EOPENDEVICE:
81 log_warnx("ao: failed to open device");
82 break;
83 case AO_EFAIL:
84 default:
85 log_warnx("ao: failed opening driver");
86 break;
87 }
88 errno = EINVAL;
89 break;
90 }
91 log_info("%s: device (re)opened", __func__);
92 memcpy(&f, &fmt, sizeof(f));
93 }
95 if (ao_play(device, buf, buflen) == 0) {
96 log_warnx("ao_play failed");
97 break;
98 }
99 }
101 log_info("quitting audio thread");
102 close(sock);
103 return NULL;
106 int
107 audio_open(void (*cb)(void *, int))
109 ao_initialize();
110 onmove_cb = cb;
112 if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) == -1) {
113 log_warn("socketpair");
114 return (-1);
117 if (pthread_create(&at, NULL, aworker, NULL) == -1) {
118 log_warn("pthread_create");
119 return (-1);
122 return 0;
125 int
126 audio_setup(unsigned int bits, unsigned int rate, unsigned int channels,
127 struct pollfd *pfds, int nfds)
129 fmt.bits = bits;
130 fmt.rate = rate;
131 fmt.channels = channels;
132 fmt.byte_format = AO_FMT_NATIVE;
133 fmt.matrix = NULL;
135 if (bits == 8)
136 bpf = 1;
137 else if (bits == 16)
138 bpf = 2;
139 else if (bits == 24 || bits == 32)
140 bpf = 4;
141 else {
142 log_warnx("can't handle %d bits", bits);
143 return -1;
146 return 0;
149 int
150 audio_nfds(void)
152 return 1;
155 int
156 audio_pollfd(struct pollfd *pfds, int nfds, int events)
158 if (nfds != 1) {
159 errno = EINVAL;
160 return -1;
163 pfds[0].fd = sp[0];
164 pfds[0].events = POLLIN;
165 return 0;
168 int
169 audio_revents(struct pollfd *pfds, int nfds)
171 if (nfds != 1) {
172 log_warnx("%s: called with nfds=%d", __func__, nfds);
173 return 0;
176 /* don't need to check; if we're here the audio thread is ready */
177 return POLLOUT;
180 size_t
181 audio_write(const void *data, size_t len)
183 char ch;
184 ssize_t r;
186 if ((r = read(sp[0], &ch, 1)) == -1) {
187 log_warn("ao/%s: read failed", __func__);
188 return 0;
190 if (r == 0)
191 return 0;
193 if (len > sizeof(buf))
194 len = sizeof(buf);
196 memcpy(buf, data, len);
197 buflen = len;
199 ch = 1;
200 if ((r = write(sp[0], &ch, 1)) == -1) {
201 log_warn("ao/%s: write failed", __func__);
202 return 0;
204 if (r == 0) {
205 log_warnx("ao/%s: write got EOF", __func__);
206 return 0;
209 if (onmove_cb)
210 onmove_cb(NULL, len / bpf);
212 return len;
215 int
216 audio_flush(void)
218 return 0;
221 int
222 audio_stop(void)
224 return 0;