From c412d6251e9cd3abe735b7622af4003502e54f72 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@duempel.org>
Date: Tue, 10 Nov 2009 17:11:34 +0100
Subject: audio_format: changed "bits" to "enum sample_format"

This patch prepares support for floating point samples (and probably
other formats).  It changes the meaning of the "bits" attribute from a
bit count to a symbolic value.
---
 src/audio_check.c                    |  4 +-
 src/audio_check.h                    |  6 +--
 src/audio_format.c                   | 29 +++++++++++-
 src/audio_format.h                   | 86 ++++++++++++++++++++++++++++--------
 src/audio_parser.c                   | 42 +++++++++++++++---
 src/decoder/_flac_common.c           | 27 ++++++++++-
 src/decoder/_flac_common.h           |  2 +
 src/decoder/audiofile_plugin.c       | 26 +++++++++--
 src/decoder/faad_plugin.c            |  2 +-
 src/decoder/ffmpeg_plugin.c          | 30 +++++++++----
 src/decoder/flac_pcm.c               | 16 ++++---
 src/decoder/flac_pcm.h               |  4 +-
 src/decoder/fluidsynth_plugin.c      |  2 +-
 src/decoder/mad_plugin.c             |  3 +-
 src/decoder/mikmod_plugin.c          |  2 +-
 src/decoder/modplug_plugin.c         |  2 +-
 src/decoder/mp4ff_plugin.c           |  3 +-
 src/decoder/mpcdec_plugin.c          |  3 +-
 src/decoder/mpg123_decoder_plugin.c  |  2 +-
 src/decoder/sidplay_plugin.cxx       |  2 +-
 src/decoder/sndfile_decoder_plugin.c |  3 +-
 src/decoder/vorbis_plugin.c          |  3 +-
 src/decoder/wavpack_plugin.c         | 47 +++++++++++++++-----
 src/decoder/wildmidi_plugin.c        |  2 +-
 src/encoder/flac_encoder.c           | 44 +++++++++++++-----
 src/encoder/lame_encoder.c           |  2 +-
 src/encoder/twolame_encoder.c        |  2 +-
 src/encoder/vorbis_encoder.c         |  2 +-
 src/encoder/wave_encoder.c           | 29 ++++++++++--
 src/filter/volume_filter_plugin.c    |  5 ++-
 src/normalize.c                      |  3 +-
 src/output/alsa_plugin.c             | 73 ++++++++++++++++++------------
 src/output/ao_plugin.c               | 23 +++++++---
 src/output/jack_output_plugin.c      | 11 ++---
 src/output/mvp_plugin.c              | 12 ++---
 src/output/openal_plugin.c           | 20 +++++----
 src/output/oss_plugin.c              |  9 ++--
 src/output/osx_plugin.c              | 19 ++++++--
 src/output/pulse_output_plugin.c     |  2 +-
 src/output/solaris_output_plugin.c   |  4 +-
 src/pcm_convert.c                    | 36 +++++++--------
 src/pcm_format.c                     | 45 +++++++++++--------
 src/pcm_format.h                     |  8 ++--
 src/pcm_mix.c                        | 11 ++---
 src/pcm_volume.c                     |  8 ++--
 45 files changed, 506 insertions(+), 210 deletions(-)

(limited to 'src')

diff --git a/src/audio_check.c b/src/audio_check.c
index 4046e8862..a843975fa 100644
--- a/src/audio_check.c
+++ b/src/audio_check.c
@@ -35,7 +35,7 @@ audio_check_sample_rate(unsigned long sample_rate, GError **error_r)
 }
 
 bool
