diff --git a/src/DecoderAPI.cxx b/src/DecoderAPI.cxx
index 06fa787f9..c2f1267c1 100644
--- a/src/DecoderAPI.cxx
+++ b/src/DecoderAPI.cxx
@@ -37,19 +37,18 @@
 #include <string.h>
 
 void
-decoder_initialized(struct decoder *decoder,
+decoder_initialized(Decoder &decoder,
 		    const AudioFormat audio_format,
 		    bool seekable, float total_time)
 {
-	decoder_control &dc = decoder->dc;
+	decoder_control &dc = decoder.dc;
 	struct audio_format_string af_string;
 
 	assert(dc.state == DecoderState::START);
 	assert(dc.pipe != nullptr);
-	assert(decoder != nullptr);
-	assert(decoder->stream_tag == nullptr);
-	assert(decoder->decoder_tag == nullptr);
-	assert(!decoder->seeking);
+	assert(decoder.stream_tag == nullptr);
+	assert(decoder.decoder_tag == nullptr);
+	assert(!decoder.seeking);
 	assert(audio_format.IsDefined());
 	assert(audio_format.IsValid());
 
@@ -80,9 +79,9 @@ decoder_initialized(struct decoder *decoder,
  */
 gcc_pure
 static bool
-decoder_prepare_initial_seek(struct decoder *decoder)
+decoder_prepare_initial_seek(Decoder &decoder)
 {
-	const decoder_control &dc = decoder->dc;
+	const decoder_control &dc = decoder.dc;
 	assert(dc.pipe != nullptr);
 
 	if (dc.state != DecoderState::DECODE)
@@ -91,30 +90,30 @@ decoder_prepare_initial_seek(struct decoder *decoder)
 		   virtual "SEEK" command */
 		return false;
 
-	if (decoder->initial_seek_running)
+	if (decoder.initial_seek_running)
 		/* initial seek has already begun - override any other
 		   command */
 		return true;
 
-	if (decoder->initial_seek_pending) {
+	if (decoder.initial_seek_pending) {
 		if (!dc.seekable) {
 			/* seeking is not possible */
-			decoder->initial_seek_pending = false;
+			decoder.initial_seek_pending = false;
 			return false;
 		}
 
 		if (dc.command == DecoderCommand::NONE) {
 			/* begin initial seek */
 
-			decoder->initial_seek_pending = false;
-			decoder->initial_seek_running = true;
+			decoder.initial_seek_pending = false;
+			decoder.initial_seek_running = true;
 			return true;
 		}
 
 		/* skip initial seek when there's another command
 		   (e.g. STOP) */
 
-		decoder->initial_seek_pending = false;
+		decoder.initial_seek_pending = false;
 	}
 
 	return false;
@@ -127,9 +126,9 @@ decoder_prepare_initial_seek(struct decoder *decoder)
  */
 gcc_pure
 static DecoderCommand
-decoder_get_virtual_command(struct decoder *decoder)
+decoder_get_virtual_command(Decoder &decoder)
 {
-	const decoder_control &dc = decoder->dc;
+	const decoder_control &dc = decoder.dc;
 	assert(dc.pipe != nullptr);
 
 	if (decoder_prepare_initial_seek(decoder))
@@ -139,49 +138,49 @@ decoder_get_virtual_command(struct decoder *decoder)
 }
 
 DecoderCommand
-decoder_get_command(struct decoder *decoder)
+decoder_get_command(Decoder &decoder)
 {
 	return decoder_get_virtual_command(decoder);
 }
 
 void
-decoder_command_finished(struct decoder *decoder)
+decoder_command_finished(Decoder &decoder)
 {
-	decoder_control &dc = decoder->dc;
+	decoder_control &dc = decoder.dc;
 
 	dc.Lock();
 
 	assert(dc.command != DecoderCommand::NONE ||
-	       decoder->initial_seek_running);
+	       decoder.initial_seek_running);
 	assert(dc.command != DecoderCommand::SEEK ||
-	       decoder->initial_seek_running ||
-	       dc.seek_error || decoder->seeking);
+	       decoder.initial_seek_running ||
+	       dc.seek_error || decoder.seeking);
 	assert(dc.pipe != nullptr);
 
-	if (decoder->initial_seek_running) {
-		assert(!decoder->seeking);
-		assert(decoder->chunk == nullptr);
+	if (decoder.initial_seek_running) {
+		assert(!decoder.seeking);
+		assert(decoder.chunk == nullptr);
 		assert(dc.pipe->IsEmpty());
 
-		decoder->initial_seek_running = false;
-		decoder->timestamp = dc.start_ms / 1000.;
+		decoder.initial_seek_running = false;
+		decoder.timestamp = dc.start_ms / 1000.;
 		dc.Unlock();
 		return;
 	}
 
-	if (decoder->seeking) {
-		decoder->seeking = false;
+	if (decoder.seeking) {
+		decoder.seeking = false;
 
 		/* delete frames from the old song position */
 
-		if (decoder->chunk != nullptr) {
-			dc.buffer->Return(decoder->chunk);
-			decoder->chunk = nullptr;
+		if (decoder.chunk != nullptr) {
+			dc.buffer->Return(decoder.chunk);
+			decoder.chunk = nullptr;
 		}
 
 		dc.pipe->Clear(*dc.buffer);
 
-		decoder->timestamp = dc.seek_where;
+		decoder.timestamp = dc.seek_where;
 	}
 
 	dc.command = DecoderCommand::NONE;
@@ -189,39 +188,39 @@ decoder_command_finished(struct decoder *decoder)
 	dc.Unlock();
 }
 
-double decoder_seek_where(gcc_unused struct decoder * decoder)
+double decoder_seek_where(gcc_unused Decoder & decoder)
 {
-	const decoder_control &dc = decoder->dc;
+	const decoder_control &dc = decoder.dc;
 
 	assert(dc.pipe != nullptr);
 
-	if (decoder->initial_seek_running)
+	if (decoder.initial_seek_running)
 		return dc.start_ms / 1000.;
 
 	assert(dc.command == DecoderCommand::SEEK);
 
-	decoder->seeking = true;
+	decoder.seeking = true;
 
 	return dc.seek_where;
 }
 
-void decoder_seek_error(struct decoder * decoder)
+void decoder_seek_error(Decoder & decoder)
 {
-	decoder_control &dc = decoder->dc;
+	decoder_control &dc = decoder.dc;
 
 	assert(dc.pipe != nullptr);
 
-	if (decoder->initial_seek_running) {
+	if (decoder.initial_seek_running) {
 		/* d'oh, we can't seek to the sub-song start position,
 		   what now? - no idea, ignoring the problem for now. */
-		decoder->initial_seek_running = false;
+		decoder.initial_seek_running = false;
 		return;
 	}
 
 	assert(dc.command == DecoderCommand::SEEK);
 
 	dc.seek_error = true;
-	decoder->seeking = false;
+	decoder.seeking = false;
 
 	decoder_command_finished(decoder);
 }
@@ -232,7 +231,7 @@ void decoder_seek_error(struct decoder * decoder)
  */
 gcc_pure
 static inline bool
-decoder_check_cancel_read(const struct decoder *decoder)
+decoder_check_cancel_read(const Decoder *decoder)
 {
 	if (decoder == nullptr)
 		return false;
@@ -250,9 +249,10 @@ decoder_check_cancel_read(const struct decoder *decoder)
 	return true;
 }
 
-size_t decoder_read(struct decoder *decoder,
-		    struct input_stream *is,
-		    void *buffer, size_t length)
+size_t
+decoder_read(Decoder *decoder,
+	     struct input_stream *is,
+	     void *buffer, size_t length)
 {
 	/* XXX don't allow decoder==nullptr */
 
@@ -293,12 +293,11 @@ size_t decoder_read(struct decoder *decoder,
 }
 
 void
-decoder_timestamp(struct decoder *decoder, double t)
+decoder_timestamp(Decoder &decoder, double t)
 {
-	assert(decoder != nullptr);
 	assert(t >= 0);
 
-	decoder->timestamp = t;
+	decoder.timestamp = t;
 }
 
 /**
@@ -306,23 +305,23 @@ decoder_timestamp(struct decoder *decoder, double t)
  * (decoder.chunk) if there is one.
  */
 static DecoderCommand
-do_send_tag(struct decoder *decoder, const Tag &tag)
+do_send_tag(Decoder &decoder, const Tag &tag)
 {
 	struct music_chunk *chunk;
 
-	if (decoder->chunk != nullptr) {
+	if (decoder.chunk != nullptr) {
 		/* there is a partial chunk - flush it, we want the
 		   tag in a new chunk */
 		decoder_flush_chunk(decoder);
-		decoder->dc.client_cond.signal();
+		decoder.dc.client_cond.signal();
 	}
 
-	assert(decoder->chunk == nullptr);
+	assert(decoder.chunk == nullptr);
 
 	chunk = decoder_get_chunk(decoder);
 	if (chunk == nullptr) {
-		assert(decoder->dc.command != DecoderCommand::NONE);
-		return decoder->dc.command;
+		assert(decoder.dc.command != DecoderCommand::NONE);
+		return decoder.dc.command;
 	}
 
 	chunk->tag = new Tag(tag);
@@ -330,7 +329,7 @@ do_send_tag(struct decoder *decoder, const Tag &tag)
 }
 
 static bool
-update_stream_tag(struct decoder *decoder, struct input_stream *is)
+update_stream_tag(Decoder &decoder, struct input_stream *is)
 {
 	Tag *tag;
 
@@ -338,27 +337,27 @@ update_stream_tag(struct decoder *decoder, struct input_stream *is)
 		? is->LockReadTag()
 		: nullptr;
 	if (tag == nullptr) {
-		tag = decoder->song_tag;
+		tag = decoder.song_tag;
 		if (tag == nullptr)
 			return false;
 
 		/* no stream tag present - submit the song tag
 		   instead */
-		decoder->song_tag = nullptr;
+		decoder.song_tag = nullptr;
 	}
 
-	delete decoder->stream_tag;
-	decoder->stream_tag = tag;
+	delete decoder.stream_tag;
+	decoder.stream_tag = tag;
 	return true;
 }
 
 DecoderCommand
-decoder_data(struct decoder *decoder,
+decoder_data(Decoder &decoder,
 	     struct input_stream *is,
 	     const void *data, size_t length,
 	     uint16_t kbit_rate)
 {
-	decoder_control &dc = decoder->dc;
+	decoder_control &dc = decoder.dc;
 	DecoderCommand cmd;
 
 	assert(dc.state == DecoderState::DECODE);
@@ -376,15 +375,15 @@ decoder_data(struct decoder *decoder,
 	/* send stream tags */
 
 	if (update_stream_tag(decoder, is)) {
-		if (decoder->decoder_tag != nullptr) {
+		if (decoder.decoder_tag != nullptr) {
 			/* merge with tag from decoder plugin */
-			Tag *tag = Tag::Merge(*decoder->decoder_tag,
-					      *decoder->stream_tag);
+			Tag *tag = Tag::Merge(*decoder.decoder_tag,
+					      *decoder.stream_tag);
 			cmd = do_send_tag(decoder, *tag);
 			delete tag;
 		} else
 			/* send only the stream tag */
-			cmd = do_send_tag(decoder, *decoder->stream_tag);
+			cmd = do_send_tag(decoder, *decoder.stream_tag);
 
 		if (cmd != DecoderCommand::NONE)
 			return cmd;
@@ -392,7 +391,7 @@ decoder_data(struct decoder *decoder,
 
 	if (dc.in_audio_format != dc.out_audio_format) {
 		Error error;
-		data = decoder->conv_state.Convert(dc.in_audio_format,
+		data = decoder.conv_state.Convert(dc.in_audio_format,
 						   data, length,
 						   dc.out_audio_format,
 						   &length,
@@ -418,7 +417,7 @@ decoder_data(struct decoder *decoder,
 		}
 
 		void *dest = chunk->Write(dc.out_audio_format,
-					  decoder->timestamp -
+					  decoder.timestamp -
 					  dc.song->start_ms / 1000.0,
 					  kbit_rate, &nbytes);
 		if (dest == nullptr) {
@@ -449,11 +448,11 @@ decoder_data(struct decoder *decoder,
 		data = (const uint8_t *)data + nbytes;
 		length -= nbytes;
 
-		decoder->timestamp += (double)nbytes /
+		decoder.timestamp += (double)nbytes /
 			dc.out_audio_format.GetTimeToSize();
 
 		if (dc.end_ms > 0 &&
-		    decoder->timestamp >= dc.end_ms / 1000.0)
+		    decoder.timestamp >= dc.end_ms / 1000.0)
 			/* the end of this range has been reached:
 			   stop decoding */
 			return DecoderCommand::STOP;
@@ -463,10 +462,10 @@ decoder_data(struct decoder *decoder,
 }
 
 DecoderCommand
-decoder_tag(gcc_unused struct decoder *decoder, struct input_stream *is,
+decoder_tag(Decoder &decoder, struct input_stream *is,
 	    Tag &&tag)
 {
-	gcc_unused const decoder_control &dc = decoder->dc;
+	gcc_unused const decoder_control &dc = decoder.dc;
 	DecoderCommand cmd;
 
 	assert(dc.state == DecoderState::DECODE);
@@ -474,8 +473,8 @@ decoder_tag(gcc_unused struct decoder *decoder, struct input_stream *is,
 
 	/* save the tag */
 
-	delete decoder->decoder_tag;
-	decoder->decoder_tag = new Tag(tag);
+	delete decoder.decoder_tag;
+	decoder.decoder_tag = new Tag(tag);
 
 	/* check for a new stream tag */
 
@@ -491,27 +490,25 @@ decoder_tag(gcc_unused struct decoder *decoder, struct input_stream *is,
 
 	/* send tag to music pipe */
 
-	if (decoder->stream_tag != nullptr) {
+	if (decoder.stream_tag != nullptr) {
 		/* merge with tag from input stream */
 		Tag *merged;
 
-		merged = Tag::Merge(*decoder->stream_tag,
-				    *decoder->decoder_tag);
+		merged = Tag::Merge(*decoder.stream_tag,
+				    *decoder.decoder_tag);
 		cmd = do_send_tag(decoder, *merged);
 		delete merged;
 	} else
 		/* send only the decoder tag */
-		cmd = do_send_tag(decoder, *decoder->decoder_tag);
+		cmd = do_send_tag(decoder, *decoder.decoder_tag);
 
 	return cmd;
 }
 
 void
-decoder_replay_gain(struct decoder *decoder,
+decoder_replay_gain(Decoder &decoder,
 		    const struct replay_gain_info *replay_gain_info)
 {
-	assert(decoder != nullptr);
-
 	if (replay_gain_info != nullptr) {
 		static unsigned serial;
 		if (++serial == 0)
@@ -522,33 +519,32 @@ decoder_replay_gain(struct decoder *decoder,
 			if (rgm != REPLAY_GAIN_ALBUM)
 				rgm = REPLAY_GAIN_TRACK;
 
-			decoder->dc.replay_gain_db = 20.0 * log10f(
+			decoder.dc.replay_gain_db = 20.0 * log10f(
 				replay_gain_tuple_scale(
 					&replay_gain_info->tuples[rgm],
 					replay_gain_preamp, replay_gain_missing_preamp,
 					replay_gain_limit));
 		}
 
-		decoder->replay_gain_info = *replay_gain_info;
-		decoder->replay_gain_serial = serial;
+		decoder.replay_gain_info = *replay_gain_info;
+		decoder.replay_gain_serial = serial;
 
-		if (decoder->chunk != nullptr) {
+		if (decoder.chunk != nullptr) {
 			/* flush the current chunk because the new
 			   replay gain values affect the following
 			   samples */
 			decoder_flush_chunk(decoder);
-			decoder->dc.client_cond.signal();
+			decoder.dc.client_cond.signal();
 		}
 	} else
-		decoder->replay_gain_serial = 0;
+		decoder.replay_gain_serial = 0;
 }
 
 void
-decoder_mixramp(struct decoder *decoder,
+decoder_mixramp(Decoder &decoder,
 		char *mixramp_start, char *mixramp_end)
 {
-	assert(decoder != nullptr);
-	decoder_control &dc = decoder->dc;
+	decoder_control &dc = decoder.dc;
 
 	dc.MixRampStart(mixramp_start);
 	dc.MixRampEnd(mixramp_end);
diff --git a/src/DecoderAPI.hxx b/src/DecoderAPI.hxx
index 2d81884c2..4b3664297 100644
--- a/src/DecoderAPI.hxx
+++ b/src/DecoderAPI.hxx
@@ -46,7 +46,7 @@
  * @param total_time the total number of seconds in this song; -1 if unknown
  */
 void
-decoder_initialized(struct decoder *decoder,
+decoder_initialized(Decoder &decoder,
 		    AudioFormat audio_format,
 		    bool seekable, float total_time);
 
@@ -58,7 +58,7 @@ decoder_initialized(struct decoder *decoder,
  * command pending
  */
 DecoderCommand
-decoder_get_command(struct decoder *decoder);
+decoder_get_command(Decoder &decoder);
 
 /**
  * Called by the decoder when it has performed the requested command
@@ -68,7 +68,7 @@ decoder_get_command(struct decoder *decoder);
  * @param decoder the decoder object
  */
 void
-decoder_command_finished(struct decoder *decoder);
+decoder_command_finished(Decoder &decoder);
 
 /**
  * Call this when you have received the DecoderCommand::SEEK command.
@@ -77,7 +77,7 @@ decoder_command_finished(struct decoder *decoder);
  * @return the destination position for the week
  */
 double
-decoder_seek_where(struct decoder *decoder);
+decoder_seek_where(Decoder &decoder);
 
 /**
  * Call this instead of decoder_command_finished() when seeking has
@@ -86,7 +86,7 @@ decoder_seek_where(struct decoder *decoder);
  * @param decoder the decoder object
  */
 void
-decoder_seek_error(struct decoder *decoder);
+decoder_seek_error(Decoder &decoder);
 
 /**
  * Blocking read from the input stream.
@@ -99,9 +99,16 @@ decoder_seek_error(struct decoder *decoder);
  * occurs: end of file; error; command (like SEEK or STOP).
  */
 size_t
-decoder_read(struct decoder *decoder, struct input_stream *is,
+decoder_read(Decoder *decoder, struct input_stream *is,
 	     void *buffer, size_t length);
 
+static inline size_t
+decoder_read(Decoder &decoder, input_stream *is,
+	     void *buffer, size_t length)
+{
+	return decoder_read(&decoder, is, buffer, length);
+}
+
 /**
  * Sets the time stamp for the next data chunk [seconds].  The MPD
  * core automatically counts it up, and a decoder plugin only needs to
@@ -109,7 +116,7 @@ decoder_read(struct decoder *decoder, struct input_stream *is,
  * on the buffer size won't work.
  */
 void
-decoder_timestamp(struct decoder *decoder, double t);
+decoder_timestamp(Decoder &decoder, double t);
 
 /**
  * This function is called by the decoder plugin when it has
@@ -124,7 +131,7 @@ decoder_timestamp(struct decoder *decoder, double t);
  * command pending
  */
 DecoderCommand
-decoder_data(struct decoder *decoder, struct input_stream *is,
+decoder_data(Decoder &decoder, struct input_stream *is,
 	     const void *data, size_t length,
 	     uint16_t kbit_rate);
 
@@ -140,7 +147,7 @@ decoder_data(struct decoder *decoder, struct input_stream *is,
  * command pending
  */
 DecoderCommand
-decoder_tag(struct decoder *decoder, struct input_stream *is, Tag &&tag);
+decoder_tag(Decoder &decoder, struct input_stream *is, Tag &&tag);
 
 /**
  * Set replay gain values for the following chunks.
@@ -150,7 +157,7 @@ decoder_tag(struct decoder *decoder, struct input_stream *is, Tag &&tag);
  * the previous replay gain values
  */
 void
-decoder_replay_gain(struct decoder *decoder,
+decoder_replay_gain(Decoder &decoder,
 		    const struct replay_gain_info *replay_gain_info);
 
 /**
@@ -161,7 +168,7 @@ decoder_replay_gain(struct decoder *decoder,
  * @param mixramp_end the mixramp_end tag; may be nullptr to invalidate
  */
 void
-decoder_mixramp(struct decoder *decoder,
+decoder_mixramp(Decoder &decoder,
 		char *mixramp_start, char *mixramp_end);
 
 #endif
diff --git a/src/DecoderBuffer.cxx b/src/DecoderBuffer.cxx
index 2125bbebd..16e6e7f7f 100644
--- a/src/DecoderBuffer.cxx
+++ b/src/DecoderBuffer.cxx
@@ -27,7 +27,7 @@
 #include <string.h>
 
 struct DecoderBuffer {
-	struct decoder *decoder;
+	Decoder *decoder;
 	struct input_stream *is;
 
 	/** the allocated size of the buffer */
@@ -45,7 +45,7 @@ struct DecoderBuffer {
 };
 
 DecoderBuffer *
-decoder_buffer_new(struct decoder *decoder, struct input_stream *is,
+decoder_buffer_new(Decoder *decoder, struct input_stream *is,
 		   size_t size)
 {
 	DecoderBuffer *buffer = (DecoderBuffer *)
diff --git a/src/DecoderBuffer.hxx b/src/DecoderBuffer.hxx
index 8411f9269..cba788d0b 100644
--- a/src/DecoderBuffer.hxx
+++ b/src/DecoderBuffer.hxx
@@ -29,7 +29,7 @@
  */
 struct DecoderBuffer;
 
-struct decoder;
+struct Decoder;
 struct input_stream;
 
 /**
@@ -41,7 +41,7 @@ struct input_stream;
  * @return the new decoder_buffer object
  */
 DecoderBuffer *
-decoder_buffer_new(struct decoder *decoder, struct input_stream *is,
+decoder_buffer_new(Decoder *decoder, struct input_stream *is,
 		   size_t size);
 
 /**
diff --git a/src/DecoderInternal.cxx b/src/DecoderInternal.cxx
index 8d89155cc..c3ce6d433 100644
--- a/src/DecoderInternal.cxx
+++ b/src/DecoderInternal.cxx
@@ -27,7 +27,7 @@
 
 #include <assert.h>
 
-decoder::~decoder()
+Decoder::~Decoder()
 {
 	/* caller must flush the chunk */
 	assert(chunk == nullptr);
@@ -59,26 +59,24 @@ need_chunks(decoder_control &dc, bool do_wait)
 }
 
 struct music_chunk *
-decoder_get_chunk(struct decoder *decoder)
+decoder_get_chunk(Decoder &decoder)
 {
-	decoder_control &dc = decoder->dc;
+	decoder_control &dc = decoder.dc;
 	DecoderCommand cmd;
 
-	assert(decoder != nullptr);
-
-	if (decoder->chunk != nullptr)
-		return decoder->chunk;
+	if (decoder.chunk != nullptr)
+		return decoder.chunk;
 
 	do {
-		decoder->chunk = dc.buffer->Allocate();
-		if (decoder->chunk != nullptr) {
-			decoder->chunk->replay_gain_serial =
-				decoder->replay_gain_serial;
-			if (decoder->replay_gain_serial != 0)
-				decoder->chunk->replay_gain_info =
-					decoder->replay_gain_info;
+		decoder.chunk = dc.buffer->Allocate();
+		if (decoder.chunk != nullptr) {
+			decoder.chunk->replay_gain_serial =
+				decoder.replay_gain_serial;
+			if (decoder.replay_gain_serial != 0)
+				decoder.chunk->replay_gain_info =
+					decoder.replay_gain_info;
 
-			return decoder->chunk;
+			return decoder.chunk;
 		}
 
 		dc.Lock();
@@ -90,17 +88,16 @@ decoder_get_chunk(struct decoder *decoder)
 }
 
 void
-decoder_flush_chunk(struct decoder *decoder)
+decoder_flush_chunk(Decoder &decoder)
 {
-	decoder_control &dc = decoder->dc;
+	decoder_control &dc = decoder.dc;
 
-	assert(decoder != nullptr);
-	assert(decoder->chunk != nullptr);
+	assert(decoder.chunk != nullptr);
 
-	if (decoder->chunk->IsEmpty())
-		dc.buffer->Return(decoder->chunk);
+	if (decoder.chunk->IsEmpty())
+		dc.buffer->Return(decoder.chunk);
 	else
-		dc.pipe->Push(decoder->chunk);
+		dc.pipe->Push(decoder.chunk);
 
-	decoder->chunk = nullptr;
+	decoder.chunk = nullptr;
 }
diff --git a/src/DecoderInternal.hxx b/src/DecoderInternal.hxx
index fa776c26f..742357d5f 100644
--- a/src/DecoderInternal.hxx
+++ b/src/DecoderInternal.hxx
@@ -28,7 +28,7 @@ struct decoder_control;
 struct input_stream;
 struct Tag;
 
-struct decoder {
+struct Decoder {
 	decoder_control &dc;
 
 	PcmConvert conv_state;
@@ -83,7 +83,7 @@ struct decoder {
 	 */
 	unsigned replay_gain_serial;
 
-	decoder(decoder_control &_dc, bool _initial_seek_pending, Tag *_tag)
+	Decoder(decoder_control &_dc, bool _initial_seek_pending, Tag *_tag)
 		:dc(_dc),
 		 timestamp(0),
 		 initial_seek_pending(_initial_seek_pending),
@@ -94,7 +94,7 @@ struct decoder {
 		 replay_gain_serial(0) {
 	}
 
-	~decoder();
+	~Decoder();
 };
 
 /**
@@ -104,12 +104,12 @@ struct decoder {
  * @return the chunk, or NULL if we have received a decoder command
  */
 struct music_chunk *
-decoder_get_chunk(struct decoder *decoder);
+decoder_get_chunk(Decoder &decoder);
 
 /**
  * Flushes the current chunk.
  */
 void
-decoder_flush_chunk(struct decoder *decoder);
+decoder_flush_chunk(Decoder &decoder);
 
 #endif
diff --git a/src/DecoderPlugin.hxx b/src/DecoderPlugin.hxx
index 7aa508c53..600d4e3b3 100644
--- a/src/DecoderPlugin.hxx
+++ b/src/DecoderPlugin.hxx
@@ -31,7 +31,7 @@ struct tag_handler;
  * Opaque handle which the decoder plugin passes to the functions in
  * this header.
  */
-struct decoder;
+struct Decoder;
 
 struct DecoderPlugin {
 	const char *name;
@@ -59,15 +59,14 @@ struct DecoderPlugin {
 	 * possible, it is recommended to implement this method,
 	 * because it is more versatile.
 	 */
-	void (*stream_decode)(struct decoder *decoder,
-			      struct input_stream *is);
+	void (*stream_decode)(Decoder &decoder, input_stream *is);
 
 	/**
 	 * Decode a local file.
 	 *
 	 * Either implement this method or stream_decode().
 	 */
-	void (*file_decode)(struct decoder *decoder, const char *path_fs);
+	void (*file_decode)(Decoder &decoder, const char *path_fs);
 
 	/**
 	 * Scan metadata of a file.
@@ -128,15 +127,15 @@ struct DecoderPlugin {
 	/**
 	 * Decode a stream.
 	 */
-	void StreamDecode(decoder &decoder, input_stream &is) const {
-		stream_decode(&decoder, &is);
+	void StreamDecode(Decoder &decoder, input_stream &is) const {
+		stream_decode(decoder, &is);
 	}
 
 	/**
 	 * Decode a file.
 	 */
-	void FileDecode(decoder &decoder, const char *path_fs) const {
-		file_decode(&decoder, path_fs);
+	void FileDecode(Decoder &decoder, const char *path_fs) const {
+		file_decode(decoder, path_fs);
 	}
 
 	/**
diff --git a/src/DecoderThread.cxx b/src/DecoderThread.cxx
index 6fe1ff746..f70b7c34f 100644
--- a/src/DecoderThread.cxx
+++ b/src/DecoderThread.cxx
@@ -112,64 +112,62 @@ decoder_input_stream_open(decoder_control &dc, const char *uri)
 
 static bool
 decoder_stream_decode(const DecoderPlugin &plugin,
-		      struct decoder *decoder,
+		      Decoder &decoder,
 		      struct input_stream *input_stream)
 {
 	assert(plugin.stream_decode != nullptr);
-	assert(decoder != nullptr);
-	assert(decoder->stream_tag == nullptr);
-	assert(decoder->decoder_tag == nullptr);
+	assert(decoder.stream_tag == nullptr);
+	assert(decoder.decoder_tag == nullptr);
 	assert(input_stream != nullptr);
 	assert(input_stream->ready);
-	assert(decoder->dc.state == DecoderState::START);
+	assert(decoder.dc.state == DecoderState::START);
 
 	FormatDebug(decoder_thread_domain, "probing plugin %s", plugin.name);
 
-	if (decoder->dc.command == DecoderCommand::STOP)
+	if (decoder.dc.command == DecoderCommand::STOP)
 		return true;
 
 	/* rewind the stream, so each plugin gets a fresh start */
 	input_stream->Seek(0, SEEK_SET, IgnoreError());
 
-	decoder->dc.Unlock();
+	decoder.dc.Unlock();
 
-	plugin.StreamDecode(*decoder, *input_stream);
+	plugin.StreamDecode(decoder, *input_stream);
 
-	decoder->dc.Lock();
+	decoder.dc.Lock();
 
-	assert(decoder->dc.state == DecoderState::START ||
-	       decoder->dc.state == DecoderState::DECODE);
+	assert(decoder.dc.state == DecoderState::START ||
+	       decoder.dc.state == DecoderState::DECODE);
 
-	return decoder->dc.state != DecoderState::START;
+	return decoder.dc.state != DecoderState::START;
 }
 
 static bool
 decoder_file_decode(const DecoderPlugin &plugin,
-		    struct decoder *decoder, const char *path)
+		    Decoder &decoder, const char *path)
 {
 	assert(plugin.file_decode != nullptr);
-	assert(decoder != nullptr);
-	assert(decoder->stream_tag == nullptr);
-	assert(decoder->decoder_tag == nullptr);
+	assert(decoder.stream_tag == nullptr);
+	assert(decoder.decoder_tag == nullptr);
 	assert(path != nullptr);
 	assert(PathTraits::IsAbsoluteFS(path));
-	assert(decoder->dc.state == DecoderState::START);
+	assert(decoder.dc.state == DecoderState::START);
 
 	FormatDebug(decoder_thread_domain, "probing plugin %s", plugin.name);
 
-	if (decoder->dc.command == DecoderCommand::STOP)
+	if (decoder.dc.command == DecoderCommand::STOP)
 		return true;
 
-	decoder->dc.Unlock();
+	decoder.dc.Unlock();
 
-	plugin.FileDecode(*decoder, path);
+	plugin.FileDecode(decoder, path);
 
-	decoder->dc.Lock();
+	decoder.dc.Lock();
 
-	assert(decoder->dc.state == DecoderState::START ||
-	       decoder->dc.state == DecoderState::DECODE);
+	assert(decoder.dc.state == DecoderState::START ||
+	       decoder.dc.state == DecoderState::DECODE);
 
-	return decoder->dc.state != DecoderState::START;
+	return decoder.dc.state != DecoderState::START;
 }
 
 /**
@@ -187,7 +185,7 @@ deconst_plugin(const struct DecoderPlugin *plugin)
  * @param tried_r a list of plugins which were tried
  */
 static bool
-decoder_run_stream_mime_type(struct decoder *decoder, struct input_stream *is,
+decoder_run_stream_mime_type(Decoder &decoder, struct input_stream *is,
 			     GSList **tried_r)
 {
 	assert(tried_r != nullptr);
@@ -223,7 +221,7 @@ decoder_run_stream_mime_type(struct decoder *decoder, struct input_stream *is,
  * @param tried_r a list of plugins which were tried
  */
 static bool
-decoder_run_stream_suffix(struct decoder *decoder, struct input_stream *is,
+decoder_run_stream_suffix(Decoder &decoder, struct input_stream *is,
 			  const char *uri, GSList **tried_r)
 {
 	assert(tried_r != nullptr);
@@ -255,7 +253,7 @@ decoder_run_stream_suffix(struct decoder *decoder, struct input_stream *is,
  * Try decoding a stream, using the fallback plugin.
  */
 static bool
-decoder_run_stream_fallback(struct decoder *decoder, struct input_stream *is)
+decoder_run_stream_fallback(Decoder &decoder, struct input_stream *is)
 {
 	const struct DecoderPlugin *plugin;
 
@@ -268,9 +266,9 @@ decoder_run_stream_fallback(struct decoder *decoder, struct input_stream *is)
  * Try decoding a stream.
  */
 static bool
-decoder_run_stream(struct decoder *decoder, const char *uri)
+decoder_run_stream(Decoder &decoder, const char *uri)
 {
-	decoder_control &dc = decoder->dc;
+	decoder_control &dc = decoder.dc;
 	struct input_stream *input_stream;
 	bool success;
 
@@ -311,7 +309,7 @@ decoder_run_stream(struct decoder *decoder, const char *uri)
  * decoder_replay_gain().
  */
 static void
-decoder_load_replay_gain(struct decoder *decoder, const char *path_fs)
+decoder_load_replay_gain(Decoder &decoder, const char *path_fs)
 {
 	struct replay_gain_info info;
 	if (replay_gain_ape_read(path_fs, &info))
@@ -322,9 +320,9 @@ decoder_load_replay_gain(struct decoder *decoder, const char *path_fs)
  * Try decoding a file.
  */
 static bool
-decoder_run_file(struct decoder *decoder, const char *path_fs)
+decoder_run_file(Decoder &decoder, const char *path_fs)
 {
-	decoder_control &dc = decoder->dc;
+	decoder_control &dc = decoder.dc;
 	const char *suffix = uri_get_suffix(path_fs);
 	const struct DecoderPlugin *plugin = nullptr;
 
@@ -375,7 +373,7 @@ static void
 decoder_run_song(decoder_control &dc,
 		 const Song *song, const char *uri)
 {
-	decoder decoder(dc, dc.start_ms > 0,
+	Decoder decoder(dc, dc.start_ms > 0,
 			song->tag != nullptr && song->IsFile()
 			? new Tag(*song->tag) : nullptr);
 	int ret;
@@ -385,15 +383,15 @@ decoder_run_song(decoder_control &dc,
 	decoder_command_finished_locked(dc);
 
 	ret = song->IsFile()
-		? decoder_run_file(&decoder, uri)
-		: decoder_run_stream(&decoder, uri);
+		? decoder_run_file(decoder, uri)
+		: decoder_run_stream(decoder, uri);
 
 	dc.Unlock();
 
 	/* flush the last chunk */
 
 	if (decoder.chunk != nullptr)
-		decoder_flush_chunk(&decoder);
+		decoder_flush_chunk(decoder);
 
 	dc.Lock();
 
diff --git a/src/decoder/AdPlugDecoderPlugin.cxx b/src/decoder/AdPlugDecoderPlugin.cxx
index bf9cb71e4..8f2ed3bd7 100644
--- a/src/decoder/AdPlugDecoderPlugin.cxx
+++ b/src/decoder/AdPlugDecoderPlugin.cxx
@@ -48,7 +48,7 @@ adplug_init(const config_param &param)
 }
 
 static void
-adplug_file_decode(struct decoder *decoder, const char *path_fs)
+adplug_file_decode(Decoder &decoder, const char *path_fs)
 {
 	CEmuopl opl(sample_rate, true, true);
 	opl.init();
diff --git a/src/decoder/AudiofileDecoderPlugin.cxx b/src/decoder/AudiofileDecoderPlugin.cxx
index 65fb14c0f..5b3ffa642 100644
--- a/src/decoder/AudiofileDecoderPlugin.cxx
+++ b/src/decoder/AudiofileDecoderPlugin.cxx
@@ -157,7 +157,7 @@ audiofile_setup_sample_format(AFfilehandle af_fp)
 }
 
 static void
-audiofile_stream_decode(struct decoder *decoder, struct input_stream *is)
+audiofile_stream_decode(Decoder &decoder, struct input_stream *is)
 {
 	AFvirtualfile *vf;
 	int fs, frame_count;
diff --git a/src/decoder/DsdLib.cxx b/src/decoder/DsdLib.cxx
index 491b158e6..ab1a132fc 100644
--- a/src/decoder/DsdLib.cxx
+++ b/src/decoder/DsdLib.cxx
@@ -51,7 +51,7 @@ dsdlib_id_equals(const struct dsdlib_id *id, const char *s)
 }
 
 bool
-dsdlib_read(struct decoder *decoder, struct input_stream *is,
+dsdlib_read(Decoder *decoder, struct input_stream *is,
 	    void *data, size_t length)
 {
 	size_t nbytes = decoder_read(decoder, is, data, length);
@@ -62,7 +62,7 @@ dsdlib_read(struct decoder *decoder, struct input_stream *is,
  * Skip the #input_stream to the specified offset.
  */
 bool
-dsdlib_skip_to(struct decoder *decoder, struct input_stream *is,
+dsdlib_skip_to(Decoder *decoder, struct input_stream *is,
 	       int64_t offset)
 {
 	if (is->IsSeekable())
@@ -90,7 +90,7 @@ dsdlib_skip_to(struct decoder *decoder, struct input_stream *is,
  * Skip some bytes from the #input_stream.
  */
 bool
-dsdlib_skip(struct decoder *decoder, struct input_stream *is,
+dsdlib_skip(Decoder *decoder, struct input_stream *is,
 	    int64_t delta)
 {
 	assert(delta >= 0);
diff --git a/src/decoder/DsdLib.hxx b/src/decoder/DsdLib.hxx
index b1c708fca..261273091 100644
--- a/src/decoder/DsdLib.hxx
+++ b/src/decoder/DsdLib.hxx
@@ -23,6 +23,8 @@
 #include <stdlib.h>
 #include <stdint.h>
 
+struct Decoder;
+
 struct dsdlib_id {
 	char value[4];
 };
@@ -31,15 +33,15 @@ bool
 dsdlib_id_equals(const struct dsdlib_id *id, const char *s);
 
 bool
-dsdlib_read(struct decoder *decoder, struct input_stream *is,
+dsdlib_read(Decoder *decoder, struct input_stream *is,
 	    void *data, size_t length);
 
 bool
-dsdlib_skip_to(struct decoder *decoder, struct input_stream *is,
+dsdlib_skip_to(Decoder *decoder, struct input_stream *is,
 	       int64_t offset);
 
 bool
-dsdlib_skip(struct decoder *decoder, struct input_stream *is,
+dsdlib_skip(Decoder *decoder, struct input_stream *is,
 	    int64_t delta);
 
 void
diff --git a/src/decoder/DsdiffDecoderPlugin.cxx b/src/decoder/DsdiffDecoderPlugin.cxx
index f5598826b..3f768f60f 100644
--- a/src/decoder/DsdiffDecoderPlugin.cxx
+++ b/src/decoder/DsdiffDecoderPlugin.cxx
@@ -91,21 +91,21 @@ dsdiff_init(const config_param &param)
 }
 
 static bool
-dsdiff_read_id(struct decoder *decoder, struct input_stream *is,
+dsdiff_read_id(Decoder *decoder, struct input_stream *is,
 	       struct dsdlib_id *id)
 {
 	return dsdlib_read(decoder, is, id, sizeof(*id));
 }
 
 static bool
-dsdiff_read_chunk_header(struct decoder *decoder, struct input_stream *is,
+dsdiff_read_chunk_header(Decoder *decoder, struct input_stream *is,
 			 DsdiffChunkHeader *header)
 {
 	return dsdlib_read(decoder, is, header, sizeof(*header));
 }
 
 static bool
-dsdiff_read_payload(struct decoder *decoder, struct input_stream *is,
+dsdiff_read_payload(Decoder *decoder, struct input_stream *is,
 		    const DsdiffChunkHeader *header,
 		    void *data, size_t length)
 {
@@ -121,7 +121,7 @@ dsdiff_read_payload(struct decoder *decoder, struct input_stream *is,
  * Read and parse a "SND" chunk inside "PROP".
  */
 static bool
-dsdiff_read_prop_snd(struct decoder *decoder, struct input_stream *is,
+dsdiff_read_prop_snd(Decoder *decoder, struct input_stream *is,
 		     DsdiffMetaData *metadata,
 		     input_stream::offset_type end_offset)
 {
@@ -179,7 +179,7 @@ dsdiff_read_prop_snd(struct decoder *decoder, struct input_stream *is,
  * Read and parse a "PROP" chunk.
  */
 static bool
-dsdiff_read_prop(struct decoder *decoder, struct input_stream *is,
+dsdiff_read_prop(Decoder *decoder, struct input_stream *is,
 		 DsdiffMetaData *metadata,
 		 const DsdiffChunkHeader *prop_header)
 {
@@ -239,7 +239,7 @@ dsdiff_handle_native_tag(struct input_stream *is,
  */
 
 static bool
-dsdiff_read_metadata_extra(struct decoder *decoder, struct input_stream *is,
+dsdiff_read_metadata_extra(Decoder *decoder, struct input_stream *is,
 			   DsdiffMetaData *metadata,
 			   DsdiffChunkHeader *chunk_header,
 			   const struct tag_handler *handler,
@@ -325,7 +325,7 @@ dsdiff_read_metadata_extra(struct decoder *decoder, struct input_stream *is,
  * "chunk_header" parameter.
  */
 static bool
-dsdiff_read_metadata(struct decoder *decoder, struct input_stream *is,
+dsdiff_read_metadata(Decoder *decoder, struct input_stream *is,
 		     DsdiffMetaData *metadata,
 		     DsdiffChunkHeader *chunk_header)
 {
@@ -371,7 +371,7 @@ bit_reverse_buffer(uint8_t *p, uint8_t *end)
  * Decode one "DSD" chunk.
  */
 static bool
-dsdiff_decode_chunk(struct decoder *decoder, struct input_stream *is,
+dsdiff_decode_chunk(Decoder &decoder, struct input_stream *is,
 		    unsigned channels,
 		    uint64_t chunk_size)
 {
@@ -418,17 +418,17 @@ dsdiff_decode_chunk(struct decoder *decoder, struct input_stream *is,
 			break;
 		}
 	}
-	return dsdlib_skip(decoder, is, chunk_size);
+	return dsdlib_skip(&decoder, is, chunk_size);
 }
 
 static void
-dsdiff_stream_decode(struct decoder *decoder, struct input_stream *is)
+dsdiff_stream_decode(Decoder &decoder, struct input_stream *is)
 {
 	DsdiffMetaData metadata;
 
 	DsdiffChunkHeader chunk_header;
 	/* check if it is is a proper DFF file */
-	if (!dsdiff_read_metadata(decoder, is, &metadata, &chunk_header))
+	if (!dsdiff_read_metadata(&decoder, is, &metadata, &chunk_header))
 		return;
 
 	Error error;
@@ -461,13 +461,13 @@ dsdiff_stream_decode(struct decoder *decoder, struct input_stream *is)
 					break;
 		} else {
 			/* ignore other chunks */
-			if (!dsdlib_skip(decoder, is, chunk_size))
+			if (!dsdlib_skip(&decoder, is, chunk_size))
 				break;
 		}
 
 		/* read next chunk header; the first one was read by
 		   dsdiff_read_metadata() */
-		if (!dsdiff_read_chunk_header(decoder,
+		if (!dsdiff_read_chunk_header(&decoder,
 					      is, &chunk_header))
 			break;
 	}
diff --git a/src/decoder/DsfDecoderPlugin.cxx b/src/decoder/DsfDecoderPlugin.cxx
index 8aa24a1f9..4b848d4a4 100644
--- a/src/decoder/DsfDecoderPlugin.cxx
+++ b/src/decoder/DsfDecoderPlugin.cxx
@@ -99,7 +99,7 @@ struct DsfDataChunk {
  * Read and parse all needed metadata chunks for DSF files.
  */
 static bool
-dsf_read_metadata(struct decoder *decoder, struct input_stream *is,
+dsf_read_metadata(Decoder *decoder, struct input_stream *is,
 		  DsfMetaData *metadata)
 {
 	uint64_t chunk_size;
@@ -219,7 +219,7 @@ dsf_to_pcm_order(uint8_t *dest, uint8_t *scratch, size_t nrbytes)
  * Decode one complete DSF 'data' chunk i.e. a complete song
  */
 static bool
-dsf_decode_chunk(struct decoder *decoder, struct input_stream *is,
+dsf_decode_chunk(Decoder &decoder, struct input_stream *is,
 		    unsigned channels,
 		    uint64_t chunk_size,
 		    bool bitreverse)
@@ -246,7 +246,7 @@ dsf_decode_chunk(struct decoder *decoder, struct input_stream *is,
 			now_size = now_frames * frame_size;
 		}
 
-		size_t nbytes = decoder_read(decoder, is, buffer, now_size);
+		size_t nbytes = decoder_read(&decoder, is, buffer, now_size);
 		if (nbytes != now_size)
 			return false;
 
@@ -273,15 +273,15 @@ dsf_decode_chunk(struct decoder *decoder, struct input_stream *is,
 			break;
 			}
 	}
-	return dsdlib_skip(decoder, is, chunk_size);
+	return dsdlib_skip(&decoder, is, chunk_size);
 }
 
 static void
-dsf_stream_decode(struct decoder *decoder, struct input_stream *is)
+dsf_stream_decode(Decoder &decoder, struct input_stream *is)
 {
 	/* check if it is a proper DSF file */
 	DsfMetaData metadata;
-	if (!dsf_read_metadata(decoder, is, &metadata))
+	if (!dsf_read_metadata(&decoder, is, &metadata))
 		return;
 
 	Error error;
diff --git a/src/decoder/FaadDecoderPlugin.cxx b/src/decoder/FaadDecoderPlugin.cxx
index 4056c73fa..05d929fb4 100644
--- a/src/decoder/FaadDecoderPlugin.cxx
+++ b/src/decoder/FaadDecoderPlugin.cxx
@@ -357,7 +357,7 @@ faad_get_file_time(struct input_stream *is)
 }
 
 static void
-faad_stream_decode(struct decoder *mpd_decoder, struct input_stream *is)
+faad_stream_decode(Decoder &mpd_decoder, struct input_stream *is)
 {
 	float total_time = 0;
 	AudioFormat audio_format;
@@ -365,7 +365,7 @@ faad_stream_decode(struct decoder *mpd_decoder, struct input_stream *is)
 	uint16_t bit_rate = 0;
 	DecoderBuffer *buffer;
 
-	buffer = decoder_buffer_new(mpd_decoder, is,
+	buffer = decoder_buffer_new(&mpd_decoder, is,
 				    FAAD_MIN_STREAMSIZE * AAC_MAX_CHANNELS);
 	total_time = faad_song_duration(buffer, is);
 
diff --git a/src/decoder/FfmpegDecoderPlugin.cxx b/src/decoder/FfmpegDecoderPlugin.cxx
index f7743180b..5092bdf9e 100644
--- a/src/decoder/FfmpegDecoderPlugin.cxx
+++ b/src/decoder/FfmpegDecoderPlugin.cxx
@@ -91,14 +91,14 @@ mpd_ffmpeg_log_callback(gcc_unused void *ptr, int level,
 }
 
 struct AvioStream {
-	struct decoder *decoder;
+	Decoder *const decoder;
 	struct input_stream *input;
 
 	AVIOContext *io;
 
 	unsigned char buffer[8192];
 
-	AvioStream(struct decoder *_decoder, input_stream *_input)
+	AvioStream(Decoder *_decoder, input_stream *_input)
 		:decoder(_decoder), input(_input), io(nullptr) {}
 
 	~AvioStream() {
@@ -255,7 +255,7 @@ copy_interleave_frame(const AVCodecContext *codec_context,
 }
 
 static DecoderCommand
-ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
+ffmpeg_send_packet(Decoder &decoder, struct input_stream *is,
 		   const AVPacket *packet,
 		   AVCodecContext *codec_context,
 		   const AVRational *time_base,
@@ -341,7 +341,7 @@ ffmpeg_sample_format(enum AVSampleFormat sample_fmt)
 }
 
 static AVInputFormat *
-ffmpeg_probe(struct decoder *decoder, struct input_stream *is)
+ffmpeg_probe(Decoder *decoder, struct input_stream *is)
 {
 	enum {
 		BUFFER_SIZE = 16384,
@@ -370,16 +370,16 @@ ffmpeg_probe(struct decoder *decoder, struct input_stream *is)
 }
 
 static void
-ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
+ffmpeg_decode(Decoder &decoder, struct input_stream *input)
 {
-	AVInputFormat *input_format = ffmpeg_probe(decoder, input);
+	AVInputFormat *input_format = ffmpeg_probe(&decoder, input);
 	if (input_format == nullptr)
 		return;
 
 	FormatDebug(ffmpeg_domain, "detected input format '%s' (%s)",
 		    input_format->name, input_format->long_name);
 
-	AvioStream stream(decoder, input);
+	AvioStream stream(&decoder, input);
 	if (!stream.Open()) {
 		LogError(ffmpeg_domain, "Failed to open stream");
 		return;
diff --git a/src/decoder/FlacCommon.cxx b/src/decoder/FlacCommon.cxx
index 6bafeb9c2..46fd9fff9 100644
--- a/src/decoder/FlacCommon.cxx
+++ b/src/decoder/FlacCommon.cxx
@@ -32,9 +32,9 @@
 
 #include <assert.h>
 
-flac_data::flac_data(struct decoder *_decoder,
+flac_data::flac_data(Decoder &_decoder,
 		     struct input_stream *_input_stream)
-	:FlacInput(_input_stream, _decoder),
+	:FlacInput(_input_stream, &_decoder),
 	 initialized(false), unsupported(false),
 	 total_frames(0), first_frame(0), next_frame(0), position(0),
 	 decoder(_decoder), input_stream(_input_stream)
diff --git a/src/decoder/FlacCommon.hxx b/src/decoder/FlacCommon.hxx
index 726e9de92..e35bceb30 100644
--- a/src/decoder/FlacCommon.hxx
+++ b/src/decoder/FlacCommon.hxx
@@ -75,12 +75,12 @@ struct flac_data : public FlacInput {
 
 	FLAC__uint64 position;
 
-	struct decoder *decoder;
+	Decoder &decoder;
 	struct input_stream *input_stream;
 
 	Tag tag;
 
-	flac_data(struct decoder *decoder, struct input_stream *input_stream);
+	flac_data(Decoder &decoder, struct input_stream *input_stream);
 };
 
 void flac_metadata_common_cb(const FLAC__StreamMetadata * block,
diff --git a/src/decoder/FlacDecoderPlugin.cxx b/src/decoder/FlacDecoderPlugin.cxx
index ff67727b7..168dfdd19 100644
--- a/src/decoder/FlacDecoderPlugin.cxx
+++ b/src/decoder/FlacDecoderPlugin.cxx
@@ -173,7 +173,7 @@ static void
 flac_decoder_loop(struct flac_data *data, FLAC__StreamDecoder *flac_dec,
 		  FLAC__uint64 t_start, FLAC__uint64 t_end)
 {
-	struct decoder *decoder = data->decoder;
+	Decoder &decoder = data->decoder;
 
 	data->first_frame = t_start;
 
@@ -255,7 +255,7 @@ stream_init(FLAC__StreamDecoder *flac_dec, struct flac_data *data, bool is_ogg)
 }
 
 static void
-flac_decode_internal(struct decoder * decoder,
+flac_decode_internal(Decoder &decoder,
 		     struct input_stream *input_stream,
 		     bool is_ogg)
 {
@@ -289,7 +289,7 @@ flac_decode_internal(struct decoder * decoder,
 }
 
 static void
-flac_decode(struct decoder * decoder, struct input_stream *input_stream)
+flac_decode(Decoder &decoder, struct input_stream *input_stream)
 {
 	flac_decode_internal(decoder, input_stream, false);
 }
@@ -333,9 +333,9 @@ oggflac_scan_stream(struct input_stream *is,
 }
 
 static void
-oggflac_decode(struct decoder *decoder, struct input_stream *input_stream)
+oggflac_decode(Decoder &decoder, struct input_stream *input_stream)
 {
-	if (ogg_codec_detect(decoder, input_stream) != OGG_CODEC_FLAC)
+	if (ogg_codec_detect(&decoder, input_stream) != OGG_CODEC_FLAC)
 		return;
 
 	/* rewind the stream, because ogg_codec_detect() has
diff --git a/src/decoder/FlacInput.cxx b/src/decoder/FlacInput.cxx
index 399e78905..39e323716 100644
--- a/src/decoder/FlacInput.cxx
+++ b/src/decoder/FlacInput.cxx
@@ -35,7 +35,7 @@ FlacInput::Read(FLAC__byte buffer[], size_t *bytes)
 	if (r == 0) {
 		if (input_stream->LockIsEOF() ||
 		    (decoder != nullptr &&
-		     decoder_get_command(decoder) != DecoderCommand::NONE))
+		     decoder_get_command(*decoder) != DecoderCommand::NONE))
 			return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
 		else
 			return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
@@ -83,8 +83,8 @@ FLAC__bool
 FlacInput::Eof()
 {
 	return (decoder != nullptr &&
-		decoder_get_command(decoder) != DecoderCommand::NONE &&
-		decoder_get_command(decoder) != DecoderCommand::SEEK) ||
+		decoder_get_command(*decoder) != DecoderCommand::NONE &&
+		decoder_get_command(*decoder) != DecoderCommand::SEEK) ||
 		input_stream->LockIsEOF();
 }
 
@@ -92,7 +92,7 @@ void
 FlacInput::Error(FLAC__StreamDecoderErrorStatus status)
 {
 	if (decoder == nullptr ||
-	    decoder_get_command(decoder) != DecoderCommand::STOP)
+	    decoder_get_command(*decoder) != DecoderCommand::STOP)
 		LogWarning(flac_domain,
 			   FLAC__StreamDecoderErrorStatusString[status]);
 }
diff --git a/src/decoder/FlacInput.hxx b/src/decoder/FlacInput.hxx
index 8fc69f960..2879f5dd4 100644
--- a/src/decoder/FlacInput.hxx
+++ b/src/decoder/FlacInput.hxx
@@ -22,18 +22,20 @@
 
 #include <FLAC/stream_decoder.h>
 
+struct Decoder;
+
 /**
  * This class wraps an #input_stream in libFLAC stream decoder
  * callbacks.
  */
 class FlacInput {
-	struct decoder *decoder;
+	Decoder *const decoder;
 
 	struct input_stream *input_stream;
 
 public:
 	FlacInput(struct input_stream *_input_stream,
-		  struct decoder *_decoder=nullptr)
+		  Decoder *_decoder=nullptr)
 		:decoder(_decoder), input_stream(_input_stream) {}
 
 protected:
diff --git a/src/decoder/FluidsynthDecoderPlugin.cxx b/src/decoder/FluidsynthDecoderPlugin.cxx
index 93d935c6c..fa946f219 100644
--- a/src/decoder/FluidsynthDecoderPlugin.cxx
+++ b/src/decoder/FluidsynthDecoderPlugin.cxx
@@ -92,7 +92,7 @@ fluidsynth_init(const config_param &param)
 }
 
 static void
-fluidsynth_file_decode(struct decoder *decoder, const char *path_fs)
+fluidsynth_file_decode(Decoder &decoder, const char *path_fs)
 {
 	char setting_sample_rate[] = "synth.sample-rate";
 	/*
diff --git a/src/decoder/GmeDecoderPlugin.cxx b/src/decoder/GmeDecoderPlugin.cxx
index 76b0eea9d..815fd8d69 100644
--- a/src/decoder/GmeDecoderPlugin.cxx
+++ b/src/decoder/GmeDecoderPlugin.cxx
@@ -130,7 +130,7 @@ gme_container_scan(const char *path_fs, const unsigned int tnum)
 }
 
 static void
-gme_file_decode(struct decoder *decoder, const char *path_fs)
+gme_file_decode(Decoder &decoder, const char *path_fs)
 {
 	char *path_container = get_container_name(path_fs);
 
diff --git a/src/decoder/MadDecoderPlugin.cxx b/src/decoder/MadDecoderPlugin.cxx
index e36bfeaac..4b25ef87b 100644
--- a/src/decoder/MadDecoderPlugin.cxx
+++ b/src/decoder/MadDecoderPlugin.cxx
@@ -139,11 +139,11 @@ struct MadDecoder {
 	bool found_first_frame;
 	bool decoded_first_frame;
 	unsigned long bit_rate;
-	struct decoder *decoder;
+	Decoder *const decoder;
 	struct input_stream *input_stream;
 	enum mad_layer layer;
 
-	MadDecoder(struct decoder *decoder, struct input_stream *input_stream);
+	MadDecoder(Decoder *decoder, struct input_stream *input_stream);
 	~MadDecoder();
 
 	bool Seek(long offset);
@@ -184,7 +184,7 @@ struct MadDecoder {
 	bool Read();
 };
 
-MadDecoder::MadDecoder(struct decoder *_decoder,
+MadDecoder::MadDecoder(Decoder *_decoder,
 		       struct input_stream *_input_stream)
 	:mute_frame(MUTEFRAME_NONE),
 	 frame_offsets(nullptr),
@@ -397,12 +397,12 @@ MadDecoder::ParseId3(size_t tagsize, Tag **mpd_tag)
 		char *mixramp_end;
 
 		if (parse_id3_replay_gain_info(&rgi, id3_tag)) {
-			decoder_replay_gain(decoder, &rgi);
+			decoder_replay_gain(*decoder, &rgi);
 			found_replay_gain = true;
 		}
 
 		if (parse_id3_mixramp(&mixramp_start, &mixramp_end, id3_tag))
-			decoder_mixramp(decoder, mixramp_start, mixramp_end);
+			decoder_mixramp(*decoder, mixramp_start, mixramp_end);
 	}
 
 	id3_tag_delete(id3_tag);
@@ -875,7 +875,7 @@ MadDecoder::DecodeFirstFrame(Tag **tag)
 				replay_gain_info_init(&rgi);
 				rgi.tuples[REPLAY_GAIN_TRACK].gain = lame.track_gain;
 				rgi.tuples[REPLAY_GAIN_TRACK].peak = lame.peak;
-				decoder_replay_gain(decoder, &rgi);
+				decoder_replay_gain(*decoder, &rgi);
 			}
 		}
 	}
@@ -979,7 +979,7 @@ MadDecoder::SendPCM(unsigned i, unsigned pcm_length)
 				       MAD_NCHANNELS(&frame.header));
 		num_samples *= MAD_NCHANNELS(&frame.header);
 
-		auto cmd = decoder_data(decoder, input_stream, output_buffer,
+		auto cmd = decoder_data(*decoder, input_stream, output_buffer,
 					sizeof(output_buffer[0]) * num_samples,
 					bit_rate / 1000);
 		if (cmd != DecoderCommand::NONE)
@@ -1065,17 +1065,17 @@ MadDecoder::Read()
 
 			assert(input_stream->IsSeekable());
 
-			j = TimeToFrame(decoder_seek_where(decoder));
+			j = TimeToFrame(decoder_seek_where(*decoder));
 			if (j < highest_frame) {
 				if (Seek(frame_offsets[j])) {
 					current_frame = j;
-					decoder_command_finished(decoder);
+					decoder_command_finished(*decoder);
 				} else
-					decoder_seek_error(decoder);
+					decoder_seek_error(*decoder);
 			} else {
-				seek_where = decoder_seek_where(decoder);
+				seek_where = decoder_seek_where(*decoder);
 				mute_frame = MUTEFRAME_SEEK;
-				decoder_command_finished(decoder);
+				decoder_command_finished(*decoder);
 			}
 		} else if (cmd != DecoderCommand::NONE)
 			return false;
@@ -1090,7 +1090,7 @@ MadDecoder::Read()
 			ret = DecodeNextFrameHeader(&tag);
 
 			if (tag != nullptr) {
-				decoder_tag(decoder, input_stream,
+				decoder_tag(*decoder, input_stream,
 					    std::move(*tag));
 				delete tag;
 			}
@@ -1116,9 +1116,9 @@ MadDecoder::Read()
 }
 
 static void
-mp3_decode(struct decoder *decoder, struct input_stream *input_stream)
+mp3_decode(Decoder &decoder, struct input_stream *input_stream)
 {
-	MadDecoder data(decoder, input_stream);
+	MadDecoder data(&decoder, input_stream);
 
 	Tag *tag = nullptr;
 	if (!data.DecodeFirstFrame(&tag)) {
diff --git a/src/decoder/MikmodDecoderPlugin.cxx b/src/decoder/MikmodDecoderPlugin.cxx
index e2d8d76d1..34381aafa 100644
--- a/src/decoder/MikmodDecoderPlugin.cxx
+++ b/src/decoder/MikmodDecoderPlugin.cxx
@@ -146,7 +146,7 @@ mikmod_decoder_finish(void)
 }
 
 static void
-mikmod_decoder_file_decode(struct decoder *decoder, const char *path_fs)
+mikmod_decoder_file_decode(Decoder &decoder, const char *path_fs)
 {
 	/* deconstify the path because libmikmod wants a non-const
 	   string pointer */
diff --git a/src/decoder/ModplugDecoderPlugin.cxx b/src/decoder/ModplugDecoderPlugin.cxx
index d8c911f8e..a86166622 100644
--- a/src/decoder/ModplugDecoderPlugin.cxx
+++ b/src/decoder/ModplugDecoderPlugin.cxx
@@ -29,6 +29,7 @@
 
 #include <libmodplug/modplug.h>
 
+
 #include <assert.h>
 
 static constexpr Domain modplug_domain("modplug");
@@ -51,7 +52,7 @@ modplug_decoder_init(const config_param &param)
 }
 
 static WritableBuffer<uint8_t>
-mod_loadfile(struct decoder *decoder, struct input_stream *is)
+mod_loadfile(Decoder *decoder, struct input_stream *is)
 {
 	const input_stream::offset_type size = is->GetSize();
 
@@ -106,7 +107,7 @@ mod_loadfile(struct decoder *decoder, struct input_stream *is)
 }
 
 static ModPlugFile *
-LoadModPlugFile(struct decoder *decoder, struct input_stream *is)
+LoadModPlugFile(Decoder *decoder, struct input_stream *is)
 {
 	const auto buffer = mod_loadfile(decoder, is);
 	if (buffer.IsNull()) {
@@ -120,7 +121,7 @@ LoadModPlugFile(struct decoder *decoder, struct input_stream *is)
 }
 
 static void
-mod_decode(struct decoder *decoder, struct input_stream *is)
+mod_decode(Decoder &decoder, struct input_stream *is)
 {
 	ModPlug_Settings settings;
 	int ret;
@@ -136,7 +137,7 @@ mod_decode(struct decoder *decoder, struct input_stream *is)
 	/* insert more setting changes here */
 	ModPlug_SetSettings(&settings);
 
-	ModPlugFile *f = LoadModPlugFile(decoder, is);
+	ModPlugFile *f = LoadModPlugFile(&decoder, is);
 	if (f == nullptr) {
 		LogWarning(modplug_domain, "could not decode stream");
 		return;
diff --git a/src/decoder/MpcdecDecoderPlugin.cxx b/src/decoder/MpcdecDecoderPlugin.cxx
index cc16ad838..1d38ec23b 100644
--- a/src/decoder/MpcdecDecoderPlugin.cxx
+++ b/src/decoder/MpcdecDecoderPlugin.cxx
@@ -36,7 +36,7 @@
 
 struct mpc_decoder_data {
 	struct input_stream *is;
-	struct decoder *decoder;
+	Decoder *decoder;
 };
 
 static constexpr Domain mpcdec_domain("mpcdec");
@@ -130,13 +130,13 @@ mpc_to_mpd_buffer(int32_t *dest, const MPC_SAMPLE_FORMAT *src,
 }
 
 static void
-mpcdec_decode(struct decoder *mpd_decoder, struct input_stream *is)
+mpcdec_decode(Decoder &mpd_decoder, struct input_stream *is)
 {
 	MPC_SAMPLE_FORMAT sample_buffer[MPC_DECODER_BUFFER_LENGTH];
 
 	struct mpc_decoder_data data;
 	data.is = is;
-	data.decoder = mpd_decoder;
+	data.decoder = &mpd_decoder;
 
 	mpc_reader reader;
 	reader.read = mpc_read_cb;
diff --git a/src/decoder/Mpg123DecoderPlugin.cxx b/src/decoder/Mpg123DecoderPlugin.cxx
index 9aa06831f..df23f7847 100644
--- a/src/decoder/Mpg123DecoderPlugin.cxx
+++ b/src/decoder/Mpg123DecoderPlugin.cxx
@@ -104,7 +104,7 @@ mpd_mpg123_open(mpg123_handle *handle, const char *path_fs,
 }
 
 static void
-mpd_mpg123_file_decode(struct decoder *decoder, const char *path_fs)
+mpd_mpg123_file_decode(Decoder &decoder, const char *path_fs)
 {
 	mpg123_handle *handle;
 	int error;
diff --git a/src/decoder/OggCodec.cxx b/src/decoder/OggCodec.cxx
index d7e5b7642..adc88188e 100644
--- a/src/decoder/OggCodec.cxx
+++ b/src/decoder/OggCodec.cxx
@@ -27,7 +27,7 @@
 #include <string.h>
 
 enum ogg_codec
-ogg_codec_detect(struct decoder *decoder, struct input_stream *is)
+ogg_codec_detect(Decoder *decoder, struct input_stream *is)
 {
 	/* oggflac detection based on code in ogg123 and this post
 	 * http://lists.xiph.org/pipermail/flac/2004-December/000393.html
diff --git a/src/decoder/OggCodec.hxx b/src/decoder/OggCodec.hxx
index eb709286b..b24b4ec5c 100644
--- a/src/decoder/OggCodec.hxx
+++ b/src/decoder/OggCodec.hxx
@@ -34,6 +34,6 @@ enum ogg_codec {
 };
 
 enum ogg_codec
-ogg_codec_detect(struct decoder *decoder, struct input_stream *is);
+ogg_codec_detect(Decoder *decoder, struct input_stream *is);
 
 #endif /* _OGG_COMMON_H */
diff --git a/src/decoder/OggSyncState.hxx b/src/decoder/OggSyncState.hxx
index eaeb9bd8c..c85b931bb 100644
--- a/src/decoder/OggSyncState.hxx
+++ b/src/decoder/OggSyncState.hxx
@@ -34,10 +34,10 @@ class OggSyncState {
 	ogg_sync_state oy;
 
 	input_stream &is;
-	struct decoder *const decoder;
+	Decoder *const decoder;
 
 public:
-	OggSyncState(input_stream &_is, struct decoder *const _decoder=nullptr)
+	OggSyncState(input_stream &_is, Decoder *const _decoder=nullptr)
 		:is(_is), decoder(_decoder) {
 		ogg_sync_init(&oy);
 	}
diff --git a/src/decoder/OggUtil.cxx b/src/decoder/OggUtil.cxx
index 0e2f48f51..294a87276 100644
--- a/src/decoder/OggUtil.cxx
+++ b/src/decoder/OggUtil.cxx
@@ -22,7 +22,7 @@
 #include "DecoderAPI.hxx"
 
 bool
-OggFeed(ogg_sync_state &oy, struct decoder *decoder,
+OggFeed(ogg_sync_state &oy, Decoder *decoder,
 	input_stream *input_stream, size_t size)
 {
 		char *buffer = ogg_sync_buffer(&oy, size);
@@ -40,7 +40,7 @@ OggFeed(ogg_sync_state &oy, struct decoder *decoder,
 
 bool
 OggExpectPage(ogg_sync_state &oy, ogg_page &page,
-	      decoder *decoder, input_stream *input_stream)
+	      Decoder *decoder, input_stream *input_stream)
 {
 	while (true) {
 		int r = ogg_sync_pageout(&oy, &page);
@@ -54,7 +54,7 @@ OggExpectPage(ogg_sync_state &oy, ogg_page &page,
 
 bool
 OggExpectFirstPage(ogg_sync_state &oy, ogg_stream_state &os,
-		   decoder *decoder, input_stream *is)
+		   Decoder *decoder, input_stream *is)
 {
 	ogg_page page;
 	if (!OggExpectPage(oy, page, decoder, is))
@@ -67,7 +67,7 @@ OggExpectFirstPage(ogg_sync_state &oy, ogg_stream_state &os,
 
 bool
 OggExpectPageIn(ogg_sync_state &oy, ogg_stream_state &os,
-		decoder *decoder, input_stream *is)
+		Decoder *decoder, input_stream *is)
 {
 	ogg_page page;
 	if (!OggExpectPage(oy, page, decoder, is))
@@ -79,7 +79,7 @@ OggExpectPageIn(ogg_sync_state &oy, ogg_stream_state &os,
 
 bool
 OggExpectPageSeek(ogg_sync_state &oy, ogg_page &page,
-		  decoder *decoder, input_stream *input_stream)
+		  Decoder *decoder, input_stream *input_stream)
 {
 	size_t remaining_skipped = 16384;
 
@@ -107,7 +107,7 @@ OggExpectPageSeek(ogg_sync_state &oy, ogg_page &page,
 
 bool
 OggExpectPageSeekIn(ogg_sync_state &oy, ogg_stream_state &os,
-		    decoder *decoder, input_stream *is)
+		    Decoder *decoder, input_stream *is)
 {
 	ogg_page page;
 	if (!OggExpectPageSeek(oy, page, decoder, is))
diff --git a/src/decoder/OggUtil.hxx b/src/decoder/OggUtil.hxx
index 324797815..77ec83f2c 100644
--- a/src/decoder/OggUtil.hxx
+++ b/src/decoder/OggUtil.hxx
@@ -27,7 +27,7 @@
 #include <stddef.h>
 
 struct input_stream;
-struct decoder;
+struct Decoder;
 
 /**
  * Feed data from the #input_stream into the #ogg_sync_state.
@@ -35,7 +35,7 @@ struct decoder;
  * @return false on error or end-of-file
  */
 bool
-OggFeed(ogg_sync_state &oy, struct decoder *decoder, input_stream *is,
+OggFeed(ogg_sync_state &oy, Decoder *decoder, input_stream *is,
 	size_t size);
 
 /**
@@ -46,7 +46,7 @@ OggFeed(ogg_sync_state &oy, struct decoder *decoder, input_stream *is,
  */
 bool
 OggExpectPage(ogg_sync_state &oy, ogg_page &page,
-	      decoder *decoder, input_stream *input_stream);
+	      Decoder *decoder, input_stream *input_stream);
 
 /**
  * Combines OggExpectPage(), ogg_stream_init() and
@@ -57,7 +57,7 @@ OggExpectPage(ogg_sync_state &oy, ogg_page &page,
  */
 bool
 OggExpectFirstPage(ogg_sync_state &oy, ogg_stream_state &os,
-		   decoder *decoder, input_stream *is);
+		   Decoder *decoder, input_stream *is);
 
 /**
  * Combines OggExpectPage() and ogg_stream_pagein().
@@ -66,14 +66,14 @@ OggExpectFirstPage(ogg_sync_state &oy, ogg_stream_state &os,
  */
 bool
 OggExpectPageIn(ogg_sync_state &oy, ogg_stream_state &os,
-		decoder *decoder, input_stream *is);
+		Decoder *decoder, input_stream *is);
 
 /**
  * Like OggExpectPage(), but allow skipping garbage (after seeking).
  */
 bool
 OggExpectPageSeek(ogg_sync_state &oy, ogg_page &page,
-		  decoder *decoder, input_stream *input_stream);
+		  Decoder *decoder, input_stream *input_stream);
 
 /**
  * Combines OggExpectPageSeek() and ogg_stream_pagein().
@@ -82,6 +82,6 @@ OggExpectPageSeek(ogg_sync_state &oy, ogg_page &page,
  */
 bool
 OggExpectPageSeekIn(ogg_sync_state &oy, ogg_stream_state &os,
-		    decoder *decoder, input_stream *is);
+		    Decoder *decoder, input_stream *is);
 
 #endif
diff --git a/src/decoder/OpusDecoderPlugin.cxx b/src/decoder/OpusDecoderPlugin.cxx
index ade611e31..48267e104 100644
--- a/src/decoder/OpusDecoderPlugin.cxx
+++ b/src/decoder/OpusDecoderPlugin.cxx
@@ -67,7 +67,7 @@ mpd_opus_init(gcc_unused const config_param &param)
 }
 
 class MPDOpusDecoder {
-	struct decoder *decoder;
+	Decoder &decoder;
 	struct input_stream *input_stream;
 
 	ogg_stream_state os;
@@ -84,7 +84,7 @@ class MPDOpusDecoder {
 	size_t frame_size;
 
 public:
-	MPDOpusDecoder(struct decoder *_decoder,
+	MPDOpusDecoder(Decoder &_decoder,
 		       struct input_stream *_input_stream)
 		:decoder(_decoder), input_stream(_input_stream),
 		 opus_decoder(nullptr),
@@ -265,10 +265,10 @@ MPDOpusDecoder::HandleAudio(const ogg_packet &packet)
 }
 
 static void
-mpd_opus_stream_decode(struct decoder *decoder,
+mpd_opus_stream_decode(Decoder &decoder,
 		       struct input_stream *input_stream)
 {
-	if (ogg_codec_detect(decoder, input_stream) != OGG_CODEC_OPUS)
+	if (ogg_codec_detect(&decoder, input_stream) != OGG_CODEC_OPUS)
 		return;
 
 	/* rewind the stream, because ogg_codec_detect() has
@@ -276,7 +276,7 @@ mpd_opus_stream_decode(struct decoder *decoder,
 	input_stream->LockSeek(0, SEEK_SET, IgnoreError());
 
 	MPDOpusDecoder d(decoder, input_stream);
-	OggSyncState oy(*input_stream, decoder);
+	OggSyncState oy(*input_stream, &decoder);
 
 	if (!d.ReadFirstPage(oy))
 		return;
diff --git a/src/decoder/PcmDecoderPlugin.cxx b/src/decoder/PcmDecoderPlugin.cxx
index 8cde45cd7..0a19ed59b 100644
--- a/src/decoder/PcmDecoderPlugin.cxx
+++ b/src/decoder/PcmDecoderPlugin.cxx
@@ -31,7 +31,7 @@
 #include <stdio.h> /* for SEEK_SET */
 
 static void
-pcm_stream_decode(struct decoder *decoder, struct input_stream *is)
+pcm_stream_decode(Decoder &decoder, struct input_stream *is)
 {
 	static constexpr AudioFormat audio_format = {
 		44100,
diff --git a/src/decoder/SidplayDecoderPlugin.cxx b/src/decoder/SidplayDecoderPlugin.cxx
index a31eee3a8..d2778867c 100644
--- a/src/decoder/SidplayDecoderPlugin.cxx
+++ b/src/decoder/SidplayDecoderPlugin.cxx
@@ -201,7 +201,7 @@ get_song_length(const char *path_fs)
 }
 
 static void
-sidplay_file_decode(struct decoder *decoder, const char *path_fs)
+sidplay_file_decode(Decoder &decoder, const char *path_fs)
 {
 	int channels;
 
diff --git a/src/decoder/SndfileDecoderPlugin.cxx b/src/decoder/SndfileDecoderPlugin.cxx
index 6f4c5bb33..5f5c256e9 100644
--- a/src/decoder/SndfileDecoderPlugin.cxx
+++ b/src/decoder/SndfileDecoderPlugin.cxx
@@ -113,7 +113,7 @@ time_to_frame(float t, const AudioFormat *audio_format)
 }
 
 static void
-sndfile_stream_decode(struct decoder *decoder, struct input_stream *is)
+sndfile_stream_decode(Decoder &decoder, struct input_stream *is)
 {
 	SNDFILE *sf;
 	SF_INFO info;
diff --git a/src/decoder/VorbisDecoderPlugin.cxx b/src/decoder/VorbisDecoderPlugin.cxx
index a7fbb9290..105ca018e 100644
--- a/src/decoder/VorbisDecoderPlugin.cxx
+++ b/src/decoder/VorbisDecoderPlugin.cxx
@@ -53,7 +53,7 @@
 #include <unistd.h>
 
 struct vorbis_input_stream {
-	struct decoder *decoder;
+	Decoder *decoder;
 
 	struct input_stream *input_stream;
 	bool seekable;
@@ -76,7 +76,8 @@ static int ogg_seek_cb(void *data, ogg_int64_t offset, int whence)
 
 	Error error;
 	return vis->seekable &&
-		(!vis->decoder || decoder_get_command(vis->decoder) != DecoderCommand::STOP) &&
+		(vis->decoder == nullptr ||
+		 decoder_get_command(*vis->decoder) != DecoderCommand::STOP) &&
 		vis->input_stream->LockSeek(offset, whence, error)
 		? 0 : -1;
 }
@@ -127,7 +128,7 @@ vorbis_strerror(int code)
 
 static bool
 vorbis_is_open(struct vorbis_input_stream *vis, OggVorbis_File *vf,
-	       struct decoder *decoder, struct input_stream *input_stream)
+	       Decoder *decoder, struct input_stream *input_stream)
 {
 	vis->decoder = decoder;
 	vis->input_stream = input_stream;
@@ -136,7 +137,7 @@ vorbis_is_open(struct vorbis_input_stream *vis, OggVorbis_File *vf,
 	int ret = ov_open_callbacks(vis, vf, NULL, 0, vorbis_is_callbacks);
 	if (ret < 0) {
 		if (decoder == NULL ||
-		    decoder_get_command(decoder) == DecoderCommand::NONE)
+		    decoder_get_command(*decoder) == DecoderCommand::NONE)
 			FormatWarning(vorbis_domain,
 				      "Failed to open Ogg Vorbis stream: %s",
 				      vorbis_strerror(ret));
@@ -147,7 +148,7 @@ vorbis_is_open(struct vorbis_input_stream *vis, OggVorbis_File *vf,
 }
 
 static void
-vorbis_send_comments(struct decoder *decoder, struct input_stream *is,
+vorbis_send_comments(Decoder &decoder, struct input_stream *is,
 		     char **comments)
 {
 	Tag *tag = vorbis_comments_to_tag(comments);
@@ -175,10 +176,10 @@ vorbis_interleave(float *dest, const float *const*src,
 
 /* public */
 static void
-vorbis_stream_decode(struct decoder *decoder,
+vorbis_stream_decode(Decoder &decoder,
 		     struct input_stream *input_stream)
 {
-	if (ogg_codec_detect(decoder, input_stream) != OGG_CODEC_VORBIS)
+	if (ogg_codec_detect(&decoder, input_stream) != OGG_CODEC_VORBIS)
 		return;
 
 	/* rewind the stream, because ogg_codec_detect() has
@@ -187,7 +188,7 @@ vorbis_stream_decode(struct decoder *decoder,
 
 	struct vorbis_input_stream vis;
 	OggVorbis_File vf;
-	if (!vorbis_is_open(&vis, &vf, decoder, input_stream))
+	if (!vorbis_is_open(&vis, &vf, &decoder, input_stream))
 		return;
 
 	const vorbis_info *vi = ov_info(&vf, -1);
diff --git a/src/decoder/WavpackDecoderPlugin.cxx b/src/decoder/WavpackDecoderPlugin.cxx
index 098436c4f..9805a5f78 100644
--- a/src/decoder/WavpackDecoderPlugin.cxx
+++ b/src/decoder/WavpackDecoderPlugin.cxx
@@ -138,7 +138,7 @@ wavpack_bits_to_sample_format(bool is_float, int bytes_per_sample)
  * Requires an already opened WavpackContext.
  */
 static void
-wavpack_decode(struct decoder *decoder, WavpackContext *wpc, bool can_seek)
+wavpack_decode(Decoder &decoder, WavpackContext *wpc, bool can_seek)
 {
 	bool is_float;
 	SampleFormat sample_format;
@@ -345,7 +345,7 @@ wavpack_scan_file(const char *fname,
 
 /* This struct is needed for per-stream last_byte storage. */
 struct wavpack_input {
-	struct decoder *decoder;
+	Decoder *decoder;
 	struct input_stream *is;
 	/* Needed for push_back_byte() */
 	int last_byte;
@@ -449,16 +449,16 @@ static WavpackStreamReader mpd_is_reader = {
 };
 
 static void
-wavpack_input_init(struct wavpack_input *isp, struct decoder *decoder,
+wavpack_input_init(struct wavpack_input *isp, Decoder &decoder,
 		   struct input_stream *is)
 {
-	isp->decoder = decoder;
+	isp->decoder = &decoder;
 	isp->is = is;
 	isp->last_byte = EOF;
 }
 
 static struct input_stream *
-wavpack_open_wvc(struct decoder *decoder, const char *uri,
+wavpack_open_wvc(Decoder &decoder, const char *uri,
 		 Mutex &mutex, Cond &cond,
 		 struct wavpack_input *wpi)
 {
@@ -504,7 +504,7 @@ wavpack_open_wvc(struct decoder *decoder, const char *uri,
  * Decodes a stream.
  */
 static void
-wavpack_streamdecode(struct decoder * decoder, struct input_stream *is)
+wavpack_streamdecode(Decoder & decoder, struct input_stream *is)
 {
 	char error[ERRORLEN];
 	WavpackContext *wpc;
@@ -550,7 +550,7 @@ wavpack_streamdecode(struct decoder * decoder, struct input_stream *is)
  * Decodes a file.
  */
 static void
-wavpack_filedecode(struct decoder *decoder, const char *fname)
+wavpack_filedecode(Decoder &decoder, const char *fname)
 {
 	char error[ERRORLEN];
 	WavpackContext *wpc;
diff --git a/src/decoder/WildmidiDecoderPlugin.cxx b/src/decoder/WildmidiDecoderPlugin.cxx
index 0b2ffb6c3..3da3f1387 100644
--- a/src/decoder/WildmidiDecoderPlugin.cxx
+++ b/src/decoder/WildmidiDecoderPlugin.cxx
@@ -65,7 +65,7 @@ wildmidi_finish(void)
 }
 
 static void
-wildmidi_file_decode(struct decoder *decoder, const char *path_fs)
+wildmidi_file_decode(Decoder &decoder, const char *path_fs)
 {
 	static constexpr AudioFormat audio_format = {
 		WILDMIDI_SAMPLE_RATE,
diff --git a/test/dump_playlist.cxx b/test/dump_playlist.cxx
index 90cbc4564..6233443c2 100644
--- a/test/dump_playlist.cxx
+++ b/test/dump_playlist.cxx
@@ -54,7 +54,7 @@ my_log_func(const gchar *log_domain, gcc_unused GLogLevelFlags log_level,
 }
 
 void
-decoder_initialized(gcc_unused struct decoder *decoder,
+decoder_initialized(gcc_unused Decoder &decoder,
 		    gcc_unused const AudioFormat audio_format,
 		    gcc_unused bool seekable,
 		    gcc_unused float total_time)
@@ -62,29 +62,29 @@ decoder_initialized(gcc_unused struct decoder *decoder,
 }
 
 DecoderCommand
-decoder_get_command(gcc_unused struct decoder *decoder)
+decoder_get_command(gcc_unused Decoder &decoder)
 {
 	return DecoderCommand::NONE;
 }
 
 void
-decoder_command_finished(gcc_unused struct decoder *decoder)
+decoder_command_finished(gcc_unused Decoder &decoder)
 {
 }
 
 double
-decoder_seek_where(gcc_unused struct decoder *decoder)
+decoder_seek_where(gcc_unused Decoder &decoder)
 {
 	return 1.0;
 }
 
 void
-decoder_seek_error(gcc_unused struct decoder *decoder)
+decoder_seek_error(gcc_unused Decoder &decoder)
 {
 }
 
 size_t
-decoder_read(gcc_unused struct decoder *decoder,
+decoder_read(gcc_unused Decoder *decoder,
 	     struct input_stream *is,
 	     void *buffer, size_t length)
 {
@@ -93,13 +93,13 @@ decoder_read(gcc_unused struct decoder *decoder,
 }
 
 void
-decoder_timestamp(gcc_unused struct decoder *decoder,
+decoder_timestamp(gcc_unused Decoder &decoder,
 		  gcc_unused double t)
 {
 }
 
 DecoderCommand
-decoder_data(gcc_unused struct decoder *decoder,
+decoder_data(gcc_unused Decoder &decoder,
 	     gcc_unused struct input_stream *is,
 	     const void *data, size_t datalen,
 	     gcc_unused uint16_t kbit_rate)
@@ -109,7 +109,7 @@ decoder_data(gcc_unused struct decoder *decoder,
 }
 
 DecoderCommand
-decoder_tag(gcc_unused struct decoder *decoder,
+decoder_tag(gcc_unused Decoder &decoder,
 	    gcc_unused struct input_stream *is,
 	    gcc_unused Tag &&tag)
 {
@@ -117,7 +117,7 @@ decoder_tag(gcc_unused struct decoder *decoder,
 }
 
 void
-decoder_replay_gain(gcc_unused struct decoder *decoder,
+decoder_replay_gain(gcc_unused Decoder &decoder,
 		    const struct replay_gain_info *replay_gain_info)
 {
 	const struct replay_gain_tuple *tuple =
@@ -133,7 +133,7 @@ decoder_replay_gain(gcc_unused struct decoder *decoder,
 }
 
 void
-decoder_mixramp(gcc_unused struct decoder *decoder,
+decoder_mixramp(gcc_unused Decoder &decoder,
 		char *mixramp_start, char *mixramp_end)
 {
 	g_free(mixramp_start);
diff --git a/test/read_tags.cxx b/test/read_tags.cxx
index a3d48be47..449557597 100644
--- a/test/read_tags.cxx
+++ b/test/read_tags.cxx
@@ -42,7 +42,7 @@
 #endif
 
 void
-decoder_initialized(gcc_unused struct decoder *decoder,
+decoder_initialized(gcc_unused Decoder &decoder,
 		    gcc_unused const AudioFormat audio_format,
 		    gcc_unused bool seekable,
 		    gcc_unused float total_time)
@@ -50,26 +50,29 @@ decoder_initialized(gcc_unused struct decoder *decoder,
 }
 
 DecoderCommand
-decoder_get_command(gcc_unused struct decoder *decoder)
+decoder_get_command(gcc_unused Decoder &decoder)
 {
 	return DecoderCommand::NONE;
 }
 
-void decoder_command_finished(gcc_unused struct decoder *decoder)
+void
+decoder_command_finished(gcc_unused Decoder &decoder)
 {
 }
 
-double decoder_seek_where(gcc_unused struct decoder *decoder)
+double
+decoder_seek_where(gcc_unused Decoder &decoder)
 {
 	return 1.0;
 }
 
-void decoder_seek_error(gcc_unused struct decoder *decoder)
+void
+decoder_seek_error(gcc_unused Decoder &decoder)
 {
 }
 
 size_t
-decoder_read(gcc_unused struct decoder *decoder,
+decoder_read(gcc_unused Decoder *decoder,
 	     struct input_stream *is,
 	     void *buffer, size_t length)
 {
@@ -78,23 +81,23 @@ decoder_read(gcc_unused struct decoder *decoder,
 }
 
 void
-decoder_timestamp(gcc_unused struct decoder *decoder,
+decoder_timestamp(gcc_unused Decoder &decoder,
 		  gcc_unused double t)
 {
 }
 
 DecoderCommand
-decoder_data(gcc_unused struct decoder *decoder,
+decoder_data(gcc_unused Decoder &decoder,
 	     gcc_unused struct input_stream *is,
 	     const void *data, size_t datalen,
-	     gcc_unused uint16_t bit_rate)
+	     gcc_unused uint16_t kbit_rate)
 {
 	gcc_unused ssize_t nbytes = write(1, data, datalen);
 	return DecoderCommand::NONE;
 }
 
 DecoderCommand
-decoder_tag(gcc_unused struct decoder *decoder,
+decoder_tag(gcc_unused Decoder &decoder,
 	    gcc_unused struct input_stream *is,
 	    gcc_unused Tag &&tag)
 {
@@ -102,13 +105,13 @@ decoder_tag(gcc_unused struct decoder *decoder,
 }
 
 void
-decoder_replay_gain(gcc_unused struct decoder *decoder,
+decoder_replay_gain(gcc_unused Decoder &decoder,
 		    gcc_unused const struct replay_gain_info *replay_gain_info)
 {
 }
 
 void
-decoder_mixramp(gcc_unused struct decoder *decoder,
+decoder_mixramp(gcc_unused Decoder &decoder,
 		char *mixramp_start, char *mixramp_end)
 {
 	g_free(mixramp_start);
diff --git a/test/run_decoder.cxx b/test/run_decoder.cxx
index cd61d3beb..99a312f3d 100644
--- a/test/run_decoder.cxx
+++ b/test/run_decoder.cxx
@@ -45,7 +45,7 @@ my_log_func(const gchar *log_domain, gcc_unused GLogLevelFlags log_level,
 		g_printerr("%s\n", message);
 }
 
-struct decoder {
+struct Decoder {
 	const char *uri;
 
 	const struct DecoderPlugin *plugin;
@@ -54,43 +54,46 @@ struct decoder {
 };
 
 void
-decoder_initialized(struct decoder *decoder,
+decoder_initialized(Decoder &decoder,
 		    const AudioFormat audio_format,
 		    gcc_unused bool seekable,
 		    gcc_unused float total_time)
 {
 	struct audio_format_string af_string;
 
-	assert(!decoder->initialized);
+	assert(!decoder.initialized);
 	assert(audio_format.IsValid());
 
 	g_printerr("audio_format=%s\n",
 		   audio_format_to_string(audio_format, &af_string));
 
-	decoder->initialized = true;
+	decoder.initialized = true;
 }
 
 DecoderCommand
-decoder_get_command(gcc_unused struct decoder *decoder)
+decoder_get_command(gcc_unused Decoder &decoder)
 {
 	return DecoderCommand::NONE;
 }
 
-void decoder_command_finished(gcc_unused struct decoder *decoder)
+void
+decoder_command_finished(gcc_unused Decoder &decoder)
 {
 }
 
-double decoder_seek_where(gcc_unused struct decoder *decoder)
+double
+decoder_seek_where(gcc_unused Decoder &decoder)
 {
 	return 1.0;
 }
 
-void decoder_seek_error(gcc_unused struct decoder *decoder)
+void
+decoder_seek_error(gcc_unused Decoder &decoder)
 {
 }
 
 size_t
-decoder_read(gcc_unused struct decoder *decoder,
+decoder_read(gcc_unused Decoder *decoder,
 	     struct input_stream *is,
 	     void *buffer, size_t length)
 {
@@ -98,13 +101,13 @@ decoder_read(gcc_unused struct decoder *decoder,
 }
 
 void
-decoder_timestamp(gcc_unused struct decoder *decoder,
+decoder_timestamp(gcc_unused Decoder &decoder,
 		  gcc_unused double t)
 {
 }
 
 DecoderCommand
-decoder_data(gcc_unused struct decoder *decoder,
+decoder_data(gcc_unused Decoder &decoder,
 	     gcc_unused struct input_stream *is,
 	     const void *data, size_t datalen,
 	     gcc_unused uint16_t kbit_rate)
@@ -114,7 +117,7 @@ decoder_data(gcc_unused struct decoder *decoder,
 }
 
 DecoderCommand
-decoder_tag(gcc_unused struct decoder *decoder,
+decoder_tag(gcc_unused Decoder &decoder,
 	    gcc_unused struct input_stream *is,
 	    gcc_unused Tag &&tag)
 {
@@ -122,7 +125,7 @@ decoder_tag(gcc_unused struct decoder *decoder,
 }
 
 void
-decoder_replay_gain(gcc_unused struct decoder *decoder,
+decoder_replay_gain(gcc_unused Decoder &decoder,
 		    const struct replay_gain_info *replay_gain_info)
 {
 	const struct replay_gain_tuple *tuple =
@@ -138,7 +141,7 @@ decoder_replay_gain(gcc_unused struct decoder *decoder,
 }
 
 void
-decoder_mixramp(gcc_unused struct decoder *decoder,
+decoder_mixramp(gcc_unused Decoder &decoder,
 		char *mixramp_start, char *mixramp_end)
 {
 	g_free(mixramp_start);
@@ -148,13 +151,13 @@ decoder_mixramp(gcc_unused struct decoder *decoder,
 int main(int argc, char **argv)
 {
 	const char *decoder_name;
-	struct decoder decoder;
 
 	if (argc != 3) {
 		g_printerr("Usage: run_decoder DECODER URI >OUT\n");
 		return 1;
 	}
 
+	Decoder decoder;
 	decoder_name = argv[1];
 	decoder.uri = argv[2];