From a9bcf8d50dd98897db8e37d5c5141004f475d39f Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@duempel.org>
Date: Fri, 16 Oct 2015 14:55:00 +0200
Subject: [PATCH 01/14] configure.ac: prepare for 0.19.11

---
 NEWS         | 2 ++
 configure.ac | 4 ++--
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/NEWS b/NEWS
index 1d72d188b..65252a614 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,5 @@
+ver 0.19.11 (not yet released)
+
 ver 0.19.10 (2015/06/21)
 * input
   - curl: fix deadlock on small responses
diff --git a/configure.ac b/configure.ac
index 1a3471f36..3d29e724f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,10 +1,10 @@
 AC_PREREQ(2.60)
 
-AC_INIT(mpd, 0.19.10, musicpd-dev-team@lists.sourceforge.net)
+AC_INIT(mpd, 0.19.11, musicpd-dev-team@lists.sourceforge.net)
 
 VERSION_MAJOR=0
 VERSION_MINOR=19
-VERSION_REVISION=10
+VERSION_REVISION=11
 VERSION_EXTRA=0
 
 AC_CONFIG_SRCDIR([src/Main.cxx])

From 205fba74cffffb9df985cdf928101633ffc41772 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@duempel.org>
Date: Fri, 16 Oct 2015 14:40:46 +0200
Subject: [PATCH 02/14] tag/ApeLoader: fix buffer overflow after unterminated
 key

---
 NEWS                  |  2 ++
 src/tag/ApeLoader.cxx | 12 ++++++------
 2 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/NEWS b/NEWS
index 65252a614..7e5f41b3b 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,6 @@
 ver 0.19.11 (not yet released)
+* tags
+  - ape: fix buffer overflow
 
 ver 0.19.10 (2015/06/21)
 * input
diff --git a/src/tag/ApeLoader.cxx b/src/tag/ApeLoader.cxx
index f473c910e..f51cb5c0b 100644
--- a/src/tag/ApeLoader.cxx
+++ b/src/tag/ApeLoader.cxx
@@ -78,12 +78,12 @@ ape_scan_internal(FILE *fp, ApeTagCallback callback)
 
 		/* get the key */
 		const char *key = p;
-		while (remaining > size && *p != '\0') {
-			p++;
-			remaining--;
-		}
-		p++;
-		remaining--;
+		const char *key_end = (const char *)memchr(p, '\0', remaining);
+		if (key_end == nullptr)
+			break;
+
+		p = key_end + 1;
+		remaining -= p - key;
 
 		/* get the value */
 		if (remaining < size)

From 4e3d1821895c92d060fc3a9059e83ac58c8c79a3 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@duempel.org>
Date: Fri, 16 Oct 2015 18:05:34 +0200
Subject: [PATCH 03/14] encoder/flac: fix crash with 32 bit playback

Copy to encoder->audio_format *after* adjusting the sample format to
S24_P32.

Fixes http://bugs.musicpd.org/view.php?id=4433
---
 NEWS                                      | 2 ++
 src/encoder/plugins/FlacEncoderPlugin.cxx | 4 ++--
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/NEWS b/NEWS
index 7e5f41b3b..9c44eaf0a 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,8 @@
 ver 0.19.11 (not yet released)
 * tags
   - ape: fix buffer overflow
+* encoder
+  - flac: fix crash with 32 bit playback
 
 ver 0.19.10 (2015/06/21)
 * input
diff --git a/src/encoder/plugins/FlacEncoderPlugin.cxx b/src/encoder/plugins/FlacEncoderPlugin.cxx
index 26987fe99..9317b02ea 100644
--- a/src/encoder/plugins/FlacEncoderPlugin.cxx
+++ b/src/encoder/plugins/FlacEncoderPlugin.cxx
@@ -157,8 +157,6 @@ flac_encoder_open(Encoder *_encoder, AudioFormat &audio_format, Error &error)
 	struct flac_encoder *encoder = (struct flac_encoder *)_encoder;
 	unsigned bits_per_sample;
 
