diff --git a/NEWS b/NEWS
index d6d6f073b..43731fcb7 100644
--- a/NEWS
+++ b/NEWS
@@ -29,6 +29,7 @@ ver 0.16 (20??/??/??)
 * output:
   - recorder: new output plugin for recording radio streams
   - alsa: don't recover on CANCEL
+  - alsa: fill period buffer with silence before draining
   - openal: new output plugin
   - pulse: announce "media.role=music"
   - pulse: renamed context to "Music Player Daemon"
diff --git a/src/output/alsa_plugin.c b/src/output/alsa_plugin.c
index 48a40fb9b..64a8127ba 100644
--- a/src/output/alsa_plugin.c
+++ b/src/output/alsa_plugin.c
@@ -69,6 +69,16 @@ struct alsa_data {
 
 	/** the size of one audio frame */
 	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",
 		(unsigned)alsa_buffer_size, (unsigned)alsa_period_size);
 
+	ad->period_frames = alsa_period_size;
+	ad->period_position = 0;
+
 	return true;
 
 error:
@@ -479,6 +492,7 @@ alsa_recover(struct alsa_data *ad, int err)
 		/* fall-through to snd_pcm_prepare: */
 	case SND_PCM_STATE_SETUP:
 	case SND_PCM_STATE_XRUN:
+		ad->period_position = 0;
 		err = snd_pcm_prepare(ad->pcm);
 		break;
 	case SND_PCM_STATE_DISCONNECTED:
@@ -500,8 +514,33 @@ alsa_drain(void *data)
 {
 	struct alsa_data *ad = data;
 
-	if (snd_pcm_state(ad->pcm) == SND_PCM_STATE_RUNNING)
-		snd_pcm_drain(ad->pcm);
+	if (snd_pcm_state(ad->pcm) != SND_PCM_STATE_RUNNING)
+		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(&params);
+		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
@@ -509,6 +548,8 @@ alsa_cancel(void *data)
 {
 	struct alsa_data *ad = data;
 
+	ad->period_position = 0;
+
 	snd_pcm_drop(ad->pcm);
 }
 
@@ -529,8 +570,11 @@ alsa_play(void *data, const void *chunk, size_t size, GError **error)
 
 	while (true) {
 		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;
+		}
 
 		if (ret < 0 && ret != -EAGAIN && ret != -EINTR &&
 		    alsa_recover(ad, ret) < 0) {