summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sound/core/pcm_lib.c31
1 files changed, 24 insertions, 7 deletions
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 670572c9a8cc..17fc80a654be 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -90,15 +90,32 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram
* This filling mode aims at free-running mode (used for example by dmix),
* which doesn't update the application pointer.
*/
- if (new_hw_ptr == ULONG_MAX) { /* initialization */
- 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;
+ snd_pcm_uframes_t hw_ptr = runtime->status->hw_ptr;
+ if (new_hw_ptr == ULONG_MAX) {
+ /*
+ * Initialization, fill the whole unused buffer with silence.
+ *
+ * Usually, this is entered while stopped, before data is queued,
+ * so both pointers are expected to be zero.
+ */
+ snd_pcm_sframes_t avail = runtime->control->appl_ptr - hw_ptr;
+ if (avail < 0)
+ avail += runtime->boundary;
+ /*
+ * In free-running mode, appl_ptr will be zero even while running,
+ * so we end up with a huge number. There is no useful way to
+ * handle this, so we just clear the whole buffer.
+ */
+ runtime->silence_filled = avail > runtime->buffer_size ? 0 : avail;
+ runtime->silence_start = hw_ptr;
} else {
- update_silence_vars(runtime, runtime->status->hw_ptr, new_hw_ptr);
+ /* Silence the just played area immediately */
+ update_silence_vars(runtime, hw_ptr, new_hw_ptr);
}
+ /*
+ * In this mode, silence_filled actually includes the valid
+ * sample data from the user.
+ */
frames = runtime->buffer_size - runtime->silence_filled;
}
if (snd_BUG_ON(frames > runtime->buffer_size))