diff mbox series

[3/5] ALSA: pcm: fix playback silence - correct the incremental silencing

Message ID 20230505073813.1219175-4-perex@perex.cz (mailing list archive)
State New, archived
Headers show
Series rewrite snd_pcm_playback_silence() again | expand

Commit Message

Jaroslav Kysela May 5, 2023, 7:38 a.m. UTC
The incremental silencing was broken with the threshold mode. The silenced
area was smaller than expected in some cases. The updated area starts
at runtime->silence_start + runtime->silence_filled position not
only at runtime->silence_start in this mode.

Unify the runtime->silence_start use for all cases (threshold and top-up).

Suggested-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
 sound/core/pcm_lib.c | 24 +++++++++++-------------
 1 file changed, 11 insertions(+), 13 deletions(-)

Comments

Takashi Iwai May 5, 2023, 9:57 a.m. UTC | #1
On Fri, 05 May 2023 09:38:11 +0200,
Jaroslav Kysela wrote:
> 
> The incremental silencing was broken with the threshold mode. The silenced
> area was smaller than expected in some cases. The updated area starts
> at runtime->silence_start + runtime->silence_filled position not
> only at runtime->silence_start in this mode.
> 
> Unify the runtime->silence_start use for all cases (threshold and top-up).
> 
> Suggested-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
> Signed-off-by: Jaroslav Kysela <perex@perex.cz>

While this change itself follows the original code, and is fine from
the code-refactoring POV.

But, the difficulty of the code (even after this patch) is that the
filling behavior is completely different between the threshold and the
fill-up modes, and we still try to use the similar code.
When reconsidering what we actually need, you can notice that, in the
fill-up mode, we don't have to keep tracking silence_start and
silence_size at all.

Namely, in the fill-up mode, what we need are:
- at init, fill silence in the unused buffer:

	ofs = runtime->control->appl_ptr % runtime->buffer_size;
	frames = snd_pcm_playback_avail(runtime);
	fill_silence_in_loop(ofs, frames);

- at each incremental hw_ptr update, fill the area with silence:

	ofs = runtime->status->hw_ptr % runtime->buffer_size;
	frames = new_hw_ptr - runtime->status->hw_ptr;
	if (frames < 0)
		frames += runtime->boundary;
	fill_silence_in_loop(ofs, frames);

That's all, and far simpler than keeping silence_start and
silence_filled.  (I really had hard time to understand why filling at
silence_start + silence_filled in the incremental mode works
correctly...)

I might have overlooked something and there can be a bit more room for
optimization, but the point is that unifying the code for two behavior
isn't always good.  Treating separately can be sometimes easier.


thanks,

Takashi
Oswald Buddenhagen May 5, 2023, 10:17 a.m. UTC | #2
On Fri, May 05, 2023 at 11:57:00AM +0200, Takashi Iwai wrote:
>But, the difficulty of the code (even after this patch) is that the
>filling behavior is completely different between the threshold and the
>fill-up modes, and we still try to use the similar code.
>
what is slightly confusing is that we're kinda abusing silence_filled to 
mean silence_filled + real_samples.
i can add a comment to that effect, but i don't think it's worth tearing 
apart the paths.

regards
diff mbox series

Patch

diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 8a01aeda2213..cfdb4aa5f6fa 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -78,26 +78,24 @@  void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram
 		if (frames > runtime->silence_size)
 			frames = runtime->silence_size;
 	} else {
-		if (new_hw_ptr == ULONG_MAX) {	/* initialization */
+		if (new_hw_ptr == ULONG_MAX) {
+			/* initialization, fill silence to whole unused buffer */
 			snd_pcm_sframes_t avail = snd_pcm_playback_hw_avail(runtime);
 			if (avail > runtime->buffer_size)
 				avail = runtime->buffer_size;
 			runtime->silence_filled = avail > 0 ? avail : 0;
-			runtime->silence_start = (runtime->status->hw_ptr +
-						  runtime->silence_filled) %
-						 runtime->boundary;
+			runtime->silence_start = runtime->status->hw_ptr;
 		} else {
-			ofs = runtime->status->hw_ptr;
-			frames = new_hw_ptr - ofs;
+			/* top-up mode (appl_ptr is not required) */
+			/* silence the played area immediately */
+			frames = new_hw_ptr - runtime->status->hw_ptr;
 			if ((snd_pcm_sframes_t)frames < 0)
 				frames += runtime->boundary;
-			runtime->silence_filled -= frames;
-			if ((snd_pcm_sframes_t)runtime->silence_filled < 0) {
+			if ((snd_pcm_uframes_t)frames < runtime->silence_filled)
+				runtime->silence_filled -= frames;
+			else
 				runtime->silence_filled = 0;
-				runtime->silence_start = new_hw_ptr;
-			} else {
-				runtime->silence_start = ofs;
-			}
+			runtime->silence_start = new_hw_ptr;
 		}
 		frames = runtime->buffer_size - runtime->silence_filled;
 	}
@@ -105,7 +103,7 @@  void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram
 		return;
 	if (frames == 0)
 		return;
-	ofs = runtime->silence_start % runtime->buffer_size;
+	ofs = (runtime->silence_start + runtime->silence_filled) % runtime->buffer_size;
 	while (frames > 0) {
 		transfer = ofs + frames > runtime->buffer_size ? runtime->buffer_size - ofs : frames;
 		err = fill_silence_frames(substream, ofs, transfer);