From 7e2afffb81ab8b495d4f769858a855c2df2c0610 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 22 Jul 2009 22:38:38 +0200 Subject: alsa: deal properly with IO functions asking us to write 0 bytes --- src/modules/alsa/alsa-sink.c | 6 +++++- src/modules/alsa/alsa-source.c | 8 +++++++- 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'src/modules/alsa') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 0cde694c..e7925902 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -494,6 +494,9 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle 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 (frames == 0) + break; + /* Check these are multiples of 8 bit */ pa_assert((areas[0].first & 7) == 0); pa_assert((areas[0].step & 7)== 0); @@ -631,7 +634,8 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle 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); - pa_assert(frames != 0); + if (frames == 0) + break; if (PA_UNLIKELY(frames < 0)) { diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index a6760e1e..41bb768b 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -473,6 +473,9 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled 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 (frames == 0) + break; + /* Check these are multiples of 8 bit */ pa_assert((areas[0].first & 7) == 0); pa_assert((areas[0].step & 7)== 0); @@ -599,7 +602,10 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled frames = snd_pcm_readi(u->pcm_handle, (uint8_t*) p, (snd_pcm_uframes_t) frames); pa_memblock_release(chunk.memblock); - pa_assert(frames != 0); + if (frames == 0) { + pa_memblock_unref(chunk.memblock); + break; + } if (PA_UNLIKELY(frames < 0)) { pa_memblock_unref(chunk.memblock); -- cgit From 1160cad9c3f87659e810ebdb9c4a20626e8a4eed Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 23 Jul 2009 18:44:26 +0200 Subject: alsa: control 'Speaker' element as well --- src/modules/alsa/mixer/paths/analog-output-headphones.conf | 4 ++++ src/modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf | 6 ++++++ src/modules/alsa/mixer/paths/analog-output-mono.conf | 6 ++++++ src/modules/alsa/mixer/paths/analog-output.conf | 6 ++++++ 4 files changed, 22 insertions(+) (limited to 'src/modules/alsa') diff --git a/src/modules/alsa/mixer/paths/analog-output-headphones.conf b/src/modules/alsa/mixer/paths/analog-output-headphones.conf index c018e0eb..691cb3f2 100644 --- a/src/modules/alsa/mixer/paths/analog-output-headphones.conf +++ b/src/modules/alsa/mixer/paths/analog-output-headphones.conf @@ -44,6 +44,10 @@ volume = merge override-map.1 = all override-map.2 = all-left,all-right +[Element Speaker] +switch = off +volume = off + [Element Front] switch = off volume = off diff --git a/src/modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf b/src/modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf index 7a267890..2db976a5 100644 --- a/src/modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf +++ b/src/modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf @@ -45,6 +45,12 @@ override-map.2 = lfe,lfe switch = off volume = off +[Element Speaker] +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + [Element Front] switch = off volume = off diff --git a/src/modules/alsa/mixer/paths/analog-output-mono.conf b/src/modules/alsa/mixer/paths/analog-output-mono.conf index f6cb9f8a..a58cc970 100644 --- a/src/modules/alsa/mixer/paths/analog-output-mono.conf +++ b/src/modules/alsa/mixer/paths/analog-output-mono.conf @@ -42,6 +42,12 @@ override-map.2 = all-left,all-right switch = off volume = off +[Element Speaker] +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + [Element Front] switch = off volume = off diff --git a/src/modules/alsa/mixer/paths/analog-output.conf b/src/modules/alsa/mixer/paths/analog-output.conf index ea108aaf..b412a437 100644 --- a/src/modules/alsa/mixer/paths/analog-output.conf +++ b/src/modules/alsa/mixer/paths/analog-output.conf @@ -41,6 +41,12 @@ volume = off switch = off volume = off +[Element Speaker] +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + [Element Front] switch = mute volume = merge -- cgit From ac38c4d89845237cd5d19c29d8d3ef55f0374dca Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 23 Jul 2009 18:49:28 +0200 Subject: build-sys: add a couple of stub Makefiles --- src/modules/alsa/mixer/Makefile | 1 + src/modules/alsa/mixer/paths/Makefile | 1 + src/modules/alsa/mixer/profile-sets/Makefile | 1 + 3 files changed, 3 insertions(+) create mode 120000 src/modules/alsa/mixer/Makefile create mode 120000 src/modules/alsa/mixer/paths/Makefile create mode 120000 src/modules/alsa/mixer/profile-sets/Makefile (limited to 'src/modules/alsa') diff --git a/src/modules/alsa/mixer/Makefile b/src/modules/alsa/mixer/Makefile new file mode 120000 index 00000000..b4955194 --- /dev/null +++ b/src/modules/alsa/mixer/Makefile @@ -0,0 +1 @@ +../../../pulse/Makefile \ No newline at end of file diff --git a/src/modules/alsa/mixer/paths/Makefile b/src/modules/alsa/mixer/paths/Makefile new file mode 120000 index 00000000..dc23aaa2 --- /dev/null +++ b/src/modules/alsa/mixer/paths/Makefile @@ -0,0 +1 @@ +../../../../pulse/Makefile \ No newline at end of file diff --git a/src/modules/alsa/mixer/profile-sets/Makefile b/src/modules/alsa/mixer/profile-sets/Makefile new file mode 120000 index 00000000..dc23aaa2 --- /dev/null +++ b/src/modules/alsa/mixer/profile-sets/Makefile @@ -0,0 +1 @@ +../../../../pulse/Makefile \ No newline at end of file -- cgit From c325b93c01c5973db659aead6b66f753aa719168 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 23 Jul 2009 19:24:26 +0200 Subject: alsa: don't reset volume/mute when selecting path --- src/modules/alsa/alsa-mixer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/modules/alsa') diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c index a5515e1b..6a0b4ab7 100644 --- a/src/modules/alsa/alsa-mixer.c +++ b/src/modules/alsa/alsa-mixer.c @@ -940,7 +940,6 @@ int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) { PA_LLIST_FOREACH(e, p->elements) { switch (e->switch_use) { - case PA_ALSA_SWITCH_MUTE: case PA_ALSA_SWITCH_OFF: r = element_set_switch(e, m, FALSE); break; @@ -949,6 +948,7 @@ int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) { r = element_set_switch(e, m, TRUE); break; + case PA_ALSA_SWITCH_MUTE: case PA_ALSA_SWITCH_IGNORE: case PA_ALSA_SWITCH_SELECT: r = 0; @@ -960,7 +960,6 @@ int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) { switch (e->volume_use) { case PA_ALSA_VOLUME_OFF: - case PA_ALSA_VOLUME_MERGE: r = element_mute_volume(e, m); break; @@ -968,6 +967,7 @@ int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) { r = element_zero_volume(e, m); break; + case PA_ALSA_VOLUME_MERGE: case PA_ALSA_VOLUME_IGNORE: r = 0; break; -- cgit From 18433c19b690432179e9a0ed83eff611f5cecc67 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 24 Jul 2009 13:45:59 +0200 Subject: alsa: handle correctly if alsa returns us 0 or EAGAIN on snd_pcm_mmap_begin if we didn't call snd_pcm_avail immediately before --- src/modules/alsa/alsa-sink.c | 22 ++++++++++++++++++---- src/modules/alsa/alsa-source.c | 28 +++++++++++++++++++++------- 2 files changed, 39 insertions(+), 11 deletions(-) (limited to 'src/modules/alsa') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index e7925902..46562cbd 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -401,6 +401,7 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle snd_pcm_sframes_t n; size_t n_bytes; int r; + pa_bool_t after_avail = TRUE; /* First we determine how many samples are missing to fill the * buffer up to 100% */ @@ -484,6 +485,9 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle if (PA_UNLIKELY((err = pa_alsa_safe_mmap_begin(u->pcm_handle, &areas, &offset, &frames, u->hwbuf_size, &u->sink->sample_spec)) < 0)) { + if (!after_avail && err == -EAGAIN) + break; + if ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0) continue; @@ -494,9 +498,12 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle 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 (frames == 0) + if (!after_avail && frames == 0) break; + 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); @@ -617,6 +624,7 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle for (;;) { snd_pcm_sframes_t frames; void *p; + pa_bool_t after_avail = TRUE; /* pa_log_debug("%lu frames to write", (unsigned long) frames); */ @@ -634,17 +642,23 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle 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 (frames == 0) - break; - 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; diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 41bb768b..8a1fbe5f 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -391,6 +391,7 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled 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)) { @@ -463,6 +464,9 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled 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; @@ -473,9 +477,12 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled 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 (frames == 0) + if (!after_avail && frames == 0) break; + 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); @@ -542,6 +549,7 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled 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)) { @@ -602,20 +610,26 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled frames = snd_pcm_readi(u->pcm_handle, (uint8_t*) p, (snd_pcm_uframes_t) frames); pa_memblock_release(chunk.memblock); - if (frames == 0) { - pa_memblock_unref(chunk.memblock); - break; - } - if (PA_UNLIKELY(frames < 0)) { pa_memblock_unref(chunk.memblock); - if ((r = try_recover(u, "snd_pcm_readi", (int) (frames))) == 0) + 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; -- cgit From 5efb07281d5be1cddaed5b0610325fedd7f599ec Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 24 Jul 2009 20:13:52 +0200 Subject: alsa: throw timing data away after device resume --- src/modules/alsa/alsa-sink.c | 6 +++++- src/modules/alsa/alsa-source.c | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'src/modules/alsa') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 46562cbd..7fc602be 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -899,9 +899,13 @@ static int unsuspend(struct userdata *u) { if (build_pollfd(u) < 0) goto fail; + u->write_count = 0; + pa_smoother_reset(u->smoother, pa_rtclock_now(), TRUE); + u->first = TRUE; u->since_start = 0; + pa_log_info("Resumed successfully..."); return 0; @@ -1204,7 +1208,7 @@ static int process_rewind(struct userdata *u) { if (rewind_nbytes <= 0) pa_log_info("Tried rewind, but was apparently not possible."); else { - u->write_count -= out_frames * u->frame_size; + u->write_count -= rewind_nbytes; pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes); pa_sink_process_rewind(u->sink, rewind_nbytes); diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 8a1fbe5f..ed9c1480 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -854,7 +854,9 @@ static int unsuspend(struct userdata *u) { /* FIXME: We need to reload the volume somehow */ snd_pcm_start(u->pcm_handle); - pa_smoother_resume(u->smoother, pa_rtclock_now(), TRUE); + + u->read_count = 0; + pa_smoother_reset(u->smoother, pa_rtclock_now(), TRUE); pa_log_info("Resumed successfully..."); -- cgit From 39aa1cf94d43efaa80571b3f9d3dc56297f2f5be Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 31 Jul 2009 02:07:24 +0200 Subject: alsa: revert to first set number of periods, then set buffer size Apparently some ALSA drivers aren't happy with getting the buffer size configured first followed the period size. So swap the order again and document this for future reference so that we don't turn that around again. --- src/modules/alsa/alsa-util.c | 55 +++++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 26 deletions(-) (limited to 'src/modules/alsa') diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c index 1f3e5dcd..a47a8958 100644 --- a/src/modules/alsa/alsa-util.c +++ b/src/modules/alsa/alsa-util.c @@ -233,14 +233,16 @@ int pa_alsa_set_hw_params( goto finish; } - if (_period_size && tsched_size && _periods) { + if (_period_size > 0 && tsched_size > 0 && _periods > 0) { + snd_pcm_uframes_t buffer_size; + unsigned int p; /* Adjust the buffer sizes, if we didn't get the rate we were asking for */ _period_size = (snd_pcm_uframes_t) (((uint64_t) _period_size * r) / ss->rate); tsched_size = (snd_pcm_uframes_t) (((uint64_t) tsched_size * r) / ss->rate); if (_use_tsched) { - snd_pcm_uframes_t buffer_size = 0; + buffer_size = 0; if ((ret = snd_pcm_hw_params_get_buffer_size_max(hwparams, &buffer_size)) < 0) pa_log_warn("snd_pcm_hw_params_get_buffer_size_max() failed: %s", pa_alsa_strerror(ret)); @@ -251,32 +253,33 @@ int pa_alsa_set_hw_params( _periods = 1; } - if (_period_size > 0 && _periods > 0) { - snd_pcm_uframes_t buffer_size; - - buffer_size = _periods * _period_size; - - if ((ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &buffer_size)) < 0) - pa_log_info("snd_pcm_hw_params_set_buffer_size_near() failed: %s", pa_alsa_strerror(ret)); - } - - if (_periods > 0) { - - /* First we pass 0 as direction to get exactly what we - * asked for. That this is necessary is presumably a bug - * in ALSA. All in all this is mostly a hint to ALSA, so - * we don't care if this fails. */ - - dir = 0; - if (snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir) < 0) { - dir = 1; - if (snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir) < 0) { - dir = -1; - if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0) - pa_log_info("snd_pcm_hw_params_set_periods_near() failed: %s", pa_alsa_strerror(ret)); - } + /* Some ALSA drivers really don't like if we set the buffer + * size first and the number of periods second. (which would + * make a lot more sense to me) So, follow this rule and + * adjust the periods first and the buffer size second */ + + /* First we pass 0 as direction to get exactly what we + * asked for. That this is necessary is presumably a bug + * in ALSA. All in all this is mostly a hint to ALSA, so + * we don't care if this fails. */ + + p = _periods; + dir = 0; + if (snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &p, &dir) < 0) { + p = _periods; + dir = 1; + if (snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &p, &dir) < 0) { + p = _periods; + dir = -1; + if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &p, &dir)) < 0) + pa_log_info("snd_pcm_hw_params_set_periods_near() failed: %s", pa_alsa_strerror(ret)); } } + + /* Now set the buffer size */ + buffer_size = _periods * _period_size; + if ((ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &buffer_size)) < 0) + pa_log_info("snd_pcm_hw_params_set_buffer_size_near() failed: %s", pa_alsa_strerror(ret)); } if ((ret = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) -- cgit From 34f31f666e2a2c301e0735e48e48da44ff503acc Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Sun, 2 Aug 2009 17:52:05 +0200 Subject: Recover stream when it's suspended upon rewind Error from snd_pcm_rewind() might mean we just woke up from suspend and didn't have a chance to try to recover the stream since we didn't write to it in between. Call try_recover() in such cases. Note that for this to work kernel must return ESTRPIPE instead of EBADF for rewind/forward attempts on suspended streams, so that snd_pcm_recover() can recognize it should snd_pcm_resume() the stream. This is not the case yet (2.6.31-rc5), patch is available. Signed-off-by: Lubomir Rintel --- src/modules/alsa/alsa-sink.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src/modules/alsa') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 7fc602be..2a734e35 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -340,6 +340,9 @@ static int try_recover(struct userdata *u, const char *call, int err) { if (err == -EPIPE) pa_log_debug("%s: Buffer underrun!", call); + if (err == -EBADFD) + pa_log_debug("%s: Stream suspended!", call); + if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) < 0) { pa_log("%s: %s", call, pa_alsa_strerror(err)); return -1; @@ -1199,8 +1202,11 @@ static int process_rewind(struct userdata *u) { pa_log_debug("before: %lu", (unsigned long) in_frames); if ((out_frames = snd_pcm_rewind(u->pcm_handle, (snd_pcm_uframes_t) in_frames)) < 0) { pa_log("snd_pcm_rewind() failed: %s", pa_alsa_strerror((int) out_frames)); - return -1; + if (try_recover(u, "process_rewind", out_frames) < 0) + return -1; + out_frames = 0; } + pa_log_debug("after: %lu", (unsigned long) out_frames); rewind_nbytes = (size_t) out_frames * u->frame_size; -- cgit From 9b2534b6d0667589f69bf36702f9b998841d3d80 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 4 Aug 2009 00:23:43 +0200 Subject: alsa: properly treat ESTRPIPE as system suspend --- src/modules/alsa/alsa-sink.c | 4 ++-- src/modules/alsa/alsa-source.c | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'src/modules/alsa') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 2a734e35..1c38430f 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -340,8 +340,8 @@ static int try_recover(struct userdata *u, const char *call, int err) { if (err == -EPIPE) pa_log_debug("%s: Buffer underrun!", call); - if (err == -EBADFD) - pa_log_debug("%s: Stream suspended!", call); + if (err == -ESTRPIPE) + pa_log_debug("%s: System suspended!", call); if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) < 0) { pa_log("%s: %s", call, pa_alsa_strerror(err)); diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index ed9c1480..9a51f857 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -333,6 +333,9 @@ static int try_recover(struct userdata *u, const char *call, int err) { if (err == -EPIPE) pa_log_debug("%s: Buffer overrun!", call); + if (err == -ESTRPIPE) + pa_log_debug("%s: System suspended!", call); + if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) < 0) { pa_log("%s: %s", call, pa_alsa_strerror(err)); return -1; -- cgit From 462cdf44b7fe36768c836c90761f6b8153290517 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 12 Aug 2009 00:53:38 +0200 Subject: alsa: adjust priority bonus of mappings that match the configured default channel map We need to make sure that having both input and output weighs more for selecting the default profile than a channel map that matches the default channel map has. https://bugzilla.redhat.com/show_bug.cgi?id=496320 --- src/modules/alsa/alsa-mixer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/modules/alsa') diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c index 6a0b4ab7..a4c2ee0f 100644 --- a/src/modules/alsa/alsa-mixer.c +++ b/src/modules/alsa/alsa-mixer.c @@ -2838,9 +2838,9 @@ static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) { if (bonus) { if (pa_channel_map_equal(&m->channel_map, bonus)) - m->priority += 5000; + m->priority += 50; else if (m->channel_map.channels == bonus->channels) - m->priority += 4000; + m->priority += 30; } return 0; -- cgit From e8340345f6a102cd03b6676576bcd3879ead7aad Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 12 Aug 2009 00:56:10 +0200 Subject: alsa: enable ext. amplifier by default --- src/modules/alsa/mixer/paths/analog-output.conf.common | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/modules/alsa') diff --git a/src/modules/alsa/mixer/paths/analog-output.conf.common b/src/modules/alsa/mixer/paths/analog-output.conf.common index cc1185f4..3c6ce803 100644 --- a/src/modules/alsa/mixer/paths/analog-output.conf.common +++ b/src/modules/alsa/mixer/paths/analog-output.conf.common @@ -104,8 +104,8 @@ switch = select [Option External Amplifier:on] name = output-amplifier-on -priority = 0 +priority = 10 [Option External Amplifier:off] name = output-amplifier-off -priority = 10 +priority = 0 -- cgit From 58d441f7ea7994d5a0e8bc5397e2986707eb466b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 15 Aug 2009 00:16:25 +0200 Subject: log: place more rate limit invocations --- src/modules/alsa/alsa-sink.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/modules/alsa') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 1c38430f..a91b4b8a 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -1292,7 +1292,8 @@ static void thread_func(void *userdata) { * we have filled the buffer at least once * completely.*/ - pa_log_debug("Cutting sleep time for the initial iterations by half."); + if (pa_log_ratelimit()) + pa_log_debug("Cutting sleep time for the initial iterations by half."); sleep_usec /= 2; } -- cgit From 24e582808c18d6866d8c10f8f0320b1af0ab758b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 19 Aug 2009 01:35:43 +0200 Subject: source: rework volume handling - drop the 'virtual_' prefix from s->virtual_volume since we don't distuingish between reference and real volumes for sources - introduce an accuracy for source volumes: if the hardware can control the volume "close enough" don't necessarily adjust the rest in software unless it is beyond a certain threshold. This should save a little bit of CPU at the expensive of a bit of accuracy in volume handling. - other minor cleanups --- src/modules/alsa/alsa-source.c | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) (limited to 'src/modules/alsa') diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 9a51f857..7da37553 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -65,6 +65,8 @@ #define TSCHED_MIN_SLEEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms */ #define TSCHED_MIN_WAKEUP_USEC (4*PA_USEC_PER_MSEC) /* 4ms */ +#define VOLUME_ACCURACY (PA_VOLUME_NORM/100) + struct userdata { pa_core *core; pa_module *module; @@ -987,15 +989,11 @@ static void source_get_volume_cb(pa_source *s) { if (pa_cvolume_equal(&u->hardware_volume, &r)) return; - s->virtual_volume = u->hardware_volume = r; - - if (u->mixer_path->has_dB) { - pa_cvolume reset; + s->volume = u->hardware_volume = r; - /* Hmm, so the hardware volume changed, let's reset our software volume */ - pa_cvolume_reset(&reset, s->sample_spec.channels); - pa_source_set_soft_volume(s, &reset); - } + /* Hmm, so the hardware volume changed, let's reset our software volume */ + if (u->mixer_path->has_dB) + pa_source_set_soft_volume(s, NULL); } static void source_set_volume_cb(pa_source *s) { @@ -1008,7 +1006,7 @@ static void source_set_volume_cb(pa_source *s) { pa_assert(u->mixer_handle); /* Shift up by the base volume */ - pa_sw_cvolume_divide_scalar(&r, &s->virtual_volume, s->base_volume); + pa_sw_cvolume_divide_scalar(&r, &s->volume, s->base_volume); if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r) < 0) return; @@ -1019,13 +1017,26 @@ static void source_set_volume_cb(pa_source *s) { u->hardware_volume = r; if (u->mixer_path->has_dB) { + pa_cvolume new_soft_volume; + pa_bool_t accurate_enough; /* Match exactly what the user requested by software */ - pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &u->hardware_volume); + pa_sw_cvolume_divide(&new_soft_volume, &s->volume, &u->hardware_volume); + + /* If the adjustment to do in software is only minimal we + * can skip it. That saves us CPU at the expense of a bit of + * accuracy */ + accurate_enough = + (pa_cvolume_min(&new_soft_volume) >= (PA_VOLUME_NORM - VOLUME_ACCURACY)) && + (pa_cvolume_max(&new_soft_volume) <= (PA_VOLUME_NORM + VOLUME_ACCURACY)); - pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->virtual_volume)); + pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->volume)); pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &u->hardware_volume)); - pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->soft_volume)); + pa_log_debug("Calculated software volume: %s (accurate-enough=%s)", pa_cvolume_snprint(t, sizeof(t), &new_soft_volume), + pa_yes_no(accurate_enough)); + + if (!accurate_enough) + s->soft_volume = new_soft_volume; } else { pa_log_debug("Wrote hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &r)); @@ -1033,7 +1044,7 @@ static void source_set_volume_cb(pa_source *s) { /* We can't match exactly what the user requested, hence let's * at least tell the user about it */ - s->virtual_volume = r; + s->volume = r; } } -- cgit From d6f598ab3e1cdb71dc3b408592d06bba23f53a71 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 19 Aug 2009 02:29:59 +0200 Subject: udev: allow passing of ignore_dB= parameter to alsa modules --- src/modules/alsa/alsa-sink.c | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) (limited to 'src/modules/alsa') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index a91b4b8a..12538368 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -68,6 +68,8 @@ #define TSCHED_MIN_SLEEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms -- Sleep at least 10ms on each iteration */ #define TSCHED_MIN_WAKEUP_USEC (4*PA_USEC_PER_MSEC) /* 4ms -- Wakeup at least this long before the buffer runs empty*/ +#define VOLUME_ACCURACY (PA_VOLUME_NORM/100) /* don't require volume adjustments to be perfectly correct. don't necessarily extend granularity in software unless the differences get greater than this level */ + struct userdata { pa_core *core; pa_module *module; @@ -1034,15 +1036,11 @@ static void sink_get_volume_cb(pa_sink *s) { if (pa_cvolume_equal(&u->hardware_volume, &r)) return; - s->virtual_volume = u->hardware_volume = r; - - if (u->mixer_path->has_dB) { - pa_cvolume reset; + s->real_volume = u->hardware_volume = r; - /* Hmm, so the hardware volume changed, let's reset our software volume */ - pa_cvolume_reset(&reset, s->sample_spec.channels); - pa_sink_set_soft_volume(s, &reset); - } + /* Hmm, so the hardware volume changed, let's reset our software volume */ + if (u->mixer_path->has_dB) + pa_sink_set_soft_volume(s, NULL); } static void sink_set_volume_cb(pa_sink *s) { @@ -1055,7 +1053,7 @@ static void sink_set_volume_cb(pa_sink *s) { pa_assert(u->mixer_handle); /* Shift up by the base volume */ - pa_sw_cvolume_divide_scalar(&r, &s->virtual_volume, s->base_volume); + pa_sw_cvolume_divide_scalar(&r, &s->real_volume, s->base_volume); if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r) < 0) return; @@ -1066,13 +1064,26 @@ static void sink_set_volume_cb(pa_sink *s) { u->hardware_volume = r; if (u->mixer_path->has_dB) { + pa_cvolume new_soft_volume; + pa_bool_t accurate_enough; /* Match exactly what the user requested by software */ - pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &u->hardware_volume); + pa_sw_cvolume_divide(&new_soft_volume, &s->real_volume, &u->hardware_volume); + + /* If the adjustment to do in software is only minimal we + * can skip it. That saves us CPU at the expense of a bit of + * accuracy */ + accurate_enough = + (pa_cvolume_min(&new_soft_volume) >= (PA_VOLUME_NORM - VOLUME_ACCURACY)) && + (pa_cvolume_max(&new_soft_volume) <= (PA_VOLUME_NORM + VOLUME_ACCURACY)); - pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->virtual_volume)); + pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->real_volume)); pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &u->hardware_volume)); - pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->soft_volume)); + pa_log_debug("Calculated software volume: %s (accurate-enough=%s)", pa_cvolume_snprint(t, sizeof(t), &new_soft_volume), + pa_yes_no(accurate_enough)); + + if (!accurate_enough) + s->soft_volume = new_soft_volume; } else { pa_log_debug("Wrote hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &r)); @@ -1080,7 +1091,7 @@ static void sink_set_volume_cb(pa_sink *s) { /* We can't match exactly what the user requested, hence let's * at least tell the user about it */ - s->virtual_volume = r; + s->real_volume = r; } } -- cgit From 8c31974f56ebbbfc1a4978150026acf77c32689e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 19 Aug 2009 02:55:02 +0200 Subject: sink: volume handling rework, new flat volume logic - We now implement a logic where the sink maintains two distinct volumes: the 'reference' volume which is shown to the users, and the 'real' volume, which is configured to the hardware. The latter is configured to the max of all streams. Volume changes on sinks are propagated back to the streams proportional to the reference volume change. Volume changes on sink inputs are forwarded to the sink by 'pushing' the volume if necessary. This renames the old 'virtual_volume' to 'real_volume'. The 'reference_volume' is now the one exposed to users. By this logic the sink volume visible to the user, will always be the "upper" boundary for everything that is played. Saved/restored stream volumes are measured relative to this boundary, the factor here is always < 1.0. - introduce accuracy for sink volumes, similar to the accuracy we already have for source volumes. - other cleanups. --- src/modules/alsa/alsa-sink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/modules/alsa') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 12538368..e3707ae7 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -1009,7 +1009,7 @@ static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) { return 0; if (mask & SND_CTL_EVENT_MASK_VALUE) { - pa_sink_get_volume(u->sink, TRUE, FALSE); + pa_sink_get_volume(u->sink, TRUE); pa_sink_get_mute(u->sink, TRUE); } -- cgit From c1b6a87b27b569cda135da05b53cc98aa9ca37cb Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 20 Aug 2009 13:40:27 +0200 Subject: alsa-sink: reduce the amount of smoother updates Exponentially increase the amount of time between smoother updates. We start with a 2ms interval and increase up to 200ms intervals. Smoother updates and the resulting linear regression take a fair amount of CPU so we want to reduce the amount of updates. --- src/modules/alsa/alsa-sink.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) (limited to 'src/modules/alsa') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index e3707ae7..c3694729 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -68,6 +68,9 @@ #define TSCHED_MIN_SLEEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms -- Sleep at least 10ms on each iteration */ #define TSCHED_MIN_WAKEUP_USEC (4*PA_USEC_PER_MSEC) /* 4ms -- Wakeup at least this long before the buffer runs empty*/ +#define SMOOTHER_MIN_INTERVAL (2*PA_USEC_PER_MSEC) /* 2ms -- min smoother update interval */ +#define SMOOTHER_MAX_INTERVAL (200*PA_USEC_PER_MSEC) /* 200ms -- max smoother update inteval */ + #define VOLUME_ACCURACY (PA_VOLUME_NORM/100) /* don't require volume adjustments to be perfectly correct. don't necessarily extend granularity in software unless the differences get greater than this level */ struct userdata { @@ -115,6 +118,8 @@ struct userdata { pa_smoother *smoother; uint64_t write_count; uint64_t since_start; + pa_usec_t smoother_interval; + pa_usec_t last_smoother_update; pa_reserve_wrapper *reserve; pa_hook_slot *reserve_slot; @@ -723,17 +728,27 @@ static void update_smoother(struct userdata *u) { now1 = pa_timespec_load(&htstamp); } + /* Hmm, if the timestamp is 0, then it wasn't set and we take the current time */ + if (now1 <= 0) + now1 = pa_rtclock_now(); + + /* check if the time since the last update is bigger than the interval */ + if (u->last_smoother_update > 0) { + if (u->last_smoother_update + u->smoother_interval > now1) + return; + } + position = (int64_t) u->write_count - ((int64_t) delay * (int64_t) u->frame_size); if (PA_UNLIKELY(position < 0)) position = 0; - /* Hmm, if the timestamp is 0, then it wasn't set and we take the current time */ - if (now1 <= 0) - now1 = pa_rtclock_now(); - now2 = pa_bytes_to_usec((uint64_t) position, &u->sink->sample_spec); + u->last_smoother_update = now1; + /* exponentially increase the update interval up to the MAX limit */ + u->smoother_interval = PA_MIN (u->smoother_interval * 2, SMOOTHER_MAX_INTERVAL); + pa_smoother_put(u->smoother, now1, now2); } @@ -906,6 +921,8 @@ static int unsuspend(struct userdata *u) { u->write_count = 0; pa_smoother_reset(u->smoother, pa_rtclock_now(), TRUE); + u->smoother_interval = SMOOTHER_MIN_INTERVAL; + u->last_smoother_update = 0; u->first = TRUE; u->since_start = 0; @@ -1622,6 +1639,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca 5, pa_rtclock_now(), TRUE); + u->smoother_interval = SMOOTHER_MIN_INTERVAL; dev_id = pa_modargs_get_value( ma, "device_id", -- cgit From 8a2a6b2004cd299467de1955f7f99e25033faa63 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 21 Aug 2009 03:43:53 +0200 Subject: adjust various data/library paths automatically if we are run from a build tree --- src/modules/alsa/alsa-mixer.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'src/modules/alsa') diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c index a4c2ee0f..61c92cd0 100644 --- a/src/modules/alsa/alsa-mixer.c +++ b/src/modules/alsa/alsa-mixer.c @@ -929,7 +929,7 @@ static int element_zero_volume(pa_alsa_element *e, snd_mixer_t *m) { int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) { pa_alsa_element *e; - int r; + int r = 0; pa_assert(m); pa_assert(p); @@ -1849,7 +1849,12 @@ pa_alsa_path* pa_alsa_path_new(const char *fname, pa_alsa_direction_t direction) items[1].data = &p->description; items[2].data = &p->name; - fn = pa_maybe_prefix_path(fname, PA_ALSA_PATHS_DIR); + fn = pa_maybe_prefix_path(fname, +#if defined(__linux__) && !defined(__OPTIMIZE__) + pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/paths/" : +#endif + PA_ALSA_PATHS_DIR); + r = pa_config_parse(fn, NULL, items, p); pa_xfree(fn); @@ -3110,7 +3115,12 @@ pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel if (!fname) fname = "default.conf"; - fn = pa_maybe_prefix_path(fname, PA_ALSA_PROFILE_SETS_DIR); + fn = pa_maybe_prefix_path(fname, +#if defined(__linux__) && !defined(__OPTIMIZE__) + pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/profile-sets/" : +#endif + PA_ALSA_PROFILE_SETS_DIR); + r = pa_config_parse(fn, NULL, items, ps); pa_xfree(fn); -- cgit From fe9a577cf2799c0864a05a08c785161ba3738d88 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 21 Aug 2009 03:45:58 +0200 Subject: alsa: leave headphone jack enabled in normal mixer paths --- src/modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf | 7 +++++-- src/modules/alsa/mixer/paths/analog-output-mono.conf | 7 +++++-- src/modules/alsa/mixer/paths/analog-output.conf | 7 +++++-- 3 files changed, 15 insertions(+), 6 deletions(-) (limited to 'src/modules/alsa') diff --git a/src/modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf b/src/modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf index 2db976a5..3457d4a2 100644 --- a/src/modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf +++ b/src/modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf @@ -41,9 +41,12 @@ volume = merge override-map.1 = lfe override-map.2 = lfe,lfe +; This profile path is intended to control the speaker, not the +; headphones. But it should not hurt if we leave the headphone jack +; enabled nonetheless. [Element Headphone] -switch = off -volume = off +switch = mute +volume = zero [Element Speaker] switch = mute diff --git a/src/modules/alsa/mixer/paths/analog-output-mono.conf b/src/modules/alsa/mixer/paths/analog-output-mono.conf index a58cc970..dc270cfe 100644 --- a/src/modules/alsa/mixer/paths/analog-output-mono.conf +++ b/src/modules/alsa/mixer/paths/analog-output-mono.conf @@ -38,9 +38,12 @@ volume = merge override-map.1 = all override-map.2 = all-left,all-right +; This profile path is intended to control the speaker, not the +; headphones. But it should not hurt if we leave the headphone jack +; enabled nonetheless. [Element Headphone] -switch = off -volume = off +switch = mute +volume = zero [Element Speaker] switch = mute diff --git a/src/modules/alsa/mixer/paths/analog-output.conf b/src/modules/alsa/mixer/paths/analog-output.conf index b412a437..f71a05a1 100644 --- a/src/modules/alsa/mixer/paths/analog-output.conf +++ b/src/modules/alsa/mixer/paths/analog-output.conf @@ -37,9 +37,12 @@ override-map.2 = all-left,all-right switch = off volume = off +; This profile path is intended to control the speaker, not the +; headphones. But it should not hurt if we leave the headphone jack +; enabled nonetheless. [Element Headphone] -switch = off -volume = off +switch = mute +volume = zero [Element Speaker] switch = mute -- cgit From 80c693730365c1a375a5c0e781f38e7f165b37bf Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 23 Aug 2009 22:34:42 +0200 Subject: alsa: increase interval between smoother updates exponentially for alsa sources, following the scheme for sinks --- src/modules/alsa/alsa-sink.c | 8 +++----- src/modules/alsa/alsa-source.c | 20 ++++++++++++++++++-- 2 files changed, 21 insertions(+), 7 deletions(-) (limited to 'src/modules/alsa') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index c3694729..b99ed782 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -733,10 +733,9 @@ static void update_smoother(struct userdata *u) { now1 = pa_rtclock_now(); /* check if the time since the last update is bigger than the interval */ - if (u->last_smoother_update > 0) { + if (u->last_smoother_update > 0) if (u->last_smoother_update + u->smoother_interval > now1) return; - } position = (int64_t) u->write_count - ((int64_t) delay * (int64_t) u->frame_size); @@ -745,11 +744,11 @@ static void update_smoother(struct userdata *u) { now2 = pa_bytes_to_usec((uint64_t) position, &u->sink->sample_spec); + pa_smoother_put(u->smoother, now1, now2); + u->last_smoother_update = now1; /* exponentially increase the update interval up to the MAX limit */ u->smoother_interval = PA_MIN (u->smoother_interval * 2, SMOOTHER_MAX_INTERVAL); - - pa_smoother_put(u->smoother, now1, now2); } static pa_usec_t sink_get_latency(struct userdata *u) { @@ -927,7 +926,6 @@ static int unsuspend(struct userdata *u) { u->first = TRUE; u->since_start = 0; - pa_log_info("Resumed successfully..."); return 0; diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 7da37553..336027a2 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -65,6 +65,9 @@ #define TSCHED_MIN_SLEEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms */ #define TSCHED_MIN_WAKEUP_USEC (4*PA_USEC_PER_MSEC) /* 4ms */ +#define SMOOTHER_MIN_INTERVAL (2*PA_USEC_PER_MSEC) /* 2ms */ +#define SMOOTHER_MAX_INTERVAL (200*PA_USEC_PER_MSEC) /* 200ms */ + #define VOLUME_ACCURACY (PA_VOLUME_NORM/100) struct userdata { @@ -108,6 +111,8 @@ struct userdata { pa_smoother *smoother; uint64_t read_count; + pa_usec_t smoother_interval; + pa_usec_t last_smoother_update; pa_reserve_wrapper *reserve; pa_hook_slot *reserve_slot; @@ -691,15 +696,23 @@ static void update_smoother(struct userdata *u) { now1 = pa_timespec_load(&htstamp); } - position = u->read_count + ((uint64_t) delay * (uint64_t) u->frame_size); - /* Hmm, if the timestamp is 0, then it wasn't set and we take the current time */ if (now1 <= 0) now1 = pa_rtclock_now(); + /* check if the time since the last update is bigger than the interval */ + if (u->last_smoother_update > 0) + if (u->last_smoother_update + u->smoother_interval > now1) + return; + + position = u->read_count + ((uint64_t) delay * (uint64_t) u->frame_size); now2 = pa_bytes_to_usec(position, &u->source->sample_spec); pa_smoother_put(u->smoother, now1, now2); + + u->last_smoother_update = now1; + /* exponentially increase the update interval up to the MAX limit */ + u->smoother_interval = PA_MIN (u->smoother_interval * 2, SMOOTHER_MAX_INTERVAL); } static pa_usec_t source_get_latency(struct userdata *u) { @@ -862,6 +875,8 @@ static int unsuspend(struct userdata *u) { u->read_count = 0; pa_smoother_reset(u->smoother, pa_rtclock_now(), TRUE); + u->smoother_interval = SMOOTHER_MIN_INTERVAL; + u->last_smoother_update = 0; pa_log_info("Resumed successfully..."); @@ -1469,6 +1484,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p 5, pa_rtclock_now(), FALSE); + u->smoother_interval = SMOOTHER_MIN_INTERVAL; dev_id = pa_modargs_get_value( ma, "device_id", -- cgit From 050a3a99e1d151b4f55c89f82073ef33f3399646 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 24 Aug 2009 03:26:56 +0200 Subject: alsa: automatically decrease watermark after a time of stability --- src/modules/alsa/alsa-sink.c | 146 +++++++++++++++++++++++++++++++---------- src/modules/alsa/alsa-source.c | 145 ++++++++++++++++++++++++++++++---------- 2 files changed, 223 insertions(+), 68 deletions(-) (limited to 'src/modules/alsa') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index b99ed782..07d53880 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -62,14 +62,21 @@ /* #define DEBUG_TIMING */ #define DEFAULT_DEVICE "default" -#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) /* 2s -- Overall buffer size */ -#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC) /* 20ms -- Fill up when only this much is left in the buffer */ -#define TSCHED_WATERMARK_STEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms -- On underrun, increase watermark by this */ -#define TSCHED_MIN_SLEEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms -- Sleep at least 10ms on each iteration */ -#define TSCHED_MIN_WAKEUP_USEC (4*PA_USEC_PER_MSEC) /* 4ms -- Wakeup at least this long before the buffer runs empty*/ -#define SMOOTHER_MIN_INTERVAL (2*PA_USEC_PER_MSEC) /* 2ms -- min smoother update interval */ -#define SMOOTHER_MAX_INTERVAL (200*PA_USEC_PER_MSEC) /* 200ms -- max smoother update inteval */ +#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) /* 2s -- Overall buffer size */ +#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC) /* 20ms -- Fill up when only this much is left in the buffer */ + +#define TSCHED_WATERMARK_INC_STEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms -- On underrun, increase watermark by this */ +#define TSCHED_WATERMARK_DEC_STEP_USEC (5*PA_USEC_PER_MSEC) /* 5ms -- When everything's great, decrease watermark by this */ +#define TSCHED_WATERMARK_VERIFY_AFTER_USEC (20*PA_USEC_PER_SEC) /* 20s -- How long after a drop out recheck if things are good now */ +#define TSCHED_WATERMARK_INC_THRESHOLD_USEC (1*PA_USEC_PER_MSEC) /* 3ms -- If the buffer level ever below this theshold, increase the watermark */ +#define TSCHED_WATERMARK_DEC_THRESHOLD_USEC (100*PA_USEC_PER_MSEC) /* 100ms -- If the buffer level didn't drop below this theshold in the verification time, decrease the watermark */ + +#define TSCHED_MIN_SLEEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms -- Sleep at least 10ms on each iteration */ +#define TSCHED_MIN_WAKEUP_USEC (4*PA_USEC_PER_MSEC) /* 4ms -- Wakeup at least this long before the buffer runs empty*/ + +#define SMOOTHER_MIN_INTERVAL (2*PA_USEC_PER_MSEC) /* 2ms -- min smoother update interval */ +#define SMOOTHER_MAX_INTERVAL (200*PA_USEC_PER_MSEC) /* 200ms -- max smoother update inteval */ #define VOLUME_ACCURACY (PA_VOLUME_NORM/100) /* don't require volume adjustments to be perfectly correct. don't necessarily extend granularity in software unless the differences get greater than this level */ @@ -99,7 +106,12 @@ struct userdata { hwbuf_unused, min_sleep, min_wakeup, - watermark_step; + watermark_inc_step, + watermark_dec_step, + watermark_inc_threshold, + watermark_dec_threshold; + + pa_usec_t watermark_dec_not_before; unsigned nfragments; pa_memchunk memchunk; @@ -248,6 +260,7 @@ static void fix_min_sleep_wakeup(struct userdata *u) { size_t max_use, max_use_2; pa_assert(u); + pa_assert(u->use_tsched); max_use = u->hwbuf_size - u->hwbuf_unused; max_use_2 = pa_frame_align(max_use/2, &u->sink->sample_spec); @@ -262,6 +275,7 @@ static void fix_min_sleep_wakeup(struct userdata *u) { static void fix_tsched_watermark(struct userdata *u) { size_t max_use; pa_assert(u); + pa_assert(u->use_tsched); max_use = u->hwbuf_size - u->hwbuf_unused; @@ -272,7 +286,7 @@ static void fix_tsched_watermark(struct userdata *u) { u->tsched_watermark = u->min_wakeup; } -static void adjust_after_underrun(struct userdata *u) { +static void increase_watermark(struct userdata *u) { size_t old_watermark; pa_usec_t old_min_latency, new_min_latency; @@ -281,31 +295,64 @@ static void adjust_after_underrun(struct userdata *u) { /* First, just try to increase the watermark */ old_watermark = u->tsched_watermark; - u->tsched_watermark = PA_MIN(u->tsched_watermark * 2, u->tsched_watermark + u->watermark_step); + u->tsched_watermark = PA_MIN(u->tsched_watermark * 2, u->tsched_watermark + u->watermark_inc_step); fix_tsched_watermark(u); if (old_watermark != u->tsched_watermark) { - pa_log_notice("Increasing wakeup watermark to %0.2f ms", - (double) pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec) / PA_USEC_PER_MSEC); + pa_log_info("Increasing wakeup watermark to %0.2f ms", + (double) pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec) / PA_USEC_PER_MSEC); return; } /* Hmm, we cannot increase the watermark any further, hence let's raise the latency */ old_min_latency = u->sink->thread_info.min_latency; - new_min_latency = PA_MIN(old_min_latency * 2, old_min_latency + TSCHED_WATERMARK_STEP_USEC); + new_min_latency = PA_MIN(old_min_latency * 2, old_min_latency + TSCHED_WATERMARK_INC_STEP_USEC); new_min_latency = PA_MIN(new_min_latency, u->sink->thread_info.max_latency); if (old_min_latency != new_min_latency) { - pa_log_notice("Increasing minimal latency to %0.2f ms", - (double) new_min_latency / PA_USEC_PER_MSEC); + pa_log_info("Increasing minimal latency to %0.2f ms", + (double) new_min_latency / PA_USEC_PER_MSEC); pa_sink_set_latency_range_within_thread(u->sink, new_min_latency, u->sink->thread_info.max_latency); - return; } /* When we reach this we're officialy fucked! */ } +static void decrease_watermark(struct userdata *u) { + size_t old_watermark; + pa_usec_t now; + + pa_assert(u); + pa_assert(u->use_tsched); + + now = pa_rtclock_now(); + + if (u->watermark_dec_not_before <= 0) + goto restart; + + if (u->watermark_dec_not_before > now) + return; + + old_watermark = u->tsched_watermark; + + if (u->tsched_watermark < u->watermark_dec_step) + u->tsched_watermark = u->tsched_watermark / 2; + else + u->tsched_watermark = PA_MAX(u->tsched_watermark / 2, u->tsched_watermark - u->watermark_dec_step); + + fix_tsched_watermark(u); + + if (old_watermark != u->tsched_watermark) + pa_log_info("Decreasing wakeup watermark to %0.2f ms", + (double) pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec) / PA_USEC_PER_MSEC); + + /* We don't change the latency range*/ + +restart: + u->watermark_dec_not_before = now + TSCHED_WATERMARK_VERIFY_AFTER_USEC; +} + static void hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*process_usec) { pa_usec_t usec, wm; @@ -313,6 +360,7 @@ static void hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*p pa_assert(process_usec); pa_assert(u); + pa_assert(u->use_tsched); usec = pa_sink_get_requested_latency_within_thread(u->sink); @@ -360,7 +408,7 @@ static int try_recover(struct userdata *u, const char *call, int err) { return 0; } -static size_t check_left_to_play(struct userdata *u, size_t n_bytes) { +static size_t check_left_to_play(struct userdata *u, size_t n_bytes, pa_bool_t on_timeout) { size_t left_to_play; /* We use <= instead of < for this check here because an underrun @@ -368,34 +416,55 @@ static size_t check_left_to_play(struct userdata *u, size_t n_bytes) { * it is removed from the buffer. This is particularly important * when block transfer is used. */ - if (n_bytes <= u->hwbuf_size) { + if (n_bytes <= u->hwbuf_size) left_to_play = u->hwbuf_size - n_bytes; + else { + + /* We got a dropout. What a mess! */ + left_to_play = 0; #ifdef DEBUG_TIMING - pa_log_debug("%0.2f ms left to play", (double) pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) / PA_USEC_PER_MSEC); + PA_DEBUG_TRAP; #endif - } else { - left_to_play = 0; + if (!u->first && !u->after_rewind) + if (pa_log_ratelimit()) + pa_log_info("Underrun!"); + } #ifdef DEBUG_TIMING - PA_DEBUG_TRAP; + pa_log_debug("%0.2f ms left to play; inc threshold = %0.2f ms; dec threshold = %0.2f ms", + (double) pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) / PA_USEC_PER_MSEC, + (double) pa_bytes_to_usec(u->watermark_inc_threshold, &u->sink->sample_spec) / PA_USEC_PER_MSEC, + (double) pa_bytes_to_usec(u->watermark_dec_threshold, &u->sink->sample_spec) / PA_USEC_PER_MSEC); #endif + if (u->use_tsched) { + pa_bool_t reset_not_before = TRUE; + if (!u->first && !u->after_rewind) { + if (left_to_play < u->watermark_inc_threshold) + increase_watermark(u); + else if (left_to_play > u->watermark_dec_threshold) { + reset_not_before = FALSE; - if (pa_log_ratelimit()) - pa_log_info("Underrun!"); + /* We decrease the watermark only if have actually + * been woken up by a timeout. If something else woke + * us up it's too easy to fulfill the deadlines... */ - if (u->use_tsched) - adjust_after_underrun(u); + if (on_timeout) + decrease_watermark(u); + } } + + if (reset_not_before) + u->watermark_dec_not_before = 0; } return left_to_play; } -static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled) { +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; @@ -430,7 +499,8 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle pa_log_debug("avail: %lu", (unsigned long) n_bytes); #endif - left_to_play = check_left_to_play(u, n_bytes); + left_to_play = check_left_to_play(u, n_bytes, on_timeout); + on_timeout = FALSE; if (u->use_tsched) @@ -565,7 +635,7 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle return work_done ? 1 : 0; } -static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled) { +static int unix_write(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_play; @@ -591,7 +661,8 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle } n_bytes = (size_t) n * u->frame_size; - left_to_play = check_left_to_play(u, n_bytes); + left_to_play = check_left_to_play(u, n_bytes, on_timeout); + on_timeout = FALSE; if (u->use_tsched) @@ -1278,15 +1349,16 @@ static void thread_func(void *userdata) { if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) { int work_done; pa_usec_t sleep_usec = 0; + pa_bool_t on_timeout = pa_rtpoll_timer_elapsed(u->rtpoll); if (PA_UNLIKELY(u->sink->thread_info.rewind_requested)) if (process_rewind(u) < 0) goto fail; if (u->use_mmap) - work_done = mmap_write(u, &sleep_usec, revents & POLLOUT); + work_done = mmap_write(u, &sleep_usec, revents & POLLOUT, on_timeout); else - work_done = unix_write(u, &sleep_usec, revents & POLLOUT); + work_done = unix_write(u, &sleep_usec, revents & POLLOUT, on_timeout); if (work_done < 0) goto fail; @@ -1787,7 +1859,6 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca u->fragment_size = frag_size = (uint32_t) (period_frames * frame_size); u->nfragments = nfrags; u->hwbuf_size = u->fragment_size * nfrags; - u->tsched_watermark = pa_usec_to_bytes_round_up(pa_bytes_to_usec_round_up(tsched_watermark, &requested_ss), &u->sink->sample_spec); pa_cvolume_mute(&u->hardware_volume, u->sink->sample_spec.channels); pa_log_info("Using %u fragments of size %lu bytes, buffer time is %0.2fms", @@ -1798,7 +1869,13 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca pa_sink_set_max_rewind(u->sink, u->hwbuf_size); if (u->use_tsched) { - u->watermark_step = pa_usec_to_bytes(TSCHED_WATERMARK_STEP_USEC, &u->sink->sample_spec); + u->tsched_watermark = pa_usec_to_bytes_round_up(pa_bytes_to_usec_round_up(tsched_watermark, &requested_ss), &u->sink->sample_spec); + + u->watermark_inc_step = pa_usec_to_bytes(TSCHED_WATERMARK_INC_STEP_USEC, &u->sink->sample_spec); + u->watermark_dec_step = pa_usec_to_bytes(TSCHED_WATERMARK_DEC_STEP_USEC, &u->sink->sample_spec); + + u->watermark_inc_threshold = pa_usec_to_bytes_round_up(TSCHED_WATERMARK_INC_THRESHOLD_USEC, &u->sink->sample_spec); + u->watermark_dec_threshold = pa_usec_to_bytes_round_up(TSCHED_WATERMARK_DEC_THRESHOLD_USEC, &u->sink->sample_spec); fix_min_sleep_wakeup(u); fix_tsched_watermark(u); @@ -1812,6 +1889,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca } else pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(u->hwbuf_size, &ss)); + reserve_update(u); if (update_sw_params(u) < 0) diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 336027a2..165b2e3b 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -59,14 +59,22 @@ /* #define DEBUG_TIMING */ #define DEFAULT_DEVICE "default" -#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) /* 2s */ -#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC) /* 20ms */ -#define TSCHED_WATERMARK_STEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms */ -#define TSCHED_MIN_SLEEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms */ -#define TSCHED_MIN_WAKEUP_USEC (4*PA_USEC_PER_MSEC) /* 4ms */ -#define SMOOTHER_MIN_INTERVAL (2*PA_USEC_PER_MSEC) /* 2ms */ -#define SMOOTHER_MAX_INTERVAL (200*PA_USEC_PER_MSEC) /* 200ms */ +#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) /* 2s */ +#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC) /* 20ms */ + +#define TSCHED_WATERMARK_INC_STEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms */ +#define TSCHED_WATERMARK_DEC_STEP_USEC (5*PA_USEC_PER_MSEC) /* 5ms */ +#define TSCHED_WATERMARK_VERIFY_AFTER_USEC (20*PA_USEC_PER_SEC) /* 20s */ +#define TSCHED_WATERMARK_INC_THRESHOLD_USEC (1*PA_USEC_PER_MSEC) /* 3ms */ +#define TSCHED_WATERMARK_DEC_THRESHOLD_USEC (100*PA_USEC_PER_MSEC) /* 100ms */ +#define TSCHED_WATERMARK_STEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms */ + +#define TSCHED_MIN_SLEEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms */ +#define TSCHED_MIN_WAKEUP_USEC (4*PA_USEC_PER_MSEC) /* 4ms */ + +#define SMOOTHER_MIN_INTERVAL (2*PA_USEC_PER_MSEC) /* 2ms */ +#define SMOOTHER_MAX_INTERVAL (200*PA_USEC_PER_MSEC) /* 200ms */ #define VOLUME_ACCURACY (PA_VOLUME_NORM/100) @@ -96,7 +104,12 @@ struct userdata { hwbuf_unused, min_sleep, min_wakeup, - watermark_step; + watermark_inc_step, + watermark_dec_step, + watermark_inc_threshold, + watermark_dec_threshold; + + pa_usec_t watermark_dec_not_before; unsigned nfragments; @@ -241,6 +254,7 @@ static int reserve_monitor_init(struct userdata *u, const char *dname) { static void fix_min_sleep_wakeup(struct userdata *u) { size_t max_use, max_use_2; pa_assert(u); + pa_assert(u->use_tsched); max_use = u->hwbuf_size - u->hwbuf_unused; max_use_2 = pa_frame_align(max_use/2, &u->source->sample_spec); @@ -255,6 +269,7 @@ static void fix_min_sleep_wakeup(struct userdata *u) { static void fix_tsched_watermark(struct userdata *u) { size_t max_use; pa_assert(u); + pa_assert(u->use_tsched); max_use = u->hwbuf_size - u->hwbuf_unused; @@ -265,7 +280,7 @@ static void fix_tsched_watermark(struct userdata *u) { u->tsched_watermark = u->min_wakeup; } -static void adjust_after_overrun(struct userdata *u) { +static void increase_watermark(struct userdata *u) { size_t old_watermark; pa_usec_t old_min_latency, new_min_latency; @@ -274,36 +289,72 @@ static void adjust_after_overrun(struct userdata *u) { /* First, just try to increase the watermark */ old_watermark = u->tsched_watermark; - u->tsched_watermark = PA_MIN(u->tsched_watermark * 2, u->tsched_watermark + u->watermark_step); - + u->tsched_watermark = PA_MIN(u->tsched_watermark * 2, u->tsched_watermark + u->watermark_inc_step); fix_tsched_watermark(u); if (old_watermark != u->tsched_watermark) { - pa_log_notice("Increasing wakeup watermark to %0.2f ms", - (double) pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec) / PA_USEC_PER_MSEC); + pa_log_info("Increasing wakeup watermark to %0.2f ms", + (double) pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec) / PA_USEC_PER_MSEC); return; } /* Hmm, we cannot increase the watermark any further, hence let's raise the latency */ old_min_latency = u->source->thread_info.min_latency; - new_min_latency = PA_MIN(old_min_latency * 2, old_min_latency + TSCHED_WATERMARK_STEP_USEC); + new_min_latency = PA_MIN(old_min_latency * 2, old_min_latency + TSCHED_WATERMARK_INC_STEP_USEC); new_min_latency = PA_MIN(new_min_latency, u->source->thread_info.max_latency); if (old_min_latency != new_min_latency) { - pa_log_notice("Increasing minimal latency to %0.2f ms", - (double) new_min_latency / PA_USEC_PER_MSEC); + pa_log_info("Increasing minimal latency to %0.2f ms", + (double) new_min_latency / PA_USEC_PER_MSEC); pa_source_set_latency_range_within_thread(u->source, new_min_latency, u->source->thread_info.max_latency); - return; } /* When we reach this we're officialy fucked! */ } +static void decrease_watermark(struct userdata *u) { + size_t old_watermark; + pa_usec_t now; + + pa_assert(u); + pa_assert(u->use_tsched); + + now = pa_rtclock_now(); + + if (u->watermark_dec_not_before <= 0) + goto restart; + + if (u->watermark_dec_not_before > now) + return; + + old_watermark = u->tsched_watermark; + + if (u->tsched_watermark < u->watermark_dec_step) + u->tsched_watermark = u->tsched_watermark / 2; + else + u->tsched_watermark = PA_MAX(u->tsched_watermark / 2, u->tsched_watermark - u->watermark_dec_step); + + fix_tsched_watermark(u); + + if (old_watermark != u->tsched_watermark) + pa_log_info("Decreasing wakeup watermark to %0.2f ms", + (double) pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec) / PA_USEC_PER_MSEC); + + /* We don't change the latency range*/ + +restart: + u->watermark_dec_not_before = now + TSCHED_WATERMARK_VERIFY_AFTER_USEC; +} + 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(sleep_usec); + pa_assert(process_usec); + pa_assert(u); + pa_assert(u->use_tsched); usec = pa_source_get_requested_latency_within_thread(u->source); @@ -352,7 +403,7 @@ static int try_recover(struct userdata *u, const char *call, int err) { return 0; } -static size_t check_left_to_record(struct userdata *u, size_t n_bytes) { +static size_t check_left_to_record(struct userdata *u, size_t n_bytes, pa_bool_t on_timeout) { size_t left_to_record; size_t rec_space = u->hwbuf_size - u->hwbuf_unused; @@ -361,14 +412,11 @@ static size_t check_left_to_record(struct userdata *u, size_t n_bytes) { * it is removed from the buffer. This is particularly important * when block transfer is used. */ - if (n_bytes <= rec_space) { + if (n_bytes <= rec_space) left_to_record = rec_space - n_bytes; + else { -#ifdef DEBUG_TIMING - 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); -#endif - - } else { + /* We got a dropout. What a mess! */ left_to_record = 0; #ifdef DEBUG_TIMING @@ -377,15 +425,36 @@ static size_t check_left_to_record(struct userdata *u, size_t n_bytes) { if (pa_log_ratelimit()) pa_log_info("Overrun!"); + } - if (u->use_tsched) - adjust_after_overrun(u); +#ifdef DEBUG_TIMING + 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); +#endif + + if (u->use_tsched) { + pa_bool_t reset_not_before = TRUE; + + if (left_to_record < u->watermark_inc_threshold) + increase_watermark(u); + else if (left_to_record > u->watermark_dec_threshold) { + reset_not_before = FALSE; + + /* We decrease the watermark only if have actually been + * woken up by a timeout. If something else woke us up + * it's too easy to fulfill the deadlines... */ + + if (on_timeout) + decrease_watermark(u); + } + + if (reset_not_before) + u->watermark_dec_not_before = 0; } return left_to_record; } -static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled) { +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; @@ -417,7 +486,8 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled pa_log_debug("avail: %lu", (unsigned long) n_bytes); #endif - left_to_record = check_left_to_record(u, n_bytes); + left_to_record = check_left_to_record(u, n_bytes, on_timeout); + on_timeout = FALSE; if (u->use_tsched) if (!polled && @@ -543,7 +613,7 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled return work_done ? 1 : 0; } -static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled) { +static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled, pa_bool_t on_timeout) { int work_done = FALSE; pa_usec_t max_sleep_usec = 0, process_usec = 0; size_t left_to_record; @@ -570,7 +640,8 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled } n_bytes = (size_t) n * u->frame_size; - left_to_record = check_left_to_record(u, n_bytes); + left_to_record = check_left_to_record(u, n_bytes, on_timeout); + on_timeout = FALSE; if (u->use_tsched) if (!polled && @@ -1158,11 +1229,12 @@ static void thread_func(void *userdata) { if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) { int work_done; pa_usec_t sleep_usec = 0; + pa_bool_t on_timeout = pa_rtpoll_timer_elapsed(u->rtpoll); if (u->use_mmap) - work_done = mmap_read(u, &sleep_usec, revents & POLLIN); + work_done = mmap_read(u, &sleep_usec, revents & POLLIN, on_timeout); else - work_done = unix_read(u, &sleep_usec, revents & POLLIN); + work_done = unix_read(u, &sleep_usec, revents & POLLIN, on_timeout); if (work_done < 0) goto fail; @@ -1632,7 +1704,6 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p u->fragment_size = frag_size = (uint32_t) (period_frames * frame_size); u->nfragments = nfrags; u->hwbuf_size = u->fragment_size * nfrags; - u->tsched_watermark = pa_usec_to_bytes_round_up(pa_bytes_to_usec_round_up(tsched_watermark, &requested_ss), &u->source->sample_spec); pa_cvolume_mute(&u->hardware_volume, u->source->sample_spec.channels); pa_log_info("Using %u fragments of size %lu bytes, buffer time is %0.2fms", @@ -1640,7 +1711,13 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p (double) pa_bytes_to_usec(u->hwbuf_size, &ss) / PA_USEC_PER_MSEC); if (u->use_tsched) { - u->watermark_step = pa_usec_to_bytes(TSCHED_WATERMARK_STEP_USEC, &u->source->sample_spec); + u->tsched_watermark = pa_usec_to_bytes_round_up(pa_bytes_to_usec_round_up(tsched_watermark, &requested_ss), &u->source->sample_spec); + + u->watermark_inc_step = pa_usec_to_bytes(TSCHED_WATERMARK_INC_STEP_USEC, &u->source->sample_spec); + u->watermark_dec_step = pa_usec_to_bytes(TSCHED_WATERMARK_DEC_STEP_USEC, &u->source->sample_spec); + + u->watermark_inc_threshold = pa_usec_to_bytes_round_up(TSCHED_WATERMARK_INC_THRESHOLD_USEC, &u->source->sample_spec); + u->watermark_dec_threshold = pa_usec_to_bytes_round_up(TSCHED_WATERMARK_DEC_THRESHOLD_USEC, &u->source->sample_spec); fix_min_sleep_wakeup(u); fix_tsched_watermark(u); -- cgit From 6b6d14626d5ae8b49edd1abe8ecc2432262358c9 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 1 Sep 2009 04:54:18 +0200 Subject: alsa: distuingish real underruns from left_to_play=0 --- src/modules/alsa/alsa-sink.c | 4 +++- src/modules/alsa/alsa-source.c | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'src/modules/alsa') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 07d53880..afea8e08 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -410,6 +410,7 @@ static int try_recover(struct userdata *u, const char *call, int err) { static size_t check_left_to_play(struct userdata *u, size_t n_bytes, pa_bool_t on_timeout) { size_t left_to_play; + pa_bool_t underrun = FALSE; /* We use <= instead of < for this check here because an underrun * only happens after the last sample was processed, not already when @@ -422,6 +423,7 @@ static size_t check_left_to_play(struct userdata *u, size_t n_bytes, pa_bool_t o /* We got a dropout. What a mess! */ left_to_play = 0; + underrun = TRUE; #ifdef DEBUG_TIMING PA_DEBUG_TRAP; @@ -443,7 +445,7 @@ static size_t check_left_to_play(struct userdata *u, size_t n_bytes, pa_bool_t o pa_bool_t reset_not_before = TRUE; if (!u->first && !u->after_rewind) { - if (left_to_play < u->watermark_inc_threshold) + if (underrun || left_to_play < u->watermark_inc_threshold) increase_watermark(u); else if (left_to_play > u->watermark_dec_threshold) { reset_not_before = FALSE; diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 165b2e3b..643566b5 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -406,6 +406,7 @@ static int try_recover(struct userdata *u, const char *call, int err) { static size_t check_left_to_record(struct userdata *u, size_t n_bytes, pa_bool_t on_timeout) { size_t left_to_record; size_t rec_space = u->hwbuf_size - u->hwbuf_unused; + pa_bool_t overrun = FALSE; /* We use <= instead of < for this check here because an overrun * only happens after the last sample was processed, not already when @@ -418,6 +419,7 @@ static size_t check_left_to_record(struct userdata *u, size_t n_bytes, pa_bool_t /* We got a dropout. What a mess! */ left_to_record = 0; + overrun = TRUE; #ifdef DEBUG_TIMING PA_DEBUG_TRAP; @@ -434,7 +436,7 @@ static size_t check_left_to_record(struct userdata *u, size_t n_bytes, pa_bool_t if (u->use_tsched) { pa_bool_t reset_not_before = TRUE; - if (left_to_record < u->watermark_inc_threshold) + if (overrun || left_to_record < u->watermark_inc_threshold) increase_watermark(u); else if (left_to_record > u->watermark_dec_threshold) { reset_not_before = FALSE; -- cgit From 46b9ca7fd5747d1ddbb7f2d18d2b572ddb785dc6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 1 Sep 2009 04:55:05 +0200 Subject: alsa: by default increase watermarks only on real underruns, don't try to be smart --- src/modules/alsa/alsa-sink.c | 5 ++++- src/modules/alsa/alsa-source.c | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'src/modules/alsa') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index afea8e08..195bdf6e 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -69,9 +69,12 @@ #define TSCHED_WATERMARK_INC_STEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms -- On underrun, increase watermark by this */ #define TSCHED_WATERMARK_DEC_STEP_USEC (5*PA_USEC_PER_MSEC) /* 5ms -- When everything's great, decrease watermark by this */ #define TSCHED_WATERMARK_VERIFY_AFTER_USEC (20*PA_USEC_PER_SEC) /* 20s -- How long after a drop out recheck if things are good now */ -#define TSCHED_WATERMARK_INC_THRESHOLD_USEC (1*PA_USEC_PER_MSEC) /* 3ms -- If the buffer level ever below this theshold, increase the watermark */ +#define TSCHED_WATERMARK_INC_THRESHOLD_USEC (0*PA_USEC_PER_MSEC) /* 0ms -- If the buffer level ever below this theshold, increase the watermark */ #define TSCHED_WATERMARK_DEC_THRESHOLD_USEC (100*PA_USEC_PER_MSEC) /* 100ms -- If the buffer level didn't drop below this theshold in the verification time, decrease the watermark */ +/* Note that TSCHED_WATERMARK_INC_THRESHOLD_USEC == 0 means tht we + * will increase the watermark only if we hit a real underrun. */ + #define TSCHED_MIN_SLEEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms -- Sleep at least 10ms on each iteration */ #define TSCHED_MIN_WAKEUP_USEC (4*PA_USEC_PER_MSEC) /* 4ms -- Wakeup at least this long before the buffer runs empty*/ diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 643566b5..f42d3542 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -66,7 +66,7 @@ #define TSCHED_WATERMARK_INC_STEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms */ #define TSCHED_WATERMARK_DEC_STEP_USEC (5*PA_USEC_PER_MSEC) /* 5ms */ #define TSCHED_WATERMARK_VERIFY_AFTER_USEC (20*PA_USEC_PER_SEC) /* 20s */ -#define TSCHED_WATERMARK_INC_THRESHOLD_USEC (1*PA_USEC_PER_MSEC) /* 3ms */ +#define TSCHED_WATERMARK_INC_THRESHOLD_USEC (0*PA_USEC_PER_MSEC) /* 0ms */ #define TSCHED_WATERMARK_DEC_THRESHOLD_USEC (100*PA_USEC_PER_MSEC) /* 100ms */ #define TSCHED_WATERMARK_STEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms */ -- cgit From 8cd635bc614834c13d0f1c586d472b4a52b98664 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 3 Sep 2009 04:45:55 +0200 Subject: alsa: add more input sources to path set --- src/modules/alsa/mixer/paths/analog-input.conf.common | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/modules/alsa') diff --git a/src/modules/alsa/mixer/paths/analog-input.conf.common b/src/modules/alsa/mixer/paths/analog-input.conf.common index 6728a6ae..87af38b3 100644 --- a/src/modules/alsa/mixer/paths/analog-input.conf.common +++ b/src/modules/alsa/mixer/paths/analog-input.conf.common @@ -78,6 +78,10 @@ priority = 19 name = input-microphone priority = 19 +[Option Input Source:Internal Mic] +name = input-microphone +priority = 19 + [Option Input Source:Line] name = input-linein priority = 18 @@ -90,6 +94,10 @@ priority = 18 name = input-linein priority = 18 +[Option Input Source:Docking-Station] +name = input-docking +priority = 17 + ;;; ' Capture Source' [Element Capture Source] -- cgit From f5046759cdd72daf5ba3b31c9dfc7b8d5be6bc9b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 8 Sep 2009 23:46:23 +0200 Subject: llvm-clang-analyzer: drop a few unnecessary assignments and other trivial fixes --- src/modules/alsa/alsa-mixer.c | 2 +- src/modules/alsa/alsa-sink.c | 3 +-- src/modules/alsa/alsa-source.c | 3 +-- src/modules/alsa/alsa-util.c | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) (limited to 'src/modules/alsa') diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c index 61c92cd0..9eb7b462 100644 --- a/src/modules/alsa/alsa-mixer.c +++ b/src/modules/alsa/alsa-mixer.c @@ -2889,7 +2889,7 @@ static void profile_set_add_auto_pair( else name = pa_sprintf_malloc("input:%s", n->name); - if ((p = pa_hashmap_get(ps->profiles, name))) { + if (pa_hashmap_get(ps->profiles, name)) { pa_xfree(name); return; } diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 195bdf6e..63a3e906 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -1638,7 +1638,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca const char *dev_id = NULL; pa_sample_spec ss, requested_ss; pa_channel_map map; - uint32_t nfrags, hwbuf_size, frag_size, tsched_size, tsched_watermark; + uint32_t nfrags, frag_size, tsched_size, tsched_watermark; snd_pcm_uframes_t period_frames, tsched_frames; size_t frame_size; pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE; @@ -1673,7 +1673,6 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca goto fail; } - hwbuf_size = frag_size * nfrags; period_frames = frag_size/frame_size; tsched_frames = tsched_size/frame_size; diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index f42d3542..64f89806 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -1483,7 +1483,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p const char *dev_id = NULL; pa_sample_spec ss, requested_ss; pa_channel_map map; - uint32_t nfrags, hwbuf_size, frag_size, tsched_size, tsched_watermark; + uint32_t nfrags, frag_size, tsched_size, tsched_watermark; snd_pcm_uframes_t period_frames, tsched_frames; size_t frame_size; pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE; @@ -1518,7 +1518,6 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p goto fail; } - hwbuf_size = frag_size * nfrags; period_frames = frag_size/frame_size; tsched_frames = tsched_size/frame_size; diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c index a47a8958..4d75c63c 100644 --- a/src/modules/alsa/alsa-util.c +++ b/src/modules/alsa/alsa-util.c @@ -900,7 +900,7 @@ void pa_alsa_init_proplist_ctl(pa_proplist *p, const char *name) { snd_ctl_card_info_alloca(&info); if ((err = snd_ctl_open(&ctl, name, 0)) < 0) { - pa_log_warn("Error opening low-level control device '%s'", name); + pa_log_warn("Error opening low-level control device '%s': %s", name, snd_strerror(err)); return; } -- cgit From 382eced35de93cba8098610f3990c16ae35b6ea0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 8 Sep 2009 23:47:23 +0200 Subject: alsa-sink: init after_avail earlier (llvm-clang-analyzer) --- src/modules/alsa/alsa-sink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/modules/alsa') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 63a3e906..883b32a7 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -656,6 +656,7 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle 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->sink->sample_spec)) < 0)) { @@ -710,7 +711,6 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle for (;;) { snd_pcm_sframes_t frames; void *p; - pa_bool_t after_avail = TRUE; /* pa_log_debug("%lu frames to write", (unsigned long) frames); */ -- cgit From 557c4295107dc7374c850b0bd5331dd35e8fdd0f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 9 Sep 2009 04:28:52 +0200 Subject: alsa: rework buffer/period configuration - As discussed on alsa-devel it's probably better to initialize the buffer size first, followed by the period size. If that fails try the other way round. If that fails try to configure only buffer size. If that fails try to configure only period size. Finally, try to configure neither. - Don't require integral periods anymore. Both of these changes should help improving compatibility with various weirder sound devices, such as TV cards. --- src/modules/alsa/alsa-sink.c | 44 ++++---- src/modules/alsa/alsa-source.c | 45 ++++---- src/modules/alsa/alsa-util.c | 248 +++++++++++++++++++++++++++-------------- src/modules/alsa/alsa-util.h | 10 +- 4 files changed, 217 insertions(+), 130 deletions(-) (limited to 'src/modules/alsa') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 883b32a7..c4ff10de 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -116,7 +116,6 @@ struct userdata { pa_usec_t watermark_dec_not_before; - unsigned nfragments; pa_memchunk memchunk; char *device_name; /* name of the PCM device */ @@ -943,8 +942,7 @@ static int unsuspend(struct userdata *u) { pa_sample_spec ss; int err; pa_bool_t b, d; - unsigned nfrags; - snd_pcm_uframes_t period_size; + snd_pcm_uframes_t period_size, buffer_size; pa_assert(u); pa_assert(!u->pcm_handle); @@ -961,12 +959,12 @@ static int unsuspend(struct userdata *u) { } ss = u->sink->sample_spec; - nfrags = u->nfragments; period_size = u->fragment_size / u->frame_size; + buffer_size = u->hwbuf_size / u->frame_size; b = u->use_mmap; d = u->use_tsched; - if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &nfrags, &period_size, u->hwbuf_size / u->frame_size, &b, &d, TRUE)) < 0) { + if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &period_size, &buffer_size, 0, &b, &d, TRUE)) < 0) { pa_log("Failed to set hardware parameters: %s", pa_alsa_strerror(err)); goto fail; } @@ -981,10 +979,11 @@ static int unsuspend(struct userdata *u) { goto fail; } - if (nfrags != u->nfragments || period_size*u->frame_size != u->fragment_size) { - pa_log_warn("Resume failed, couldn't restore original fragment settings. (Old: %lu*%lu, New %lu*%lu)", - (unsigned long) u->nfragments, (unsigned long) u->fragment_size, - (unsigned long) nfrags, period_size * u->frame_size); + if (period_size*u->frame_size != u->fragment_size || + buffer_size*u->frame_size != u->hwbuf_size) { + pa_log_warn("Resume failed, couldn't restore original fragment settings. (Old: %lu/%lu, New %lu/%lu)", + (unsigned long) u->hwbuf_size, (unsigned long) u->fragment_size, + (unsigned long) (buffer_size*u->fragment_size), (unsigned long) (period_size*u->frame_size)); goto fail; } @@ -1638,8 +1637,8 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca const char *dev_id = NULL; pa_sample_spec ss, requested_ss; pa_channel_map map; - uint32_t nfrags, frag_size, tsched_size, tsched_watermark; - snd_pcm_uframes_t period_frames, tsched_frames; + uint32_t nfrags, frag_size, buffer_size, tsched_size, tsched_watermark; + snd_pcm_uframes_t period_frames, buffer_frames, tsched_frames; size_t frame_size; pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE; pa_sink_new_data data; @@ -1673,7 +1672,10 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca goto fail; } + buffer_size = nfrags * frag_size; + period_frames = frag_size/frame_size; + buffer_frames = buffer_size/frame_size; tsched_frames = tsched_size/frame_size; if (pa_modargs_get_value_boolean(ma, "mmap", &use_mmap) < 0) { @@ -1740,7 +1742,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca &u->device_name, &ss, &map, SND_PCM_STREAM_PLAYBACK, - &nfrags, &period_frames, tsched_frames, + &period_frames, &buffer_frames, tsched_frames, &b, &d, mapping))) goto fail; @@ -1755,7 +1757,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca &u->device_name, &ss, &map, SND_PCM_STREAM_PLAYBACK, - &nfrags, &period_frames, tsched_frames, + &period_frames, &buffer_frames, tsched_frames, &b, &d, profile_set, &mapping))) goto fail; @@ -1767,7 +1769,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca &u->device_name, &ss, &map, SND_PCM_STREAM_PLAYBACK, - &nfrags, &period_frames, tsched_frames, + &period_frames, &buffer_frames, tsched_frames, &b, &d, FALSE))) goto fail; } @@ -1819,7 +1821,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca pa_alsa_init_proplist_pcm(m->core, data.proplist, u->pcm_handle); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name); - pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (period_frames * frame_size * nfrags)); + pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (buffer_frames * frame_size)); pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size)); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial")); @@ -1860,13 +1862,15 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca pa_sink_set_rtpoll(u->sink, u->rtpoll); u->frame_size = frame_size; - u->fragment_size = frag_size = (uint32_t) (period_frames * frame_size); - u->nfragments = nfrags; - u->hwbuf_size = u->fragment_size * nfrags; + u->fragment_size = frag_size = (size_t) (period_frames * frame_size); + u->hwbuf_size = buffer_size = (size_t) (buffer_frames * frame_size); pa_cvolume_mute(&u->hardware_volume, u->sink->sample_spec.channels); - pa_log_info("Using %u fragments of size %lu bytes, buffer time is %0.2fms", - nfrags, (long unsigned) u->fragment_size, + pa_log_info("Using %0.1f fragments of size %lu bytes (%0.2fms), buffer size is %lu bytes (%0.2fms)", + (double) u->hwbuf_size / (double) u->fragment_size, + (long unsigned) u->fragment_size, + (double) pa_bytes_to_usec(u->fragment_size, &ss) / PA_USEC_PER_MSEC, + (long unsigned) u->hwbuf_size, (double) pa_bytes_to_usec(u->hwbuf_size, &ss) / PA_USEC_PER_MSEC); pa_sink_set_max_request(u->sink, u->hwbuf_size); diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 64f89806..5f89d88c 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -111,8 +111,6 @@ struct userdata { pa_usec_t watermark_dec_not_before; - unsigned nfragments; - char *device_name; char *control_device; @@ -891,8 +889,7 @@ static int unsuspend(struct userdata *u) { pa_sample_spec ss; int err; pa_bool_t b, d; - unsigned nfrags; - snd_pcm_uframes_t period_size; + snd_pcm_uframes_t period_size, buffer_size; pa_assert(u); pa_assert(!u->pcm_handle); @@ -909,12 +906,12 @@ static int unsuspend(struct userdata *u) { } ss = u->source->sample_spec; - nfrags = u->nfragments; period_size = u->fragment_size / u->frame_size; + buffer_size = u->hwbuf_size / u->frame_size; b = u->use_mmap; d = u->use_tsched; - if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &nfrags, &period_size, u->hwbuf_size / u->frame_size, &b, &d, TRUE)) < 0) { + if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &period_size, &buffer_size, 0, &b, &d, TRUE)) < 0) { pa_log("Failed to set hardware parameters: %s", pa_alsa_strerror(err)); goto fail; } @@ -929,10 +926,11 @@ static int unsuspend(struct userdata *u) { goto fail; } - if (nfrags != u->nfragments || period_size*u->frame_size != u->fragment_size) { - pa_log_warn("Resume failed, couldn't restore original fragment settings. (Old: %lu*%lu, New %lu*%lu)", - (unsigned long) u->nfragments, (unsigned long) u->fragment_size, - (unsigned long) nfrags, period_size * u->frame_size); + if (period_size*u->frame_size != u->fragment_size || + buffer_size*u->frame_size != u->hwbuf_size) { + pa_log_warn("Resume failed, couldn't restore original fragment settings. (Old: %lu/%lu, New %lu/%lu)", + (unsigned long) u->hwbuf_size, (unsigned long) u->fragment_size, + (unsigned long) (buffer_size*u->fragment_size), (unsigned long) (period_size*u->frame_size)); goto fail; } @@ -1483,8 +1481,8 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p const char *dev_id = NULL; pa_sample_spec ss, requested_ss; pa_channel_map map; - uint32_t nfrags, frag_size, tsched_size, tsched_watermark; - snd_pcm_uframes_t period_frames, tsched_frames; + uint32_t nfrags, frag_size, buffer_size, tsched_size, tsched_watermark; + snd_pcm_uframes_t period_frames, buffer_frames, tsched_frames; size_t frame_size; pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE; pa_source_new_data data; @@ -1518,7 +1516,10 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p goto fail; } + buffer_size = nfrags * frag_size; + period_frames = frag_size/frame_size; + buffer_frames = buffer_size/frame_size; tsched_frames = tsched_size/frame_size; if (pa_modargs_get_value_boolean(ma, "mmap", &use_mmap) < 0) { @@ -1584,7 +1585,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p &u->device_name, &ss, &map, SND_PCM_STREAM_CAPTURE, - &nfrags, &period_frames, tsched_frames, + &period_frames, &buffer_frames, tsched_frames, &b, &d, mapping))) goto fail; @@ -1598,7 +1599,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p &u->device_name, &ss, &map, SND_PCM_STREAM_CAPTURE, - &nfrags, &period_frames, tsched_frames, + &period_frames, &buffer_frames, tsched_frames, &b, &d, profile_set, &mapping))) goto fail; @@ -1609,7 +1610,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p &u->device_name, &ss, &map, SND_PCM_STREAM_CAPTURE, - &nfrags, &period_frames, tsched_frames, + &period_frames, &buffer_frames, tsched_frames, &b, &d, FALSE))) goto fail; } @@ -1661,7 +1662,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p pa_alsa_init_proplist_pcm(m->core, data.proplist, u->pcm_handle); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name); - pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (period_frames * frame_size * nfrags)); + pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (buffer_frames * frame_size)); pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size)); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial")); @@ -1702,13 +1703,15 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p pa_source_set_rtpoll(u->source, u->rtpoll); u->frame_size = frame_size; - u->fragment_size = frag_size = (uint32_t) (period_frames * frame_size); - u->nfragments = nfrags; - u->hwbuf_size = u->fragment_size * nfrags; + u->fragment_size = frag_size = (size_t) (period_frames * frame_size); + u->hwbuf_size = buffer_size = (size_t) (buffer_frames * frame_size); pa_cvolume_mute(&u->hardware_volume, u->source->sample_spec.channels); - pa_log_info("Using %u fragments of size %lu bytes, buffer time is %0.2fms", - nfrags, (long unsigned) u->fragment_size, + pa_log_info("Using %0.1f fragments of size %lu bytes (%0.2fms), buffer size is %lu bytes (%0.2fms)", + (double) u->hwbuf_size / (double) u->fragment_size, + (long unsigned) u->fragment_size, + (double) pa_bytes_to_usec(u->fragment_size, &ss) / PA_USEC_PER_MSEC, + (long unsigned) u->hwbuf_size, (double) pa_bytes_to_usec(u->hwbuf_size, &ss) / PA_USEC_PER_MSEC); if (u->use_tsched) { diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c index 4d75c63c..91474527 100644 --- a/src/modules/alsa/alsa-util.c +++ b/src/modules/alsa/alsa-util.c @@ -93,6 +93,7 @@ static int set_format(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, pa_s int ret; pa_assert(pcm_handle); + pa_assert(hwparams); pa_assert(f); if ((ret = snd_pcm_hw_params_set_format(pcm_handle, hwparams, format_trans[*f])) >= 0) @@ -148,33 +149,71 @@ try_auto: return -1; } +static int set_period_size(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, snd_pcm_uframes_t size) { + snd_pcm_uframes_t s; + int d, ret; + + pa_assert(pcm_handle); + pa_assert(hwparams); + + s = size; + d = 0; + if (snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, &s, &d) < 0) { + s = size; + d = -1; + if (snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, &s, &d) < 0) { + s = size; + d = 1; + if ((ret = snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, &s, &d)) < 0) { + pa_log_info("snd_pcm_hw_params_set_period_size_near() failed: %s", pa_alsa_strerror(ret)); + return ret; + } + } + } + + return 0; +} + +static int set_buffer_size(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, snd_pcm_uframes_t size) { + int ret; + + pa_assert(pcm_handle); + pa_assert(hwparams); + + if ((ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &size)) < 0) { + pa_log_info("snd_pcm_hw_params_set_buffer_size_near() failed: %s", pa_alsa_strerror(ret)); + return ret; + } + + return 0; +} + /* Set the hardware parameters of the given ALSA device. Returns the * selected fragment settings in *period and *period_size */ int pa_alsa_set_hw_params( snd_pcm_t *pcm_handle, pa_sample_spec *ss, - uint32_t *periods, snd_pcm_uframes_t *period_size, + snd_pcm_uframes_t *buffer_size, snd_pcm_uframes_t tsched_size, pa_bool_t *use_mmap, pa_bool_t *use_tsched, pa_bool_t require_exact_channel_number) { int ret = -1; + snd_pcm_hw_params_t *hwparams, *hwparams_copy; + int dir; snd_pcm_uframes_t _period_size = period_size ? *period_size : 0; - unsigned int _periods = periods ? *periods : 0; - unsigned int r = ss->rate; - unsigned int c = ss->channels; - pa_sample_format_t f = ss->format; - snd_pcm_hw_params_t *hwparams; + snd_pcm_uframes_t _buffer_size = buffer_size ? *buffer_size : 0; pa_bool_t _use_mmap = use_mmap && *use_mmap; pa_bool_t _use_tsched = use_tsched && *use_tsched; - int dir; + pa_sample_spec _ss = *ss; pa_assert(pcm_handle); pa_assert(ss); snd_pcm_hw_params_alloca(&hwparams); + snd_pcm_hw_params_alloca(&hwparams_copy); if ((ret = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) { pa_log_debug("snd_pcm_hw_params_any() failed: %s", pa_alsa_strerror(ret)); @@ -208,114 +247,140 @@ int pa_alsa_set_hw_params( if (!_use_mmap) _use_tsched = FALSE; - if ((ret = set_format(pcm_handle, hwparams, &f)) < 0) + if ((ret = set_format(pcm_handle, hwparams, &_ss.format)) < 0) goto finish; - if ((ret = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &r, NULL)) < 0) { + if ((ret = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &_ss.rate, NULL)) < 0) { pa_log_debug("snd_pcm_hw_params_set_rate_near() failed: %s", pa_alsa_strerror(ret)); goto finish; } if (require_exact_channel_number) { - if ((ret = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, c)) < 0) { - pa_log_debug("snd_pcm_hw_params_set_channels(%u) failed: %s", c, pa_alsa_strerror(ret)); + if ((ret = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, _ss.channels)) < 0) { + pa_log_debug("snd_pcm_hw_params_set_channels(%u) failed: %s", _ss.channels, pa_alsa_strerror(ret)); goto finish; } } else { + unsigned int c = _ss.channels; + if ((ret = snd_pcm_hw_params_set_channels_near(pcm_handle, hwparams, &c)) < 0) { - pa_log_debug("snd_pcm_hw_params_set_channels_near(%u) failed: %s", c, pa_alsa_strerror(ret)); + pa_log_debug("snd_pcm_hw_params_set_channels_near(%u) failed: %s", _ss.channels, pa_alsa_strerror(ret)); goto finish; } - } - if ((ret = snd_pcm_hw_params_set_periods_integer(pcm_handle, hwparams)) < 0) { - pa_log_debug("snd_pcm_hw_params_set_periods_integer() failed: %s", pa_alsa_strerror(ret)); - goto finish; + _ss.channels = c; } - if (_period_size > 0 && tsched_size > 0 && _periods > 0) { - snd_pcm_uframes_t buffer_size; - unsigned int p; + if (_use_tsched && tsched_size > 0) { + _buffer_size = pa_convert_size(tsched_size, ss, &_ss); + _period_size = _buffer_size; + } else { + _period_size = pa_convert_size(_period_size, ss, &_ss); + _buffer_size = pa_convert_size(_buffer_size, ss, &_ss); + } - /* Adjust the buffer sizes, if we didn't get the rate we were asking for */ - _period_size = (snd_pcm_uframes_t) (((uint64_t) _period_size * r) / ss->rate); - tsched_size = (snd_pcm_uframes_t) (((uint64_t) tsched_size * r) / ss->rate); + if (_buffer_size > 0 || _period_size > 0) { + snd_pcm_uframes_t max_frames = 0; - if (_use_tsched) { - buffer_size = 0; + if ((ret = snd_pcm_hw_params_get_buffer_size_max(hwparams, &max_frames)) < 0) + pa_log_warn("snd_pcm_hw_params_get_buffer_size_max() failed: %s", pa_alsa_strerror(ret)); + else + pa_log_debug("Maximum hw buffer size is %lu ms", (long unsigned) max_frames * PA_MSEC_PER_SEC / _ss.rate); - if ((ret = snd_pcm_hw_params_get_buffer_size_max(hwparams, &buffer_size)) < 0) - pa_log_warn("snd_pcm_hw_params_get_buffer_size_max() failed: %s", pa_alsa_strerror(ret)); - else - pa_log_debug("Maximum hw buffer size is %u ms", (unsigned) buffer_size * 1000 / r); + /* Some ALSA drivers really don't like if we set the buffer + * size first and the number of periods second. (which would + * make a lot more sense to me) So, try a few combinations + * before we give up. */ + + if (_buffer_size > 0 && _period_size > 0) { + snd_pcm_hw_params_copy(hwparams_copy, hwparams); + + /* First try: set buffer size first, followed by period size */ + if (set_buffer_size(pcm_handle, hwparams_copy, _buffer_size) >= 0 && + set_period_size(pcm_handle, hwparams_copy, _period_size) >= 0 && + snd_pcm_hw_params(pcm_handle, hwparams_copy) >= 0) { + pa_log_debug("Set buffer size first, period size second."); + goto success; + } - _period_size = tsched_size; - _periods = 1; + /* Second try: set period size first, followed by buffer size */ + if (set_period_size(pcm_handle, hwparams_copy, _period_size) >= 0 && + set_buffer_size(pcm_handle, hwparams_copy, _buffer_size) >= 0 && + snd_pcm_hw_params(pcm_handle, hwparams_copy) >= 0) { + pa_log_debug("Set period size first, buffer size second."); + goto success; + } } - /* Some ALSA drivers really don't like if we set the buffer - * size first and the number of periods second. (which would - * make a lot more sense to me) So, follow this rule and - * adjust the periods first and the buffer size second */ - - /* First we pass 0 as direction to get exactly what we - * asked for. That this is necessary is presumably a bug - * in ALSA. All in all this is mostly a hint to ALSA, so - * we don't care if this fails. */ - - p = _periods; - dir = 0; - if (snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &p, &dir) < 0) { - p = _periods; - dir = 1; - if (snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &p, &dir) < 0) { - p = _periods; - dir = -1; - if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &p, &dir)) < 0) - pa_log_info("snd_pcm_hw_params_set_periods_near() failed: %s", pa_alsa_strerror(ret)); + if (_buffer_size > 0) { + snd_pcm_hw_params_copy(hwparams_copy, hwparams); + + /* Third try: set only buffer size */ + if (set_buffer_size(pcm_handle, hwparams_copy, _buffer_size) >= 0 && + snd_pcm_hw_params(pcm_handle, hwparams_copy) >= 0) { + pa_log_debug("Set only buffer size second."); + goto success; } } - /* Now set the buffer size */ - buffer_size = _periods * _period_size; - if ((ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &buffer_size)) < 0) - pa_log_info("snd_pcm_hw_params_set_buffer_size_near() failed: %s", pa_alsa_strerror(ret)); + if (_period_size > 0) { + snd_pcm_hw_params_copy(hwparams_copy, hwparams); + + /* Fourth try: set only period size */ + if (set_period_size(pcm_handle, hwparams_copy, _period_size) >= 0 && + snd_pcm_hw_params(pcm_handle, hwparams_copy) >= 0) { + pa_log_debug("Set only period size second."); + goto success; + } + } } - if ((ret = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) + pa_log_debug("Set neither period nor buffer size."); + + /* Last chance, set nothing */ + if ((ret = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) { + pa_log_info("snd_pcm_hw_params failed: %s", pa_alsa_strerror(ret)); goto finish; + } + +success: - if (ss->rate != r) - pa_log_info("Device %s doesn't support %u Hz, changed to %u Hz.", snd_pcm_name(pcm_handle), ss->rate, r); + if (ss->rate != _ss.rate) + pa_log_info("Device %s doesn't support %u Hz, changed to %u Hz.", snd_pcm_name(pcm_handle), ss->rate, _ss.rate); - if (ss->channels != c) - pa_log_info("Device %s doesn't support %u channels, changed to %u.", snd_pcm_name(pcm_handle), ss->channels, c); + if (ss->channels != _ss.channels) + pa_log_info("Device %s doesn't support %u channels, changed to %u.", snd_pcm_name(pcm_handle), ss->channels, _ss.channels); - if (ss->format != f) - pa_log_info("Device %s doesn't support sample format %s, changed to %s.", snd_pcm_name(pcm_handle), pa_sample_format_to_string(ss->format), pa_sample_format_to_string(f)); + if (ss->format != _ss.format) + pa_log_info("Device %s doesn't support sample format %s, changed to %s.", snd_pcm_name(pcm_handle), pa_sample_format_to_string(ss->format), pa_sample_format_to_string(_ss.format)); if ((ret = snd_pcm_prepare(pcm_handle)) < 0) { pa_log_info("snd_pcm_prepare() failed: %s", pa_alsa_strerror(ret)); goto finish; } + if ((ret = snd_pcm_hw_params_current(pcm_handle, hwparams)) < 0) { + pa_log_info("snd_pcm_hw_params_current() failed: %s", pa_alsa_strerror(ret)); + goto finish; + } + if ((ret = snd_pcm_hw_params_get_period_size(hwparams, &_period_size, &dir)) < 0 || - (ret = snd_pcm_hw_params_get_periods(hwparams, &_periods, &dir)) < 0) { - pa_log_info("snd_pcm_hw_params_get_period{s|_size}() failed: %s", pa_alsa_strerror(ret)); + (ret = snd_pcm_hw_params_get_buffer_size(hwparams, &_buffer_size)) < 0) { + pa_log_info("snd_pcm_hw_params_get_{period|buffer}_size() failed: %s", pa_alsa_strerror(ret)); goto finish; } /* If the sample rate deviates too much, we need to resample */ - if (r < ss->rate*.95 || r > ss->rate*1.05) - ss->rate = r; - ss->channels = (uint8_t) c; - ss->format = f; + if (_ss.rate < ss->rate*.95 || _ss.rate > ss->rate*1.05) + ss->rate = _ss.rate; + ss->channels = _ss.channels; + ss->format = _ss.format; - pa_assert(_periods > 0); pa_assert(_period_size > 0); + pa_assert(_buffer_size > 0); - if (periods) - *periods = _periods; + if (buffer_size) + *buffer_size = _buffer_size; if (period_size) *period_size = _period_size; @@ -393,8 +458,8 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto( pa_sample_spec *ss, pa_channel_map* map, int mode, - uint32_t *nfrags, snd_pcm_uframes_t *period_size, + snd_pcm_uframes_t *buffer_size, snd_pcm_uframes_t tsched_size, pa_bool_t *use_mmap, pa_bool_t *use_tsched, @@ -410,8 +475,6 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto( pa_assert(dev); pa_assert(ss); pa_assert(map); - pa_assert(nfrags); - pa_assert(period_size); pa_assert(ps); /* First we try to find a device string with a superset of the @@ -433,8 +496,8 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto( ss, map, mode, - nfrags, period_size, + buffer_size, tsched_size, use_mmap, use_tsched, @@ -460,8 +523,8 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto( ss, map, mode, - nfrags, period_size, + buffer_size, tsched_size, use_mmap, use_tsched, @@ -478,7 +541,18 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto( /* OK, we didn't find any good device, so let's try the raw hw: stuff */ d = pa_sprintf_malloc("hw:%s", dev_id); pa_log_debug("Trying %s as last resort...", d); - pcm_handle = pa_alsa_open_by_device_string(d, dev, ss, map, mode, nfrags, period_size, tsched_size, use_mmap, use_tsched, FALSE); + pcm_handle = pa_alsa_open_by_device_string( + d, + dev, + ss, + map, + mode, + period_size, + buffer_size, + tsched_size, + use_mmap, + use_tsched, + FALSE); pa_xfree(d); if (pcm_handle && mapping) @@ -493,8 +567,8 @@ snd_pcm_t *pa_alsa_open_by_device_id_mapping( pa_sample_spec *ss, pa_channel_map* map, int mode, - uint32_t *nfrags, snd_pcm_uframes_t *period_size, + snd_pcm_uframes_t *buffer_size, snd_pcm_uframes_t tsched_size, pa_bool_t *use_mmap, pa_bool_t *use_tsched, @@ -508,8 +582,6 @@ snd_pcm_t *pa_alsa_open_by_device_id_mapping( pa_assert(dev); pa_assert(ss); pa_assert(map); - pa_assert(nfrags); - pa_assert(period_size); pa_assert(m); try_ss.channels = m->channel_map.channels; @@ -524,8 +596,8 @@ snd_pcm_t *pa_alsa_open_by_device_id_mapping( &try_ss, &try_map, mode, - nfrags, period_size, + buffer_size, tsched_size, use_mmap, use_tsched, @@ -547,8 +619,8 @@ snd_pcm_t *pa_alsa_open_by_device_string( pa_sample_spec *ss, pa_channel_map* map, int mode, - uint32_t *nfrags, snd_pcm_uframes_t *period_size, + snd_pcm_uframes_t *buffer_size, snd_pcm_uframes_t tsched_size, pa_bool_t *use_mmap, pa_bool_t *use_tsched, @@ -579,7 +651,15 @@ snd_pcm_t *pa_alsa_open_by_device_string( pa_log_debug("Managed to open %s", d); - if ((err = pa_alsa_set_hw_params(pcm_handle, ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, require_exact_channel_number)) < 0) { + if ((err = pa_alsa_set_hw_params( + pcm_handle, + ss, + period_size, + buffer_size, + tsched_size, + use_mmap, + use_tsched, + require_exact_channel_number)) < 0) { if (!reformat) { reformat = TRUE; @@ -632,8 +712,8 @@ snd_pcm_t *pa_alsa_open_by_template( pa_sample_spec *ss, pa_channel_map* map, int mode, - uint32_t *nfrags, snd_pcm_uframes_t *period_size, + snd_pcm_uframes_t *buffer_size, snd_pcm_uframes_t tsched_size, pa_bool_t *use_mmap, pa_bool_t *use_tsched, @@ -653,8 +733,8 @@ snd_pcm_t *pa_alsa_open_by_template( ss, map, mode, - nfrags, period_size, + buffer_size, tsched_size, use_mmap, use_tsched, diff --git a/src/modules/alsa/alsa-util.h b/src/modules/alsa/alsa-util.h index 830a922e..265cd28c 100644 --- a/src/modules/alsa/alsa-util.h +++ b/src/modules/alsa/alsa-util.h @@ -42,8 +42,8 @@ int pa_alsa_set_hw_params( snd_pcm_t *pcm_handle, pa_sample_spec *ss, /* modified at return */ - uint32_t *periods, /* modified at return */ snd_pcm_uframes_t *period_size, /* modified at return */ + snd_pcm_uframes_t *buffer_size, /* modified at return */ snd_pcm_uframes_t tsched_size, pa_bool_t *use_mmap, /* modified at return */ pa_bool_t *use_tsched, /* modified at return */ @@ -60,8 +60,8 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto( pa_sample_spec *ss, /* modified at return */ pa_channel_map* map, /* modified at return */ int mode, - uint32_t *nfrags, /* modified at return */ snd_pcm_uframes_t *period_size, /* modified at return */ + snd_pcm_uframes_t *buffer_size, /* modified at return */ snd_pcm_uframes_t tsched_size, pa_bool_t *use_mmap, /* modified at return */ pa_bool_t *use_tsched, /* modified at return */ @@ -75,8 +75,8 @@ snd_pcm_t *pa_alsa_open_by_device_id_mapping( pa_sample_spec *ss, /* modified at return */ pa_channel_map* map, /* modified at return */ int mode, - uint32_t *nfrags, /* modified at return */ snd_pcm_uframes_t *period_size, /* modified at return */ + snd_pcm_uframes_t *buffer_size, /* modified at return */ snd_pcm_uframes_t tsched_size, pa_bool_t *use_mmap, /* modified at return */ pa_bool_t *use_tsched, /* modified at return */ @@ -89,8 +89,8 @@ snd_pcm_t *pa_alsa_open_by_device_string( pa_sample_spec *ss, /* modified at return */ pa_channel_map* map, /* modified at return */ int mode, - uint32_t *nfrags, /* modified at return */ snd_pcm_uframes_t *period_size, /* modified at return */ + snd_pcm_uframes_t *buffer_size, /* modified at return */ snd_pcm_uframes_t tsched_size, pa_bool_t *use_mmap, /* modified at return */ pa_bool_t *use_tsched, /* modified at return */ @@ -104,8 +104,8 @@ snd_pcm_t *pa_alsa_open_by_template( pa_sample_spec *ss, /* modified at return */ pa_channel_map* map, /* modified at return */ int mode, - uint32_t *nfrags, /* modified at return */ snd_pcm_uframes_t *period_size, /* modified at return */ + snd_pcm_uframes_t *buffer_size, /* modified at return */ snd_pcm_uframes_t tsched_size, pa_bool_t *use_mmap, /* modified at return */ pa_bool_t *use_tsched, /* modified at return */ -- cgit From 84ade2140ed44bf241eda494d0970390e48b21d3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 9 Sep 2009 04:44:51 +0200 Subject: alsa: pass SND_PCM_NONBLOCK when opening device during unsuspend, the same way we do it for initial opening --- src/modules/alsa/alsa-sink.c | 2 +- src/modules/alsa/alsa-source.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/modules/alsa') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index c4ff10de..7fe77831 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -950,7 +950,7 @@ static int unsuspend(struct userdata *u) { pa_log_info("Trying resume..."); if ((err = snd_pcm_open(&u->pcm_handle, u->device_name, SND_PCM_STREAM_PLAYBACK, - /*SND_PCM_NONBLOCK|*/ + SND_PCM_NONBLOCK| SND_PCM_NO_AUTO_RESAMPLE| SND_PCM_NO_AUTO_CHANNELS| SND_PCM_NO_AUTO_FORMAT)) < 0) { diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 5f89d88c..a7f2a018 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -897,7 +897,7 @@ static int unsuspend(struct userdata *u) { pa_log_info("Trying resume..."); if ((err = snd_pcm_open(&u->pcm_handle, u->device_name, SND_PCM_STREAM_CAPTURE, - /*SND_PCM_NONBLOCK|*/ + SND_PCM_NONBLOCK| SND_PCM_NO_AUTO_RESAMPLE| SND_PCM_NO_AUTO_CHANNELS| SND_PCM_NO_AUTO_FORMAT)) < 0) { -- cgit From 8364b959b452a2b9e3f230705feb176a1fa6de06 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 9 Sep 2009 23:56:51 +0200 Subject: alsa: when probing for profiles configure buffer/period sizes since some broken drivers apparently need that --- src/modules/alsa/alsa-mixer.c | 23 ++++++++++++++++++++--- src/modules/alsa/alsa-mixer.h | 2 +- src/modules/alsa/module-alsa-card.c | 2 +- 3 files changed, 22 insertions(+), 5 deletions(-) (limited to 'src/modules/alsa') diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c index 9eb7b462..685169b9 100644 --- a/src/modules/alsa/alsa-mixer.c +++ b/src/modules/alsa/alsa-mixer.c @@ -3145,7 +3145,13 @@ fail: return NULL; } -void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, const char *dev_id, const pa_sample_spec *ss) { +void pa_alsa_profile_set_probe( + pa_alsa_profile_set *ps, + const char *dev_id, + const pa_sample_spec *ss, + unsigned default_n_fragments, + unsigned default_fragment_size_msec) { + void *state; pa_alsa_profile *p, *last = NULL; pa_alsa_mapping *m; @@ -3160,6 +3166,7 @@ void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, const char *dev_id, cons PA_HASHMAP_FOREACH(p, ps->profiles, state) { pa_sample_spec try_ss; pa_channel_map try_map; + snd_pcm_uframes_t try_period_size, try_buffer_size; uint32_t idx; /* Is this already marked that it is supported? (i.e. from the config file) */ @@ -3213,13 +3220,18 @@ void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, const char *dev_id, cons try_ss = *ss; try_ss.channels = try_map.channels; + try_period_size = + pa_usec_to_bytes(default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) / + pa_frame_size(&try_ss); + try_buffer_size = default_n_fragments * try_period_size; + if (!(m ->output_pcm = pa_alsa_open_by_template( m->device_strings, dev_id, NULL, &try_ss, &try_map, SND_PCM_STREAM_PLAYBACK, - NULL, NULL, 0, NULL, NULL, + &try_period_size, &try_buffer_size, 0, NULL, NULL, TRUE))) { p->supported = FALSE; break; @@ -3237,13 +3249,18 @@ void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, const char *dev_id, cons try_ss = *ss; try_ss.channels = try_map.channels; + try_period_size = + pa_usec_to_bytes(default_fragment_size_msec*PA_USEC_PER_MSEC, &try_ss) / + pa_frame_size(&try_ss); + try_buffer_size = default_n_fragments * try_period_size; + if (!(m ->input_pcm = pa_alsa_open_by_template( m->device_strings, dev_id, NULL, &try_ss, &try_map, SND_PCM_STREAM_CAPTURE, - NULL, NULL, 0, NULL, NULL, + &try_period_size, &try_buffer_size, 0, NULL, NULL, TRUE))) { p->supported = FALSE; break; diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h index 76788183..a0d4fcbe 100644 --- a/src/modules/alsa/alsa-mixer.h +++ b/src/modules/alsa/alsa-mixer.h @@ -269,7 +269,7 @@ void pa_alsa_mapping_dump(pa_alsa_mapping *m); void pa_alsa_profile_dump(pa_alsa_profile *p); pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus); -void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, const char *dev_id, const pa_sample_spec *ss); +void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, const char *dev_id, const pa_sample_spec *ss, unsigned default_n_fragments, unsigned default_fragment_size_msec); void pa_alsa_profile_set_free(pa_alsa_profile_set *s); void pa_alsa_profile_set_dump(pa_alsa_profile_set *s); diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c index 55f6a6e2..6bea33d7 100644 --- a/src/modules/alsa/module-alsa-card.c +++ b/src/modules/alsa/module-alsa-card.c @@ -329,7 +329,7 @@ int pa__init(pa_module *m) { if (!u->profile_set) goto fail; - pa_alsa_profile_set_probe(u->profile_set, u->device_id, &m->core->default_sample_spec); + pa_alsa_profile_set_probe(u->profile_set, u->device_id, &m->core->default_sample_spec, m->core->default_n_fragments, m->core->default_fragment_size_msec); pa_card_new_data_init(&data); data.driver = __FILE__; -- cgit From d5f43bd4c6a7eecff7bc0c4ff1be9152b33cb1e0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 9 Sep 2009 23:57:49 +0200 Subject: alsa: disable tsched for software devices before we configure the buffer metrics so that we don't accidently set a buffer size that is suitable for tsched where we don't use tsched --- src/modules/alsa/alsa-sink.c | 5 ----- src/modules/alsa/alsa-source.c | 5 ----- src/modules/alsa/alsa-util.c | 5 ++++- 3 files changed, 4 insertions(+), 11 deletions(-) (limited to 'src/modules/alsa') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 7fe77831..76cbe46f 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -1795,11 +1795,6 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca u->use_tsched = use_tsched = FALSE; } - if (use_tsched && !pa_alsa_pcm_is_hw(u->pcm_handle)) { - pa_log_info("Device is not a hardware device, disabling timer-based scheduling."); - u->use_tsched = use_tsched = FALSE; - } - if (u->use_mmap) pa_log_info("Successfully enabled mmap() mode."); diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index a7f2a018..88f2d8ae 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -1636,11 +1636,6 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p u->use_tsched = use_tsched = FALSE; } - if (use_tsched && !pa_alsa_pcm_is_hw(u->pcm_handle)) { - pa_log_info("Device is not a hardware device, disabling timer-based scheduling."); - u->use_tsched = use_tsched = FALSE; - } - if (u->use_mmap) pa_log_info("Successfully enabled mmap() mode."); diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c index 91474527..f934285a 100644 --- a/src/modules/alsa/alsa-util.c +++ b/src/modules/alsa/alsa-util.c @@ -189,7 +189,7 @@ static int set_buffer_size(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, } /* Set the hardware parameters of the given ALSA device. Returns the - * selected fragment settings in *period and *period_size */ + * selected fragment settings in *buffer_size and *period_size. If tsched mode can be enabled */ int pa_alsa_set_hw_params( snd_pcm_t *pcm_handle, pa_sample_spec *ss, @@ -247,6 +247,9 @@ int pa_alsa_set_hw_params( if (!_use_mmap) _use_tsched = FALSE; + if (!pa_alsa_pcm_is_hw(pcm_handle)) + _use_tsched = FALSE; + if ((ret = set_format(pcm_handle, hwparams, &_ss.format)) < 0) goto finish; -- cgit From bb36bb4bbe1ee01fb95debdca79d9769851a06da Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Sep 2009 01:21:46 +0200 Subject: alsa: properly convert sample buffer sizes --- src/modules/alsa/alsa-util.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/modules/alsa') diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c index f934285a..56d60dfb 100644 --- a/src/modules/alsa/alsa-util.c +++ b/src/modules/alsa/alsa-util.c @@ -275,11 +275,11 @@ int pa_alsa_set_hw_params( } if (_use_tsched && tsched_size > 0) { - _buffer_size = pa_convert_size(tsched_size, ss, &_ss); + _buffer_size = (snd_pcm_uframes_t) (((uint64_t) tsched_size * _ss.rate) / ss->rate); _period_size = _buffer_size; } else { - _period_size = pa_convert_size(_period_size, ss, &_ss); - _buffer_size = pa_convert_size(_buffer_size, ss, &_ss); + _period_size = (snd_pcm_uframes_t) (((uint64_t) _period_size * _ss.rate) / ss->rate); + _buffer_size = (snd_pcm_uframes_t) (((uint64_t) _buffer_size * _ss.rate) / ss->rate); } if (_buffer_size > 0 || _period_size > 0) { -- cgit From 80b44574765738532bc688e5d6fa5b40dc61402b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Sep 2009 01:22:10 +0200 Subject: alsa: properly report suspension error codes --- src/modules/alsa/alsa-sink.c | 23 ++++++++++++++--------- src/modules/alsa/alsa-source.c | 22 +++++++++++++--------- 2 files changed, 27 insertions(+), 18 deletions(-) (limited to 'src/modules/alsa') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 76cbe46f..22e88b4a 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -1011,7 +1011,7 @@ fail: u->pcm_handle = NULL; } - return -1; + return -PA_ERR_IO; } /* Called from IO context */ @@ -1035,28 +1035,33 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) { - case PA_SINK_SUSPENDED: + case PA_SINK_SUSPENDED: { + int r; + pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state)); - if (suspend(u) < 0) - return -1; + if ((r = suspend(u)) < 0) + return r; break; + } case PA_SINK_IDLE: - case PA_SINK_RUNNING: + case PA_SINK_RUNNING: { + int r; if (u->sink->thread_info.state == PA_SINK_INIT) { if (build_pollfd(u) < 0) - return -1; + return -PA_ERR_IO; } if (u->sink->thread_info.state == PA_SINK_SUSPENDED) { - if (unsuspend(u) < 0) - return -1; + if ((r = unsuspend(u)) < 0) + return r; } break; + } case PA_SINK_UNLINKED: case PA_SINK_INIT: @@ -1084,7 +1089,7 @@ static int sink_set_state_cb(pa_sink *s, pa_sink_state_t new_state) { reserve_done(u); else if (old_state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(new_state)) if (reserve_init(u, u->device_name) < 0) - return -1; + return -PA_ERR_BUSY; return 0; } diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 88f2d8ae..fa3ac0aa 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -959,7 +959,7 @@ fail: u->pcm_handle = NULL; } - return -1; + return -PA_ERR_IO; } static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { @@ -982,30 +982,34 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) { - case PA_SOURCE_SUSPENDED: + case PA_SOURCE_SUSPENDED: { + int r; pa_assert(PA_SOURCE_IS_OPENED(u->source->thread_info.state)); - if (suspend(u) < 0) - return -1; + if ((r = suspend(u)) < 0) + return r; break; + } case PA_SOURCE_IDLE: - case PA_SOURCE_RUNNING: + case PA_SOURCE_RUNNING: { + int r; if (u->source->thread_info.state == PA_SOURCE_INIT) { if (build_pollfd(u) < 0) - return -1; + return -PA_ERR_IO; snd_pcm_start(u->pcm_handle); } if (u->source->thread_info.state == PA_SOURCE_SUSPENDED) { - if (unsuspend(u) < 0) - return -1; + if ((r = unsuspend(u)) < 0) + return r; } break; + } case PA_SOURCE_UNLINKED: case PA_SOURCE_INIT: @@ -1033,7 +1037,7 @@ static int source_set_state_cb(pa_source *s, pa_source_state_t new_state) { reserve_done(u); else if (old_state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(new_state)) if (reserve_init(u, u->device_name) < 0) - return -1; + return -PA_ERR_BUSY; return 0; } -- cgit