-audio_check_sample_format(unsigned sample_format, GError **error_r)
+audio_check_sample_format(enum sample_format sample_format, GError **error_r)
 {
 	if (!audio_valid_sample_format(sample_format)) {
 		g_set_error(error_r, audio_format_quark(), 0,
@@ -60,7 +60,7 @@ audio_check_channel_count(unsigned channels, GError **error_r)
 
 bool
 audio_format_init_checked(struct audio_format *af, unsigned long sample_rate,
-			  unsigned sample_format, unsigned channels,
+			  enum sample_format sample_format, unsigned channels,
 			  GError **error_r)
 {
 	if (audio_check_sample_rate(sample_rate, error_r) &&
diff --git a/src/audio_check.h b/src/audio_check.h
index a11fbe3d3..197dedd48 100644
--- a/src/audio_check.h
+++ b/src/audio_check.h
@@ -20,11 +20,11 @@
 #ifndef MPD_AUDIO_CHECK_H
 #define MPD_AUDIO_CHECK_H
 
+#include "audio_format.h"
+
 #include <glib.h>
 #include <stdbool.h>
 
-struct audio_format;
-
 /**
  * The GLib quark used for errors reported by this library.
  */
@@ -48,7 +48,7 @@ audio_check_channel_count(unsigned sample_format, GError **error_r);
  */
 bool
 audio_format_init_checked(struct audio_format *af, unsigned long sample_rate,
-			  unsigned sample_format, unsigned channels,
+			  enum sample_format sample_format, unsigned channels,
 			  GError **error_r);
 
 #endif
diff --git a/src/audio_format.c b/src/audio_format.c
index f88735c73..33cd90f58 100644
--- a/src/audio_format.c
+++ b/src/audio_format.c
@@ -28,6 +28,31 @@
 #define REVERSE_ENDIAN_SUFFIX "_be"
 #endif
 
+const char *
+sample_format_to_string(enum sample_format format)
+{
+	switch (format) {
+	case SAMPLE_FORMAT_UNDEFINED:
+		return "?";
+
+	case SAMPLE_FORMAT_S8:
+		return "8";
+
+	case SAMPLE_FORMAT_S16:
+		return "16";
+
+	case SAMPLE_FORMAT_S24_P32:
+		return "24";
+
+	case SAMPLE_FORMAT_S32:
+		return "32";
+	}
+
+	/* unreachable */
+	assert(false);
+	return "?";
+}
+
 const char *
 audio_format_to_string(const struct audio_format *af,
 		       struct audio_format_string *s)
@@ -35,8 +60,8 @@ audio_format_to_string(const struct audio_format *af,
 	assert(af != NULL);
 	assert(s != NULL);
 
-	snprintf(s->buffer, sizeof(s->buffer), "%u:%u%s:%u",
-		 af->sample_rate, af->bits,
+	snprintf(s->buffer, sizeof(s->buffer), "%u:%s%s:%u",
+		 af->sample_rate, sample_format_to_string(af->format),
 		 af->reverse_endian ? REVERSE_ENDIAN_SUFFIX : "",
 		 af->channels);
 
diff --git a/src/audio_format.h b/src/audio_format.h
index 0c1e425a9..6e7d50c46 100644
--- a/src/audio_format.h
+++ b/src/audio_format.h
@@ -23,6 +23,21 @@
 #include <stdint.h>
 #include <stdbool.h>
 
+enum sample_format {
+	SAMPLE_FORMAT_UNDEFINED = 0,
+
+	SAMPLE_FORMAT_S8,
+	SAMPLE_FORMAT_S16,
+
+	/**
+	 * Signed 24 bit integer samples, packed in 32 bit integers
+	 * (the most significant byte is filled with the sign bit).
+	 */
+	SAMPLE_FORMAT_S24_P32,
+
+	SAMPLE_FORMAT_S32,
+};
+
 /**
  * This structure describes the format of a raw PCM stream.
  */
@@ -35,11 +50,10 @@ struct audio_format {
 	uint32_t sample_rate;
 
 	/**
-	 * The number of significant bits per sample.  Samples are
-	 * currently always signed.  Supported values are 8, 16, 24,
-	 * 32.  24 bit samples are packed in 32 bit integers.
+	 * The format samples are stored in.  See the #sample_format
+	 * enum for valid values.
 	 */
-	uint8_t bits;
+	uint8_t format;
 
 	/**
 	 * The number of channels.  Only mono (1) and stereo (2) are
@@ -69,7 +83,7 @@ struct audio_format_string {
 static inline void audio_format_clear(struct audio_format *af)
 {
 	af->sample_rate = 0;
-	af->bits = 0;
+	af->format = SAMPLE_FORMAT_UNDEFINED;
 	af->channels = 0;
 	af->reverse_endian = 0;
 }
@@ -80,10 +94,10 @@ static inline void audio_format_clear(struct audio_format *af)
  */
 static inline void audio_format_init(struct audio_format *af,
 				     uint32_t sample_rate,
-				     uint8_t bits, uint8_t channels)
+				     enum sample_format format, uint8_t channels)
 {
 	af->sample_rate = sample_rate;
-	af->bits = bits;
+	af->format = (uint8_t)format;
 	af->channels = channels;
 	af->reverse_endian = 0;
 }
@@ -105,7 +119,8 @@ static inline bool audio_format_defined(const struct audio_format *af)
 static inline bool
 audio_format_fully_defined(const struct audio_format *af)
 {
-	return af->sample_rate != 0 && af->bits != 0 && af->channels != 0;
+	return af->sample_rate != 0 && af->format != SAMPLE_FORMAT_UNDEFINED &&
+		af->channels != 0;
 }
 
 /**
@@ -115,7 +130,8 @@ audio_format_fully_defined(const struct audio_format *af)
 static inline bool
 audio_format_mask_defined(const struct audio_format *af)
 {
-	return af->sample_rate != 0 || af->bits != 0 || af->channels != 0;
+	return af->sample_rate != 0 || af->format != SAMPLE_FORMAT_UNDEFINED ||
+		af->channels != 0;
 }
 
 /**
@@ -135,9 +151,20 @@ audio_valid_sample_rate(unsigned sample_rate)
  * @param bits the number of significant bits per sample
  */
 static inline bool
-audio_valid_sample_format(unsigned bits)
+audio_valid_sample_format(enum sample_format format)
 {
-	return bits == 16 || bits == 24 || bits == 32 || bits == 8;
+	switch (format) {
+	case SAMPLE_FORMAT_S8:
+	case SAMPLE_FORMAT_S16:
+	case SAMPLE_FORMAT_S24_P32:
+	case SAMPLE_FORMAT_S32:
+		return true;
+
+	case SAMPLE_FORMAT_UNDEFINED:
+		break;
+	}
+
+	return false;
 }
 
 /**
@@ -156,7 +183,7 @@ audio_valid_channel_count(unsigned channels)
 static inline bool audio_format_valid(const struct audio_format *af)
 {
 	return audio_valid_sample_rate(af->sample_rate) &&
-		audio_valid_sample_format(af->bits) &&
+		audio_valid_sample_format((enum sample_format)af->format) &&
 		audio_valid_channel_count(af->channels);
 }
 
@@ -168,7 +195,8 @@ static inline bool audio_format_mask_valid(const struct audio_format *af)
 {
 	return (af->sample_rate == 0 ||
 		audio_valid_sample_rate(af->sample_rate)) &&
-		(af->bits == 0 || audio_valid_sample_format(af->bits)) &&
+		(af->format == SAMPLE_FORMAT_UNDEFINED ||
+		 audio_valid_sample_format((enum sample_format)af->format)) &&
 		(af->channels == 0 || audio_valid_channel_count(af->channels));
 }
 
@@ -176,7 +204,7 @@ static inline bool audio_format_equals(const struct audio_format *a,
 				       const struct audio_format *b)
 {
 	return a->sample_rate == b->sample_rate &&
-		a->bits == b->bits &&
+		a->format == b->format &&
 		a->channels == b->channels &&
 		a->reverse_endian == b->reverse_endian;
 }
@@ -188,8 +216,8 @@ audio_format_mask_apply(struct audio_format *af,
 	if (mask->sample_rate != 0)
 		af->sample_rate = mask->sample_rate;
 
-	if (mask->bits != 0)
-		af->bits = mask->bits;
+	if (mask->format != SAMPLE_FORMAT_UNDEFINED)
+		af->format = mask->format;
 
 	if (mask->channels != 0)
 		af->channels = mask->channels;
@@ -200,12 +228,22 @@ audio_format_mask_apply(struct audio_format *af,
  */
 static inline unsigned audio_format_sample_size(const struct audio_format *af)
 {
-	if (af->bits <= 8)
+	switch (af->format) {
+	case SAMPLE_FORMAT_S8:
 		return 1;
-	else if (af->bits <= 16)
+
+	case SAMPLE_FORMAT_S16:
 		return 2;
-	else
+
+	case SAMPLE_FORMAT_S24_P32:
+	case SAMPLE_FORMAT_S32:
 		return 4;
+
+	case SAMPLE_FORMAT_UNDEFINED:
+		break;
+	}
+
+	return 0;
 }
 
 /**
@@ -226,6 +264,16 @@ static inline double audio_format_time_to_size(const struct audio_format *af)
 	return af->sample_rate * audio_format_frame_size(af);
 }
 
+/**
+ * Renders a #sample_format enum into a string, e.g. for printing it
+ * in a log file.
+ *
+ * @param format a #sample_format enum value
+ * @return the string
+ */
+const char *
+sample_format_to_string(enum sample_format format);
+
 /**
  * Renders the #audio_format object into a string, e.g. for printing
  * it in a log file.
diff --git a/src/audio_parser.c b/src/audio_parser.c
index df87be325..210ea7a62 100644
--- a/src/audio_parser.c
+++ b/src/audio_parser.c
@@ -27,6 +27,7 @@
 #include "audio_format.h"
 #include "audio_check.h"
 
+#include <assert.h>
 #include <stdlib.h>
 
 /**
@@ -65,14 +66,16 @@ parse_sample_rate(const char *src, bool mask, uint32_t *sample_rate_r,
 }
 
 static bool
-parse_sample_format(const char *src, bool mask, uint8_t *bits_r,
+parse_sample_format(const char *src, bool mask,
+		    enum sample_format *sample_format_r,
 		    const char **endptr_r, GError **error_r)
 {
 	unsigned long value;
 	char *endptr;
+	enum sample_format sample_format;
 
 	if (mask && *src == '*') {
-		*bits_r = 0;
+		*sample_format_r = SAMPLE_FORMAT_UNDEFINED;
 		*endptr_r = src + 1;
 		return true;
 	}
@@ -82,10 +85,34 @@ parse_sample_format(const char *src, bool mask, uint8_t *bits_r,
 		g_set_error(error_r, audio_parser_quark(), 0,
 			    "Failed to parse the sample format");
 		return false;
-	} else if (!audio_check_sample_format(value, error_r))
+	}
+
+	switch (value) {
+	case 8:
+		sample_format = SAMPLE_FORMAT_S8;
+		break;
+
+	case 16:
+		sample_format = SAMPLE_FORMAT_S16;
+		break;
+
+	case 24:
+		sample_format = SAMPLE_FORMAT_S24_P32;
+		break;
+
+	case 32:
+		sample_format = SAMPLE_FORMAT_S32;
+		break;
+
+	default:
+		g_set_error(error_r, audio_parser_quark(), 0,
+			    "Invalid sample format: %lu", value);
 		return false;
+	}
+
+	assert(audio_valid_sample_format(sample_format));
 
-	*bits_r = value;
+	*sample_format_r = sample_format;
 	*endptr_r = endptr;
 	return true;
 }
@@ -121,7 +148,8 @@ audio_format_parse(struct audio_format *dest, const char *src,
 		   bool mask, GError **error_r)
 {
 	uint32_t rate;
-	uint8_t bits, channels;
+	enum sample_format sample_format;
+	uint8_t channels;
 
 	audio_format_clear(dest);
 
@@ -138,7 +166,7 @@ audio_format_parse(struct audio_format *dest, const char *src,
 
 	/* parse sample format */
 
-	if (!parse_sample_format(src, mask, &bits, &src, error_r))
+	if (!parse_sample_format(src, mask, &sample_format, &src, error_r))
 		return false;
 
 	if (*src++ != ':') {
@@ -158,7 +186,7 @@ audio_format_parse(struct audio_format *dest, const char *src,
 		return false;
 	}
 
-	audio_format_init(dest, rate, bits, channels);
+	audio_format_init(dest, rate, sample_format, channels);
 
 	return true;
 }
diff --git a/src/decoder/_flac_common.c b/src/decoder/_flac_common.c
index f12b8bff0..70b2c0202 100644
--- a/src/decoder/_flac_common.c
+++ b/src/decoder/_flac_common.c
@@ -60,6 +60,27 @@ flac_data_deinit(struct flac_data *data)
 		tag_free(data->tag);
 }
 
+static enum sample_format
+flac_sample_format(const FLAC__StreamMetadata_StreamInfo *si)
+{
+	switch (si->bits_per_sample) {
+	case 8:
+		return SAMPLE_FORMAT_S8;
+
+	case 16:
+		return SAMPLE_FORMAT_S16;
+
+	case 24:
+		return SAMPLE_FORMAT_S24_P32;
+
+	case 32:
+		return SAMPLE_FORMAT_S32;
+
+	default:
+		return SAMPLE_FORMAT_UNDEFINED;
+	}
+}
+
 bool
 flac_data_get_audio_format(struct flac_data *data,
 			   struct audio_format *audio_format)
@@ -71,9 +92,11 @@ flac_data_get_audio_format(struct flac_data *data,
 		return false;
 	}
 
+	data->sample_format = flac_sample_format(&data->stream_info);
+
 	if (!audio_format_init_checked(audio_format,
 				       data->stream_info.sample_rate,
-				       data->stream_info.bits_per_sample,
+				       data->sample_format,
 				       data->stream_info.channels, &error)) {
 		g_warning("%s", error->message);
 		g_error_free(error);
@@ -144,7 +167,7 @@ flac_common_write(struct flac_data *data, const FLAC__Frame * frame,
 	buffer = pcm_buffer_get(&data->buffer, buffer_size);
 
 	flac_convert(buffer, frame->header.channels,
-		     frame->header.bits_per_sample, buf,
+		     data->sample_format, buf,
 		     0, frame->header.blocksize);
 
 	if (data->next_frame >= data->first_frame)
diff --git a/src/decoder/_flac_common.h b/src/decoder/_flac_common.h
index 1d211fcfb..2f328afa6 100644
--- a/src/decoder/_flac_common.h
+++ b/src/decoder/_flac_common.h
@@ -38,6 +38,8 @@
 struct flac_data {
 	struct pcm_buffer buffer;
 
+	enum sample_format sample_format;
+
 	/**
 	 * The size of one frame in the output buffer.
 	 */
diff --git a/src/decoder/audiofile_plugin.c b/src/decoder/audiofile_plugin.c
index 5a2096d00..fcb431db7 100644
--- a/src/decoder/audiofile_plugin.c
+++ b/src/decoder/audiofile_plugin.c
@@ -101,13 +101,33 @@ setup_virtual_fops(struct input_stream *stream)
 	return vf;
 }
 
-static uint8_t
+static enum sample_format
+audiofile_bits_to_sample_format(int bits)
+{
+	switch (bits) {
+	case 8:
+		return SAMPLE_FORMAT_S8;
+
+	case 16:
+		return SAMPLE_FORMAT_S16;
+
+	case 24:
+		return SAMPLE_FORMAT_S24_P32;
+
+	case 32:
+		return SAMPLE_FORMAT_S32;
+	}
+
+	return SAMPLE_FORMAT_UNDEFINED;
+}
+
+static enum sample_format
 audiofile_setup_sample_format(AFfilehandle af_fp)
 {
 	int fs, bits;
 
 	afGetSampleFormat(af_fp, AF_DEFAULT_TRACK, &fs, &bits);
-	if (!audio_valid_sample_format(bits)) {
+	if (!audio_valid_sample_format(audiofile_bits_to_sample_format(bits))) {
 		g_debug("input file has %d bit samples, converting to 16",
 			bits);
 		bits = 16;
@@ -117,7 +137,7 @@ audiofile_setup_sample_format(AFfilehandle af_fp)
 	                         AF_SAMPFMT_TWOSCOMP, bits);
 	afGetVirtualSampleFormat(af_fp, AF_DEFAULT_TRACK, &fs, &bits);
 
-	return bits;
+	return audiofile_bits_to_sample_format(bits);
 }
 
 static void
diff --git a/src/decoder/faad_plugin.c b/src/decoder/faad_plugin.c
index 55df15555..2a05e33e8 100644
--- a/src/decoder/faad_plugin.c
+++ b/src/decoder/faad_plugin.c
@@ -283,7 +283,7 @@ faad_decoder_init(faacDecHandle decoder, struct decoder_buffer *buffer,
 	decoder_buffer_consume(buffer, nbytes);
 
 	return audio_format_init_checked(audio_format, sample_rate,
-					 16, channels, error_r);
+					 SAMPLE_FORMAT_S16, channels, error_r);
 }
 
 /**
diff --git a/src/decoder/ffmpeg_plugin.c b/src/decoder/ffmpeg_plugin.c
index 9b025153b..af9320322 100644
--- a/src/decoder/ffmpeg_plugin.c
+++ b/src/decoder/ffmpeg_plugin.c
@@ -277,6 +277,26 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
 	return cmd;
 }
 
+static enum sample_format
+ffmpeg_sample_format(G_GNUC_UNUSED const AVCodecContext *codec_context)
+{
+#if LIBAVCODEC_VERSION_INT >= ((51<<16)+(41<<8)+0)
+	int bits = (uint8_t) av_get_bits_per_sample_format(codec_context->sample_fmt);
+
+	/* XXX implement & test other sample formats */
+
+	switch (bits) {
+	case 16:
+		return SAMPLE_FORMAT_S16;
+	}
+
+	return SAMPLE_FORMAT_UNDEFINED;
+#else
+	/* XXX fixme 16-bit for older ffmpeg (13 Aug 2007) */
+	return SAMPLE_FORMAT_S16;
+#endif
+}
+
 static bool
 ffmpeg_decode_internal(struct ffmpeg_context *ctx)
 {
@@ -288,7 +308,6 @@ ffmpeg_decode_internal(struct ffmpeg_context *ctx)
 	struct audio_format audio_format;
 	enum decoder_command cmd;
 	int total_time;
-	uint8_t bits;
 
 	total_time = 0;
 
@@ -296,14 +315,9 @@ ffmpeg_decode_internal(struct ffmpeg_context *ctx)
 		codec_context->channels = 2;
 	}
 
-#if LIBAVCODEC_VERSION_INT >= ((51<<16)+(41<<8)+0)
-	bits = (uint8_t) av_get_bits_per_sample_format(codec_context->sample_fmt);
-#else
-	/* XXX fixme 16-bit for older ffmpeg (13 Aug 2007) */
-	bits = (uint8_t) 16;
-#endif
 	if (!audio_format_init_checked(&audio_format,
-				       codec_context->sample_rate, bits,
+				       codec_context->sample_rate,
+				       ffmpeg_sample_format(codec_context),
 				       codec_context->channels, &error)) {
 		g_warning("%s", error->message);
 		g_error_free(error);
diff --git a/src/decoder/flac_pcm.c b/src/decoder/flac_pcm.c
index 737d5b043..a8bf6f293 100644
--- a/src/decoder/flac_pcm.c
+++ b/src/decoder/flac_pcm.c
@@ -20,6 +20,8 @@
 #include "config.h"
 #include "flac_pcm.h"
 
+#include <assert.h>
+
 static void flac_convert_stereo16(int16_t *dest,
 				  const FLAC__int32 * const buf[],
 				  unsigned int position, unsigned int end)
@@ -74,12 +76,12 @@ flac_convert_8(int8_t *dest,
 
 void
 flac_convert(void *dest,
-	     unsigned int num_channels, unsigned sample_format,
+	     unsigned int num_channels, enum sample_format sample_format,
 	     const FLAC__int32 *const buf[],
 	     unsigned int position, unsigned int end)
 {
 	switch (sample_format) {
-	case 16:
+	case SAMPLE_FORMAT_S16:
 		if (num_channels == 2)
 			flac_convert_stereo16((int16_t*)dest, buf,
 					      position, end);
@@ -88,15 +90,19 @@ flac_convert(void *dest,
 					position, end);
 		break;
 
-	case 24:
-	case 32:
+	case SAMPLE_FORMAT_S24_P32:
+	case SAMPLE_FORMAT_S32:
 		flac_convert_32((int32_t*)dest, num_channels, buf,
 				position, end);
 		break;
 
-	case 8:
+	case SAMPLE_FORMAT_S8:
 		flac_convert_8((int8_t*)dest, num_channels, buf,
 			       position, end);
 		break;
+
+	case SAMPLE_FORMAT_UNDEFINED:
+		/* unreachable */
+		assert(false);
 	}
 }
diff --git a/src/decoder/flac_pcm.h b/src/decoder/flac_pcm.h
index dca9d6824..4d7a51c4d 100644
--- a/src/decoder/flac_pcm.h
+++ b/src/decoder/flac_pcm.h
@@ -20,11 +20,13 @@
 #ifndef MPD_FLAC_PCM_H
 #define MPD_FLAC_PCM_H
 
+#include "audio_format.h"
+
 #include <FLAC/ordinals.h>
 
 void
 flac_convert(void *dest,
-	     unsigned int num_channels, unsigned sample_format,
+	     unsigned int num_channels, enum sample_format sample_format,
 	     const FLAC__int32 *const buf[],
 	     unsigned int position, unsigned int end);
 
diff --git a/src/decoder/fluidsynth_plugin.c b/src/decoder/fluidsynth_plugin.c
index 3e8a4edc4..1b1e6a531 100644
--- a/src/decoder/fluidsynth_plugin.c
+++ b/src/decoder/fluidsynth_plugin.c
@@ -88,7 +88,7 @@ fluidsynth_file_decode(struct decoder *decoder, const char *path_fs)
 {
 	static const struct audio_format audio_format = {
 		.sample_rate = 48000,
-		.bits = 16,
+		.format = SAMPLE_FORMAT_S16,
 		.channels = 2,
 	};
 	char setting_sample_rate[] = "synth.sample-rate";
diff --git a/src/decoder/mad_plugin.c b/src/decoder/mad_plugin.c
index da93fe45b..cba40aea0 100644
--- a/src/decoder/mad_plugin.c
+++ b/src/decoder/mad_plugin.c
@@ -1188,7 +1188,8 @@ mp3_decode(struct decoder *decoder, struct input_stream *input_stream)
 	}
 
 	if (!audio_format_init_checked(&audio_format,
-				       data.frame.header.samplerate, 24,
+				       data.frame.header.samplerate,
+				       SAMPLE_FORMAT_S24_P32,
 				       MAD_NCHANNELS(&data.frame.header),
 				       &error)) {
 		g_warning("%s", error->message);
diff --git a/src/decoder/mikmod_plugin.c b/src/decoder/mikmod_plugin.c
index 204dd5ce0..1e64aeffd 100644
--- a/src/decoder/mikmod_plugin.c
+++ b/src/decoder/mikmod_plugin.c
@@ -163,7 +163,7 @@ mikmod_decoder_file_decode(struct decoder *decoder, const char *path_fs)
 	/* Prevent module from looping forever */
 	handle->loop = 0;
 
-	audio_format_init(&audio_format, mikmod_sample_rate, 16, 2);
+	audio_format_init(&audio_format, mikmod_sample_rate, SAMPLE_FORMAT_S16, 2);
 	assert(audio_format_valid(&audio_format));
 
 	decoder_initialized(decoder, &audio_format, false, 0);
diff --git a/src/decoder/modplug_plugin.c b/src/decoder/modplug_plugin.c
index 6c08c2199..02292992d 100644
--- a/src/decoder/modplug_plugin.c
+++ b/src/decoder/modplug_plugin.c
@@ -122,7 +122,7 @@ mod_decode(struct decoder *decoder, struct input_stream *is)
 		return;
 	}
 
-	audio_format_init(&audio_format, 44100, 16, 2);
+	audio_format_init(&audio_format, 44100, SAMPLE_FORMAT_S16, 2);
 	assert(audio_format_valid(&audio_format));
 
 	decoder_initialized(decoder, &audio_format,
diff --git a/src/decoder/mp4ff_plugin.c b/src/decoder/mp4ff_plugin.c
index 70ca4bdc3..8d3a4b9e9 100644
--- a/src/decoder/mp4ff_plugin.c
+++ b/src/decoder/mp4ff_plugin.c
@@ -132,7 +132,8 @@ mp4_faad_new(mp4ff_t *mp4fh, int *track_r, struct audio_format *audio_format)
 		return NULL;
 	}
 
-	if (!audio_format_init_checked(audio_format, sample_rate, 16, channels,
+	if (!audio_format_init_checked(audio_format, sample_rate,
+				       SAMPLE_FORMAT_S16, channels,
 				       &error)) {
 		g_warning("%s", error->message);
 		g_error_free(error);
diff --git a/src/decoder/mpcdec_plugin.c b/src/decoder/mpcdec_plugin.c
index d985f8459..2f1936e55 100644
--- a/src/decoder/mpcdec_plugin.c
+++ b/src/decoder/mpcdec_plugin.c
@@ -196,7 +196,8 @@ mpcdec_decode(struct decoder *mpd_decoder, struct input_stream *is)
 	mpc_demux_get_info(demux, &info);
 #endif
 
-	if (!audio_format_init_checked(&audio_format, info.sample_freq, 24,
+	if (!audio_format_init_checked(&audio_format, info.sample_freq,
+				       SAMPLE_FORMAT_S24_P32,
 				       info.channels, &error)) {
 		g_warning("%s", error->message);
 		g_error_free(error);
diff --git a/src/decoder/mpg123_decoder_plugin.c b/src/decoder/mpg123_decoder_plugin.c
index 922e56484..62e6b00b0 100644
--- a/src/decoder/mpg123_decoder_plugin.c
+++ b/src/decoder/mpg123_decoder_plugin.c
@@ -87,7 +87,7 @@ mpd_mpg123_open(mpg123_handle *handle, const char *path_fs,
 		return false;
 	}
 
-	if (!audio_format_init_checked(audio_format, rate, 16,
+	if (!audio_format_init_checked(audio_format, rate, SAMPLE_FORMAT_S16,
 				       channels, &gerror)) {
 		g_warning("%s", gerror->message);
 		g_error_free(gerror);
diff --git a/src/decoder/sidplay_plugin.cxx b/src/decoder/sidplay_plugin.cxx
index b6e557e08..63e46a285 100644
--- a/src/decoder/sidplay_plugin.cxx
+++ b/src/decoder/sidplay_plugin.cxx
@@ -277,7 +277,7 @@ sidplay_file_decode(struct decoder *decoder, const char *path_fs)
 	/* initialize the MPD decoder */
 
 	struct audio_format audio_format;
-	audio_format_init(&audio_format, 48000, 16, 2);
+	audio_format_init(&audio_format, 48000, SAMPLE_FORMAT_S16, 2);
 	assert(audio_format_valid(&audio_format));
 
 	decoder_initialized(decoder, &audio_format, true, (float)song_len);
diff --git a/src/decoder/sndfile_decoder_plugin.c b/src/decoder/sndfile_decoder_plugin.c
index 84835c449..fee0f8292 100644
--- a/src/decoder/sndfile_decoder_plugin.c
+++ b/src/decoder/sndfile_decoder_plugin.c
@@ -130,7 +130,8 @@ sndfile_stream_decode(struct decoder *decoder, struct input_stream *is)
 	/* for now, always read 32 bit samples.  Later, we could lower
 	   MPD's CPU usage by reading 16 bit samples with
 	   sf_readf_short() on low-quality source files. */
-	if (!audio_format_init_checked(&audio_format, info.samplerate, 32,
+	if (!audio_format_init_checked(&audio_format, info.samplerate,
+				       SAMPLE_FORMAT_S32,
 				       info.channels, &error)) {
 		g_warning("%s", error->message);
 		g_error_free(error);
diff --git a/src/decoder/vorbis_plugin.c b/src/decoder/vorbis_plugin.c
index 3a41869a0..cb61e5999 100755
--- a/src/decoder/vorbis_plugin.c
+++ b/src/decoder/vorbis_plugin.c
@@ -311,7 +311,8 @@ vorbis_stream_decode(struct decoder *decoder,
 		return;
 	}
 
-	if (!audio_format_init_checked(&audio_format, vi->rate, 16,
+	if (!audio_format_init_checked(&audio_format, vi->rate,
+				       SAMPLE_FORMAT_S16,
 				       vi->channels, &error)) {
 		g_warning("%s", error->message);
 		g_error_free(error);
diff --git a/src/decoder/wavpack_plugin.c b/src/decoder/wavpack_plugin.c
index 9b32a79f2..dffa078f9 100644
--- a/src/decoder/wavpack_plugin.c
+++ b/src/decoder/wavpack_plugin.c
@@ -123,6 +123,33 @@ format_samples_float(G_GNUC_UNUSED int bytes_per_sample, void *buffer,
 	}
 }
 
+/**
+ * Choose a MPD sample format from libwavpacks' number of bits.
+ */
+static enum sample_format
+wavpack_bits_to_sample_format(bool is_float, int bytes_per_sample)
+{
+	if (is_float)
+		return SAMPLE_FORMAT_S24_P32;
+
+	switch (bytes_per_sample) {
+	case 1:
+		return SAMPLE_FORMAT_S8;
+
+	case 2:
+		return SAMPLE_FORMAT_S16;
+
+	case 3:
+		return SAMPLE_FORMAT_S24_P32;
+
+	case 4:
+		return SAMPLE_FORMAT_S32;
+
+	default:
+		return SAMPLE_FORMAT_UNDEFINED;
+	}
+}
+
 /*
  * This does the main decoding thing.
  * Requires an already opened WavpackContext.
@@ -132,7 +159,8 @@ wavpack_decode(struct decoder *decoder, WavpackContext *wpc, bool can_seek,
 	       struct replay_gain_info *replay_gain_info)
 {
 	GError *error = NULL;
-	unsigned bits;
+	bool is_float;
+	enum sample_format sample_format;
 	struct audio_format audio_format;
 	format_samples_t format_samples;
 	char chunk[CHUNK_SIZE];
@@ -141,19 +169,14 @@ wavpack_decode(struct decoder *decoder, WavpackContext *wpc, bool can_seek,
 	int bytes_per_sample, output_sample_size;
 	int position;
 
-	bits = WavpackGetBitsPerSample(wpc);
-
-	/* round bitwidth to 8-bit units */
-	bits = (bits + 7) & (~7);
-	/* MPD handles max 32-bit samples */
-	if (bits > 32)
-		bits = 32;
-
-	if ((WavpackGetMode(wpc) & MODE_FLOAT) == MODE_FLOAT)
-		bits = 24;
+	is_float = (WavpackGetMode(wpc) & MODE_FLOAT) != 0;
+	sample_format =
+		wavpack_bits_to_sample_format(is_float,
+					      WavpackGetBytesPerSample(wpc));
 
 	if (!audio_format_init_checked(&audio_format,
-				       WavpackGetSampleRate(wpc), bits,
+				       WavpackGetSampleRate(wpc),
+				       sample_format,
 				       WavpackGetNumChannels(wpc), &error)) {
 		g_warning("%s", error->message);
 		g_error_free(error);
diff --git a/src/decoder/wildmidi_plugin.c b/src/decoder/wildmidi_plugin.c
index 718f24c2e..70b4d5ef9 100644
--- a/src/decoder/wildmidi_plugin.c
+++ b/src/decoder/wildmidi_plugin.c
@@ -59,7 +59,7 @@ wildmidi_file_decode(struct decoder *decoder, const char *path_fs)
 {
 	static const struct audio_format audio_format = {
 		.sample_rate = WILDMIDI_SAMPLE_RATE,
-		.bits = 16,
+		.format = SAMPLE_FORMAT_S16,
 		.channels = 2,
 	};
 	midi *wm;
diff --git a/src/encoder/flac_encoder.c b/src/encoder/flac_encoder.c
index ab7dc0c39..4f80fe568 100644
--- a/src/encoder/flac_encoder.c
+++ b/src/encoder/flac_encoder.c
@@ -89,7 +89,8 @@ flac_encoder_finish(struct encoder *_encoder)
 }
 
 static bool
-flac_encoder_setup(struct flac_encoder *encoder, GError **error)
+flac_encoder_setup(struct flac_encoder *encoder, unsigned bits_per_sample,
+		   GError **error)
 {
 	if ( !FLAC__stream_encoder_set_compression_level(encoder->fse,
 					encoder->compression)) {
@@ -106,10 +107,10 @@ flac_encoder_setup(struct flac_encoder *encoder, GError **error)
 		return false;
 	}
 	if ( !FLAC__stream_encoder_set_bits_per_sample(encoder->fse,
-					encoder->audio_format.bits)) {
+						       bits_per_sample)) {
 		g_set_error(error, flac_encoder_quark(), 0,
 			    "error setting flac bit format to %d",
-			    encoder->audio_format.bits);
+			    bits_per_sample);
 		return false;
 	}
 	if ( !FLAC__stream_encoder_set_sample_rate(encoder->fse,
@@ -143,13 +144,29 @@ flac_encoder_open(struct encoder *_encoder, struct audio_format *audio_format,
 		     GError **error)
 {
 	struct flac_encoder *encoder = (struct flac_encoder *)_encoder;
+	unsigned bits_per_sample;
 	FLAC__StreamEncoderInitStatus init_status;
 
 	encoder->audio_format = *audio_format;
 
 	/* FIXME: flac should support 32bit as well */
-	if (audio_format->bits > 24)
-		audio_format->bits = 24;
+	switch (audio_format->format) {
+	case SAMPLE_FORMAT_S8:
+		bits_per_sample = 8;
+		break;
+
+	case SAMPLE_FORMAT_S16:
+		bits_per_sample = 16;
+		break;
+
+	case SAMPLE_FORMAT_S24_P32:
+		bits_per_sample = 24;
+		break;
+
+	default:
+		bits_per_sample = 24;
+		audio_format->format = SAMPLE_FORMAT_S24_P32;
+	}
 
 	/* allocate the encoder */
 	encoder->fse = FLAC__stream_encoder_new();
@@ -159,7 +176,7 @@ flac_encoder_open(struct encoder *_encoder, struct audio_format *audio_format,
 		return false;
 	}
 
-	if (!flac_encoder_setup(encoder, error)) {
+	if (!flac_encoder_setup(encoder, bits_per_sample, error)) {
 		FLAC__stream_encoder_delete(encoder->fse);
 		return false;
 	}
@@ -237,20 +254,23 @@ flac_encoder_write(struct encoder *_encoder,
 	num_frames = length / audio_format_frame_size(&encoder->audio_format);
 	num_samples = num_frames * encoder->audio_format.channels;
 
-	switch (encoder->audio_format.bits) {
-	case 8:
+	switch (encoder->audio_format.format) {
+	case SAMPLE_FORMAT_S8:
 		exbuffer = pcm_buffer_get(&encoder->expand_buffer, length*4);
 		pcm8_to_flac(exbuffer, data, num_samples);
 		buffer = exbuffer;
 		break;
-	case 16:
+
+	case SAMPLE_FORMAT_S16:
 		exbuffer = pcm_buffer_get(&encoder->expand_buffer, length*2);
 		pcm16_to_flac(exbuffer, data, num_samples);
 		buffer = exbuffer;
 		break;
-	case 24:
-	case 32: /* nothing need to be done
-		  * format is the same for both mpd and libFLAC */
+
+	case SAMPLE_FORMAT_S24_P32:
+	case SAMPLE_FORMAT_S32:
+		/* nothing need to be done; format is the same for
+		   both mpd and libFLAC */
 		buffer = data;
 		break;
 	}
diff --git a/src/encoder/lame_encoder.c b/src/encoder/lame_encoder.c
index 812ff39c5..97431a817 100644
--- a/src/encoder/lame_encoder.c
+++ b/src/encoder/lame_encoder.c
@@ -185,7 +185,7 @@ lame_encoder_open(struct encoder *_encoder, struct audio_format *audio_format,
 {
 	struct lame_encoder *encoder = (struct lame_encoder *)_encoder;
 
-	audio_format->bits = 16;
+	audio_format->format = SAMPLE_FORMAT_S16;
 	audio_format->channels = 2;
 
 	encoder->audio_format = *audio_format;
diff --git a/src/encoder/twolame_encoder.c b/src/encoder/twolame_encoder.c
index cddf5773e..e7af89bf6 100644
--- a/src/encoder/twolame_encoder.c
+++ b/src/encoder/twolame_encoder.c
@@ -192,7 +192,7 @@ twolame_encoder_open(struct encoder *_encoder, struct audio_format *audio_format
 {
 	struct twolame_encoder *encoder = (struct twolame_encoder *)_encoder;
 
-	audio_format->bits = 16;
+	audio_format->format = SAMPLE_FORMAT_S16;
 	audio_format->channels = 2;
 
 	encoder->audio_format = *audio_format;
diff --git a/src/encoder/vorbis_encoder.c b/src/encoder/vorbis_encoder.c
index 2fa0fd950..d072bcd3f 100644
--- a/src/encoder/vorbis_encoder.c
+++ b/src/encoder/vorbis_encoder.c
@@ -212,7 +212,7 @@ vorbis_encoder_open(struct encoder *_encoder,
 	struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder;
 	bool ret;
 
-	audio_format->bits = 16;
+	audio_format->format = SAMPLE_FORMAT_S16;
 
 	encoder->audio_format = *audio_format;
 
diff --git a/src/encoder/wave_encoder.c b/src/encoder/wave_encoder.c
index e66cc1917..f34ae0241 100644
--- a/src/encoder/wave_encoder.c
+++ b/src/encoder/wave_encoder.c
@@ -114,16 +114,39 @@ wave_encoder_open(struct encoder *_encoder,
 	struct wave_encoder *encoder = (struct wave_encoder *)_encoder;
 	void *buffer;
 
-	encoder->bits = audio_format->bits;
+	assert(audio_format_valid(audio_format));
+
+	switch (audio_format->format) {
+	case SAMPLE_FORMAT_S8:
+		encoder->bits = 8;
+		break;
+
+	case SAMPLE_FORMAT_S16:
+		encoder->bits = 16;
+		break;
+
+	case SAMPLE_FORMAT_S24_P32:
+		encoder->bits = 24;
+		break;
+
+	case SAMPLE_FORMAT_S32:
+		encoder->bits = 32;
+		break;
+
+	default:
+		audio_format->format = SAMPLE_FORMAT_S16;
+		encoder->bits = 16;
+		break;
+	}
 
 	buffer = pcm_buffer_get(&encoder->buffer, sizeof(struct wave_header) );
 
 	/* create PCM wave header in initial buffer */
 	fill_wave_header((struct wave_header *) buffer, 
 			audio_format->channels,
-			audio_format->bits,
+			 encoder->bits,
 			audio_format->sample_rate,
-			(audio_format->bits / 8) * audio_format->channels );
+			 (encoder->bits / 8) * audio_format->channels );
 
 	encoder->buffer_length = sizeof(struct wave_header);
 	return true;
diff --git a/src/filter/volume_filter_plugin.c b/src/filter/volume_filter_plugin.c
index f6639a75f..285a4b7a4 100644
--- a/src/filter/volume_filter_plugin.c
+++ b/src/filter/volume_filter_plugin.c
@@ -75,8 +75,9 @@ volume_filter_open(struct filter *_filter,
 {
 	struct volume_filter *filter = (struct volume_filter *)_filter;
 
-	if (audio_format->bits != 8 && audio_format->bits != 16 &&
-	    audio_format->bits != 24) {
+	if (audio_format->format != SAMPLE_FORMAT_S8 &&
+	    audio_format->format != SAMPLE_FORMAT_S16 &&
+	    audio_format->format != SAMPLE_FORMAT_S24_P32) {
 		g_set_error(error_r, volume_quark(), 0,
 			    "Unsupported audio format");
 		return false;
diff --git a/src/normalize.c b/src/normalize.c
index f9201df64..1c8173def 100644
--- a/src/normalize.c
+++ b/src/normalize.c
@@ -47,7 +47,8 @@ void finishNormalization(void)
 void normalizeData(void *buffer, int bufferSize,
 		   const struct audio_format *format)
 {
-	if ((format->bits != 16) || (format->channels != 2)) return;
+	if (format->format != SAMPLE_FORMAT_S16 || format->channels != 2)
+		return;
 
 	Compressor_Process_int16(compressor, buffer, bufferSize / 2);
 }
diff --git a/src/output/alsa_plugin.c b/src/output/alsa_plugin.c
index 2c642015d..b7325de07 100644
--- a/src/output/alsa_plugin.c
+++ b/src/output/alsa_plugin.c
@@ -185,13 +185,22 @@ alsa_test_default_device(void)
 static snd_pcm_format_t
 get_bitformat(const struct audio_format *af)
 {
-	switch (af->bits) {
-	case 8: return SND_PCM_FORMAT_S8;
-	case 16: return SND_PCM_FORMAT_S16;
-	case 24: return SND_PCM_FORMAT_S24;
-	case 32: return SND_PCM_FORMAT_S32;
+	switch (af->format) {
+	case SAMPLE_FORMAT_S8:
+		return SND_PCM_FORMAT_S8;
+
+	case SAMPLE_FORMAT_S16:
+		return SND_PCM_FORMAT_S16;
+
+	case SAMPLE_FORMAT_S24_P32:
+		return SND_PCM_FORMAT_S24;
+
+	case SAMPLE_FORMAT_S32:
+		return SND_PCM_FORMAT_S32;
+
+	default:
+		return SND_PCM_FORMAT_UNKNOWN;
 	}
-	return SND_PCM_FORMAT_UNKNOWN;
 }
 
 static snd_pcm_format_t
@@ -264,61 +273,67 @@ configure_hw:
 		err = snd_pcm_hw_params_set_format(ad->pcm, hwparams,
 						   byteswap_bitformat(bitformat));
 		if (err == 0) {
-			g_debug("ALSA device \"%s\": converting %u bit to reverse-endian\n",
-				alsa_device(ad), audio_format->bits);
+			g_debug("ALSA device \"%s\": converting format %s to reverse-endian",
+				alsa_device(ad),
+				sample_format_to_string(audio_format->format));
 			audio_format->reverse_endian = 1;
 		}
 	}
-	if (err == -EINVAL && (audio_format->bits == 24 ||
-			       audio_format->bits == 16)) {
+	if (err == -EINVAL && (audio_format->format == SAMPLE_FORMAT_S24_P32 ||
+			       audio_format->format == SAMPLE_FORMAT_S16)) {
 		/* fall back to 32 bit, let pcm_convert.c do the conversion */
 		err = snd_pcm_hw_params_set_format(ad->pcm, hwparams,
 						   SND_PCM_FORMAT_S32);
 		if (err == 0) {
-			g_debug("ALSA device \"%s\": converting %u bit to 32 bit\n",
-				alsa_device(ad), audio_format->bits);
-			audio_format->bits = 32;
+			g_debug("ALSA device \"%s\": converting format %s to 32 bit\n",
+				alsa_device(ad),
+				sample_format_to_string(audio_format->format));
+			audio_format->format = SAMPLE_FORMAT_S32;
 		}
 	}
-	if (err == -EINVAL && (audio_format->bits == 24 ||
-			       audio_format->bits == 16)) {
+	if (err == -EINVAL && (audio_format->format == SAMPLE_FORMAT_S24_P32 ||
+			       audio_format->format == SAMPLE_FORMAT_S16)) {
 		/* fall back to 32 bit, let pcm_convert.c do the conversion */
 		err = snd_pcm_hw_params_set_format(ad->pcm, hwparams,
 						   byteswap_bitformat(SND_PCM_FORMAT_S32));
 		if (err == 0) {
-			g_debug("ALSA device \"%s\": converting %u bit to 32 bit backward-endian\n",
-				alsa_device(ad), audio_format->bits);
-			audio_format->bits = 32;
+			g_debug("ALSA device \"%s\": converting format %s to 32 bit backward-endian\n",
+				alsa_device(ad),
+				sample_format_to_string(audio_format->format));
+			audio_format->format = SAMPLE_FORMAT_S32;
 			audio_format->reverse_endian = 1;
 		}
 	}
 
-	if (err == -EINVAL && audio_format->bits != 16) {
+	if (err == -EINVAL && audio_format->format != SAMPLE_FORMAT_S16) {
 		/* fall back to 16 bit, let pcm_convert.c do the conversion */
 		err = snd_pcm_hw_params_set_format(ad->pcm, hwparams,
 						   SND_PCM_FORMAT_S16);
 		if (err == 0) {
-			g_debug("ALSA device \"%s\": converting %u bit to 16 bit\n",
-				alsa_device(ad), audio_format->bits);
-			audio_format->bits = 16;
+			g_debug("ALSA device \"%s\": converting format %s to 16 bit\n",
+				alsa_device(ad),
+				sample_format_to_string(audio_format->format));
+			audio_format->format = SAMPLE_FORMAT_S16;
 		}
 	}
-	if (err == -EINVAL && audio_format->bits != 16) {
+	if (err == -EINVAL && audio_format->format != SAMPLE_FORMAT_S16) {
 		/* fall back to 16 bit, let pcm_convert.c do the conversion */
 		err = snd_pcm_hw_params_set_format(ad->pcm, hwparams,
 						   byteswap_bitformat(SND_PCM_FORMAT_S16));
 		if (err == 0) {
-			g_debug("ALSA device \"%s\": converting %u bit to 16 bit backward-endian\n",
-				alsa_device(ad), audio_format->bits);
-			audio_format->bits = 16;
+			g_debug("ALSA device \"%s\": converting format %s to 16 bit backward-endian\n",
+				alsa_device(ad),
+				sample_format_to_string(audio_format->format));
+			audio_format->format = SAMPLE_FORMAT_S16;
 			audio_format->reverse_endian = 1;
 		}
 	}
 
 	if (err < 0) {
 		g_set_error(error, alsa_output_quark(), err,
-			    "ALSA device \"%s\" does not support %u bit audio: %s",
-			    alsa_device(ad), audio_format->bits,
+			    "ALSA device \"%s\" does not support format %s: %s",
+			    alsa_device(ad),
+			    sample_format_to_string(audio_format->format),
 			    snd_strerror(-err));
 		return false;
 	}
@@ -449,7 +464,7 @@ alsa_open(void *data, struct audio_format *audio_format, GError **error)
 		/* sample format is not supported by this plugin -
 		   fall back to 16 bit samples */
 
-		audio_format->bits = 16;
+		audio_format->format = SAMPLE_FORMAT_S16;
 		bitformat = SND_PCM_FORMAT_S16;
 	}
 
diff --git a/src/output/ao_plugin.c b/src/output/ao_plugin.c
index d69175272..7afca0db2 100644
--- a/src/output/ao_plugin.c
+++ b/src/output/ao_plugin.c
@@ -170,13 +170,24 @@ ao_output_open(void *data, struct audio_format *audio_format,
 	ao_sample_format format;
 	struct ao_data *ad = (struct ao_data *)data;
 
-	/* support for 24 bit samples in libao is currently dubious,
-	   and until we have sorted that out, resample everything to
-	   16 bit */
-	if (audio_format->bits > 16)
-		audio_format->bits = 16;
+	switch (audio_format->format) {
+	case SAMPLE_FORMAT_S8:
+		format.bits = 8;
+		break;
+
+	case SAMPLE_FORMAT_S16:
+		format.bits = 16;
+		break;
+
+	default:
+		/* support for 24 bit samples in libao is currently
+		   dubious, and until we have sorted that out,
+		   convert everything to 16 bit */
+		audio_format->format = SAMPLE_FORMAT_S16;
+		format.bits = 16;
+		break;
+	}
 
-	format.bits = audio_format->bits;
 	format.rate = audio_format->sample_rate;
 	format.byte_format = AO_FMT_NATIVE;
 	format.channels = audio_format->channels;
diff --git a/src/output/jack_output_plugin.c b/src/output/jack_output_plugin.c
index 7e5a52993..f50bc37d0 100644
--- a/src/output/jack_output_plugin.c
+++ b/src/output/jack_output_plugin.c
@@ -157,8 +157,9 @@ set_audioformat(struct jack_data *jd, struct audio_format *audio_format)
 	else if (audio_format->channels > jd->num_source_ports)
 		audio_format->channels = 2;
 
-	if (audio_format->bits != 16 && audio_format->bits != 24)
-		audio_format->bits = 24;
+	if (audio_format->format != SAMPLE_FORMAT_S16 &&
+	    audio_format->format != SAMPLE_FORMAT_S24_P32)
+		audio_format->format = SAMPLE_FORMAT_S24_P32;
 }
 
 static void
@@ -606,13 +607,13 @@ static void
 mpd_jack_write_samples(struct jack_data *jd, const void *src,
 		       unsigned num_samples)
 {
-	switch (jd->audio_format.bits) {
-	case 16:
+	switch (jd->audio_format.format) {
+	case SAMPLE_FORMAT_S16:
 		mpd_jack_write_samples_16(jd, (const int16_t*)src,
 					  num_samples);
 		break;
 
-	case 24:
+	case SAMPLE_FORMAT_S24_P32:
 		mpd_jack_write_samples_24(jd, (const int32_t*)src,
 					  num_samples);
 		break;
diff --git a/src/output/mvp_plugin.c b/src/output/mvp_plugin.c
index 5a9a9b48b..86f147e5a 100644
--- a/src/output/mvp_plugin.c
+++ b/src/output/mvp_plugin.c
@@ -172,19 +172,19 @@ mvp_set_pcm_params(struct mvp_data *md, struct audio_format *audio_format,
 	}
 
 	/* 0,1=24bit(24) , 2,3=16bit */
-	switch (audio_format->bits) {
-	case 16:
+	switch (audio_format->format) {
+	case SAMPLE_FORMAT_S16:
 		mix[1] = 2;
 		break;
 
-	case 24:
+	case SAMPLE_FORMAT_S24_P32:
 		mix[1] = 0;
 		break;
 
 	default:
-		g_debug("unsupported sample format %u - falling back to stereo",
-			audio_format->bits);
-		audio_format->bits = 16;
+		g_debug("unsupported sample format %s - falling back to 16 bit",
+			sample_format_to_string(audio_format->format));
+		audio_format->format = SAMPLE_FORMAT_S16;
 		mix[1] = 2;
 		break;
 	}
diff --git a/src/output/openal_plugin.c b/src/output/openal_plugin.c
index 8fda110e1..0aded4d9a 100644
--- a/src/output/openal_plugin.c
+++ b/src/output/openal_plugin.c
@@ -58,25 +58,29 @@ openal_output_quark(void)
 static ALenum
 openal_audio_format(struct audio_format *audio_format)
 {
-	/* Only 8 and 16 bit samples are supported */
-	if (audio_format->bits != 16 && audio_format->bits != 8)
-		audio_format->bits = 16;
-
-	switch (audio_format->bits)
-	{
-	case 16:
+	switch (audio_format->format) {
+	case SAMPLE_FORMAT_S16:
 		if (audio_format->channels == 2)
 			return AL_FORMAT_STEREO16;
 		if (audio_format->channels == 1)
 			return AL_FORMAT_MONO16;
 		break;
 
-	case 8:
+	case SAMPLE_FORMAT_S8:
 		if (audio_format->channels == 2)
 			return AL_FORMAT_STEREO8;
 		if (audio_format->channels == 1)
 			return AL_FORMAT_MONO8;
 		break;
+
+	default:
+		/* fall back to 16 bit */
+		audio_format->format = SAMPLE_FORMAT_S16;
+		if (audio_format->channels == 2)
+			return AL_FORMAT_STEREO16;
+		if (audio_format->channels == 1)
+			return AL_FORMAT_MONO16;
+		break;
 	}
 
 	return 0;
diff --git a/src/output/oss_plugin.c b/src/output/oss_plugin.c
index b02d7d62e..f16374e39 100644
--- a/src/output/oss_plugin.c
+++ b/src/output/oss_plugin.c
@@ -490,17 +490,18 @@ oss_setup(struct oss_data *od, GError **error)
 	}
 	od->audio_format.sample_rate = tmp;
 
-	switch (od->audio_format.bits) {
-	case 8:
+	switch (od->audio_format.format) {
+	case SAMPLE_FORMAT_S8:
 		tmp = AFMT_S8;
 		break;
-	case 16:
+
+	case SAMPLE_FORMAT_S16:
 		tmp = AFMT_S16_MPD;
 		break;
 
 	default:
 		/* not supported by OSS - fall back to 16 bit */
-		od->audio_format.bits = 16;
+		od->audio_format.format = SAMPLE_FORMAT_S16;
 		tmp = AFMT_S16_MPD;
 		break;
 	}
diff --git a/src/output/osx_plugin.c b/src/output/osx_plugin.c
index afcd143b3..22b742ee5 100644
--- a/src/output/osx_plugin.c
+++ b/src/output/osx_plugin.c
@@ -166,9 +166,6 @@ osx_output_open(void *data, struct audio_format *audio_format, GError **error)
 	OSStatus status;
 	ComponentResult result;
 
-	if (audio_format->bits > 16)
-		audio_format->bits = 16;
-
 	desc.componentType = kAudioUnitType_Output;
 	desc.componentSubType = kAudioUnitSubType_DefaultOutput;
 	desc.componentManufacturer = kAudioUnitManufacturer_Apple;
@@ -226,7 +223,21 @@ osx_output_open(void *data, struct audio_format *audio_format, GError **error)
 	stream_description.mFramesPerPacket = 1;
 	stream_description.mBytesPerFrame = stream_description.mBytesPerPacket;
 	stream_description.mChannelsPerFrame = audio_format->channels;
-	stream_description.mBitsPerChannel = audio_format->bits;
+
+	switch (audio_format->format) {
+	case SAMPLE_FORMAT_S8:
+		stream_description.mBitsPerChannel = 8;
+		break;
+
+	case SAMPLE_FORMAT_S16:
+		stream_description.mBitsPerChannel = 16;
+		break;
+
+	default:
+		audio_format->format = SAMPLE_FORMAT_S16;
+		stream_description.mBitsPerChannel = 16;
+		break;
+	}
 
 	result = AudioUnitSetProperty(od->au, kAudioUnitProperty_StreamFormat,
 				      kAudioUnitScope_Input, 0,
diff --git a/src/output/pulse_output_plugin.c b/src/output/pulse_output_plugin.c
index 3da1b3593..a64157920 100644
--- a/src/output/pulse_output_plugin.c
+++ b/src/output/pulse_output_plugin.c
@@ -467,7 +467,7 @@ pulse_output_open(void *data, struct audio_format *audio_format,
 
 	/* MPD doesn't support the other pulseaudio sample formats, so
 	   we just force MPD to send us everything as 16 bit */
-	audio_format->bits = 16;
+	audio_format->format = SAMPLE_FORMAT_S16;
 
 	ss.format = PA_SAMPLE_S16NE;
 	ss.rate = audio_format->sample_rate;
diff --git a/src/output/solaris_output_plugin.c b/src/output/solaris_output_plugin.c
index b187630ee..fe84068f1 100644
--- a/src/output/solaris_output_plugin.c
+++ b/src/output/solaris_output_plugin.c
@@ -89,7 +89,7 @@ solaris_output_open(void *data, struct audio_format *audio_format,
 
 	/* support only 16 bit mono/stereo for now; nothing else has
 	   been tested */
-	audio_format->bits = 16;
+	audio_format->format = SAMPLE_FORMAT_S16;
 
 	/* open the device in non-blocking mode */
 
@@ -119,7 +119,7 @@ solaris_output_open(void *data, struct audio_format *audio_format,
 
 	info.play.sample_rate = audio_format->sample_rate;
 	info.play.channels = audio_format->channels;
-	info.play.precision = audio_format->bits;
+	info.play.precision = 16;
 	info.play.encoding = AUDIO_ENCODING_LINEAR;
 
 	ret = ioctl(so->fd, AUDIO_SETINFO, &info);
diff --git a/src/pcm_convert.c b/src/pcm_convert.c
index 1d6299964..8d529dd5f 100644
--- a/src/pcm_convert.c
+++ b/src/pcm_convert.c
@@ -63,15 +63,15 @@ pcm_convert_16(struct pcm_convert_state *state,
 	const int16_t *buf;
 	size_t len;
 
-	assert(dest_format->bits == 16);
+	assert(dest_format->format == SAMPLE_FORMAT_S16);
 
 	buf = pcm_convert_to_16(&state->format_buffer, &state->dither,
-				src_format->bits, src_buffer, src_size,
+				src_format->format, src_buffer, src_size,
 				&len);
 	if (buf == NULL) {
 		g_set_error(error_r, pcm_convert_quark(), 0,
-			    "Conversion from %u to 16 bit is not implemented",
-			    src_format->bits);
+			    "Conversion from %s to 16 bit is not implemented",
+			    sample_format_to_string(src_format->format));
 		return NULL;
 	}
 
@@ -119,14 +119,14 @@ pcm_convert_24(struct pcm_convert_state *state,
 	const int32_t *buf;
 	size_t len;
 
-	assert(dest_format->bits == 24);
+	assert(dest_format->format == SAMPLE_FORMAT_S24_P32);
 
-	buf = pcm_convert_to_24(&state->format_buffer, src_format->bits,
+	buf = pcm_convert_to_24(&state->format_buffer, src_format->format,
 				src_buffer, src_size, &len);
 	if (buf == NULL) {
 		g_set_error(error_r, pcm_convert_quark(), 0,
-			    "Conversion from %u to 24 bit is not implemented",
-			    src_format->bits);
+			    "Conversion from %s to 24 bit is not implemented",
+			    sample_format_to_string(src_format->format));
 		return NULL;
 	}
 
@@ -174,14 +174,14 @@ pcm_convert_32(struct pcm_convert_state *state,
 	const int32_t *buf;
 	size_t len;
 
-	assert(dest_format->bits == 32);
+	assert(dest_format->format == SAMPLE_FORMAT_S32);
 
-	buf = pcm_convert_to_32(&state->format_buffer, src_format->bits,
+	buf = pcm_convert_to_32(&state->format_buffer, src_format->format,
 				src_buffer, src_size, &len);
 	if (buf == NULL) {
 		g_set_error(error_r, pcm_convert_quark(), 0,
-			    "Conversion from %u to 24 bit is not implemented",
-			    src_format->bits);
+			    "Conversion from %s to 24 bit is not implemented",
+			    sample_format_to_string(src_format->format));
 		return NULL;
 	}
 
@@ -227,20 +227,20 @@ pcm_convert(struct pcm_convert_state *state,
 	    size_t *dest_size_r,
 	    GError **error_r)
 {
-	switch (dest_format->bits) {
-	case 16:
+	switch (dest_format->format) {
+	case SAMPLE_FORMAT_S16:
 		return pcm_convert_16(state,
 				      src_format, src, src_size,
 				      dest_format, dest_size_r,
 				      error_r);
 
-	case 24:
+	case SAMPLE_FORMAT_S24_P32:
 		return pcm_convert_24(state,
 				      src_format, src, src_size,
 				      dest_format, dest_size_r,
 				      error_r);
 
-	case 32:
+	case SAMPLE_FORMAT_S32:
 		return pcm_convert_32(state,
 				      src_format, src, src_size,
 				      dest_format, dest_size_r,
@@ -248,8 +248,8 @@ pcm_convert(struct pcm_convert_state *state,
 
 	default:
 		g_set_error(error_r, pcm_convert_quark(), 0,
-			    "PCM conversion to %u bit is not implemented",
-			    dest_format->bits);
+			    "PCM conversion to %s is not implemented",
+			    sample_format_to_string(dest_format->format));
 		return NULL;
 	}
 }
diff --git a/src/pcm_format.c b/src/pcm_format.c
index 8da253db9..b0dad2ba3 100644
--- a/src/pcm_format.c
+++ b/src/pcm_format.c
@@ -50,14 +50,17 @@ pcm_convert_32_to_16(struct pcm_dither *dither,
 
 const int16_t *
 pcm_convert_to_16(struct pcm_buffer *buffer, struct pcm_dither *dither,
-		  uint8_t bits, const void *src,
+		  enum sample_format src_format, const void *src,
 		  size_t src_size, size_t *dest_size_r)
 {
 	unsigned num_samples;
 	int16_t *dest;
 
-	switch (bits) {
-	case 8:
+	switch (src_format) {
+	case SAMPLE_FORMAT_UNDEFINED:
+		break;
+
+	case SAMPLE_FORMAT_S8:
 		num_samples = src_size;
 		*dest_size_r = src_size * sizeof(*dest);
 		dest = pcm_buffer_get(buffer, *dest_size_r);
@@ -67,11 +70,11 @@ pcm_convert_to_16(struct pcm_buffer *buffer, struct pcm_dither *dither,
 				    num_samples);
 		return dest;
 
-	case 16:
+	case SAMPLE_FORMAT_S16:
 		*dest_size_r = src_size;
 		return src;
 
-	case 24:
+	case SAMPLE_FORMAT_S24_P32:
 		num_samples = src_size / 4;
 		*dest_size_r = num_samples * sizeof(*dest);
 		dest = pcm_buffer_get(buffer, *dest_size_r);
@@ -81,7 +84,7 @@ pcm_convert_to_16(struct pcm_buffer *buffer, struct pcm_dither *dither,
 				     num_samples);
 		return dest;
 
-	case 32:
+	case SAMPLE_FORMAT_S32:
 		num_samples = src_size / 4;
 		*dest_size_r = num_samples * sizeof(*dest);
 		dest = pcm_buffer_get(buffer, *dest_size_r);
@@ -127,14 +130,17 @@ pcm_convert_32_to_24(int32_t *out, const int16_t *in,
 
 const int32_t *
 pcm_convert_to_24(struct pcm_buffer *buffer,
-		  uint8_t bits, const void *src,
+		  enum sample_format src_format, const void *src,
 		  size_t src_size, size_t *dest_size_r)
 {
 	unsigned num_samples;
 	int32_t *dest;
 
-	switch (bits) {
-	case 8:
+	switch (src_format) {
+	case SAMPLE_FORMAT_UNDEFINED:
+		break;
+
+	case SAMPLE_FORMAT_S8:
 		num_samples = src_size;
 		*dest_size_r = src_size * sizeof(*dest);
 		dest = pcm_buffer_get(buffer, *dest_size_r);
@@ -143,7 +149,7 @@ pcm_convert_to_24(struct pcm_buffer *buffer,
 				    num_samples);
 		return dest;
 
-	case 16:
+	case SAMPLE_FORMAT_S16:
 		num_samples = src_size / 2;
 		*dest_size_r = num_samples * sizeof(*dest);
 		dest = pcm_buffer_get(buffer, *dest_size_r);
@@ -152,11 +158,11 @@ pcm_convert_to_24(struct pcm_buffer *buffer,
 				     num_samples);
 		return dest;
 
-	case 24:
+	case SAMPLE_FORMAT_S24_P32:
 		*dest_size_r = src_size;
 		return src;
 
-	case 32:
+	case SAMPLE_FORMAT_S32:
 		num_samples = src_size / 4;
 		*dest_size_r = num_samples * sizeof(*dest);
 		dest = pcm_buffer_get(buffer, *dest_size_r);
@@ -201,14 +207,17 @@ pcm_convert_24_to_32(int32_t *out, const int32_t *in,
 
 const int32_t *
 pcm_convert_to_32(struct pcm_buffer *buffer,
-		  uint8_t bits, const void *src,
+		  enum sample_format src_format, const void *src,
 		  size_t src_size, size_t *dest_size_r)
 {
 	unsigned num_samples;
 	int32_t *dest;
 
-	switch (bits) {
-	case 8:
+	switch (src_format) {
+	case SAMPLE_FORMAT_UNDEFINED:
+		break;
+
+	case SAMPLE_FORMAT_S8:
 		num_samples = src_size;
 		*dest_size_r = src_size * sizeof(*dest);
 		dest = pcm_buffer_get(buffer, *dest_size_r);
@@ -217,7 +226,7 @@ pcm_convert_to_32(struct pcm_buffer *buffer,
 				    num_samples);
 		return dest;
 
-	case 16:
+	case SAMPLE_FORMAT_S16:
 		num_samples = src_size / 2;
 		*dest_size_r = num_samples * sizeof(*dest);
 		dest = pcm_buffer_get(buffer, *dest_size_r);
@@ -226,7 +235,7 @@ pcm_convert_to_32(struct pcm_buffer *buffer,
 				     num_samples);
 		return dest;
 
-	case 24:
+	case SAMPLE_FORMAT_S24_P32:
 		num_samples = src_size / 4;
 		*dest_size_r = num_samples * sizeof(*dest);
 		dest = pcm_buffer_get(buffer, *dest_size_r);
@@ -235,7 +244,7 @@ pcm_convert_to_32(struct pcm_buffer *buffer,
 				     num_samples);
 		return dest;
 
-	case 32:
+	case SAMPLE_FORMAT_S32:
 		*dest_size_r = src_size;
 		return src;
 	}
diff --git a/src/pcm_format.h b/src/pcm_format.h
index 350566827..6ea5573bd 100644
--- a/src/pcm_format.h
+++ b/src/pcm_format.h
@@ -20,6 +20,8 @@
 #ifndef PCM_FORMAT_H
 #define PCM_FORMAT_H
 
+#include "audio_format.h"
+
 #include <stdint.h>
 #include <stddef.h>
 
@@ -40,7 +42,7 @@ struct pcm_dither;
  */
 const int16_t *
 pcm_convert_to_16(struct pcm_buffer *buffer, struct pcm_dither *dither,
-		  uint8_t bits, const void *src,
+		  enum sample_format src_format, const void *src,
 		  size_t src_size, size_t *dest_size_r);
 
 /**
@@ -55,7 +57,7 @@ pcm_convert_to_16(struct pcm_buffer *buffer, struct pcm_dither *dither,
  */
 const int32_t *
 pcm_convert_to_24(struct pcm_buffer *buffer,
-		  uint8_t bits, const void *src,
+		  enum sample_format src_format, const void *src,
 		  size_t src_size, size_t *dest_size_r);
 
 /**
@@ -70,7 +72,7 @@ pcm_convert_to_24(struct pcm_buffer *buffer,
  */
 const int32_t *
 pcm_convert_to_32(struct pcm_buffer *buffer,
-		  uint8_t bits, const void *src,
+		  enum sample_format src_format, const void *src,
 		  size_t src_size, size_t *dest_size_r);
 
 #endif
diff --git a/src/pcm_mix.c b/src/pcm_mix.c
index 6e678a912..0f767a1d9 100644
--- a/src/pcm_mix.c
+++ b/src/pcm_mix.c
@@ -103,18 +103,18 @@ pcm_add(void *buffer1, const void *buffer2, size_t size,
 	int vol1, int vol2,
 	const struct audio_format *format)
 {
-	switch (format->bits) {
-	case 8:
+	switch (format->format) {
+	case SAMPLE_FORMAT_S8:
 		pcm_add_8((int8_t *)buffer1, (const int8_t *)buffer2,
 			  size, vol1, vol2);
 		break;
 
-	case 16:
+	case SAMPLE_FORMAT_S16:
 		pcm_add_16((int16_t *)buffer1, (const int16_t *)buffer2,
 			   size / 2, vol1, vol2);
 		break;
 
-	case 24:
+	case SAMPLE_FORMAT_S24_P32:
 		pcm_add_24((int32_t*)buffer1,
 			   (const int32_t*)buffer2,
 			   size / 4, vol1, vol2);
@@ -127,7 +127,8 @@ pcm_add(void *buffer1, const void *buffer2, size_t size,
 		break;
 
 	default:
-		g_error("%u bits not supported by pcm_add!\n", format->bits);
+		g_error("format %s not supported by pcm_add",
+			sample_format_to_string(format->format));
 	}
 }
 
diff --git a/src/pcm_volume.c b/src/pcm_volume.c
index 90ad17d6d..acb6c41a8 100644
--- a/src/pcm_volume.c
+++ b/src/pcm_volume.c
@@ -150,17 +150,17 @@ pcm_volume(void *buffer, int length,
 		return true;
 	}
 
-	switch (format->bits) {
-	case 8:
+	switch (format->format) {
+	case SAMPLE_FORMAT_S8:
 		pcm_volume_change_8((int8_t *)buffer, length, volume);
 		return true;
 
-	case 16:
+	case SAMPLE_FORMAT_S16:
 		pcm_volume_change_16((int16_t *)buffer, length / 2,
 				     volume);
 		return true;
 
-	case 24:
+	case SAMPLE_FORMAT_S24_P32:
 		pcm_volume_change_24((int32_t*)buffer, length / 4,
 				     volume);
 		return true;
-- 
cgit v1.2.3