- return 0;
-fatal:
- abandon();
-error:
- /* We assume the error is temporary and that we'll retry in a bit. */
- if(pcm) {
- snd_pcm_close(pcm);
- pcm = 0;
- }
- return -1;
-}
-
-/** @brief Play via ALSA */
-static size_t alsa_play(size_t frames) {
- snd_pcm_sframes_t pcm_written_frames;
- int err;
-
- pcm_written_frames = snd_pcm_writei(pcm,
- playing->buffer + playing->start,
- frames);
- D(("actually play %zu frames, wrote %d",
- frames, (int)pcm_written_frames));
- if(pcm_written_frames < 0) {
- switch(pcm_written_frames) {
- case -EPIPE: /* underrun */
- error(0, "snd_pcm_writei reports underrun");
- if((err = snd_pcm_prepare(pcm)) < 0)
- fatal(0, "error calling snd_pcm_prepare: %d", err);
- return 0;
- case -EAGAIN:
- return 0;
- default:
- fatal(0, "error calling snd_pcm_writei: %d",
- (int)pcm_written_frames);
- }
- } else
- return pcm_written_frames;
-}
-
-static int alsa_slots, alsa_nslots = -1;
-
-/** @brief Fill in poll fd array for ALSA */
-static void alsa_beforepoll(void) {
- /* We send sample data to ALSA as fast as it can accept it, relying on
- * the fact that it has a relatively small buffer to minimize pause
- * latency. */
- int retry = 3, err;
-
- alsa_slots = fdno;
- do {
- retry = 0;
- alsa_nslots = snd_pcm_poll_descriptors(pcm, &fds[fdno], NFDS - fdno);
- if((alsa_nslots <= 0
- || !(fds[alsa_slots].events & POLLOUT))
- && snd_pcm_state(pcm) == SND_PCM_STATE_XRUN) {
- error(0, "underrun detected after call to snd_pcm_poll_descriptors()");
- if((err = snd_pcm_prepare(pcm)))
- fatal(0, "error calling snd_pcm_prepare: %d", err);
- } else
- break;
- } while(retry-- > 0);
- if(alsa_nslots >= 0)
- fdno += alsa_nslots;
-}
-
-/** @brief Process poll() results for ALSA */
-static int alsa_afterpoll(void) {
- int err;
-
- if(alsa_slots != -1) {
- unsigned short alsa_revents;
-
- if((err = snd_pcm_poll_descriptors_revents(pcm,
- &fds[alsa_slots],
- alsa_nslots,
- &alsa_revents)) < 0)
- fatal(0, "error calling snd_pcm_poll_descriptors_revents: %d", err);
- if(alsa_revents & (POLLOUT | POLLERR))
- play(3 * FRAMES);
- return 0;
- } else
- return 1;
-}
-
-/** @brief ALSA deactivation */
-static void alsa_deactivate(void) {
- if(pcm) {
- int err;
-
- if((err = snd_pcm_nonblock(pcm, 0)) < 0)
- fatal(0, "error calling snd_pcm_nonblock: %d", err);
- D(("draining pcm"));
- snd_pcm_drain(pcm);
- D(("closing pcm"));
- snd_pcm_close(pcm);
- pcm = 0;
- forceplay = 0;
- D(("released audio device"));
- }
-}
-#endif
-
-/** @brief Command backend initialization */
-static void command_init(void) {
- info("selected command backend");
- fork_cmd();
-}
-
-/** @brief Play to a subprocess */
-static size_t command_play(size_t frames) {
- size_t bytes = frames * bpf;
- int written_bytes;
-
- written_bytes = write(cmdfd, playing->buffer + playing->start, bytes);
- D(("actually play %zu bytes, wrote %d",
- bytes, written_bytes));
- if(written_bytes < 0) {
- switch(errno) {
- case EPIPE:
- error(0, "hmm, command died; trying another");
- fork_cmd();
- return 0;
- case EAGAIN:
- return 0;
- default:
- fatal(errno, "error writing to subprocess");
- }
- } else
- return written_bytes / bpf;
-}
-
-static int cmdfd_slot;
-
-/** @brief Update poll array for writing to subprocess */
-static void command_beforepoll(void) {
- /* We send sample data to the subprocess as fast as it can accept it.
- * This isn't ideal as pause latency can be very high as a result. */
- if(cmdfd >= 0)
- cmdfd_slot = addfd(cmdfd, POLLOUT);
-}
-
-/** @brief Process poll() results for subprocess play */
-static int command_afterpoll(void) {
- if(cmdfd_slot != -1) {
- if(fds[cmdfd_slot].revents & (POLLOUT | POLLERR))
- play(3 * FRAMES);
- return 0;
- } else
- return -1;
-}
-
-/** @brief Command/network backend activation */
-static int generic_activate(void) {
- if(!ready) {
- bufsize = 3 * FRAMES;
- bpf = bytes_per_frame(&config->sample_format);
- D(("acquired audio device"));
- ready = 1;
- }
- return 0;
-}
-
-/** @brief Network backend initialization */
-static void network_init(void) {
- struct addrinfo *res, *sres;
- static const struct addrinfo pref = {
- 0,
- PF_INET,
- SOCK_DGRAM,
- IPPROTO_UDP,
- 0,
- 0,
- 0,
- 0
- };
- static const struct addrinfo prefbind = {
- AI_PASSIVE,
- PF_INET,
- SOCK_DGRAM,
- IPPROTO_UDP,
- 0,
- 0,
- 0,
- 0
- };
- static const int one = 1;
- int sndbuf, target_sndbuf = 131072;
- socklen_t len;
- char *sockname, *ssockname;
-
- res = get_address(&config->broadcast, &pref, &sockname);
- if(!res) exit(-1);
- if(config->broadcast_from.n) {
- sres = get_address(&config->broadcast_from, &prefbind, &ssockname);
- if(!sres) exit(-1);
- } else
- sres = 0;
- if((bfd = socket(res->ai_family,
- res->ai_socktype,
- res->ai_protocol)) < 0)
- fatal(errno, "error creating broadcast socket");
- if(setsockopt(bfd, SOL_SOCKET, SO_BROADCAST, &one, sizeof one) < 0)
- fatal(errno, "error setting SO_BROADCAST on broadcast socket");
- len = sizeof sndbuf;
- if(getsockopt(bfd, SOL_SOCKET, SO_SNDBUF,
- &sndbuf, &len) < 0)
- fatal(errno, "error getting SO_SNDBUF");
- if(target_sndbuf > sndbuf) {
- if(setsockopt(bfd, SOL_SOCKET, SO_SNDBUF,
- &target_sndbuf, sizeof target_sndbuf) < 0)
- error(errno, "error setting SO_SNDBUF to %d", target_sndbuf);