-	encoder->audio_format = audio_format;
-
 	/* FIXME: flac should support 32bit as well */
 	switch (audio_format.format) {
 	case SampleFormat::S8:
@@ -178,6 +176,8 @@ flac_encoder_open(Encoder *_encoder, AudioFormat &audio_format, Error &error)
 		audio_format.format = SampleFormat::S24_P32;
 	}
 
+	encoder->audio_format = audio_format;
+
 	/* allocate the encoder */
 	encoder->fse = FLAC__stream_encoder_new();
 	if (encoder->fse == nullptr) {

From f066bb7716200a83e209d27f6b4f87f012033266 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@duempel.org>
Date: Thu, 17 Sep 2015 23:13:53 +0200
Subject: [PATCH 04/14] unix/Daemon, playlist/...: remove unused Domain
 variables

---
 src/Main.cxx                                | 3 ---
 src/encoder/plugins/ShineEncoderPlugin.cxx  | 3 ---
 src/filter/plugins/VolumeFilterPlugin.cxx   | 3 ---
 src/mixer/MixerAll.cxx                      | 3 ---
 src/output/plugins/PipeOutputPlugin.cxx     | 3 ---
 src/playlist/plugins/XspfPlaylistPlugin.cxx | 3 ---
 src/unix/Daemon.cxx                         | 3 ---
 7 files changed, 21 deletions(-)

diff --git a/src/Main.cxx b/src/Main.cxx
index 26d4e7ae4..a3a1b0021 100644
--- a/src/Main.cxx
+++ b/src/Main.cxx
@@ -54,7 +54,6 @@
 #include "system/FatalError.hxx"
 #include "util/UriUtil.hxx"
 #include "util/Error.hxx"
-#include "util/Domain.hxx"
 #include "thread/Id.hxx"
 #include "thread/Slack.hxx"
 #include "lib/icu/Init.hxx"
@@ -123,8 +122,6 @@
 static constexpr unsigned DEFAULT_BUFFER_SIZE = 4096;
 static constexpr unsigned DEFAULT_BUFFER_BEFORE_PLAY = 10;
 
-static constexpr Domain main_domain("main");
-
 #ifdef ANDROID
 Context *context;
 #endif
diff --git a/src/encoder/plugins/ShineEncoderPlugin.cxx b/src/encoder/plugins/ShineEncoderPlugin.cxx
index 61cb8609e..1b00f7d53 100644
--- a/src/encoder/plugins/ShineEncoderPlugin.cxx
+++ b/src/encoder/plugins/ShineEncoderPlugin.cxx
@@ -26,7 +26,6 @@
 #include "util/NumberParser.hxx"
 #include "util/DynamicFifoBuffer.hxx"
 #include "util/Error.hxx"
-#include "util/Domain.hxx"
 
 extern "C"
 {
@@ -60,8 +59,6 @@ struct ShineEncoder {
 	bool WriteChunk(bool flush);
 };
 
-static constexpr Domain shine_encoder_domain("shine_encoder");
-
 inline bool
 ShineEncoder::Configure(const config_param &param,
 			 gcc_unused Error &error)
diff --git a/src/filter/plugins/VolumeFilterPlugin.cxx b/src/filter/plugins/VolumeFilterPlugin.cxx
index 17e061476..39188da00 100644
--- a/src/filter/plugins/VolumeFilterPlugin.cxx
+++ b/src/filter/plugins/VolumeFilterPlugin.cxx
@@ -26,7 +26,6 @@
 #include "AudioFormat.hxx"
 #include "util/ConstBuffer.hxx"
 #include "util/Error.hxx"
-#include "util/Domain.hxx"
 
 #include <assert.h>
 #include <string.h>
@@ -50,8 +49,6 @@ public:
 				    Error &error) override;
 };
 
