From 28e07e900f397bb2146a02b166cbc00e8ad40a5d Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Mon, 17 Jun 2019 22:20:28 +0200
Subject: [PATCH] pcm/Export: convert the DSD bools to an enum

These options are exclusive.
---
 src/lib/alsa/HwSetup.cxx                |  7 +-
 src/output/plugins/AlsaOutputPlugin.cxx |  6 +-
 src/output/plugins/OSXOutputPlugin.cxx  |  6 +-
 src/pcm/Export.cxx                      | 99 +++++++++++++++++++------
 src/pcm/Export.hxx                      | 42 ++++++-----
 test/test_pcm_export.cxx                |  6 +-
 6 files changed, 113 insertions(+), 53 deletions(-)

diff --git a/src/lib/alsa/HwSetup.cxx b/src/lib/alsa/HwSetup.cxx
index e36d78084..a405f6bc1 100644
--- a/src/lib/alsa/HwSetup.cxx
+++ b/src/lib/alsa/HwSetup.cxx
@@ -95,8 +95,7 @@ TryFormatDsd(snd_pcm_t *pcm, snd_pcm_hw_params_t *hwparams,
 
 #if defined(ENABLE_DSD) && defined(HAVE_ALSA_DSD_U32)
 	if (err == 0) {
-		params.dsd_u16 = false;
-		params.dsd_u32 = false;
+		params.dsd_mode = PcmExport::DsdMode::NONE;
 	}
 
 	if (err == -EINVAL && fmt == SND_PCM_FORMAT_DSD_U8) {
@@ -106,7 +105,7 @@ TryFormatDsd(snd_pcm_t *pcm, snd_pcm_hw_params_t *hwparams,
 			: SND_PCM_FORMAT_DSD_U32_BE;
 		err = TryFormatOrByteSwap(pcm, hwparams, fmt, params);
 		if (err == 0)
-			params.dsd_u32 = true;
+			params.dsd_mode = PcmExport::DsdMode::U32;
 		else
 			fmt = SND_PCM_FORMAT_DSD_U8;
 	}
@@ -118,7 +117,7 @@ TryFormatDsd(snd_pcm_t *pcm, snd_pcm_hw_params_t *hwparams,
 			: SND_PCM_FORMAT_DSD_U16_BE;
 		err = TryFormatOrByteSwap(pcm, hwparams, fmt, params);
 		if (err == 0)
-			params.dsd_u16 = true;
+			params.dsd_mode = PcmExport::DsdMode::U16;
 		else
 			fmt = SND_PCM_FORMAT_DSD_U8;
 	}
diff --git a/src/output/plugins/AlsaOutputPlugin.cxx b/src/output/plugins/AlsaOutputPlugin.cxx
index 13dad8a56..8b16134b4 100644
--- a/src/output/plugins/AlsaOutputPlugin.cxx
+++ b/src/output/plugins/AlsaOutputPlugin.cxx
@@ -558,12 +558,12 @@ AlsaOutput::SetupOrDop(AudioFormat &audio_format, PcmExport::Params &params
 	std::exception_ptr dop_error;
 	if (dop && audio_format.format == SampleFormat::DSD) {
 		try {
-			params.dop = true;
+			params.dsd_mode = PcmExport::DsdMode::DOP;
 			SetupDop(audio_format, params);
 			return;
 		} catch (...) {
 			dop_error = std::current_exception();
-			params.dop = false;
+			params.dsd_mode = PcmExport::DsdMode::NONE;
 		}
 	}
 
@@ -663,7 +663,7 @@ AlsaOutput::Open(AudioFormat &audio_format)
 	snd_pcm_nonblock(pcm, 1);
 
 #ifdef ENABLE_DSD
-	if (params.dop)
+	if (params.dsd_mode == PcmExport::DsdMode::DOP)
 		FormatDebug(alsa_output_domain, "DoP (DSD over PCM) enabled");
 #endif
 
diff --git a/src/output/plugins/OSXOutputPlugin.cxx b/src/output/plugins/OSXOutputPlugin.cxx
index 46cb3e98f..f20600d92 100644
--- a/src/output/plugins/OSXOutputPlugin.cxx
+++ b/src/output/plugins/OSXOutputPlugin.cxx
@@ -787,7 +787,7 @@ OSXOutput::Open(AudioFormat &audio_format)
 #ifdef ENABLE_DSD
 	if (dop && audio_format.format == SampleFormat::DSD) {
 		asbd.mBitsPerChannel = 24;
-		params.dop = true;
+		params.dsd_mode = PcmExport::DsdMode::DOP;
 		asbd.mSampleRate = params.CalcOutputSampleRate(audio_format.sample_rate);
 		asbd.mBytesPerPacket = 4 * audio_format.channels;
 
@@ -802,14 +802,14 @@ OSXOutput::Open(AudioFormat &audio_format)
 
 #ifdef ENABLE_DSD
 	if(audio_format.format == SampleFormat::DSD && sample_rate != asbd.mSampleRate) { // fall back to PCM in case sample_rate cannot be synchronized
-		params.dop = false;
+		params.dsd_mode = PcmExport::DsdMode::NONE;
 		audio_format.format = SampleFormat::S32;
 		asbd.mBitsPerChannel = 32;
 		asbd.mBytesPerPacket = audio_format.GetFrameSize();
 		asbd.mSampleRate = params.CalcOutputSampleRate(audio_format.sample_rate);
 		asbd.mBytesPerFrame = asbd.mBytesPerPacket;
 	}
-	dop_enabled = params.dop;
+	dop_enabled = params.dsd_mode == PcmExport::DsdMode::DOP;
 #endif
 
 	OSStatus status =
diff --git a/src/pcm/Export.cxx b/src/pcm/Export.cxx
index fb77f43f8..7dcaebe16 100644
--- a/src/pcm/Export.cxx
+++ b/src/pcm/Export.cxx
@@ -45,28 +45,36 @@ PcmExport::Open(SampleFormat sample_format, unsigned _channels,
 		: SampleFormat::UNDEFINED;
 
 #ifdef ENABLE_DSD
-	assert((params.dsd_u16 + params.dsd_u32 + params.dop) <= 1);
-	assert(!params.dop || audio_valid_channel_count(_channels));
+	assert(params.dsd_mode != DsdMode::DOP ||
+	       audio_valid_channel_count(_channels));
 
-	dsd_u16 = params.dsd_u16 && sample_format == SampleFormat::DSD;
-	if (dsd_u16)
+	dsd_mode = sample_format == SampleFormat::DSD
+		? params.dsd_mode
+		: DsdMode::NONE;
+
+	switch (dsd_mode) {
+	case DsdMode::NONE:
+		break;
+
+	case DsdMode::U16:
 		/* after the conversion to DSD_U16, the DSD samples
 		   are stuffed inside fake 16 bit samples */
 		sample_format = SampleFormat::S16;
+		break;
 
-	dsd_u32 = params.dsd_u32 && sample_format == SampleFormat::DSD;
-	if (dsd_u32)
+	case DsdMode::U32:
 		/* after the conversion to DSD_U32, the DSD samples
 		   are stuffed inside fake 32 bit samples */
 		sample_format = SampleFormat::S32;
+		break;
 
-	dop = params.dop && sample_format == SampleFormat::DSD;
-	if (dop) {
+	case DsdMode::DOP:
 		dop_converter.Open(_channels);
 
 		/* after the conversion to DoP, the DSD
 		   samples are stuffed inside fake 24 bit samples */
 		sample_format = SampleFormat::S24_P32;
+		break;
 	}
 #endif
 
@@ -91,8 +99,16 @@ void
 PcmExport::Reset() noexcept
 {
 #ifdef ENABLE_DSD
-	if (dop)
+	switch (dsd_mode) {
+	case DsdMode::NONE:
+	case DsdMode::U16:
+	case DsdMode::U32:
+		break;
+
+	case DsdMode::DOP:
 		dop_converter.Reset();
+		break;
+	}
 #endif
 }
 
@@ -104,18 +120,23 @@ PcmExport::GetFrameSize(const AudioFormat &audio_format) const noexcept
 		return audio_format.channels * 3;
 
 #ifdef ENABLE_DSD
-	if (dsd_u16)
+	switch (dsd_mode) {
+	case DsdMode::NONE:
+		break;
+
+	case DsdMode::U16:
 		return channels * 2;
 
-	if (dsd_u32)
+	case DsdMode::U32:
 		return channels * 4;
 
-	if (dop)
+	case DsdMode::DOP:
 		/* the DSD-over-USB draft says that DSD 1-bit samples
 		   are enclosed within 24 bit samples, and MPD's
 		   representation of 24 bit is padded to 32 bit (4
 		   bytes per sample) */
 		return channels * 4;
+	}
 #endif
 
 	return audio_format.GetFrameSize();
@@ -125,20 +146,28 @@ unsigned
 PcmExport::Params::CalcOutputSampleRate(unsigned sample_rate) const noexcept
 {
 #ifdef ENABLE_DSD
-	if (dsd_u16)
+	switch (dsd_mode) {
+	case DsdMode::NONE:
+		break;
+
+	case DsdMode::U16:
 		/* DSD_U16 combines two 8-bit "samples" in one 16-bit
 		   "sample" */
 		sample_rate /= 2;
+		break;
 
-	if (dsd_u32)
+	case DsdMode::U32:
 		/* DSD_U32 combines four 8-bit "samples" in one 32-bit
 		   "sample" */
 		sample_rate /= 4;
+		break;
 
-	if (dop)
+	case DsdMode::DOP:
 		/* DoP packs two 8-bit "samples" in one 24-bit
 		   "sample" */
 		sample_rate /= 2;
+		break;
+	}
 #endif
 
 	return sample_rate;
@@ -148,14 +177,22 @@ unsigned
 PcmExport::Params::CalcInputSampleRate(unsigned sample_rate) const noexcept
 {
 #ifdef ENABLE_DSD
-	if (dsd_u16)
-		sample_rate *= 2;
+	switch (dsd_mode) {
+	case DsdMode::NONE:
+		break;
 
-	if (dsd_u32)
+	case DsdMode::U16:
+		sample_rate *= 2;
+		break;
+
+	case DsdMode::U32:
 		sample_rate *= 4;
+		break;
 
-	if (dop)
+	case DsdMode::DOP:
 		sample_rate *= 2;
+		break;
+	}
 #endif
 
 	return sample_rate;
@@ -169,19 +206,27 @@ PcmExport::Export(ConstBuffer<void> data) noexcept
 					  alsa_channel_order, channels);
 
 #ifdef ENABLE_DSD
-	if (dsd_u16)
+	switch (dsd_mode) {
+	case DsdMode::NONE:
+		break;
+
+	case DsdMode::U16:
 		data = Dsd8To16(dsd_buffer, channels,
 				ConstBuffer<uint8_t>::FromVoid(data))
 			.ToVoid();
+		break;
 
-	if (dsd_u32)
+	case DsdMode::U32:
 		data = Dsd8To32(dsd_buffer, channels,
 				ConstBuffer<uint8_t>::FromVoid(data))
 			.ToVoid();
+		break;
 
-	if (dop)
+	case DsdMode::DOP:
 		data = dop_converter.Convert(ConstBuffer<uint8_t>::FromVoid(data))
 			.ToVoid();
+		break;
+	}
 #endif
 
 	if (pack24) {
@@ -228,9 +273,17 @@ PcmExport::CalcSourceSize(size_t size) const noexcept
 		size = (size / 3) * 4;
 
 #ifdef ENABLE_DSD
-	if (dop)
+	switch (dsd_mode) {
+	case DsdMode::NONE:
+	case DsdMode::U16:
+	case DsdMode::U32:
+		break;
+
+	case DsdMode::DOP:
 		/* DoP doubles the transport size */
 		size /= 2;
+		break;
+	}
 #endif
 
 	return size;
diff --git a/src/pcm/Export.hxx b/src/pcm/Export.hxx
index 91c50e2dc..416e2351c 100644
--- a/src/pcm/Export.hxx
+++ b/src/pcm/Export.hxx
@@ -28,6 +28,8 @@
 #include "Dop.hxx"
 #endif
 
+#include <stdint.h>
+
 template<typename T> struct ConstBuffer;
 struct AudioFormat;
 
@@ -88,22 +90,30 @@ class PcmExport {
 	SampleFormat alsa_channel_order;
 
 #ifdef ENABLE_DSD
-	/**
-	 * Convert DSD (U8) to DSD_U16?
-	 */
-	bool dsd_u16;
+public:
+	enum class DsdMode : uint8_t {
+		NONE,
 
-	/**
-	 * Convert DSD (U8) to DSD_U32?
-	 */
-	bool dsd_u32;
+		/**
+		 * Convert DSD (U8) to DSD_U16?
+		 */
+		U16,
 
-	/**
-	 * Convert DSD to DSD-over-PCM (DoP)?  Input format must be
-	 * SampleFormat::DSD and output format must be
-	 * SampleFormat::S24_P32.
-	 */
-	bool dop;
+		/**
+		 * Convert DSD (U8) to DSD_U32?
+		 */
+		U32,
+
+		/**
+		 * Convert DSD to DSD-over-PCM (DoP)?  Input format
+		 * must be SampleFormat::DSD and output format must be
+		 * SampleFormat::S24_P32.
+		 */
+		DOP,
+	};
+
+private:
+	DsdMode dsd_mode;
 #endif
 
 	/**
@@ -128,9 +138,7 @@ public:
 	struct Params {
 		bool alsa_channel_order = false;
 #ifdef ENABLE_DSD
-		bool dsd_u16 = false;
-		bool dsd_u32 = false;
-		bool dop = false;
+		DsdMode dsd_mode = DsdMode::NONE;
 #endif
 		bool shift8 = false;
 		bool pack24 = false;
diff --git a/test/test_pcm_export.cxx b/test/test_pcm_export.cxx
index 9411bc580..67df4a2b8 100644
--- a/test/test_pcm_export.cxx
+++ b/test/test_pcm_export.cxx
@@ -141,7 +141,7 @@ TEST(PcmTest, ExportDsdU16)
 	};
 
 	PcmExport::Params params;
-	params.dsd_u16 = true;
+	params.dsd_mode = PcmExport::DsdMode::U16;
 
 	EXPECT_EQ(params.CalcOutputSampleRate(705600u), 352800u);
 	EXPECT_EQ(params.CalcInputSampleRate(352800u), 705600u);
@@ -171,7 +171,7 @@ TEST(PcmTest, ExportDsdU32)
 	};
 
 	PcmExport::Params params;
-	params.dsd_u32 = true;
+	params.dsd_mode = PcmExport::DsdMode::U32;
 
 	EXPECT_EQ(params.CalcOutputSampleRate(705600u), 176400u);
 	EXPECT_EQ(params.CalcInputSampleRate(176400u), 705600u);
@@ -199,7 +199,7 @@ TEST(PcmTest, ExportDop)
 	};
 
 	PcmExport::Params params;
-	params.dop = true;
+	params.dsd_mode = PcmExport::DsdMode::DOP;
 
 	EXPECT_EQ(params.CalcOutputSampleRate(705600u), 352800u);
 	EXPECT_EQ(params.CalcInputSampleRate(352800u), 705600u);