output/alsa: fill period buffer with silence before draining
ALSA passes full period buffers to the hardware. If an application doesn't finish writing a period, libasound will nonetheless send the partial buffer (with undefined trailing data). This causes noise at the end of playback. This patch attempts to track the current position within the period buffer, and generates silence at the end, before calling snd_pcm_drain().
This commit is contained in:
parent
8420f1420f
commit
54033c74e4
1
NEWS
1
NEWS
|
@ -29,6 +29,7 @@ ver 0.16 (20??/??/??)
|
||||||
* output:
|
* output:
|
||||||
- recorder: new output plugin for recording radio streams
|
- recorder: new output plugin for recording radio streams
|
||||||
- alsa: don't recover on CANCEL
|
- alsa: don't recover on CANCEL
|
||||||
|
- alsa: fill period buffer with silence before draining
|
||||||
- openal: new output plugin
|
- openal: new output plugin
|
||||||
- pulse: announce "media.role=music"
|
- pulse: announce "media.role=music"
|
||||||
- pulse: renamed context to "Music Player Daemon"
|
- pulse: renamed context to "Music Player Daemon"
|
||||||
|
|
|
@ -69,6 +69,16 @@ struct alsa_data {
|
||||||
|
|
||||||
/** the size of one audio frame */
|
/** the size of one audio frame */
|
||||||
size_t frame_size;
|
size_t frame_size;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The size of one period, in number of frames.
|
||||||
|
*/
|
||||||
|
snd_pcm_uframes_t period_frames;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of frames written in the current period.
|
||||||
|
*/
|
||||||
|
snd_pcm_uframes_t period_position;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -413,6 +423,9 @@ configure_hw:
|
||||||
g_debug("buffer_size=%u period_size=%u",
|
g_debug("buffer_size=%u period_size=%u",
|
||||||
(unsigned)alsa_buffer_size, (unsigned)alsa_period_size);
|
(unsigned)alsa_buffer_size, (unsigned)alsa_period_size);
|
||||||
|
|
||||||
|
ad->period_frames = alsa_period_size;
|
||||||
|
ad->period_position = 0;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
|
@ -479,6 +492,7 @@ alsa_recover(struct alsa_data *ad, int err)
|
||||||
/* fall-through to snd_pcm_prepare: */
|
/* fall-through to snd_pcm_prepare: */
|
||||||
case SND_PCM_STATE_SETUP:
|
case SND_PCM_STATE_SETUP:
|
||||||
case SND_PCM_STATE_XRUN:
|
case SND_PCM_STATE_XRUN:
|
||||||
|
ad->period_position = 0;
|
||||||
err = snd_pcm_prepare(ad->pcm);
|
err = snd_pcm_prepare(ad->pcm);
|
||||||
break;
|
break;
|
||||||
case SND_PCM_STATE_DISCONNECTED:
|
case SND_PCM_STATE_DISCONNECTED:
|
||||||
|
@ -500,8 +514,33 @@ alsa_drain(void *data)
|
||||||
{
|
{
|
||||||
struct alsa_data *ad = data;
|
struct alsa_data *ad = data;
|
||||||
|
|
||||||
if (snd_pcm_state(ad->pcm) == SND_PCM_STATE_RUNNING)
|
if (snd_pcm_state(ad->pcm) != SND_PCM_STATE_RUNNING)
|
||||||
snd_pcm_drain(ad->pcm);
|
return;
|
||||||
|
|
||||||
|
if (ad->period_position > 0) {
|
||||||
|
/* generate some silence to finish the partial
|
||||||
|
period */
|
||||||
|
snd_pcm_uframes_t nframes =
|
||||||
|
ad->period_frames - ad->period_position;
|
||||||
|
size_t nbytes = nframes * ad->frame_size;
|
||||||
|
void *buffer = g_malloc(nbytes);
|
||||||
|
snd_pcm_hw_params_t *params;
|
||||||
|
snd_pcm_format_t format;
|
||||||
|
unsigned channels;
|
||||||
|
|
||||||
|
snd_pcm_hw_params_alloca(¶ms);
|
||||||
|
snd_pcm_hw_params_current(ad->pcm, params);
|
||||||
|
snd_pcm_hw_params_get_format(params, &format);
|
||||||
|
snd_pcm_hw_params_get_channels(params, &channels);
|
||||||
|
|
||||||
|
snd_pcm_format_set_silence(format, buffer, nframes * channels);
|
||||||
|
ad->writei(ad->pcm, buffer, nframes);
|
||||||
|
g_free(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
snd_pcm_drain(ad->pcm);
|
||||||
|
|
||||||
|
ad->period_position = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -509,6 +548,8 @@ alsa_cancel(void *data)
|
||||||
{
|
{
|
||||||
struct alsa_data *ad = data;
|
struct alsa_data *ad = data;
|
||||||
|
|
||||||
|
ad->period_position = 0;
|
||||||
|
|
||||||
snd_pcm_drop(ad->pcm);
|
snd_pcm_drop(ad->pcm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -529,8 +570,11 @@ alsa_play(void *data, const void *chunk, size_t size, GError **error)
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
snd_pcm_sframes_t ret = ad->writei(ad->pcm, chunk, size);
|
snd_pcm_sframes_t ret = ad->writei(ad->pcm, chunk, size);
|
||||||
if (ret > 0)
|
if (ret > 0) {
|
||||||
|
ad->period_position = (ad->period_position + ret)
|
||||||
|
% ad->period_frames;
|
||||||
return ret * ad->frame_size;
|
return ret * ad->frame_size;
|
||||||
|
}
|
||||||
|
|
||||||
if (ret < 0 && ret != -EAGAIN && ret != -EINTR &&
|
if (ret < 0 && ret != -EAGAIN && ret != -EINTR &&
|
||||||
alsa_recover(ad, ret) < 0) {
|
alsa_recover(ad, ret) < 0) {
|
||||||
|
|
Loading…
Reference in New Issue