-static constexpr Domain volume_domain("pcm_volume");
-
 static Filter *
 volume_filter_init(gcc_unused const config_param &param,
 		   gcc_unused Error &error)
diff --git a/src/mixer/MixerAll.cxx b/src/mixer/MixerAll.cxx
index 5fef6a92f..91891e870 100644
--- a/src/mixer/MixerAll.cxx
+++ b/src/mixer/MixerAll.cxx
@@ -25,13 +25,10 @@
 #include "output/Internal.hxx"
 #include "pcm/Volume.hxx"
 #include "util/Error.hxx"
-#include "util/Domain.hxx"
 #include "Log.hxx"
 
 #include <assert.h>
 
-static constexpr Domain mixer_domain("mixer");
-
 static int
 output_mixer_get_volume(const AudioOutput &ao)
 {
diff --git a/src/output/plugins/PipeOutputPlugin.cxx b/src/output/plugins/PipeOutputPlugin.cxx
index 7a1f32258..d8075d505 100644
--- a/src/output/plugins/PipeOutputPlugin.cxx
+++ b/src/output/plugins/PipeOutputPlugin.cxx
@@ -22,7 +22,6 @@
 #include "../OutputAPI.hxx"
 #include "config/ConfigError.hxx"
 #include "util/Error.hxx"
-#include "util/Domain.hxx"
 
 #include <string>
 
@@ -44,8 +43,6 @@ struct PipeOutput {
 	bool Configure(const config_param &param, Error &error);
 };
 
-static constexpr Domain pipe_output_domain("pipe_output");
-
 inline bool
 PipeOutput::Configure(const config_param &param, Error &error)
 {
diff --git a/src/playlist/plugins/XspfPlaylistPlugin.cxx b/src/playlist/plugins/XspfPlaylistPlugin.cxx
index 5b6010b53..d25d6dc28 100644
--- a/src/playlist/plugins/XspfPlaylistPlugin.cxx
+++ b/src/playlist/plugins/XspfPlaylistPlugin.cxx
@@ -25,14 +25,11 @@
 #include "input/InputStream.hxx"
 #include "tag/TagBuilder.hxx"
 #include "util/Error.hxx"
-#include "util/Domain.hxx"
 #include "lib/expat/ExpatParser.hxx"
 #include "Log.hxx"
 
 #include <string.h>
 
-static constexpr Domain xspf_domain("xspf");
-
 /**
  * This is the state object for the GLib XML parser.
  */
diff --git a/src/unix/Daemon.cxx b/src/unix/Daemon.cxx
index 490b2def5..d283108ed 100644
--- a/src/unix/Daemon.cxx
+++ b/src/unix/Daemon.cxx
@@ -22,7 +22,6 @@
 #include "system/FatalError.hxx"
 #include "fs/AllocatedPath.hxx"
 #include "fs/FileSystem.hxx"
-#include "util/Domain.hxx"
 #include "PidFile.hxx"
 #include "Log.hxx"
 
@@ -37,8 +36,6 @@
 #include <grp.h>
 #endif
 
-static constexpr Domain daemon_domain("daemon");
-
 #ifndef WIN32
 
 /** the Unix user name which MPD runs as */

From 2a58f2264936787ddd96b40c7626046592c2d1a0 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@duempel.org>
Date: Wed, 5 Aug 2015 23:19:22 +0200
Subject: [PATCH 05/14] decoder/mpcdec: use Clamp()

---
 src/decoder/plugins/MpcdecDecoderPlugin.cxx | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/src/decoder/plugins/MpcdecDecoderPlugin.cxx b/src/decoder/plugins/MpcdecDecoderPlugin.cxx
index befed0f3b..91b5b55b9 100644
--- a/src/decoder/plugins/MpcdecDecoderPlugin.cxx
+++ b/src/decoder/plugins/MpcdecDecoderPlugin.cxx
@@ -26,6 +26,7 @@
 #include "util/Error.hxx"
 #include "util/Domain.hxx"
 #include "util/Macros.hxx"
+#include "util/Clamp.hxx"
 #include "Log.hxx"
 
 #include <mpc/mpcdec.h>
@@ -117,12 +118,7 @@ mpc_to_mpd_sample(MPC_SAMPLE_FORMAT sample)
 	val = sample * float_scale;
 #endif
 
-	if (val < clip_min)
-		val = clip_min;
-	else if (val > clip_max)
-		val = clip_max;
-
-	return val;
+	return Clamp(val, clip_min, clip_max);
 }
 
 static void

From a7ee64a25b7ffe0ccd499341916070c7b6d02f7a Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@duempel.org>
Date: Wed, 5 Aug 2015 23:18:21 +0200
Subject: [PATCH 06/14] decoder/mpcdec: use SampleTraits<SampleFormat::S24_P32>

Eliminates some duplicate code, and as a side effect, this works
around clang 3.8 compiler warning because a negative value was
shifted.
---
 src/decoder/plugins/MpcdecDecoderPlugin.cxx | 24 +++++++++++----------
 1 file changed, 13 insertions(+), 11 deletions(-)

diff --git a/src/decoder/plugins/MpcdecDecoderPlugin.cxx b/src/decoder/plugins/MpcdecDecoderPlugin.cxx
index 91b5b55b9..391b5d691 100644
--- a/src/decoder/plugins/MpcdecDecoderPlugin.cxx
+++ b/src/decoder/plugins/MpcdecDecoderPlugin.cxx
@@ -22,6 +22,7 @@
 #include "../DecoderAPI.hxx"
 #include "input/InputStream.hxx"
 #include "CheckAudioFormat.hxx"
+#include "pcm/Traits.hxx"
 #include "tag/TagHandler.hxx"
 #include "util/Error.hxx"
 #include "util/Domain.hxx"
@@ -43,6 +44,9 @@ struct mpc_decoder_data {
 
 static constexpr Domain mpcdec_domain("mpcdec");
 
+static constexpr SampleFormat mpcdec_sample_format = SampleFormat::S24_P32;
+typedef SampleTraits<mpcdec_sample_format> MpcdecSampleTraits;
+
 static mpc_int32_t
 mpc_read_cb(mpc_reader *reader, void *ptr, mpc_int32_t size)
 {
@@ -92,18 +96,15 @@ mpc_getsize_cb(mpc_reader *reader)
 }
 
 /* this _looks_ performance-critical, don't de-inline -- eric */
-static inline int32_t
+static inline MpcdecSampleTraits::value_type
 mpc_to_mpd_sample(MPC_SAMPLE_FORMAT sample)
 {
 	/* only doing 16-bit audio for now */
-	int32_t val;
+	MpcdecSampleTraits::value_type val;
 
-	enum {
-		bits = 24,
-	};
-
-	const int clip_min = -1 << (bits - 1);
-	const int clip_max = (1 << (bits - 1)) - 1;
+	constexpr int bits = MpcdecSampleTraits::BITS;
+	constexpr auto clip_min = MpcdecSampleTraits::MIN;
+	constexpr auto clip_max = MpcdecSampleTraits::MAX;
 
 #ifdef MPC_FIXED_POINT
 	const int shift = bits - MPC_FIXED_POINT_SCALE_SHIFT;
@@ -122,7 +123,8 @@ mpc_to_mpd_sample(MPC_SAMPLE_FORMAT sample)
 }
 
 static void
-mpc_to_mpd_buffer(int32_t *dest, const MPC_SAMPLE_FORMAT *src,
+mpc_to_mpd_buffer(MpcdecSampleTraits::pointer_type dest,
+		  const MPC_SAMPLE_FORMAT *src,
 		  unsigned num_samples)
 {
 	while (num_samples-- > 0)
@@ -158,7 +160,7 @@ mpcdec_decode(Decoder &mpd_decoder, InputStream &is)
 	Error error;
 	AudioFormat audio_format;
 	if (!audio_format_init_checked(audio_format, info.sample_freq,
-				       SampleFormat::S24_P32,
+				       mpcdec_sample_format,
 				       info.channels, error)) {
 		LogError(error);
 		mpc_demux_exit(demux);
@@ -210,7 +212,7 @@ mpcdec_decode(Decoder &mpd_decoder, InputStream &is)
 		mpc_uint32_t ret = frame.samples;
 		ret *= info.channels;
 
-		int32_t chunk[ARRAY_SIZE(sample_buffer)];
+		MpcdecSampleTraits::value_type chunk[ARRAY_SIZE(sample_buffer)];
 		mpc_to_mpd_buffer(chunk, sample_buffer, ret);
 
 		long bit_rate = vbr_update_bits * audio_format.sample_rate

From 1958f78cc1bd47ce1c9b57db41194f85aed942ab Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@duempel.org>
Date: Mon, 26 Oct 2015 13:06:29 +0100
Subject: [PATCH 07/14] decoder/ffmpeg: fix crash due to wrong
 avio_alloc_context() call

Allocate the buffer dynamically using av_malloc(), and free
AVIOContext.buffer in the destructor, as mandated by the libavformat
documentation.

Fixes http://bugs.musicpd.org/view.php?id=4446
---
 NEWS                                        |  2 ++
 src/decoder/plugins/FfmpegDecoderPlugin.cxx | 17 +++++++++++++----
 2 files changed, 15 insertions(+), 4 deletions(-)

diff --git a/NEWS b/NEWS
index 9c44eaf0a..7b5d6df07 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,8 @@
 ver 0.19.11 (not yet released)
 * tags
   - ape: fix buffer overflow
+* decoder
+  - ffmpeg: fix crash due to wrong avio_alloc_context() call
 * encoder
   - flac: fix crash with 32 bit playback
 
diff --git a/src/decoder/plugins/FfmpegDecoderPlugin.cxx b/src/decoder/plugins/FfmpegDecoderPlugin.cxx
index d5191a3c3..689089107 100644
--- a/src/decoder/plugins/FfmpegDecoderPlugin.cxx
+++ b/src/decoder/plugins/FfmpegDecoderPlugin.cxx
@@ -92,14 +92,14 @@ struct AvioStream {
 
 	AVIOContext *io;
 
-	unsigned char buffer[8192];
-
 	AvioStream(Decoder *_decoder, InputStream &_input)
 		:decoder(_decoder), input(_input), io(nullptr) {}
 
 	~AvioStream() {
-		if (io != nullptr)
+		if (io != nullptr) {
+			av_free(io->buffer);
 			av_free(io);
+		}
 	}
 
 	bool Open();
@@ -153,11 +153,20 @@ mpd_ffmpeg_stream_seek(void *opaque, int64_t pos, int whence)
 bool
 AvioStream::Open()
 {
-	io = avio_alloc_context(buffer, sizeof(buffer),
+	constexpr size_t BUFFER_SIZE = 8192;
+	auto buffer = (unsigned char *)av_malloc(BUFFER_SIZE);
+	if (buffer == nullptr)
+		return false;
+
+	io = avio_alloc_context(buffer, BUFFER_SIZE,
 				false, this,
 				mpd_ffmpeg_stream_read, nullptr,
 				input.IsSeekable()
 				? mpd_ffmpeg_stream_seek : nullptr);
+	/* If avio_alloc_context() fails, who frees the buffer?  The
+	   libavformat API documentation does not specify this, it
+	   only says that AVIOContext.buffer must be freed in the end,
+	   however no AVIOContext exists in that failure code path. */
 	return io != nullptr;
 }
 

From ac61d43720393803cb3f6bc5c74aea588e1ca68d Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@duempel.org>
Date: Mon, 26 Oct 2015 16:29:07 +0100
Subject: [PATCH 08/14] output/Command: flush the mixer cache when
 enabling/disabling output

Fixes mixer lag (http://bugs.musicpd.org/view.php?id=4425).
---
 NEWS                         | 2 ++
 src/output/OutputCommand.cxx | 8 ++++++++
 2 files changed, 10 insertions(+)

diff --git a/NEWS b/NEWS
index 7b5d6df07..ee3c318b3 100644
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,8 @@ ver 0.19.11 (not yet released)
   - ffmpeg: fix crash due to wrong avio_alloc_context() call
 * encoder
   - flac: fix crash with 32 bit playback
+* mixer
+  - fix mixer lag after enabling/disabling output
 
 ver 0.19.10 (2015/06/21)
 * input
diff --git a/src/output/OutputCommand.cxx b/src/output/OutputCommand.cxx
index 6afb70cf1..e6b8a8e7f 100644
--- a/src/output/OutputCommand.cxx
+++ b/src/output/OutputCommand.cxx
@@ -30,6 +30,7 @@
 #include "Internal.hxx"
 #include "PlayerControl.hxx"
 #include "mixer/MixerControl.hxx"
+#include "mixer/Volume.hxx"
 #include "Idle.hxx"
 
 extern unsigned audio_output_state_version;
@@ -47,6 +48,11 @@ audio_output_enable_index(MultipleOutputs &outputs, unsigned idx)
 	ao.enabled = true;
 	idle_add(IDLE_OUTPUT);
 
+	if (ao.mixer != nullptr) {
+		InvalidateHardwareVolume();
+		idle_add(IDLE_MIXER);
+	}
+
 	ao.player_control->UpdateAudio();
 
 	++audio_output_state_version;
@@ -70,6 +76,7 @@ audio_output_disable_index(MultipleOutputs &outputs, unsigned idx)
 	Mixer *mixer = ao.mixer;
 	if (mixer != nullptr) {
 		mixer_close(mixer);
+		InvalidateHardwareVolume();
 		idle_add(IDLE_MIXER);
 	}
 
@@ -94,6 +101,7 @@ audio_output_toggle_index(MultipleOutputs &outputs, unsigned idx)
 		Mixer *mixer = ao.mixer;
 		if (mixer != nullptr) {
 			mixer_close(mixer);
+			InvalidateHardwareVolume();
 			idle_add(IDLE_MIXER);
 		}
 	}

From 93c97972b9cb41284f8af8b653e056484bbf4717 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@duempel.org>
Date: Mon, 26 Oct 2015 16:32:39 +0100
Subject: [PATCH 09/14] decoder/gme: call decoder_seek_error() on seek error

---
 src/decoder/plugins/GmeDecoderPlugin.cxx | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/decoder/plugins/GmeDecoderPlugin.cxx b/src/decoder/plugins/GmeDecoderPlugin.cxx
index cc6ce5e5d..eab655c91 100644
--- a/src/decoder/plugins/GmeDecoderPlugin.cxx
+++ b/src/decoder/plugins/GmeDecoderPlugin.cxx
@@ -196,9 +196,11 @@ gme_file_decode(Decoder &decoder, Path path_fs)
 		if (cmd == DecoderCommand::SEEK) {
 			unsigned where = decoder_seek_time(decoder).ToMS();
 			gme_err = gme_seek(emu, where);
-			if (gme_err != nullptr)
+			if (gme_err != nullptr) {
 				LogWarning(gme_domain, gme_err);
-			decoder_command_finished(decoder);
+				decoder_seek_error(decoder);
+			} else
+				decoder_command_finished(decoder);
 		}
 
 		if (gme_track_ended(emu))

From a84fbbe32716ecee9c8a02b50251097fc35709ec Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@duempel.org>
Date: Mon, 26 Oct 2015 17:15:24 +0100
Subject: [PATCH 10/14] decoder/gme: free the gme_info_t as early as possible

---
 src/decoder/plugins/GmeDecoderPlugin.cxx | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/src/decoder/plugins/GmeDecoderPlugin.cxx b/src/decoder/plugins/GmeDecoderPlugin.cxx
index eab655c91..bbe2596c9 100644
--- a/src/decoder/plugins/GmeDecoderPlugin.cxx
+++ b/src/decoder/plugins/GmeDecoderPlugin.cxx
@@ -156,8 +156,11 @@ gme_file_decode(Decoder &decoder, Path path_fs)
 		return;
 	}
 
-	const SignedSongTime song_len = ti->length > 0
-		? SignedSongTime::FromMS(ti->length)
+	const int length = ti->length;
+	gme_free_info(ti);
+
+	const SignedSongTime song_len = length > 0
+		? SignedSongTime::FromMS(length)
 		: SignedSongTime::Negative();
 
 	/* initialize the MPD decoder */
@@ -168,7 +171,6 @@ gme_file_decode(Decoder &decoder, Path path_fs)
 				       SampleFormat::S16, GME_CHANNELS,
 				       error)) {
 		LogError(error);
-		gme_free_info(ti);
 		gme_delete(emu);
 		return;
 	}
@@ -179,8 +181,8 @@ gme_file_decode(Decoder &decoder, Path path_fs)
 	if (gme_err != nullptr)
 		LogWarning(gme_domain, gme_err);
 
-	if (ti->length > 0)
-		gme_set_fade(emu, ti->length);
+	if (length > 0)
+		gme_set_fade(emu, length);
 
 	/* play */
 	DecoderCommand cmd;
@@ -207,7 +209,6 @@ gme_file_decode(Decoder &decoder, Path path_fs)
 			break;
 	} while (cmd != DecoderCommand::STOP);
 
-	gme_free_info(ti);
 	gme_delete(emu);
 }
 

From 5719207dfa14cfed63e0446f7f7a8343575a465f Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@duempel.org>
Date: Mon, 26 Oct 2015 17:16:20 +0100
Subject: [PATCH 11/14] gme: don't loop forever, fall back to GME's default
 play length

Fixes http://bugs.musicpd.org/view.php?id=4432
---
 NEWS                                     | 1 +
 src/decoder/plugins/GmeDecoderPlugin.cxx | 6 +++---
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/NEWS b/NEWS
index ee3c318b3..463701e35 100644
--- a/NEWS
+++ b/NEWS
@@ -3,6 +3,7 @@ ver 0.19.11 (not yet released)
   - ape: fix buffer overflow
 * decoder
   - ffmpeg: fix crash due to wrong avio_alloc_context() call
+  - gme: don't loop forever, fall back to GME's default play length
 * encoder
   - flac: fix crash with 32 bit playback
 * mixer
diff --git a/src/decoder/plugins/GmeDecoderPlugin.cxx b/src/decoder/plugins/GmeDecoderPlugin.cxx
index bbe2596c9..b47e9ea66 100644
--- a/src/decoder/plugins/GmeDecoderPlugin.cxx
+++ b/src/decoder/plugins/GmeDecoderPlugin.cxx
@@ -156,7 +156,7 @@ gme_file_decode(Decoder &decoder, Path path_fs)
 		return;
 	}
 
-	const int length = ti->length;
+	const int length = ti->play_length;
 	gme_free_info(ti);
 
 	const SignedSongTime song_len = length > 0
@@ -239,9 +239,9 @@ gme_scan_file(Path path_fs,
 
 	assert(ti != nullptr);
 
-	if (ti->length > 0)
+	if (ti->play_length > 0)
 		tag_handler_invoke_duration(handler, handler_ctx,
-					    SongTime::FromMS(ti->length));
+					    SongTime::FromMS(ti->play_length));
 
 	if (ti->song != nullptr) {
 		if (gme_track_count(emu) > 1) {

From f901cd042bb7872cc097c6c2d846b70b022ba3f0 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@duempel.org>
Date: Tue, 27 Oct 2015 10:31:50 +0100
Subject: [PATCH 12/14] doc/user: section about real-time scheduling

---
 doc/user.xml | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/doc/user.xml b/doc/user.xml
index d1c7ff595..010d2f980 100644
--- a/doc/user.xml
+++ b/doc/user.xml
@@ -1190,6 +1190,58 @@ database {
         plugin).
       </para>
     </section>
+
+    <section id="realtime">
+      <title>Real-Time Scheduling</title>
+
+      <para>
+        On Linux, <application>MPD</application> attempts to configure
+        <ulink
+        url="https://en.wikipedia.org/wiki/Real-time_computing">real-time
+        scheduling</ulink> for some threads that benefit from it.
+      </para>
+
+      <para>
+        This is only possible you allow <application>MPD</application>
+        to do it.  This privilege is controlled by
+        <varname>RLIMIT_RTPRIO</varname>
+        <varname>RLIMIT_RTTIME</varname>.  You can configure this
+        privilege with <command>ulimit</command> before launching
+        <application>MPD</application>:
+      </para>
+
+      <programlisting>ulimit -HS -r 50; mpd</programlisting>
+
+      <para>
+        Or you can use the <command>prlimit</command> program from the
+        <application>util-linux</application> package:
+      </para>
+
+      <programlisting>prlimit --rtprio=50 --rttime=unlimited mpd</programlisting>
+
+      <para>
+        The <application>systemd</application> service file shipped
+        with <application>MPD</application> comes with this setting.
+      </para>
+
+      <para>
+        This works only if the Linux kernel was compiled with
+        <varname>CONFIG_RT_GROUP_SCHED</varname> disabled.  Use the
+        following command to check this option for your current
+        kernel:
+      </para>
+
+      <programlisting>zgrep ^CONFIG_RT_GROUP_SCHED /proc/config.gz</programlisting>
+
+      <note>
+        <para>
+          There is a rumor that real-time scheduling improves audio
+          quality.  That is not true.  All it does is reduce the
+          probability of skipping (audio buffer xruns) when the
+          computer is under heavy load.
+        </para>
+      </note>
+    </section>
   </chapter>
 
   <chapter id="use">

From 0cbfb610f232c7a77272039698e5b137267c3159 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@duempel.org>
Date: Tue, 27 Oct 2015 10:32:16 +0100
Subject: [PATCH 13/14] systemd: remove obsolete ControlGroup settings

This systemd feature has been removed a while ago without replacement,
and it turns out that systemd developers suggest not using control
groups at all to assign real-time privileges.  Therfore, a replacement
feature will not be implement in future systemd releases, and we can
really remove those lines completely.

See http://bugs.musicpd.org/view.php?id=4413
---
 systemd/mpd.service.in | 9 ---------
 1 file changed, 9 deletions(-)

diff --git a/systemd/mpd.service.in b/systemd/mpd.service.in
index a72eb925c..de91eb4d5 100644
--- a/systemd/mpd.service.in
+++ b/systemd/mpd.service.in
@@ -9,14 +9,5 @@ ExecStart=@prefix@/bin/mpd --no-daemon
 LimitRTPRIO=50
 LimitRTTIME=-1
 
-# move MPD to a top-level cgroup, as real-time budget assignment fails
-# in cgroup /system/mpd.service, because /system has a zero real-time
-# budget; see
-# http://www.freedesktop.org/wiki/Software/systemd/MyServiceCantGetRealtime/
-ControlGroup=cpu:/mpd
-
-# assign a real-time budget
-ControlGroupAttribute=cpu.rt_runtime_us 500000
-
 [Install]
 WantedBy=multi-user.target

From db9997a1068e00c5f78d4bb9834ce5a13e7b7a3b Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@duempel.org>
Date: Tue, 27 Oct 2015 10:42:20 +0100
Subject: [PATCH 14/14] release v0.19.11

---
 NEWS | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/NEWS b/NEWS
index 463701e35..94a676b84 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,4 @@
-ver 0.19.11 (not yet released)
+ver 0.19.11 (2015/10/27)
 * tags
   - ape: fix buffer overflow
 * decoder