diff options
author | Lennart Poettering <lennart@poettering.net> | 2008-05-15 23:19:37 +0000 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2008-05-15 23:19:37 +0000 |
commit | 43dfc2a00035af3eac5bef812877d7b3fd61efdc (patch) | |
tree | cd1952f84cb5a343d783683dec3240ac7ff8dcea | |
parent | 734f071df7a0a3678cafb4db62e0a7ba26585e93 (diff) | |
download | pulseaudio-43dfc2a00035af3eac5bef812877d7b3fd61efdc.tar.gz |
follow recent alsa sink changes in the alsa source
git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2443 fefdeb5f-60dc-0310-8127-8f9354f1896f
-rw-r--r-- | src/modules/module-alsa-source.c | 299 |
1 files changed, 158 insertions, 141 deletions
diff --git a/src/modules/module-alsa-source.c b/src/modules/module-alsa-source.c index 4838ad27d..e30901098 100644 --- a/src/modules/module-alsa-source.c +++ b/src/modules/module-alsa-source.c @@ -3,7 +3,7 @@ /*** This file is part of PulseAudio. - Copyright 2004-2006 Lennart Poettering + Copyright 2004-2008 Lennart Poettering Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB PulseAudio is free software; you can redistribute it and/or modify @@ -93,10 +93,10 @@ static const char* const valid_modargs[] = { }; #define DEFAULT_DEVICE "default" -#define DEFAULT_TSCHED_BUFFER_USEC (5*PA_USEC_PER_SEC) -#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC) -#define TSCHED_MIN_SLEEP_USEC (3*PA_USEC_PER_MSEC) /* 3ms */ -#define TSCHED_MIN_WAKEUP_USEC (3*PA_USEC_PER_MSEC) /* 3ms */ +#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) /* 2s */ +#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC) /* 20ms */ +#define TSCHED_MIN_SLEEP_USEC (3*PA_USEC_PER_MSEC) /* 3ms */ +#define TSCHED_MIN_WAKEUP_USEC (3*PA_USEC_PER_MSEC) /* 3ms */ struct userdata { pa_core *core; @@ -160,6 +160,31 @@ static void fix_tsched_watermark(struct userdata *u) { u->tsched_watermark = min_wakeup; } +static pa_usec_t hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*process_usec) { + pa_usec_t wm, usec; + + pa_assert(u); + + usec = pa_source_get_requested_latency_within_thread(u->source); + + if (usec == (pa_usec_t) -1) + usec = pa_bytes_to_usec(u->hwbuf_size, &u->source->sample_spec); + +/* pa_log_debug("hw buffer time: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); */ + + wm = pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec); + + if (usec >= wm) { + *sleep_usec = usec - wm; + *process_usec = wm; + } else + *process_usec = *sleep_usec = usec /= 2; + +/* pa_log_debug("after watermark: %u ms", (unsigned) (*sleep_usec / PA_USEC_PER_MSEC)); */ + + return usec; +} + static int try_recover(struct userdata *u, const char *call, int err) { pa_assert(u); pa_assert(call); @@ -167,10 +192,7 @@ static int try_recover(struct userdata *u, const char *call, int err) { pa_log_debug("%s: %s", call, snd_strerror(err)); - if (err == -EAGAIN) { - pa_log_debug("%s: EAGAIN", call); - return 1; - } + pa_assert(err != -EAGAIN); if (err == -EPIPE) pa_log_debug("%s: Buffer overrun!", call); @@ -184,7 +206,7 @@ static int try_recover(struct userdata *u, const char *call, int err) { return -1; } -static void check_left_to_record(struct userdata *u, snd_pcm_sframes_t n) { +static size_t check_left_to_record(struct userdata *u, snd_pcm_sframes_t n) { size_t left_to_record; if (n*u->frame_size < u->hwbuf_size) @@ -192,9 +214,9 @@ static void check_left_to_record(struct userdata *u, snd_pcm_sframes_t n) { else left_to_record = 0; - if (left_to_record > 0) - pa_log_debug("%0.2f ms left to record", (double) pa_bytes_to_usec(left_to_record, &u->source->sample_spec) / PA_USEC_PER_MSEC); - else { + if (left_to_record > 0) { +/* pa_log_debug("%0.2f ms left to record", (double) pa_bytes_to_usec(left_to_record, &u->source->sample_spec) / PA_USEC_PER_MSEC); */ + } else { pa_log_info("Overrun!"); if (u->use_tsched) { @@ -208,111 +230,121 @@ static void check_left_to_record(struct userdata *u, snd_pcm_sframes_t n) { (double) pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec) / PA_USEC_PER_MSEC); } } + + return left_to_record; } -static int mmap_read(struct userdata *u) { +static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec) { int work_done = 0; - pa_bool_t checked_left_to_record = FALSE; + pa_usec_t max_sleep_usec, process_usec; + size_t left_to_record; 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; - int err, r; - const snd_pcm_channel_area_t *areas; - snd_pcm_uframes_t offset, frames; - pa_memchunk chunk; - void *p; + int r; snd_pcm_hwsync(u->pcm_handle); if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) { - if ((r = try_recover(u, "snd_pcm_avail_update", err)) == 0) + if ((r = try_recover(u, "snd_pcm_avail_update", n)) == 0) continue; - else if (r > 0) - return work_done; return r; } - if (checked_left_to_record) { - check_left_to_record(u, n); - checked_left_to_record = TRUE; - } + left_to_record = check_left_to_record(u, n); + + if (u->use_tsched) + if (pa_bytes_to_usec(left_to_record, &u->source->sample_spec) > max_sleep_usec/2) + break; if (PA_UNLIKELY(n <= 0)) - return work_done; + break; - frames = n; + for (;;) { + int err; + const snd_pcm_channel_area_t *areas; + snd_pcm_uframes_t offset, frames = (snd_pcm_uframes_t) n; + pa_memchunk chunk; + void *p; - pa_log_debug("%lu frames to read", (unsigned long) frames); +/* pa_log_debug("%lu frames to read", (unsigned long) frames); */ - if (PA_UNLIKELY((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0)) { + if (PA_UNLIKELY((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0)) { - if ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0) - continue; - else if (r > 0) - return work_done; + if ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0) + continue; - return r; - } + 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; + /* 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; - /* 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((err = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0)) { + if (PA_UNLIKELY((err = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0)) { - if ((r = try_recover(u, "snd_pcm_mmap_commit", err)) == 0) - continue; - else if (r > 0) - return work_done; + if ((r = try_recover(u, "snd_pcm_mmap_commit", err)) == 0) + continue; - return r; - } + return r; + } - work_done = 1; + work_done = 1; - u->frame_index += frames; + u->frame_index += frames; - pa_log_debug("read %lu frames", (unsigned long) frames); +/* pa_log_debug("read %lu frames", (unsigned long) frames); */ - if (PA_LIKELY(frames >= (snd_pcm_uframes_t) n)) - return work_done; + if (frames >= (snd_pcm_uframes_t) n) + break; + + n -= frames; + } } + + *sleep_usec = pa_bytes_to_usec(left_to_record, &u->source->sample_spec) - process_usec; + return work_done; } -static int unix_read(struct userdata *u) { +static int unix_read(struct userdata *u, pa_usec_t *sleep_usec) { int work_done = 0; - pa_bool_t checked_left_to_record = FALSE; + pa_usec_t max_sleep_usec, process_usec; + size_t left_to_record; pa_assert(u); pa_source_assert_ref(u->source); + if (u->use_tsched) + hw_sleep_time(u, &max_sleep_usec, &process_usec); + for (;;) { - void *p; - snd_pcm_sframes_t n, frames; + snd_pcm_sframes_t n; int r; - pa_memchunk chunk; snd_pcm_hwsync(u->pcm_handle); @@ -320,61 +352,69 @@ static int unix_read(struct userdata *u) { if ((r = try_recover(u, "snd_pcm_avail_update", n)) == 0) continue; - else if (r > 0) - return work_done; return r; } - if (checked_left_to_record) { - check_left_to_record(u, n); - checked_left_to_record = TRUE; - } + left_to_record = check_left_to_record(u, n); + + if (u->use_tsched) + if (pa_bytes_to_usec(left_to_record, &u->source->sample_spec) > max_sleep_usec/2) + break; if (PA_UNLIKELY(n <= 0)) return work_done; - chunk.memblock = pa_memblock_new(u->core->mempool, (size_t) -1); + for (;;) { + void *p; + snd_pcm_sframes_t frames; + pa_memchunk chunk; - frames = pa_memblock_get_length(chunk.memblock) / u->frame_size; + chunk.memblock = pa_memblock_new(u->core->mempool, (size_t) -1); - if (frames > n) - frames = n; + frames = pa_memblock_get_length(chunk.memblock) / u->frame_size; - pa_log_debug("%lu frames to read", (unsigned long) n); + if (frames > n) + frames = n; - p = pa_memblock_acquire(chunk.memblock); - frames = snd_pcm_readi(u->pcm_handle, (uint8_t*) p, frames); - pa_memblock_release(chunk.memblock); +/* pa_log_debug("%lu frames to read", (unsigned long) n); */ - pa_assert(frames != 0); + p = pa_memblock_acquire(chunk.memblock); + frames = snd_pcm_readi(u->pcm_handle, (uint8_t*) p, frames); + pa_memblock_release(chunk.memblock); - if (PA_UNLIKELY(frames < 0)) { - pa_memblock_unref(chunk.memblock); + pa_assert(frames != 0); - if ((r = try_recover(u, "snd_pcm_readi", n)) == 0) - continue; - else if (r > 0) - return work_done; + if (PA_UNLIKELY(frames < 0)) { + pa_memblock_unref(chunk.memblock); - return r; - } + if ((r = try_recover(u, "snd_pcm_readi", n)) == 0) + continue; - chunk.index = 0; - chunk.length = frames * u->frame_size; + return r; + } - pa_source_post(u->source, &chunk); - pa_memblock_unref(chunk.memblock); + chunk.index = 0; + chunk.length = frames * u->frame_size; - work_done = 1; + pa_source_post(u->source, &chunk); + pa_memblock_unref(chunk.memblock); - u->frame_index += frames; + work_done = 1; - pa_log_debug("read %lu frames", (unsigned long) frames); + u->frame_index += frames; - if (PA_LIKELY(frames >= n)) - return work_done; +/* pa_log_debug("read %lu frames", (unsigned long) frames); */ + + if (frames >= n) + break; + + n -= frames; + } } + + *sleep_usec = pa_bytes_to_usec(left_to_record, &u->source->sample_spec) - process_usec; + return work_done; } static void update_smoother(struct userdata *u) { @@ -455,30 +495,6 @@ static int suspend(struct userdata *u) { return 0; } -static pa_usec_t hw_sleep_time(struct userdata *u) { - pa_usec_t wm, usec; - - pa_assert(u); - - usec = pa_source_get_requested_latency_within_thread(u->source); - - if (usec == (pa_usec_t) -1) - usec = pa_bytes_to_usec(u->hwbuf_size, &u->source->sample_spec); - -/* pa_log_debug("hw buffer time: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); */ - - wm = pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec); - - if (usec >= wm) - usec -= wm; - else - usec /= 2; - -/* pa_log_debug("after watermark: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); */ - - return usec; -} - static int update_sw_params(struct userdata *u) { snd_pcm_uframes_t avail_min; int err; @@ -516,10 +532,10 @@ static int update_sw_params(struct userdata *u) { avail_min = 1; if (u->use_tsched) { - pa_usec_t usec; + pa_usec_t sleep_usec, process_usec; - usec = hw_sleep_time(u); - avail_min += pa_usec_to_bytes(usec, &u->source->sample_spec); + hw_sleep_time(u, &sleep_usec, &process_usec); + avail_min += pa_usec_to_bytes(sleep_usec, &u->source->sample_spec); } pa_log_debug("setting avail_min=%lu", (unsigned long) avail_min); @@ -794,9 +810,11 @@ static int source_set_mute_cb(pa_source *s) { static void source_update_requested_latency_cb(pa_source *s) { struct userdata *u = s->userdata; - pa_assert(u); + if (!u->pcm_handle) + return; + update_sw_params(u); } @@ -816,43 +834,42 @@ static void thread_func(void *userdata) { for (;;) { int ret; - pa_log_debug("loop"); +/* pa_log_debug("loop"); */ /* Read some data and pass it to the sources */ if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) { int work_done = 0; + pa_usec_t sleep_usec; if (u->use_mmap) - work_done = mmap_read(u); + work_done = mmap_read(u, &sleep_usec); else - work_done = unix_read(u); + work_done = unix_read(u, &sleep_usec); if (work_done < 0) goto fail; - pa_log_debug("work_done = %i", work_done); +/* pa_log_debug("work_done = %i", work_done); */ if (work_done) update_smoother(u); if (u->use_tsched) { - pa_usec_t usec, cusec; + pa_usec_t cusec; /* OK, the capture buffer is now empty, let's * calculate when to wake up next */ - usec = hw_sleep_time(u); - - pa_log_debug("Waking up in %0.2fms (sound card clock).", (double) usec / PA_USEC_PER_MSEC); +/* pa_log_debug("Waking up in %0.2fms (sound card clock).", (double) sleep_usec / PA_USEC_PER_MSEC); */ /* Convert from the sound card time domain to the * system time domain */ - cusec = pa_smoother_translate(u->smoother, pa_rtclock_usec(), usec); + cusec = pa_smoother_translate(u->smoother, pa_rtclock_usec(), sleep_usec); - pa_log_debug("Waking up in %0.2fms (system clock).", (double) cusec / PA_USEC_PER_MSEC); +/* 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 */ - pa_rtpoll_set_timer_relative(u->rtpoll, PA_MIN(usec, cusec)); + pa_rtpoll_set_timer_relative(u->rtpoll, PA_MIN(sleep_usec, cusec)); } } else if (u->use_tsched) @@ -887,7 +904,7 @@ static void thread_func(void *userdata) { snd_pcm_start(u->pcm_handle); } - if (revents) + if (revents && u->use_tsched) pa_log_debug("Wakeup from ALSA! (%i)", revents); } } |