pcm: hw: setup explicit silencing for snd_pcm_drain by default
Some applications may not alignt transfers to the period size and also the driver developers may not follow the consequeces of the access beyond valid samples in the playback DMA buffer. To avoid clicks, fill a little silence at the end of the playback ring buffer when snd_pcm_drain() is called. Related: https://lore.kernel.org/alsa-devel/20230420113324.877164-2-oswald.buddenhagen@gmx.de/ Related: https://lore.kernel.org/alsa-devel/20230405201219.2197789-2-oswald.buddenhagen@gmx.de/ Signed-off-by: Jaroslav Kysela <perex@perex.cz>
This commit is contained in:
parent
b40fcda7fb
commit
2115cdb4dc
3 changed files with 71 additions and 13 deletions
|
@ -6167,6 +6167,25 @@ int snd_pcm_hw_params_get_min_align(const snd_pcm_hw_params_t *params, snd_pcm_u
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef DOXYGEN
|
||||||
|
void snd_pcm_sw_params_current_no_lock(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
|
||||||
|
{
|
||||||
|
params->proto = SNDRV_PCM_VERSION;
|
||||||
|
params->tstamp_mode = pcm->tstamp_mode;
|
||||||
|
params->tstamp_type = pcm->tstamp_type;
|
||||||
|
params->period_step = pcm->period_step;
|
||||||
|
params->sleep_min = 0;
|
||||||
|
params->avail_min = pcm->avail_min;
|
||||||
|
sw_set_period_event(params, pcm->period_event);
|
||||||
|
params->xfer_align = 1;
|
||||||
|
params->start_threshold = pcm->start_threshold;
|
||||||
|
params->stop_threshold = pcm->stop_threshold;
|
||||||
|
params->silence_threshold = pcm->silence_threshold;
|
||||||
|
params->silence_size = pcm->silence_size;
|
||||||
|
params->boundary = pcm->boundary;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Return current software configuration for a PCM
|
* \brief Return current software configuration for a PCM
|
||||||
* \param pcm PCM handle
|
* \param pcm PCM handle
|
||||||
|
@ -6183,19 +6202,7 @@ int snd_pcm_sw_params_current(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
__snd_pcm_lock(pcm); /* forced lock due to pcm field changes */
|
__snd_pcm_lock(pcm); /* forced lock due to pcm field changes */
|
||||||
params->proto = SNDRV_PCM_VERSION;
|
snd_pcm_sw_params_current_no_lock(pcm, params);
|
||||||
params->tstamp_mode = pcm->tstamp_mode;
|
|
||||||
params->tstamp_type = pcm->tstamp_type;
|
|
||||||
params->period_step = pcm->period_step;
|
|
||||||
params->sleep_min = 0;
|
|
||||||
params->avail_min = pcm->avail_min;
|
|
||||||
sw_set_period_event(params, pcm->period_event);
|
|
||||||
params->xfer_align = 1;
|
|
||||||
params->start_threshold = pcm->start_threshold;
|
|
||||||
params->stop_threshold = pcm->stop_threshold;
|
|
||||||
params->silence_threshold = pcm->silence_threshold;
|
|
||||||
params->silence_size = pcm->silence_size;
|
|
||||||
params->boundary = pcm->boundary;
|
|
||||||
__snd_pcm_unlock(pcm);
|
__snd_pcm_unlock(pcm);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,6 +98,8 @@ typedef struct {
|
||||||
bool mmap_control_fallbacked;
|
bool mmap_control_fallbacked;
|
||||||
struct snd_pcm_sync_ptr *sync_ptr;
|
struct snd_pcm_sync_ptr *sync_ptr;
|
||||||
|
|
||||||
|
bool prepare_reset_sw_params;
|
||||||
|
|
||||||
int period_event;
|
int period_event;
|
||||||
snd_timer_t *period_timer;
|
snd_timer_t *period_timer;
|
||||||
struct pollfd period_timer_pfd;
|
struct pollfd period_timer_pfd;
|
||||||
|
@ -534,6 +536,7 @@ static int snd_pcm_hw_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)
|
||||||
SYSMSG("SNDRV_PCM_IOCTL_SW_PARAMS failed (%i)", err);
|
SYSMSG("SNDRV_PCM_IOCTL_SW_PARAMS failed (%i)", err);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
hw->prepare_reset_sw_params = false;
|
||||||
if ((snd_pcm_tstamp_type_t) params->tstamp_type != pcm->tstamp_type) {
|
if ((snd_pcm_tstamp_type_t) params->tstamp_type != pcm->tstamp_type) {
|
||||||
if (hw->version < SNDRV_PROTOCOL_VERSION(2, 0, 12)) {
|
if (hw->version < SNDRV_PROTOCOL_VERSION(2, 0, 12)) {
|
||||||
int on = (snd_pcm_tstamp_type_t) params->tstamp_type ==
|
int on = (snd_pcm_tstamp_type_t) params->tstamp_type ==
|
||||||
|
@ -660,7 +663,18 @@ static int snd_pcm_hw_hwsync(snd_pcm_t *pcm)
|
||||||
static int snd_pcm_hw_prepare(snd_pcm_t *pcm)
|
static int snd_pcm_hw_prepare(snd_pcm_t *pcm)
|
||||||
{
|
{
|
||||||
snd_pcm_hw_t *hw = pcm->private_data;
|
snd_pcm_hw_t *hw = pcm->private_data;
|
||||||
|
snd_pcm_sw_params_t sw_params;
|
||||||
int fd = hw->fd, err;
|
int fd = hw->fd, err;
|
||||||
|
|
||||||
|
if (hw->prepare_reset_sw_params) {
|
||||||
|
snd_pcm_sw_params_current_no_lock(pcm, &sw_params);
|
||||||
|
if (ioctl(hw->fd, SNDRV_PCM_IOCTL_SW_PARAMS, sw_params) < 0) {
|
||||||
|
err = -errno;
|
||||||
|
SYSMSG("SNDRV_PCM_IOCTL_SW_PARAMS failed (%i)", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
hw->prepare_reset_sw_params = false;
|
||||||
|
}
|
||||||
if (ioctl(fd, SNDRV_PCM_IOCTL_PREPARE) < 0) {
|
if (ioctl(fd, SNDRV_PCM_IOCTL_PREPARE) < 0) {
|
||||||
err = -errno;
|
err = -errno;
|
||||||
SYSMSG("SNDRV_PCM_IOCTL_PREPARE failed (%i)", err);
|
SYSMSG("SNDRV_PCM_IOCTL_PREPARE failed (%i)", err);
|
||||||
|
@ -718,7 +732,40 @@ static int snd_pcm_hw_drop(snd_pcm_t *pcm)
|
||||||
static int snd_pcm_hw_drain(snd_pcm_t *pcm)
|
static int snd_pcm_hw_drain(snd_pcm_t *pcm)
|
||||||
{
|
{
|
||||||
snd_pcm_hw_t *hw = pcm->private_data;
|
snd_pcm_hw_t *hw = pcm->private_data;
|
||||||
|
snd_pcm_sw_params_t sw_params;
|
||||||
|
snd_pcm_uframes_t silence_size;
|
||||||
int err;
|
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 crossing point
|
||||||
|
* in this case because the 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;
|
||||||
|
if (ioctl(hw->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sw_params) < 0) {
|
||||||
|
err = -errno;
|
||||||
|
SYSMSG("SNDRV_PCM_IOCTL_SW_PARAMS failed (%i)", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
hw->prepare_reset_sw_params = true;
|
||||||
|
}
|
||||||
|
__skip_silence:
|
||||||
if (ioctl(hw->fd, SNDRV_PCM_IOCTL_DRAIN) < 0) {
|
if (ioctl(hw->fd, SNDRV_PCM_IOCTL_DRAIN) < 0) {
|
||||||
err = -errno;
|
err = -errno;
|
||||||
SYSMSG("SNDRV_PCM_IOCTL_DRAIN failed (%i)", err);
|
SYSMSG("SNDRV_PCM_IOCTL_DRAIN failed (%i)", err);
|
||||||
|
|
|
@ -366,6 +366,8 @@ struct _snd_pcm {
|
||||||
snd1_pcm_hw_param_get_max
|
snd1_pcm_hw_param_get_max
|
||||||
#define snd_pcm_hw_param_name \
|
#define snd_pcm_hw_param_name \
|
||||||
snd1_pcm_hw_param_name
|
snd1_pcm_hw_param_name
|
||||||
|
#define snd_pcm_sw_params_current_no_lock \
|
||||||
|
snd1_pcm_sw_params_current_no_lock
|
||||||
|
|
||||||
int snd_pcm_new(snd_pcm_t **pcmp, snd_pcm_type_t type, const char *name,
|
int snd_pcm_new(snd_pcm_t **pcmp, snd_pcm_type_t type, const char *name,
|
||||||
snd_pcm_stream_t stream, int mode);
|
snd_pcm_stream_t stream, int mode);
|
||||||
|
@ -390,6 +392,8 @@ void snd_pcm_mmap_appl_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames);
|
||||||
void snd_pcm_mmap_hw_backward(snd_pcm_t *pcm, snd_pcm_uframes_t frames);
|
void snd_pcm_mmap_hw_backward(snd_pcm_t *pcm, snd_pcm_uframes_t frames);
|
||||||
void snd_pcm_mmap_hw_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames);
|
void snd_pcm_mmap_hw_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames);
|
||||||
|
|
||||||
|
void snd_pcm_sw_params_current_no_lock(snd_pcm_t *pcm, snd_pcm_sw_params_t *params);
|
||||||
|
|
||||||
snd_pcm_sframes_t snd_pcm_mmap_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size);
|
snd_pcm_sframes_t snd_pcm_mmap_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size);
|
||||||
snd_pcm_sframes_t snd_pcm_mmap_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size);
|
snd_pcm_sframes_t snd_pcm_mmap_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size);
|
||||||
snd_pcm_sframes_t snd_pcm_mmap_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size);
|
snd_pcm_sframes_t snd_pcm_mmap_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue