From 8c8d095eaf5207d4446c561bf26f0eecd2d5f505 Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Tue, 26 Jul 2011 20:51:24 +0200 Subject: [PATCH] alsa: Select on the alsa poll fd. This teaches the post_select method of the alsa writer to get a control file descriptor from the alsa library via snd_pcm_poll_descriptors(). This file descriptor becomes readable when a buffer period has passed and new samples can be written to the alsa handle. We add the fd to the read fd set so that the select() call of the main scheduler loop returns just in time. This is more precise and simpler than the previous approach to compute the time until a buffer underrun occurs. --- alsa_write.c | 35 +++++++++++++++-------------------- error.h | 1 + 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/alsa_write.c b/alsa_write.c index 5965159e..047e88f2 100644 --- a/alsa_write.c +++ b/alsa_write.c @@ -52,6 +52,8 @@ struct private_alsa_write_data { */ unsigned channels; struct timeval drain_barrier; + /* File descriptor for select(). */ + int poll_fd; }; static snd_pcm_format_t get_alsa_pcm_format(enum sample_format sf) @@ -143,11 +145,13 @@ static int alsa_init(struct private_alsa_write_data *pad, static void alsa_write_pre_select(struct sched *s, struct task *t) { + struct pollfd pfd; struct writer_node *wn = container_of(t, struct writer_node, task); struct private_alsa_write_data *pad = wn->private_data; - snd_pcm_sframes_t avail, underrun; int ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF); + if (pad) + pad->poll_fd = -1; if (ret == 0) return; if (!pad) { @@ -158,25 +162,14 @@ static void alsa_write_pre_select(struct sched *s, struct task *t) sched_request_barrier_or_min_delay(&pad->drain_barrier, s); return; } - /* - * Data is available to be written to the alsa handle. Compute number - * of milliseconds until next buffer underrun would occur. - * - * snd_pcm_avail_update() updates the current available count of - * samples for writing. It is a light method to obtain current stream - * position, because it does not require the user <-> kernel context - * switch, but the value is less accurate, because ring buffer pointers - * are updated in kernel drivers only when an interrupt occurs. - */ - avail = snd_pcm_avail_update(pad->handle); - if (avail < 0) - avail = 0; - underrun = (pad->buffer_frames - avail) * pad->buffer_time - / pad->buffer_frames / 1000; - if (underrun < 50) - underrun = 50; - underrun -= 50; - sched_request_timeout_ms(underrun, s); + ret = snd_pcm_poll_descriptors(pad->handle, &pfd, 1); + if (ret < 0) { + PARA_ERROR_LOG("%s\n", snd_strerror(-ret)); + t->error = -E_ALSA_POLL_FDS; + return; + } + pad->poll_fd = pfd.fd; + para_fd_set(pfd.fd, &s->rfds, &s->max_fileno); } static void alsa_close(struct writer_node *wn) @@ -255,6 +248,8 @@ again: wn->min_iqs = pad->bytes_per_frame; goto again; } + if (pad->poll_fd >= 0 && !FD_ISSET(pad->poll_fd, &s->rfds)) + return; frames = bytes / pad->bytes_per_frame; frames = snd_pcm_writei(pad->handle, data, frames); if (frames == 0 || frames == -EAGAIN) diff --git a/error.h b/error.h index b8078770..da0efc47 100644 --- a/error.h +++ b/error.h @@ -441,6 +441,7 @@ extern const char **para_errlist[]; PARA_ERROR(SET_RATE, "snd_pcm_hw_params_set_rate_near failed"), \ PARA_ERROR(START_THRESHOLD, "snd_pcm_sw_params_set_start_threshold() failed"), \ PARA_ERROR(STOP_THRESHOLD, "snd_pcm_sw_params_set_stop_threshold() failed"), \ + PARA_ERROR(ALSA_POLL_FDS, "could not get alsa poll fd"), \ #define WRITE_COMMON_ERRORS \ -- 2.39.5