diff options
author | Wim Taymans <wim.taymans@collabora.co.uk> | 2010-07-14 15:14:56 +0200 |
---|---|---|
committer | Wim Taymans <wim.taymans@collabora.co.uk> | 2010-07-14 15:14:56 +0200 |
commit | 09b48da1547aa7ecb8ff9d504c6e491ab35e5b4b (patch) | |
tree | 346c5902c845f6e59aaffd376421df6e8f528f3e | |
parent | 87fbafbda1fb9ea2fd5c7fc52d2d33f481c0e043 (diff) |
alsa: more hacking
-rw-r--r-- | src/modules/alsa/alsa-manager.c | 123 | ||||
-rw-r--r-- | src/modules/alsa/alsa-manager.h | 4 | ||||
-rw-r--r-- | src/modules/alsa/alsa-sink.c | 458 | ||||
-rw-r--r-- | src/modules/alsa/alsa-source.c | 401 |
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); |