On Tue, May 02, 2023 at 01:50:07PM +0200, Jaroslav Kysela wrote:
--- a/src/pcm/pcm_hw.c
+++ b/src/pcm/pcm_hw.c
@@ -718,7 +732,40 @@ static int snd_pcm_hw_drop(snd_pcm_t *pcm)
static int snd_pcm_hw_drain(snd_pcm_t *pcm)
{
snd_pcm_hw_t *hw = pcm->private_data;
+ snd_pcm_sw_params_t sw_params;
+ snd_pcm_uframes_t silence_size;
int err;
+
+ if (pcm->stream != SND_PCM_STREAM_PLAYBACK)
+ goto __skip_silence;
+ /* compute end silence size, align to period size + extra time */
+ snd_pcm_sw_params_current_no_lock(pcm, &sw_params);
+ if ((pcm->boundary % pcm->period_size) == 0) {
+ silence_size = pcm->period_size - (*pcm->appl.ptr % pcm->period_size);
+ if (silence_size == pcm->period_size)
+ silence_size = 0;
+ } else {
+ /* it not not easy to compute the period cross point
+ * in this case because period is not aligned to the boundary
+ * - use the full range (one period) in this case
+ */
+ silence_size = pcm->period_size;
+ }
+ silence_size += pcm->rate / 10; /* 1/10th of second */
+ if (sw_params.silence_size < silence_size) {
+ /* fill the silence soon as possible (in the bellow ioctl
+ * or the next period wake up)
+ */
+ sw_params.silence_threshold = pcm->buffer_size;
+ sw_params.silence_size = silence_size;
i was reworking my kernel patch along these lines (it's easier to deploy
when the kernel is the only thing you're hacking on), and ran into this
behavior:
check thresholded silence fill, sil start 0, sil fill 0, app 66000
now sil start 66000, sil fill 0
noise dist 23997
drain raw fill 0
drain extended fill 4800
frames 3
filling 3 at 18000
period elapsed
check thresholded silence fill, sil start 66000, sil fill 3, app 66000
now sil start 66000, sil fill 3
noise dist 18000
drain raw fill 0
drain extended fill 4800
frames 4800
filling 4800 at 18003
period elapsed
check thresholded silence fill, sil start 66000, sil fill 4803, app 66000
now sil start 66000, sil fill 4803
noise dist 16800
drain raw fill 0
drain extended fill 4800
frames 4800
filling 1197 at 22803
filling 3603 at 0
period elapsed
check thresholded silence fill, sil start 66000, sil fill 9603, app 66000
now sil start 66000, sil fill 9603
noise dist 15600
drain raw fill 0
drain extended fill 4800
frames 4800
filling 4800 at 3603
period elapsed
check thresholded silence fill, sil start 66000, sil fill 14403, app 66000
now sil start 66000, sil fill 14403
noise dist 6755399441055758400
what you're seeing is this: when the drain is issued, the buffer is
initially full, and after every played period some padding is added,
totalling way beyond what was intended.
this doesn't break anything, but it's a bit inefficient, and just ugly.
this is a result of silence_threshold being the buffer size.
and setting it to the silence_size of course doesn't work reliably when
that is smaller than the period size.
while pondering how to fix that, my thoughts returned to my yet unaired
gripe with the silencing parameters being anything but intuitive. would
you mind explaining why they are like that?
why not interpret silence_size as the minimum number of playable samples
(that is, noise_dist in the kernel code) that must be maintained at all
times, and simply ignore silence_threshold?
unless i'm missing something, this is exactly what one would want for
maintaining underrun resilience, which i think is the purpose of the
thresholded silence fill mode (at least my comment updates which claim
so were accepted).
and doing that would "magically" fix your patch.
can you think of any plausible use case that this would break?
but i guess you'll be paranoid about backwards compat anyway, so an
alternative proposal would be using silence_threshold == ULONG_MAX to
trigger the new semantics. of course this would have the downside that
it wouldn't "magically" fix your code (and i suspect some other code as
well), and kernel version dependent behavior would have to be coded for
the (presumably) common usage.
regards