summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWim Taymans <wim.taymans@collabora.co.uk>2010-07-14 15:14:56 +0200
committerWim Taymans <wim.taymans@collabora.co.uk>2010-07-14 15:14:56 +0200
commit09b48da1547aa7ecb8ff9d504c6e491ab35e5b4b (patch)
tree346c5902c845f6e59aaffd376421df6e8f528f3e
parent87fbafbda1fb9ea2fd5c7fc52d2d33f481c0e043 (diff)
alsa: more hacking
-rw-r--r--src/modules/alsa/alsa-manager.c123
-rw-r--r--src/modules/alsa/alsa-manager.h4
-rw-r--r--src/modules/alsa/alsa-sink.c458
-rw-r--r--src/modules/alsa/alsa-source.c401
4 files changed, 449 insertions, 537 deletions
diff --git a/src/modules/alsa/alsa-manager.c b/src/modules/alsa/alsa-manager.c
index 90053ab80..19515d565 100644
--- a/src/modules/alsa/alsa-manager.c
+++ b/src/modules/alsa/alsa-manager.c
@@ -66,54 +66,101 @@ static void thread_func(void *userdata) {
for (;;) {
int ret;
uint32_t idx;
- pa_alsa_task *t, *st;;
+ pa_alsa_task *t, *st;
int work_done, wd;
pa_usec_t sleep_usec, s_usec;
- pa_bool_t on_timeout = pa_rtpoll_timer_elapsed(m->rtpoll);
+ pa_bool_t on_timeout;
+ size_t n_bytes, n;
+
+ st = NULL;
+ on_timeout = pa_rtpoll_timer_elapsed(m->rtpoll);
+ work_done = 0;
#ifdef DEBUG_TIMING
- pa_log_debug("Loop");
+ pa_log_debug("Loop, on_timeout %d, sink_revents %d, source_revents %d", on_timeout, sink_revents, source_revents);
#endif
- work_done = 0;
- st = NULL;
- sleep_usec = 0;
+ for (;;) {
+ pa_bool_t first = TRUE;
- /* process sinks */
- PA_IDXSET_FOREACH(t, m->sinks, idx) {
- wd = t->do_work (t->userdata, &s_usec, sink_revents & POLLOUT, on_timeout);
- if (wd < 0)
- goto fail;
+ sleep_usec = 0;
+ n_bytes = 0;
- if (wd) {
- if (st == NULL)
- st = t;
+ /* see how much we can process */
+ PA_IDXSET_FOREACH(t, m->sinks, idx) {
+ t->ready = t->get_avail (t->userdata, &n, &s_usec, sink_revents & POLLOUT, on_timeout);
+ if (t->ready < 0)
+ goto fail;
+ else if (t->ready > 0) {
+ if (st == NULL)
+ st = t;
- if (sleep_usec == 0 || s_usec < sleep_usec)
- sleep_usec = s_usec;
+#ifdef DEBUG_TIMING
+ pa_log_debug("sink task %p: n_bytes %d, sleep_usec %lu", t, (int) n, s_usec);
+#endif
+
+ if (first || n < n_bytes)
+ n_bytes = n;
+ if (first || s_usec < sleep_usec)
+ sleep_usec = s_usec;
+
+ first = FALSE;
+ }
}
- work_done |= wd;
- }
- /* process sources */
- PA_IDXSET_FOREACH(t, m->sources, idx) {
- wd = t->do_work (t->userdata, &s_usec, source_revents & POLLIN, on_timeout);
- if (wd < 0)
- goto fail;
+ PA_IDXSET_FOREACH(t, m->sources, idx) {
+ t->ready = t->get_avail (t->userdata, &n, &s_usec, source_revents & POLLIN, on_timeout);
+ if (t->ready < 0)
+ goto fail;
+ else if (t->ready > 0) {
+ if (st == NULL)
+ st = t;
- if (wd) {
- if (st == NULL)
- st = t;
+#ifdef DEBUG_TIMING
+ pa_log_debug("source task %p: n_bytes %d, sleep_usec %lu", t, (int) n, s_usec);
+#endif
+ if (first || n < n_bytes)
+ n_bytes = n;
+ if (first || s_usec < sleep_usec)
+ sleep_usec = s_usec;
- if (sleep_usec == 0 || s_usec < sleep_usec)
- sleep_usec = s_usec;
+ first = FALSE;
+ }
}
- work_done |= wd;
- }
+ on_timeout = FALSE;
+
#ifdef DEBUG_TIMING
- pa_log_debug("work done %d, sleep_usec %lu", work_done, sleep_usec);
+ pa_log_debug("total: n_bytes %d, sleep_usec %lu", (int) n_bytes, sleep_usec);
#endif
+ /* nothing available, wait some more */
+ if (n_bytes == 0 || sleep_usec > 0)
+ break;
+
+ /* process sinks */
+ PA_IDXSET_FOREACH(t, m->sinks, idx) {
+ if (t->ready <= 0)
+ continue;
+
+ wd = t->do_work (t->userdata, n_bytes);
+ if (wd < 0)
+ goto fail;
+
+ work_done = 1;
+ }
+ /* process sources */
+ PA_IDXSET_FOREACH(t, m->sources, idx) {
+ if (t->ready <= 0)
+ continue;
+
+ wd = t->do_work (t->userdata, n_bytes);
+ if (wd < 0)
+ goto fail;
+
+ work_done = 1;
+ }
+ }
+
if (work_done) {
- pa_usec_t cusec, now, now1, now2;
+ pa_usec_t now1, now2;
if (st)
ret = st->get_position (st->userdata, &now1, &now2);
@@ -131,6 +178,14 @@ static void thread_func(void *userdata) {
m->smoother_interval = PA_MIN (m->smoother_interval * 2, SMOOTHER_MAX_INTERVAL);
}
}
+ }
+
+#ifdef DEBUG_TIMING
+ pa_log_debug("sleep_usec %lu", sleep_usec);
+#endif
+
+ if (sleep_usec) {
+ pa_usec_t cusec, now;
/* Convert from the sound card time domain to the
* system time domain */
@@ -140,8 +195,10 @@ static void thread_func(void *userdata) {
/* pa_log_debug("Waking up in %0.2fms (system clock).", (double) cusec / PA_USEC_PER_MSEC); */
/* We don't trust the conversion, so we wake up whatever comes first */
+// now += PA_MIN(sleep_usec, cusec);
+// pa_rtpoll_set_timer_absolute(m->rtpoll, now);
pa_rtpoll_set_timer_relative(m->rtpoll, PA_MIN(sleep_usec, cusec));
- } else if (sleep_usec == 0)
+ } else
/* disable the timer and wait for something to happen */
pa_rtpoll_set_timer_disabled(m->rtpoll);
diff --git a/src/modules/alsa/alsa-manager.h b/src/modules/alsa/alsa-manager.h
index 9b77cc89d..16e0cd9e4 100644
--- a/src/modules/alsa/alsa-manager.h
+++ b/src/modules/alsa/alsa-manager.h
@@ -33,10 +33,12 @@ typedef struct pa_alsa_manager pa_alsa_manager;
struct pa_alsa_task {
uint32_t idx; /* filled by _add_ */
+ pa_bool_t ready; /* result from last get_avail */
void *userdata;
int (*get_position) (void *userdata, pa_usec_t *now1, pa_usec_t *now2);
- int (*do_work) (void *userdata, pa_usec_t *sleep_usec, pa_bool_t polled, pa_bool_t on_timeout);
+ int (*get_avail) (void *userdata, size_t *n_bytes, pa_usec_t *sleep_usec, pa_bool_t polled, pa_bool_t on_timeout);
+ int (*do_work) (void *userdata, size_t n_bytes);
int (*handle_poll) (void *userdata, unsigned short *revents);
};
diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index 118f7abb4..233df17c0 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -468,317 +468,151 @@ static size_t check_left_to_play(struct userdata *u, size_t n_bytes, pa_bool_t o
return left_to_play;
}
-static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled, pa_bool_t on_timeout) {
- pa_bool_t work_done = TRUE;
- pa_usec_t max_sleep_usec = 0, process_usec = 0;
- size_t left_to_play;
- unsigned j = 0;
+static int mmap_write(struct userdata *u, size_t n_bytes) {
+ pa_bool_t after_avail = TRUE;
+ pa_bool_t work_done = FALSE;
pa_assert(u);
pa_sink_assert_ref(u->sink);
- if (u->use_tsched)
- hw_sleep_time(u, &max_sleep_usec, &process_usec);
-
for (;;) {
- snd_pcm_sframes_t n;
- size_t n_bytes;
- int r;
- pa_bool_t after_avail = TRUE;
+ pa_memchunk chunk;
+ void *p;
+ int err, r;
+ const snd_pcm_channel_area_t *areas;
+ snd_pcm_uframes_t offset, frames;
+ snd_pcm_sframes_t sframes;
+
+ frames = (snd_pcm_uframes_t) (n_bytes / u->frame_size);
+/* pa_log_debug("%lu frames to write", (unsigned long) frames); */
- /* First we determine how many samples are missing to fill the
- * buffer up to 100% */
+ if (PA_UNLIKELY((err = pa_alsa_safe_mmap_begin(u->pcm_handle, &areas, &offset, &frames, u->hwbuf_size, &u->sink->sample_spec)) < 0)) {
- if (PA_UNLIKELY((n = pa_alsa_safe_avail(u->pcm_handle, u->hwbuf_size, &u->sink->sample_spec)) < 0)) {
+ if (!after_avail && err == -EAGAIN)
+ break;
- if ((r = try_recover(u, "snd_pcm_avail", (int) n)) == 0)
+ if ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0)
continue;
return r;
}
- n_bytes = (size_t) n * u->frame_size;
-
-#ifdef DEBUG_TIMING
- pa_log_debug("avail: %lu", (unsigned long) n_bytes);
-#endif
-
- left_to_play = check_left_to_play(u, n_bytes, on_timeout);
- on_timeout = FALSE;
-
- if (u->use_tsched)
-
- /* We won't fill up the playback buffer before at least
- * half the sleep time is over because otherwise we might
- * ask for more data from the clients then they expect. We
- * need to guarantee that clients only have to keep around
- * a single hw buffer length. */
-
- if (!polled &&
- pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) > process_usec+max_sleep_usec/2) {
-#ifdef DEBUG_TIMING
- pa_log_debug("Not filling up, because too early.");
-#endif
- break;
- }
-
- if (PA_UNLIKELY(n_bytes <= u->hwbuf_unused)) {
-
- if (polled)
- PA_ONCE_BEGIN {
- char *dn = pa_alsa_get_driver_name_by_pcm(u->pcm_handle);
- pa_log(_("ALSA woke us up to write new data to the device, but there was actually nothing to write!\n"
- "Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers.\n"
- "We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() returned 0 or another value < min_avail."),
- pa_strnull(dn));
- pa_xfree(dn);
- } PA_ONCE_END;
-
-#ifdef DEBUG_TIMING
- pa_log_debug("Not filling up, because not necessary.");
-#endif
- break;
- }
-
-
- if (++j > 10) {
-#ifdef DEBUG_TIMING
- pa_log_debug("Not filling up, because already too many iterations.");
-#endif
+ /* Make sure that if these memblocks need to be copied they will fit into one slot */
+ if (frames > pa_mempool_block_size_max(u->sink->core->mempool)/u->frame_size)
+ frames = pa_mempool_block_size_max(u->sink->core->mempool)/u->frame_size;
+ if (!after_avail && frames == 0)
break;
- }
- n_bytes -= u->hwbuf_unused;
- polled = FALSE;
+ pa_assert(frames > 0);
+ after_avail = FALSE;
-#ifdef DEBUG_TIMING
- pa_log_debug("Filling up");
-#endif
-
- for (;;) {
- pa_memchunk chunk;
- void *p;
- int err;
- const snd_pcm_channel_area_t *areas;
- snd_pcm_uframes_t offset, frames;
- snd_pcm_sframes_t sframes;
-
- frames = (snd_pcm_uframes_t) (n_bytes / u->frame_size);
-/* pa_log_debug("%lu frames to write", (unsigned long) frames); */
+ /* Check these are multiples of 8 bit */
+ pa_assert((areas[0].first & 7) == 0);
+ pa_assert((areas[0].step & 7)== 0);
- if (PA_UNLIKELY((err = pa_alsa_safe_mmap_begin(u->pcm_handle, &areas, &offset, &frames, u->hwbuf_size, &u->sink->sample_spec)) < 0)) {
+ /* We assume a single interleaved memory buffer */
+ pa_assert((areas[0].first >> 3) == 0);
+ pa_assert((areas[0].step >> 3) == u->frame_size);
- if (!after_avail && err == -EAGAIN)
- break;
-
- if ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0)
- continue;
+ p = (uint8_t*) areas[0].addr + (offset * u->frame_size);
- return r;
- }
+ chunk.memblock = pa_memblock_new_fixed(u->core->mempool, p, frames * u->frame_size, TRUE);
+ chunk.length = pa_memblock_get_length(chunk.memblock);
+ chunk.index = 0;
- /* Make sure that if these memblocks need to be copied they will fit into one slot */
- if (frames > pa_mempool_block_size_max(u->sink->core->mempool)/u->frame_size)
- frames = pa_mempool_block_size_max(u->sink->core->mempool)/u->frame_size;
-
- if (!after_avail && frames == 0)
- break;
+ pa_sink_render_into_full(u->sink, &chunk);
+ pa_memblock_unref_fixed(chunk.memblock);
- pa_assert(frames > 0);
- after_avail = FALSE;
+ work_done = TRUE;
- /* Check these are multiples of 8 bit */
- pa_assert((areas[0].first & 7) == 0);
- pa_assert((areas[0].step & 7)== 0);
+ if (PA_UNLIKELY((sframes = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0)) {
- /* We assume a single interleaved memory buffer */
- pa_assert((areas[0].first >> 3) == 0);
- pa_assert((areas[0].step >> 3) == u->frame_size);
-
- p = (uint8_t*) areas[0].addr + (offset * u->frame_size);
-
- chunk.memblock = pa_memblock_new_fixed(u->core->mempool, p, frames * u->frame_size, TRUE);
- chunk.length = pa_memblock_get_length(chunk.memblock);
- chunk.index = 0;
-
- pa_sink_render_into_full(u->sink, &chunk);
- pa_memblock_unref_fixed(chunk.memblock);
-
- if (PA_UNLIKELY((sframes = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0)) {
-
- if ((r = try_recover(u, "snd_pcm_mmap_commit", (int) sframes)) == 0)
- continue;
-
- return r;
- }
+ if ((r = try_recover(u, "snd_pcm_mmap_commit", (int) sframes)) == 0)
+ continue;
- work_done = TRUE;
+ return r;
+ }
- u->write_count += frames * u->frame_size;
- u->since_start += frames * u->frame_size;
+ u->write_count += frames * u->frame_size;
+ u->since_start += frames * u->frame_size;
#ifdef DEBUG_TIMING
- pa_log_debug("Wrote %lu bytes (of possible %lu bytes)", (unsigned long) (frames * u->frame_size), (unsigned long) n_bytes);
+ pa_log_debug("Wrote %lu bytes (of possible %lu bytes)", (unsigned long) (frames * u->frame_size), (unsigned long) n_bytes);
#endif
- if ((size_t) frames * u->frame_size >= n_bytes)
- break;
+ if ((size_t) frames * u->frame_size >= n_bytes)
+ break;
- n_bytes -= (size_t) frames * u->frame_size;
- }
+ n_bytes -= (size_t) frames * u->frame_size;
}
-
- if (u->use_tsched) {
- *sleep_usec = pa_bytes_to_usec(left_to_play, &u->sink->sample_spec);
-
- if (*sleep_usec > process_usec)
- *sleep_usec -= process_usec;
- else
- *sleep_usec = 0;
- } else
- *sleep_usec = 0;
-
return work_done ? 1 : 0;
}
-static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled, pa_bool_t on_timeout) {
+static int unix_write(struct userdata *u, size_t n_bytes) {
+ pa_bool_t after_avail = TRUE;
pa_bool_t work_done = FALSE;
- pa_usec_t max_sleep_usec = 0, process_usec = 0;
- size_t left_to_play;
- unsigned j = 0;
pa_assert(u);
pa_sink_assert_ref(u->sink);
- if (u->use_tsched)
- hw_sleep_time(u, &max_sleep_usec, &process_usec);
-
for (;;) {
- snd_pcm_sframes_t n;
- size_t n_bytes;
+ snd_pcm_sframes_t frames;
+ void *p;
int r;
- pa_bool_t after_avail = TRUE;
- if (PA_UNLIKELY((n = pa_alsa_safe_avail(u->pcm_handle, u->hwbuf_size, &u->sink->sample_spec)) < 0)) {
+/* pa_log_debug("%lu frames to write", (unsigned long) frames); */
- if ((r = try_recover(u, "snd_pcm_avail", (int) n)) == 0)
- continue;
+ if (u->memchunk.length <= 0)
+ pa_sink_render(u->sink, n_bytes, &u->memchunk);
- return r;
- }
+ pa_assert(u->memchunk.length > 0);
- n_bytes = (size_t) n * u->frame_size;
- left_to_play = check_left_to_play(u, n_bytes, on_timeout);
- on_timeout = FALSE;
+ frames = (snd_pcm_sframes_t) (u->memchunk.length / u->frame_size);
- if (u->use_tsched)
+ if (frames > (snd_pcm_sframes_t) (n_bytes/u->frame_size))
+ frames = (snd_pcm_sframes_t) (n_bytes/u->frame_size);
- /* We won't fill up the playback buffer before at least
- * half the sleep time is over because otherwise we might
- * ask for more data from the clients then they expect. We
- * need to guarantee that clients only have to keep around
- * a single hw buffer length. */
+ p = pa_memblock_acquire(u->memchunk.memblock);
+ frames = snd_pcm_writei(u->pcm_handle, (const uint8_t*) p + u->memchunk.index, (snd_pcm_uframes_t) frames);
+ pa_memblock_release(u->memchunk.memblock);
- if (!polled &&
- pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) > process_usec+max_sleep_usec/2)
- break;
+ if (PA_UNLIKELY(frames < 0)) {
- if (PA_UNLIKELY(n_bytes <= u->hwbuf_unused)) {
+ if (!after_avail && (int) frames == -EAGAIN)
+ break;
- if (polled)
- PA_ONCE_BEGIN {
- char *dn = pa_alsa_get_driver_name_by_pcm(u->pcm_handle);
- pa_log(_("ALSA woke us up to write new data to the device, but there was actually nothing to write!\n"
- "Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers.\n"
- "We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() returned 0 or another value < min_avail."),
- pa_strnull(dn));
- pa_xfree(dn);
- } PA_ONCE_END;
+ if ((r = try_recover(u, "snd_pcm_writei", (int) frames)) == 0)
+ continue;
- break;
+ return r;
}
- if (++j > 10) {
-#ifdef DEBUG_TIMING
- pa_log_debug("Not filling up, because already too many iterations.");
-#endif
-
+ if (!after_avail && frames == 0)
break;
- }
-
- n_bytes -= u->hwbuf_unused;
- polled = FALSE;
-
- for (;;) {
- snd_pcm_sframes_t frames;
- void *p;
-
-/* pa_log_debug("%lu frames to write", (unsigned long) frames); */
- if (u->memchunk.length <= 0)
- pa_sink_render(u->sink, n_bytes, &u->memchunk);
+ pa_assert(frames > 0);
+ after_avail = FALSE;
+ work_done = TRUE;
- pa_assert(u->memchunk.length > 0);
+ u->memchunk.index += (size_t) frames * u->frame_size;
+ u->memchunk.length -= (size_t) frames * u->frame_size;
- frames = (snd_pcm_sframes_t) (u->memchunk.length / u->frame_size);
-
- if (frames > (snd_pcm_sframes_t) (n_bytes/u->frame_size))
- frames = (snd_pcm_sframes_t) (n_bytes/u->frame_size);
-
- p = pa_memblock_acquire(u->memchunk.memblock);
- frames = snd_pcm_writei(u->pcm_handle, (const uint8_t*) p + u->memchunk.index, (snd_pcm_uframes_t) frames);
- pa_memblock_release(u->memchunk.memblock);
-
- if (PA_UNLIKELY(frames < 0)) {
-
- if (!after_avail && (int) frames == -EAGAIN)
- break;
-
- if ((r = try_recover(u, "snd_pcm_writei", (int) frames)) == 0)
- continue;
-
- return r;
- }
-
- if (!after_avail && frames == 0)
- break;
-
- pa_assert(frames > 0);
- after_avail = FALSE;
-
- u->memchunk.index += (size_t) frames * u->frame_size;
- u->memchunk.length -= (size_t) frames * u->frame_size;
-
- if (u->memchunk.length <= 0) {
- pa_memblock_unref(u->memchunk.memblock);
- pa_memchunk_reset(&u->memchunk);
- }
-
- work_done = TRUE;
+ if (u->memchunk.length <= 0) {
+ pa_memblock_unref(u->memchunk.memblock);
+ pa_memchunk_reset(&u->memchunk);
+ }
- u->write_count += frames * u->frame_size;
- u->since_start += frames * u->frame_size;
+ u->write_count += frames * u->frame_size;
+ u->since_start += frames * u->frame_size;
-/* pa_log_debug("wrote %lu frames", (unsigned long) frames); */
+/* pa_log_debug("wrote %lu frames", (unsigned long) frames); */
- if ((size_t) frames * u->frame_size >= n_bytes)
- break;
+ if ((size_t) frames * u->frame_size >= n_bytes)
+ break;
- n_bytes -= (size_t) frames * u->frame_size;
- }
+ n_bytes -= (size_t) frames * u->frame_size;
}
-
- if (u->use_tsched) {
- *sleep_usec = pa_bytes_to_usec(left_to_play, &u->sink->sample_spec);
-
- if (*sleep_usec > process_usec)
- *sleep_usec -= process_usec;
- else
- *sleep_usec = 0;
- } else
- *sleep_usec = 0;
-
return work_done ? 1 : 0;
}
@@ -1344,29 +1178,88 @@ static int process_rewind(struct userdata *u) {
return 0;
}
-static int do_work(void *userdata, pa_usec_t *sleep_usec, pa_bool_t polled, pa_bool_t on_timeout) {
+static int get_avail(void *userdata, size_t *n_bytes, pa_usec_t *sleep_usec, pa_bool_t polled, pa_bool_t on_timeout) {
struct userdata *u = userdata;
- int work_done = 0;
+ pa_bool_t do_fill = FALSE;
+ snd_pcm_sframes_t n;
+ pa_usec_t max_sleep_usec = 0, process_usec = 0;
+ size_t left_to_play = 0;
+ int r;
- /* Render some data and write it to the dsp */
- if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
- if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))
- if (process_rewind(u) < 0)
- return -1;
+ if (!PA_SINK_IS_OPENED(u->sink->thread_info.state))
+ return 0;
- if (u->use_mmap)
- work_done = mmap_write(u, sleep_usec, polled, on_timeout);
- else
- work_done = unix_write(u, sleep_usec, polled, on_timeout);
+ if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))
+ if ((r = process_rewind(u)) < 0)
+ return r;
- if (work_done) {
- if (u->first) {
- pa_log_info("Starting playback.");
- snd_pcm_start(u->pcm_handle);
- }
+ if (u->use_tsched)
+ hw_sleep_time(u, &max_sleep_usec, &process_usec);
+
+ /* First we determine how many samples are missing to fill the
+ * buffer up to 100% */
+
+ if (PA_UNLIKELY((n = pa_alsa_safe_avail(u->pcm_handle, u->hwbuf_size, &u->sink->sample_spec)) < 0)) {
+
+ if ((r = try_recover(u, "snd_pcm_avail", (int) n)) < 0)
+ return r;
+ }
+
+ *n_bytes = (size_t) n * u->frame_size;
+
+#ifdef DEBUG_TIMING
+ pa_log_debug("avail: %lu", (unsigned long) *n_bytes);
+#endif
+
+ left_to_play = check_left_to_play(u, *n_bytes, on_timeout);
+
+ if (u->use_tsched)
+
+ /* We won't fill up the playback buffer before at least
+ * half the sleep time is over because otherwise we might
+ * ask for more data from the clients then they expect. We
+ * need to guarantee that clients only have to keep around
+ * a single hw buffer length. */
+
+ if (!polled &&
+ pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) > process_usec+max_sleep_usec/2) {
+#ifdef DEBUG_TIMING
+ pa_log_debug("Not filling up, because too early.");
+#endif
+ goto done;
}
+ if (PA_UNLIKELY(*n_bytes <= u->hwbuf_unused)) {
+
+ if (polled)
+ PA_ONCE_BEGIN {
+ char *dn = pa_alsa_get_driver_name_by_pcm(u->pcm_handle);
+ pa_log(_("ALSA woke us up to write new data to the device, but there was actually nothing to write!\n"
+ "Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers.\n"
+ "We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() returned 0 or another value < min_avail."),
+ pa_strnull(dn));
+ pa_xfree(dn);
+ } PA_ONCE_END;
+
+#ifdef DEBUG_TIMING
+ pa_log_debug("Not filling up, because not necessary.");
+#endif
+ goto done;
+ }
+
+
+ do_fill = TRUE;
+
+done:
+ if (!do_fill) {
if (u->use_tsched) {
+ *sleep_usec = pa_bytes_to_usec(left_to_play, &u->sink->sample_spec);
+
+ if (*sleep_usec > process_usec)
+ *sleep_usec -= process_usec;
+ else
+ *sleep_usec = 0;
+
if (u->since_start <= u->hwbuf_size) {
/* USB devices on ALSA seem to hit a buffer
@@ -1381,15 +1274,37 @@ static int do_work(void *userdata, pa_usec_t *sleep_usec, pa_bool_t polled, pa_b
pa_log_debug("Cutting sleep time for the initial iterations by half.");
*sleep_usec /= 2;
}
+ } else
+ *sleep_usec = 0;
+ } else {
+ *n_bytes -= u->hwbuf_unused;
+ *sleep_usec = 0;
+ }
+ return 1;
+}
+
+static int do_work(void *userdata, size_t n_bytes) {
+ struct userdata *u = userdata;
+ int work_done = 0;
- /* OK, the playback buffer is now full, let's
- * calculate when to wake up next */
- /* pa_log_debug("Waking up in %0.2fms (sound card clock).", (double) sleep_usec / PA_USEC_PER_MSEC); */
+#ifdef DEBUG_TIMING
+ pa_log_debug("Filling up");
+#endif
+ /* Render some data and write it to the dsp */
+ if (u->use_mmap)
+ work_done = mmap_write(u, n_bytes);
+ else
+ work_done = unix_write(u, n_bytes);
+
+ if (work_done) {
+ if (u->first) {
+ pa_log_info("Starting playback.");
+ snd_pcm_start(u->pcm_handle);
}
- u->first = FALSE;
- u->after_rewind = FALSE;
- } else
- *sleep_usec = 0;
+ }
+
+ u->first = FALSE;
+ u->after_rewind = FALSE;
return work_done;
}
@@ -2002,6 +1917,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_al
u->task.userdata = u;
u->task.get_position = get_position;
+ u->task.get_avail = get_avail;
u->task.do_work = do_work;
u->task.handle_poll = handle_poll;
pa_alsa_manager_add_sink_task (manager, &u->task);
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index 96644fb61..4fd6ff956 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -57,7 +57,7 @@
#include "alsa-source.h"
#include "alsa-manager.h"
-/* #define DEBUG_TIMING */
+#define DEBUG_TIMING
#define DEFAULT_DEVICE "default"
@@ -451,293 +451,150 @@ static size_t check_left_to_record(struct userdata *u, size_t n_bytes, pa_bool_t
return left_to_record;
}
-static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled, pa_bool_t on_timeout) {
- pa_bool_t work_done = FALSE;
- pa_usec_t max_sleep_usec = 0, process_usec = 0;
- size_t left_to_record;
- unsigned j = 0;
+static int mmap_read(struct userdata *u, size_t n_bytes) {
+ pa_bool_t work_done = TRUE;
+ pa_bool_t after_avail = TRUE;
pa_assert(u);
pa_source_assert_ref(u->source);
- if (u->use_tsched)
- hw_sleep_time(u, &max_sleep_usec, &process_usec);
-
for (;;) {
- snd_pcm_sframes_t n;
- size_t n_bytes;
- int r;
- pa_bool_t after_avail = TRUE;
-
- if (PA_UNLIKELY((n = pa_alsa_safe_avail(u->pcm_handle, u->hwbuf_size, &u->source->sample_spec)) < 0)) {
+ int err, r;
+ const snd_pcm_channel_area_t *areas;
+ snd_pcm_uframes_t offset, frames;
+ pa_memchunk chunk;
+ void *p;
+ snd_pcm_sframes_t sframes;
- if ((r = try_recover(u, "snd_pcm_avail", (int) n)) == 0)
- continue;
+ frames = (snd_pcm_uframes_t) (n_bytes / u->frame_size);
- return r;
- }
+/* pa_log_debug("%lu frames to read", (unsigned long) frames); */
- n_bytes = (size_t) n * u->frame_size;
+ if (PA_UNLIKELY((err = pa_alsa_safe_mmap_begin(u->pcm_handle, &areas, &offset, &frames, u->hwbuf_size, &u->source->sample_spec)) < 0)) {
-#ifdef DEBUG_TIMING
- pa_log_debug("avail: %lu", (unsigned long) n_bytes);
-#endif
-
- left_to_record = check_left_to_record(u, n_bytes, on_timeout);
- on_timeout = FALSE;
-
- if (u->use_tsched)
- if (!polled &&
- pa_bytes_to_usec(left_to_record, &u->source->sample_spec) > process_usec+max_sleep_usec/2) {
-#ifdef DEBUG_TIMING
- pa_log_debug("Not reading, because too early.");
-#endif
+ if (!after_avail && err == -EAGAIN)
break;
- }
- if (PA_UNLIKELY(n_bytes <= 0)) {
-
- if (polled)
- PA_ONCE_BEGIN {
- char *dn = pa_alsa_get_driver_name_by_pcm(u->pcm_handle);
- pa_log(_("ALSA woke us up to read new data from the device, but there was actually nothing to read!\n"
- "Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers.\n"
- "We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() returned 0 or another value < min_avail."),
- pa_strnull(dn));
- pa_xfree(dn);
- } PA_ONCE_END;
+ if ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0)
+ continue;
-#ifdef DEBUG_TIMING
- pa_log_debug("Not reading, because not necessary.");
-#endif
- break;
+ return r;
}
- if (++j > 10) {
-#ifdef DEBUG_TIMING
- pa_log_debug("Not filling up, because already too many iterations.");
-#endif
+ /* Make sure that if these memblocks need to be copied they will fit into one slot */
+ if (frames > pa_mempool_block_size_max(u->source->core->mempool)/u->frame_size)
+ frames = pa_mempool_block_size_max(u->source->core->mempool)/u->frame_size;
+ if (!after_avail && frames == 0)
break;
- }
-
- polled = FALSE;
-
-#ifdef DEBUG_TIMING
- pa_log_debug("Reading");
-#endif
-
- for (;;) {
- int err;
- const snd_pcm_channel_area_t *areas;
- snd_pcm_uframes_t offset, frames;
- pa_memchunk chunk;
- void *p;
- snd_pcm_sframes_t sframes;
-
- frames = (snd_pcm_uframes_t) (n_bytes / u->frame_size);
-
-/* pa_log_debug("%lu frames to read", (unsigned long) frames); */
-
- if (PA_UNLIKELY((err = pa_alsa_safe_mmap_begin(u->pcm_handle, &areas, &offset, &frames, u->hwbuf_size, &u->source->sample_spec)) < 0)) {
-
- if (!after_avail && err == -EAGAIN)
- break;
-
- if ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0)
- continue;
- return r;
- }
-
- /* Make sure that if these memblocks need to be copied they will fit into one slot */
- if (frames > pa_mempool_block_size_max(u->source->core->mempool)/u->frame_size)
- frames = pa_mempool_block_size_max(u->source->core->mempool)/u->frame_size;
-
- if (!after_avail && frames == 0)
- break;
-
- pa_assert(frames > 0);
- after_avail = FALSE;
+ pa_assert(frames > 0);
+ after_avail = FALSE;
- /* Check these are multiples of 8 bit */
- pa_assert((areas[0].first & 7) == 0);
- pa_assert((areas[0].step & 7)== 0);
+ /* Check these are multiples of 8 bit */
+ pa_assert((areas[0].first & 7) == 0);
+ pa_assert((areas[0].step & 7)== 0);
- /* We assume a single interleaved memory buffer */
- pa_assert((areas[0].first >> 3) == 0);
- pa_assert((areas[0].step >> 3) == u->frame_size);
+ /* We assume a single interleaved memory buffer */
+ pa_assert((areas[0].first >> 3) == 0);
+ pa_assert((areas[0].step >> 3) == u->frame_size);
- p = (uint8_t*) areas[0].addr + (offset * u->frame_size);
+ p = (uint8_t*) areas[0].addr + (offset * u->frame_size);
- chunk.memblock = pa_memblock_new_fixed(u->core->mempool, p, frames * u->frame_size, TRUE);
- chunk.length = pa_memblock_get_length(chunk.memblock);
- chunk.index = 0;
+ chunk.memblock = pa_memblock_new_fixed(u->core->mempool, p, frames * u->frame_size, TRUE);
+ chunk.length = pa_memblock_get_length(chunk.memblock);
+ chunk.index = 0;
- pa_source_post(u->source, &chunk);
- pa_memblock_unref_fixed(chunk.memblock);
+ pa_source_post(u->source, &chunk);
+ pa_memblock_unref_fixed(chunk.memblock);
- if (PA_UNLIKELY((sframes = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0)) {
+ if (PA_UNLIKELY((sframes = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0)) {
- if ((r = try_recover(u, "snd_pcm_mmap_commit", (int) sframes)) == 0)
- continue;
+ if ((r = try_recover(u, "snd_pcm_mmap_commit", (int) sframes)) == 0)
+ continue;
- return r;
- }
+ return r;
+ }
- work_done = TRUE;
+ work_done = TRUE;
- u->read_count += frames * u->frame_size;
+ u->read_count += frames * u->frame_size;
#ifdef DEBUG_TIMING
- pa_log_debug("Read %lu bytes (of possible %lu bytes)", (unsigned long) (frames * u->frame_size), (unsigned long) n_bytes);
+ pa_log_debug("Read %lu bytes (of possible %lu bytes)", (unsigned long) (frames * u->frame_size), (unsigned long) n_bytes);
#endif
- if ((size_t) frames * u->frame_size >= n_bytes)
- break;
-
- n_bytes -= (size_t) frames * u->frame_size;
- }
- }
-
- if (u->use_tsched) {
- *sleep_usec = pa_bytes_to_usec(left_to_record, &u->source->sample_spec);
+ if ((size_t) frames * u->frame_size >= n_bytes)
+ break;
- if (*sleep_usec > process_usec)
- *sleep_usec -= process_usec;
- else
- *sleep_usec = 0;
+ n_bytes -= (size_t) frames * u->frame_size;
}
-
return work_done ? 1 : 0;
}
-static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled, pa_bool_t on_timeout) {
+static int unix_read(struct userdata *u, size_t n_bytes) {
int work_done = FALSE;
- pa_usec_t max_sleep_usec = 0, process_usec = 0;
- size_t left_to_record;
- unsigned j = 0;
+ pa_bool_t after_avail = TRUE;
pa_assert(u);
pa_source_assert_ref(u->source);
- if (u->use_tsched)
- hw_sleep_time(u, &max_sleep_usec, &process_usec);
-
for (;;) {
- snd_pcm_sframes_t n;
- size_t n_bytes;
+ void *p;
int r;
- pa_bool_t after_avail = TRUE;
+ snd_pcm_sframes_t frames;
+ pa_memchunk chunk;
- if (PA_UNLIKELY((n = pa_alsa_safe_avail(u->pcm_handle, u->hwbuf_size, &u->source->sample_spec)) < 0)) {
+ chunk.memblock = pa_memblock_new(u->core->mempool, (size_t) -1);
- if ((r = try_recover(u, "snd_pcm_avail", (int) n)) == 0)
- continue;
+ frames = (snd_pcm_sframes_t) (pa_memblock_get_length(chunk.memblock) / u->frame_size);
- return r;
- }
+ if (frames > (snd_pcm_sframes_t) (n_bytes/u->frame_size))
+ frames = (snd_pcm_sframes_t) (n_bytes/u->frame_size);
- n_bytes = (size_t) n * u->frame_size;
- left_to_record = check_left_to_record(u, n_bytes, on_timeout);
- on_timeout = FALSE;
+/* pa_log_debug("%lu frames to read", (unsigned long) n); */
- if (u->use_tsched)
- if (!polled &&
- pa_bytes_to_usec(left_to_record, &u->source->sample_spec) > process_usec+max_sleep_usec/2)
- break;
+ p = pa_memblock_acquire(chunk.memblock);
+ frames = snd_pcm_readi(u->pcm_handle, (uint8_t*) p, (snd_pcm_uframes_t) frames);
+ pa_memblock_release(chunk.memblock);
- if (PA_UNLIKELY(n_bytes <= 0)) {
+ if (PA_UNLIKELY(frames < 0)) {
+ pa_memblock_unref(chunk.memblock);
- if (polled)
- PA_ONCE_BEGIN {
- char *dn = pa_alsa_get_driver_name_by_pcm(u->pcm_handle);
- pa_log(_("ALSA woke us up to read new data from the device, but there was actually nothing to read!\n"
- "Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers.\n"
- "We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() returned 0 or another value < min_avail."),
- pa_strnull(dn));
- pa_xfree(dn);
- } PA_ONCE_END;
+ if (!after_avail && (int) frames == -EAGAIN)
+ break;
- break;
- }
+ if ((r = try_recover(u, "snd_pcm_readi", (int) frames)) == 0)
+ continue;
- if (++j > 10) {
-#ifdef DEBUG_TIMING
- pa_log_debug("Not filling up, because already too many iterations.");
-#endif
+ return r;
+ }
+ if (!after_avail && frames == 0) {
+ pa_memblock_unref(chunk.memblock);
break;
}
- polled = FALSE;
-
- for (;;) {
- void *p;
- snd_pcm_sframes_t frames;
- pa_memchunk chunk;
+ pa_assert(frames > 0);
+ after_avail = FALSE;
- chunk.memblock = pa_memblock_new(u->core->mempool, (size_t) -1);
+ chunk.index = 0;
+ chunk.length = (size_t) frames * u->frame_size;
- frames = (snd_pcm_sframes_t) (pa_memblock_get_length(chunk.memblock) / u->frame_size);
+ pa_source_post(u->source, &chunk);
+ pa_memblock_unref(chunk.memblock);
- if (frames > (snd_pcm_sframes_t) (n_bytes/u->frame_size))
- frames = (snd_pcm_sframes_t) (n_bytes/u->frame_size);
+ work_done = TRUE;
-/* pa_log_debug("%lu frames to read", (unsigned long) n); */
+ u->read_count += frames * u->frame_size;
- p = pa_memblock_acquire(chunk.memblock);
- frames = snd_pcm_readi(u->pcm_handle, (uint8_t*) p, (snd_pcm_uframes_t) frames);
- pa_memblock_release(chunk.memblock);
+/* pa_log_debug("read %lu frames", (unsigned long) frames); */
- if (PA_UNLIKELY(frames < 0)) {
- pa_memblock_unref(chunk.memblock);
-
- if (!after_avail && (int) frames == -EAGAIN)
- break;
-
- if ((r = try_recover(u, "snd_pcm_readi", (int) frames)) == 0)
- continue;
-
- return r;
- }
-
- if (!after_avail && frames == 0) {
- pa_memblock_unref(chunk.memblock);
- break;
- }
-
- pa_assert(frames > 0);
- after_avail = FALSE;
-
- chunk.index = 0;
- chunk.length = (size_t) frames * u->frame_size;
-
- pa_source_post(u->source, &chunk);
- pa_memblock_unref(chunk.memblock);
-
- work_done = TRUE;
-
- u->read_count += frames * u->frame_size;
-
-/* pa_log_debug("read %lu frames", (unsigned long) frames); */
-
- if ((size_t) frames * u->frame_size >= n_bytes)
- break;
-
- n_bytes -= (size_t) frames * u->frame_size;
- }
- }
-
- if (u->use_tsched) {
- *sleep_usec = pa_bytes_to_usec(left_to_record, &u->source->sample_spec);
+ if ((size_t) frames * u->frame_size >= n_bytes)
+ break;
- if (*sleep_usec > process_usec)
- *sleep_usec -= process_usec;
- else
- *sleep_usec = 0;
+ n_bytes -= (size_t) frames * u->frame_size;
}
-
return work_done ? 1 : 0;
}
@@ -1200,19 +1057,98 @@ static void source_update_requested_latency_cb(pa_source *s) {
update_sw_params(u);
}
-
-static int do_work(void *userdata, pa_usec_t *sleep_usec, pa_bool_t polled, pa_bool_t on_timeout) {
+static int get_avail(void *userdata, size_t *n_bytes, pa_usec_t *sleep_usec, pa_bool_t polled, pa_bool_t on_timeout) {
struct userdata *u = userdata;
- int work_done = 0;
+ pa_bool_t do_fill = FALSE;
+ pa_usec_t max_sleep_usec = 0, process_usec = 0;
+ size_t left_to_record;
+ snd_pcm_sframes_t n;
+ int r;
- if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
- if (u->use_mmap)
- work_done = mmap_read(u, sleep_usec, polled, on_timeout);
- else
- work_done = unix_read(u, sleep_usec, polled, on_timeout);
+ pa_assert(u);
+ pa_source_assert_ref(u->source);
+
+ if (!PA_SOURCE_IS_OPENED(u->source->thread_info.state))
+ return 0;
+
+ if (u->use_tsched)
+ hw_sleep_time(u, &max_sleep_usec, &process_usec);
+
+
+ if (PA_UNLIKELY((n = pa_alsa_safe_avail(u->pcm_handle, u->hwbuf_size, &u->source->sample_spec)) < 0)) {
+
+ if ((r = try_recover(u, "snd_pcm_avail", (int) n)) < 0)
+ return r;
+ }
+
+ *n_bytes = (size_t) n * u->frame_size;
+
+#ifdef DEBUG_TIMING
+ pa_log_debug("avail: %lu", (unsigned long) *n_bytes);
+#endif
+
+ left_to_record = check_left_to_record(u, *n_bytes, on_timeout);
+ on_timeout = FALSE;
+
+ if (u->use_tsched)
+ if (!polled &&
+ pa_bytes_to_usec(left_to_record, &u->source->sample_spec) > process_usec+max_sleep_usec/2) {
+#ifdef DEBUG_TIMING
+ pa_log_debug("Not reading, because too early.");
+#endif
+ goto done;
+ }
+
+ if (PA_UNLIKELY(*n_bytes <= 0)) {
+
+ if (polled)
+ PA_ONCE_BEGIN {
+ char *dn = pa_alsa_get_driver_name_by_pcm(u->pcm_handle);
+ pa_log(_("ALSA woke us up to read new data from the device, but there was actually nothing to read!\n"
+ "Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers.\n"
+ "We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() returned 0 or another value < min_avail."),
+ pa_strnull(dn));
+ pa_xfree(dn);
+ } PA_ONCE_END;
+
+#ifdef DEBUG_TIMING
+ pa_log_debug("Not reading, because not necessary.");
+#endif
+ goto done;
+ }
+
+ do_fill = TRUE;
+
+done:
+ if (!do_fill) {
+ if (u->use_tsched) {
+ *sleep_usec = pa_bytes_to_usec(left_to_record, &u->source->sample_spec);
+
+ if (*sleep_usec > process_usec)
+ *sleep_usec -= process_usec;
+ else
+ *sleep_usec = 0;
+ } else
+ *sleep_usec = 0;
} else
*sleep_usec = 0;
+ return 1;
+}
+
+static int do_work(void *userdata, size_t n_bytes) {
+ struct userdata *u = userdata;
+ int work_done = 0;
+
+#ifdef DEBUG_TIMING
+ pa_log_debug("Reading");
+#endif
+
+ if (u->use_mmap)
+ work_done = mmap_read(u, n_bytes);
+ else
+ work_done = unix_read(u, n_bytes);
+
return work_done;
}
@@ -1778,6 +1714,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
u->task.userdata = u;
u->task.get_position = get_position;
+ u->task.get_avail = get_avail;
u->task.do_work = do_work;
u->task.handle_poll = handle_poll;
pa_alsa_manager_add_source_task (manager, &u->task);