mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-11-01 22:58:47 -04:00
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
This commit is contained in:
parent
734f071df7
commit
43dfc2a000
1 changed files with 200 additions and 183 deletions
|
|
@ -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,24 @@ 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;
|
||||
|
||||
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)
|
||||
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;
|
||||
}
|
||||
|
||||
if (PA_UNLIKELY(n <= 0))
|
||||
return work_done;
|
||||
|
||||
frames = n;
|
||||
|
||||
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 ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0)
|
||||
continue;
|
||||
else if (r > 0)
|
||||
return work_done;
|
||||
|
||||
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;
|
||||
|
||||
/* 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);
|
||||
|
||||
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_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 ((r = try_recover(u, "snd_pcm_mmap_commit", err)) == 0)
|
||||
continue;
|
||||
else if (r > 0)
|
||||
return work_done;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
work_done = 1;
|
||||
|
||||
u->frame_index += frames;
|
||||
|
||||
pa_log_debug("read %lu frames", (unsigned long) frames);
|
||||
|
||||
if (PA_LIKELY(frames >= (snd_pcm_uframes_t) n))
|
||||
return work_done;
|
||||
}
|
||||
}
|
||||
|
||||
static int unix_read(struct userdata *u) {
|
||||
int work_done = 0;
|
||||
pa_bool_t checked_left_to_record = FALSE;
|
||||
|
||||
pa_assert(u);
|
||||
pa_source_assert_ref(u->source);
|
||||
|
||||
for (;;) {
|
||||
void *p;
|
||||
snd_pcm_sframes_t n, frames;
|
||||
int r;
|
||||
pa_memchunk chunk;
|
||||
|
||||
snd_pcm_hwsync(u->pcm_handle);
|
||||
|
||||
|
|
@ -320,61 +255,166 @@ 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))
|
||||
break;
|
||||
|
||||
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); */
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
/* 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);
|
||||
|
||||
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_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 ((r = try_recover(u, "snd_pcm_mmap_commit", err)) == 0)
|
||||
continue;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
work_done = 1;
|
||||
|
||||
u->frame_index += frames;
|
||||
|
||||
/* pa_log_debug("read %lu frames", (unsigned long) frames); */
|
||||
|
||||
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, pa_usec_t *sleep_usec) {
|
||||
int work_done = 0;
|
||||
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 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", n)) == 0)
|
||||
continue;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
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_assert(frames != 0);
|
||||
|
||||
if (PA_UNLIKELY(frames < 0)) {
|
||||
pa_memblock_unref(chunk.memblock);
|
||||
|
||||
if ((r = try_recover(u, "snd_pcm_readi", n)) == 0)
|
||||
continue;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
chunk.index = 0;
|
||||
chunk.length = frames * u->frame_size;
|
||||
|
||||
pa_source_post(u->source, &chunk);
|
||||
pa_memblock_unref(chunk.memblock);
|
||||
|
||||
if ((r = try_recover(u, "snd_pcm_readi", n)) == 0)
|
||||
continue;
|
||||
else if (r > 0)
|
||||
return work_done;
|
||||
work_done = 1;
|
||||
|
||||
return r;
|
||||
u->frame_index += frames;
|
||||
|
||||
/* pa_log_debug("read %lu frames", (unsigned long) frames); */
|
||||
|
||||
if (frames >= n)
|
||||
break;
|
||||
|
||||
n -= frames;
|
||||
}
|
||||
|
||||
chunk.index = 0;
|
||||
chunk.length = frames * u->frame_size;
|
||||
|
||||
pa_source_post(u->source, &chunk);
|
||||
pa_memblock_unref(chunk.memblock);
|
||||
|
||||
work_done = 1;
|
||||
|
||||
u->frame_index += frames;
|
||||
|
||||
pa_log_debug("read %lu frames", (unsigned long) frames);
|
||||
|
||||
if (PA_LIKELY(frames >= n))
|
||||
return work_done;
|
||||
}
|
||||
|
||||
*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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue