- rate = playing->format.rate;
- if((err = snd_pcm_hw_params_set_rate_near(pcm, hwparams, &rate, 0)) < 0) {
- error(0, "error from snd_pcm_hw_params_set_rate (%d): %d",
- playing->format.rate, err);
- goto fatal;
- }
- if(rate != (unsigned)playing->format.rate)
- info("want rate %d, got %u", playing->format.rate, rate);
- if((err = snd_pcm_hw_params_set_channels(pcm, hwparams,
- playing->format.channels)) < 0) {
- error(0, "error from snd_pcm_hw_params_set_channels (%d): %d",
- playing->format.channels, err);
- goto fatal;
- }
- bufsize = 3 * FRAMES;
- pcm_bufsize = bufsize;
- if((err = snd_pcm_hw_params_set_buffer_size_near(pcm, hwparams,
- &pcm_bufsize)) < 0)
- fatal(0, "error from snd_pcm_hw_params_set_buffer_size (%d): %d",
- 3 * FRAMES, err);
- if(pcm_bufsize != 3 * FRAMES && pcm_bufsize != last_pcm_bufsize)
- info("asked for PCM buffer of %d frames, got %d",
- 3 * FRAMES, (int)pcm_bufsize);
- last_pcm_bufsize = pcm_bufsize;
- if((err = snd_pcm_hw_params(pcm, hwparams)) < 0)
- fatal(0, "error calling snd_pcm_hw_params: %d", err);
- D(("set up sw params"));
- snd_pcm_sw_params_alloca(&swparams);
- if((err = snd_pcm_sw_params_current(pcm, swparams)) < 0)
- fatal(0, "error calling snd_pcm_sw_params_current: %d", err);
- if((err = snd_pcm_sw_params_set_avail_min(pcm, swparams, FRAMES)) < 0)
- fatal(0, "error calling snd_pcm_sw_params_set_avail_min %d: %d",
- FRAMES, err);
- if((err = snd_pcm_sw_params(pcm, swparams)) < 0)
- fatal(0, "error calling snd_pcm_sw_params: %d", err);
- pcm_format = playing->format;
- bpf = bytes_per_frame(&pcm_format);
- D(("acquired audio device"));
- log_params(hwparams, swparams);
- ready = 1;
- }
- 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;