diff --git a/src/DecoderInternal.cxx b/src/DecoderInternal.cxx
index d68f7856e..c5884d357 100644
--- a/src/DecoderInternal.cxx
+++ b/src/DecoderInternal.cxx
@@ -24,10 +24,31 @@
 #include "MusicBuffer.hxx"
 #include "MusicChunk.hxx"
 
+extern "C" {
+#include "tag.h"
+}
+
 #include "input_stream.h"
 
 #include <assert.h>
 
+decoder::~decoder()
+{
+	/* caller must flush the chunk */
+	assert(chunk == nullptr);
+
+	if (song_tag != nullptr)
+		tag_free(song_tag);
+
+	if (stream_tag != nullptr)
+		tag_free(stream_tag);
+
+	if (decoder_tag != nullptr)
+		tag_free(decoder_tag);
+
+	pcm_convert_deinit(&conv_state);
+}
+
 /**
  * All chunks are full of decoded data; wait for the player to free
  * one.
diff --git a/src/DecoderInternal.hxx b/src/DecoderInternal.hxx
index 69c8c0920..ae50a62e2 100644
--- a/src/DecoderInternal.hxx
+++ b/src/DecoderInternal.hxx
@@ -90,7 +90,11 @@ struct decoder {
 		 seeking(false),
 		 song_tag(_tag), stream_tag(nullptr), decoder_tag(nullptr),
 		 chunk(nullptr),
-		 replay_gain_serial(0) {}
+		 replay_gain_serial(0) {
+		pcm_convert_init(&conv_state);
+	}
+
+	~decoder();
 };
 
 /**
diff --git a/src/DecoderThread.cxx b/src/DecoderThread.cxx
index cf16bea2d..18228f381 100644
--- a/src/DecoderThread.cxx
+++ b/src/DecoderThread.cxx
@@ -389,30 +389,17 @@ decoder_run_song(struct decoder_control *dc,
 
 	decoder_command_finished_locked(dc);
 
-	pcm_convert_init(&decoder.conv_state);
-
 	ret = song_is_file(song)
 		? decoder_run_file(&decoder, uri)
 		: decoder_run_stream(&decoder, uri);
 
 	decoder_unlock(dc);
 
-	pcm_convert_deinit(&decoder.conv_state);
-
 	/* flush the last chunk */
 
 	if (decoder.chunk != NULL)
 		decoder_flush_chunk(&decoder);
 
-	if (decoder.song_tag != NULL)
-		tag_free(decoder.song_tag);
-
-	if (decoder.stream_tag != NULL)
-		tag_free(decoder.stream_tag);
-
-	if (decoder.decoder_tag != NULL)
-		tag_free(decoder.decoder_tag);
-
 	decoder_lock(dc);
 
 	if (ret)