002
2022-02-16
op
* Copyright (c) 2022 Omar Polo <op@openbsd.org>
004
2022-02-16
op
* Permission to use, copy, modify, and distribute this software for any
005
2022-02-16
op
* purpose with or without fee is hereby granted, provided that the above
006
2022-02-16
op
* copyright notice and this permission notice appear in all copies.
008
2022-02-16
op
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
009
2022-02-16
op
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
010
2022-02-16
op
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
011
2022-02-16
op
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
012
2022-02-16
op
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
013
2022-02-16
op
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
014
2022-02-16
op
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
017
2022-02-16
op
#include <sys/types.h>
018
2022-02-16
op
#include <sys/queue.h>
019
2022-02-16
op
#include <sys/uio.h>
021
2022-02-16
op
#include <limits.h>
023
2022-02-16
op
#include <assert.h>
024
2022-02-16
op
#include <errno.h>
025
2022-02-16
op
#include <event.h>
026
2022-02-16
op
#include <poll.h>
027
2022-02-16
op
#include <signal.h>
028
2022-02-16
op
#include <sndio.h>
029
2022-02-17
op
#include <stdio.h>
030
2022-02-16
op
#include <stdlib.h>
031
2022-02-16
op
#include <stdint.h>
032
2022-02-16
op
#include <imsg.h>
033
2022-02-16
op
#include <string.h>
034
2022-02-16
op
#include <syslog.h>
035
2022-02-16
op
#include <unistd.h>
037
2022-02-16
op
#include "amused.h"
038
2022-02-16
op
#include "log.h"
039
2022-02-16
op
#include "xmalloc.h"
041
2022-03-09
op
struct sio_hdl *hdl;
042
2022-06-10
op
struct pollfd *player_pfds;
043
2022-03-09
op
static struct imsgbuf *ibuf;
045
2022-06-10
op
static int stopped = 1;
046
2022-02-16
op
static int nextfd = -1;
048
2022-02-16
op
volatile sig_atomic_t halted;
051
2022-02-16
op
player_signal_handler(int signo)
053
2022-02-16
op
halted = 1;
057
2022-02-21
op
player_setup(int bits, int rate, int channels)
059
2022-06-10
op
static struct sio_par par;
060
2022-06-10
op
int nfds, fpct;
062
2022-02-23
op
log_debug("%s: bits=%d, rate=%d, channels=%d", __func__,
063
2022-02-23
op
bits, rate, channels);
065
2022-06-10
op
fpct = (rate*5)/100;
067
2022-06-10
op
/* don't stop if the parameters are the same */
068
2022-06-10
op
if (bits == par.bits && channels == par.pchan &&
069
2022-06-10
op
par.rate - fpct <= rate && rate <= par.rate + fpct) {
070
2022-06-10
op
if (stopped)
071
2022-06-10
op
goto start;
072
2022-06-10
op
return 0;
076
2022-06-10
op
if (!stopped) {
077
2022-06-10
op
sio_stop(hdl);
078
2022-06-10
op
stopped = 1;
081
2022-02-16
op
sio_initpar(&par);
082
2022-02-21
op
par.bits = bits;
083
2022-02-16
op
par.rate = rate;
084
2022-02-18
op
par.pchan = channels;
085
2022-06-10
op
if (!sio_setpar(hdl, &par)) {
086
2022-06-10
op
if (errno == EAGAIN) {
087
2022-06-10
op
nfds = sio_pollfd(hdl, player_pfds + 1, POLLOUT);
088
2022-06-10
op
if (poll(player_pfds + 1, nfds, INFTIM) == -1)
089
2022-06-10
op
fatal("poll");
090
2022-06-10
op
goto again;
092
2022-05-09
op
log_warnx("invalid params (bits=%d, rate=%d, channels=%d",
093
2022-05-09
op
bits, rate, channels);
094
2022-02-18
op
return -1;
096
2022-06-10
op
if (!sio_getpar(hdl, &par)) {
097
2022-06-10
op
log_warnx("can't get params");
098
2022-06-10
op
return -1;
101
2022-02-21
op
if (par.bits != bits || par.pchan != channels) {
102
2022-02-18
op
log_warnx("failed to set params");
103
2022-02-16
op
return -1;
106
2022-02-16
op
/* TODO: check the sample rate? */
109
2022-02-16
op
if (!sio_start(hdl)) {
110
2022-02-16
op
log_warn("sio_start");
111
2022-02-16
op
return -1;
113
2022-06-10
op
stopped = 0;
114
2022-02-16
op
return 0;
117
2022-02-16
op
/* process only one message */
119
2022-02-16
op
player_dispatch(void)
121
2022-05-19
op
struct pollfd pfd;
122
2022-02-16
op
struct imsg imsg;
123
2022-02-16
op
ssize_t n;
126
2022-02-16
op
if (halted != 0)
127
2022-02-16
op
return IMSG_STOP;
130
2022-02-16
op
if ((n = imsg_get(ibuf, &imsg)) == -1)
131
2022-02-16
op
fatal("imsg_get");
132
2022-05-19
op
if (n == 0) {
133
2022-05-19
op
pfd.fd = ibuf->fd;
134
2022-05-19
op
pfd.events = POLLIN;
135
2022-05-19
op
if (poll(&pfd, 1, INFTIM) == -1)
136
2022-05-19
op
fatal("poll");
137
2022-05-19
op
if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
138
2022-05-19
op
fatal("imsg_read");
139
2022-05-19
op
if (n == 0)
140
2022-05-19
op
fatalx("pipe closed");
141
2022-05-19
op
goto again;
144
2022-02-16
op
ret = imsg.hdr.type;
145
2022-02-16
op
switch (imsg.hdr.type) {
146
2022-02-16
op
case IMSG_PLAY:
147
2022-06-09
op
if (nextfd != -1)
148
2022-06-09
op
fatalx("track already enqueued");
149
2022-06-09
op
if ((nextfd = imsg.fd) == -1)
150
2022-06-09
op
fatalx("%s: got invalid file descriptor", __func__);
151
2022-06-09
op
log_debug("song enqueued");
152
2022-02-16
op
ret = IMSG_STOP;
154
2022-02-16
op
case IMSG_RESUME:
155
2022-02-16
op
case IMSG_PAUSE:
156
2022-02-16
op
case IMSG_STOP:
159
2022-02-16
op
fatalx("unknown imsg %d", imsg.hdr.type);
162
2022-06-09
op
imsg_free(&imsg);
163
2022-02-16
op
return ret;
167
2022-02-16
op
player_senderr(void)
169
2022-02-16
op
imsg_compose(ibuf, IMSG_ERR, 0, 0, -1, NULL, 0);
170
2022-02-16
op
imsg_flush(ibuf);
174
2022-02-16
op
player_sendeof(void)
176
2022-02-16
op
imsg_compose(ibuf, IMSG_EOF, 0, 0, -1, NULL, 0);
177
2022-02-16
op
imsg_flush(ibuf);
181
2022-02-16
op
player_playnext(void)
183
2022-05-09
op
static char buf[512];
184
2022-05-09
op
ssize_t r;
185
2022-02-16
op
int fd = nextfd;
187
2022-02-16
op
assert(nextfd != -1);
188
2022-02-16
op
nextfd = -1;
190
2022-05-09
op
r = read(fd, buf, sizeof(buf));
192
2022-05-09
op
/* 8 byte is the larger magic number */
193
2022-05-09
op
if (r < 8) {
194
2022-05-10
op
log_warn("read failed");
195
2022-05-09
op
goto err;
198
2022-05-09
op
if (lseek(fd, 0, SEEK_SET) == -1) {
199
2022-05-09
op
log_warn("lseek failed");
200
2022-05-09
op
goto err;
203
2022-05-09
op
if (memcmp(buf, "fLaC", 4) == 0)
204
2022-05-09
op
return play_flac(fd);
205
2022-05-09
op
if (memcmp(buf, "ID3", 3) == 0 ||
206
2022-05-09
op
memcmp(buf, "\xFF\xFB", 2) == 0)
207
2022-05-09
op
return play_mp3(fd);
208
2022-05-09
op
if (memmem(buf, r, "OpusHead", 8) != NULL)
209
2022-05-09
op
return play_opus(fd);
210
2022-05-09
op
if (memmem(buf, r, "OggS", 4) != NULL)
211
2022-05-09
op
return play_oggvorbis(fd);
213
2022-05-10
op
log_warnx("unknown file type");
215
2022-05-09
op
close(fd);
216
2022-05-09
op
return -1;
220
2022-02-16
op
player_pause(void)
224
2022-02-16
op
r = player_dispatch();
225
2022-02-16
op
return r == IMSG_RESUME;
229
2022-02-16
op
player_shouldstop(void)
231
2022-02-16
op
switch (player_dispatch()) {
232
2022-02-16
op
case IMSG_PAUSE:
233
2022-02-16
op
if (player_pause())
235
2022-02-16
op
/* fallthrough */
236
2022-02-16
op
case IMSG_STOP:
237
2022-02-16
op
return 1;
240
2022-02-16
op
return 0;
244
2022-03-09
op
play(const void *buf, size_t len)
246
2022-06-10
op
size_t w;
247
2022-06-10
op
int nfds, revents, r;
249
2022-06-10
op
while (len != 0) {
250
2022-06-10
op
nfds = sio_pollfd(hdl, player_pfds + 1, POLLOUT);
251
2022-06-10
op
r = poll(player_pfds, nfds + 1, INFTIM);
252
2022-06-10
op
if (r == -1)
253
2022-06-10
op
fatal("poll");
255
2022-06-10
op
if (player_pfds[0].revents & (POLLHUP|POLLIN)) {
256
2022-06-10
op
if (player_shouldstop()) {
257
2022-06-10
op
sio_flush(hdl);
258
2022-06-10
op
stopped = 1;
259
2022-06-10
op
return 0;
263
2022-06-10
op
revents = sio_revents(hdl, player_pfds + 1);
264
2022-06-10
op
if (revents & POLLHUP)
265
2022-06-10
op
fatalx("sndio hang-up");
266
2022-06-10
op
if (revents & POLLOUT) {
267
2022-06-10
op
w = sio_write(hdl, buf, len);
268
2022-06-10
op
len -= w;
269
2022-06-10
op
buf += w;
273
2022-03-09
op
return 1;
277
2022-02-16
op
player(int debug, int verbose)
281
2022-02-16
op
log_init(debug, LOG_DAEMON);
282
2022-02-16
op
log_setverbose(verbose);
284
2022-02-16
op
setproctitle("player");
285
2022-02-16
op
log_procinit("player");
289
2022-02-16
op
static int attached;
291
2022-02-16
op
while (!attached)
292
2022-02-16
op
sleep(1);
296
2022-06-10
op
if ((hdl = sio_open(SIO_DEVANY, SIO_PLAY, 1)) == NULL)
297
2022-06-10
op
fatal("sio_open");
299
2022-06-10
op
/* allocate one extra for imsg */
300
2022-06-10
op
player_pfds = calloc(sio_nfds(hdl) + 1, sizeof(*player_pfds));
301
2022-06-10
op
if (player_pfds == NULL)
302
2022-06-10
op
fatal("calloc");
304
2022-06-10
op
player_pfds[0].events = POLLIN;
305
2022-06-10
op
player_pfds[0].fd = 3;
307
2022-02-16
op
ibuf = xmalloc(sizeof(*ibuf));
308
2022-02-16
op
imsg_init(ibuf, 3);
310
2022-02-16
op
signal(SIGINT, player_signal_handler);
311
2022-02-16
op
signal(SIGTERM, player_signal_handler);
313
2022-02-16
op
signal(SIGHUP, SIG_IGN);
314
2022-02-16
op
signal(SIGPIPE, SIG_IGN);
316
2022-03-10
op
if (pledge("stdio recvfd audio", NULL) == -1)
317
2022-02-16
op
fatal("pledge");
319
2022-02-16
op
while (!halted) {
320
2022-02-16
op
while (nextfd == -1)
321
2022-02-16
op
player_dispatch();
323
2022-05-09
op
r = player_playnext();
324
2022-05-09
op
if (r == -1)
325
2022-05-09
op
player_senderr();
326
2022-05-09
op
if (r == 0)
327
2022-05-09
op
player_sendeof();
330
2022-02-16
op
return 0;