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 extern "C" {
18 #include "config.h"
19 }
21 #include <sys/socket.h>
23 #include <errno.h>
24 #include <limits.h>
25 #include <poll.h>
26 #include <stdio.h>
28 #include <oboe/Oboe.h>
30 extern "C" {
31 #include "amused.h"
32 #include "log.h"
33 }
35 #define ext extern "C"
37 static void (*onmove_cb)(void *, int);
38 static int sp[2]; /* main, audio thread */
39 static pthread_t at;
41 static int bpf;
42 static unsigned int bits, rate, chan;
43 static oboe::AudioFormat fmt;
44 static char buf[BUFSIZ];
45 static size_t buflen;
47 static std::shared_ptr<oboe::AudioStream> stream;
49 static void *
50 aworker(void *d)
51 {
52 unsigned int last_bits, last_rate, last_chan;
53 ssize_t r;
54 int sock = sp[1];
55 char ch;
57 stream = nullptr;
58 last_bits = last_rate = last_chan = 0;
60 log_info("%s: starting", __func__);
61 for (;;) {
62 ch = 1;
63 if ((r = write(sock, &ch, 1)) == -1)
64 fatal("write");
65 if (r == 0)
66 break;
68 if ((r = read(sock, &ch, 1)) == -1)
69 fatal("read");
70 if (r == 0)
71 break;
73 if (bits != last_bits ||
74 rate != last_rate ||
75 chan != last_chan) {
76 if (stream) {
77 stream->close();
78 stream = nullptr;
79 }
81 last_bits = bits;
82 last_rate = rate;
83 last_chan = chan;
85 log_debug("setting bits=%d rate=%d chan=%d bpf=%d",
86 bits, rate, chan, bpf);
88 oboe::AudioStreamBuilder streamBuilder;
89 streamBuilder.setFormat(fmt);
90 streamBuilder.setSampleRate(rate);
91 streamBuilder.setChannelCount(chan);
92 oboe::Result result = streamBuilder.openStream(stream);
93 if (result != oboe::Result::OK)
94 fatalx("Error opening stream %s",
95 oboe::convertToText(result));
97 stream->requestStart();
98 }
100 // oboe works in terms of FRAMES not BYTES!
101 unsigned int len = buflen / bpf;
103 // XXX should be the timeout in nanoseconds...
104 auto ret = stream->write(buf, len, 1000000000);
105 if (!ret) {
106 fatalx("write failed: %s",
107 oboe::convertToText(ret.error()));
111 return nullptr;
114 ext int
115 audio_open(void (*cb)(void *, int))
117 onmove_cb = cb;
119 if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) == -1) {
120 log_warn("socketpair");
121 return (-1);
124 if (pthread_create(&at, NULL, aworker, NULL) == -1) {
125 log_warn("pthread_create");
126 return (-1);
129 return (0);
132 ext int
133 audio_setup(unsigned int p_bits, unsigned int p_rate, unsigned int p_chan,
134 struct pollfd *pfds, int nfds)
136 bits = p_bits;
137 rate = p_rate;
138 chan = p_chan;
140 if (bits == 8) {
141 log_warnx("would require a conversion layer...");
142 return (-1);
143 } else if (bits == 16) {
144 bpf = 2;
145 fmt = oboe::AudioFormat::I16;
146 } else if (bits == 24) {
147 bpf = 4;
148 fmt = oboe::AudioFormat::I24;
149 } else if (bits == 32) {
150 // XXX not so sure...
151 bpf = 4;
152 fmt = oboe::AudioFormat::I24;
153 } else {
154 log_warnx("can't handle %d bits", bits);
155 return (-1);
158 bpf *= chan;
160 return (0);
163 ext int
164 audio_nfds(void)
166 return 1;
169 ext int
170 audio_pollfd(struct pollfd *pfds, int nfds, int events)
172 if (nfds != 1) {
173 errno = EINVAL;
174 return -1;
177 pfds[0].fd = sp[0];
178 pfds[0].events = POLLIN;
179 return (0);
182 ext int
183 audio_revents(struct pollfd *pfds, int nfds)
185 if (nfds != 1) {
186 log_warnx("%s: called with %d nfds", __func__, nfds);
187 return 0;
190 /* don't need to check; if we're here the audio thread is ready */
191 return POLLOUT;
194 ext size_t
195 audio_write(const void *data, size_t len)
197 char ch;
198 ssize_t r;
200 if ((r = read(sp[0], &ch, 1)) == -1) {
201 log_warn("oboe/%s: read failed", __func__);
202 return 0;
204 if (r == 0)
205 return 0;
207 if (len > sizeof(buf))
208 len = sizeof(buf);
210 memcpy(buf, data, len);
211 buflen = len;
213 ch = 1;
214 if ((r = write(sp[0], &ch, 1)) == -1) {
215 log_warn("oboe/%s: write failed", __func__);
216 return 0;
218 if (r == 0) {
219 log_warnx("oboe/%s: write got EOF", __func__);
220 return 0;
223 if (onmove_cb)
224 onmove_cb(NULL, len / bpf);
226 return len;
229 ext int
230 audio_flush(void)
232 return 0; // XXX request flush
235 ext int
236 audio_stop(void)
238 return 0; // XXX request stop