Commit 28e07e90 authored by Max Kellermann's avatar Max Kellermann

pcm/Export: convert the DSD bools to an enum

These options are exclusive.
parent c75dc4a6
...@@ -95,8 +95,7 @@ TryFormatDsd(snd_pcm_t *pcm, snd_pcm_hw_params_t *hwparams, ...@@ -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 defined(ENABLE_DSD) && defined(HAVE_ALSA_DSD_U32)
if (err == 0) { if (err == 0) {
params.dsd_u16 = false; params.dsd_mode = PcmExport::DsdMode::NONE;
params.dsd_u32 = false;
} }
if (err == -EINVAL && fmt == SND_PCM_FORMAT_DSD_U8) { if (err == -EINVAL && fmt == SND_PCM_FORMAT_DSD_U8) {
...@@ -106,7 +105,7 @@ TryFormatDsd(snd_pcm_t *pcm, snd_pcm_hw_params_t *hwparams, ...@@ -106,7 +105,7 @@ TryFormatDsd(snd_pcm_t *pcm, snd_pcm_hw_params_t *hwparams,
: SND_PCM_FORMAT_DSD_U32_BE; : SND_PCM_FORMAT_DSD_U32_BE;
err = TryFormatOrByteSwap(pcm, hwparams, fmt, params); err = TryFormatOrByteSwap(pcm, hwparams, fmt, params);
if (err == 0) if (err == 0)
params.dsd_u32 = true; params.dsd_mode = PcmExport::DsdMode::U32;
else else
fmt = SND_PCM_FORMAT_DSD_U8; fmt = SND_PCM_FORMAT_DSD_U8;
} }
...@@ -118,7 +117,7 @@ TryFormatDsd(snd_pcm_t *pcm, snd_pcm_hw_params_t *hwparams, ...@@ -118,7 +117,7 @@ TryFormatDsd(snd_pcm_t *pcm, snd_pcm_hw_params_t *hwparams,
: SND_PCM_FORMAT_DSD_U16_BE; : SND_PCM_FORMAT_DSD_U16_BE;
err = TryFormatOrByteSwap(pcm, hwparams, fmt, params); err = TryFormatOrByteSwap(pcm, hwparams, fmt, params);
if (err == 0) if (err == 0)
params.dsd_u16 = true; params.dsd_mode = PcmExport::DsdMode::U16;
else else
fmt = SND_PCM_FORMAT_DSD_U8; fmt = SND_PCM_FORMAT_DSD_U8;
} }
......
...@@ -558,12 +558,12 @@ AlsaOutput::SetupOrDop(AudioFormat &audio_format, PcmExport::Params &params ...@@ -558,12 +558,12 @@ AlsaOutput::SetupOrDop(AudioFormat &audio_format, PcmExport::Params &params
std::exception_ptr dop_error; std::exception_ptr dop_error;
if (dop && audio_format.format == SampleFormat::DSD) { if (dop && audio_format.format == SampleFormat::DSD) {
try { try {
params.dop = true; params.dsd_mode = PcmExport::DsdMode::DOP;
SetupDop(audio_format, params); SetupDop(audio_format, params);
return; return;
} catch (...) { } catch (...) {
dop_error = std::current_exception(); dop_error = std::current_exception();
params.dop = false; params.dsd_mode = PcmExport::DsdMode::NONE;
} }
} }
...@@ -663,7 +663,7 @@ AlsaOutput::Open(AudioFormat &audio_format) ...@@ -663,7 +663,7 @@ AlsaOutput::Open(AudioFormat &audio_format)
snd_pcm_nonblock(pcm, 1); snd_pcm_nonblock(pcm, 1);
#ifdef ENABLE_DSD #ifdef ENABLE_DSD
if (params.dop) if (params.dsd_mode == PcmExport::DsdMode::DOP)
FormatDebug(alsa_output_domain, "DoP (DSD over PCM) enabled"); FormatDebug(alsa_output_domain, "DoP (DSD over PCM) enabled");
#endif #endif
......
...@@ -787,7 +787,7 @@ OSXOutput::Open(AudioFormat &audio_format) ...@@ -787,7 +787,7 @@ OSXOutput::Open(AudioFormat &audio_format)
#ifdef ENABLE_DSD #ifdef ENABLE_DSD
if (dop && audio_format.format == SampleFormat::DSD) { if (dop && audio_format.format == SampleFormat::DSD) {
asbd.mBitsPerChannel = 24; asbd.mBitsPerChannel = 24;
params.dop = true; params.dsd_mode = PcmExport::DsdMode::DOP;
asbd.mSampleRate = params.CalcOutputSampleRate(audio_format.sample_rate); asbd.mSampleRate = params.CalcOutputSampleRate(audio_format.sample_rate);
asbd.mBytesPerPacket = 4 * audio_format.channels; asbd.mBytesPerPacket = 4 * audio_format.channels;
...@@ -802,14 +802,14 @@ OSXOutput::Open(AudioFormat &audio_format) ...@@ -802,14 +802,14 @@ OSXOutput::Open(AudioFormat &audio_format)
#ifdef ENABLE_DSD #ifdef ENABLE_DSD
if(audio_format.format == SampleFormat::DSD && sample_rate != asbd.mSampleRate) { // fall back to PCM in case sample_rate cannot be synchronized 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; audio_format.format = SampleFormat::S32;
asbd.mBitsPerChannel = 32; asbd.mBitsPerChannel = 32;
asbd.mBytesPerPacket = audio_format.GetFrameSize(); asbd.mBytesPerPacket = audio_format.GetFrameSize();
asbd.mSampleRate = params.CalcOutputSampleRate(audio_format.sample_rate); asbd.mSampleRate = params.CalcOutputSampleRate(audio_format.sample_rate);
asbd.mBytesPerFrame = asbd.mBytesPerPacket; asbd.mBytesPerFrame = asbd.mBytesPerPacket;
} }
dop_enabled = params.dop; dop_enabled = params.dsd_mode == PcmExport::DsdMode::DOP;
#endif #endif
OSStatus status = OSStatus status =
......
...@@ -45,28 +45,36 @@ PcmExport::Open(SampleFormat sample_format, unsigned _channels, ...@@ -45,28 +45,36 @@ PcmExport::Open(SampleFormat sample_format, unsigned _channels,
: SampleFormat::UNDEFINED; : SampleFormat::UNDEFINED;
#ifdef ENABLE_DSD #ifdef ENABLE_DSD
assert((params.dsd_u16 + params.dsd_u32 + params.dop) <= 1); assert(params.dsd_mode != DsdMode::DOP ||
assert(!params.dop || audio_valid_channel_count(_channels)); audio_valid_channel_count(_channels));
dsd_u16 = params.dsd_u16 && sample_format == SampleFormat::DSD; dsd_mode = sample_format == SampleFormat::DSD
if (dsd_u16) ? params.dsd_mode
: DsdMode::NONE;
switch (dsd_mode) {
case DsdMode::NONE:
break;
case DsdMode::U16:
/* after the conversion to DSD_U16, the DSD samples /* after the conversion to DSD_U16, the DSD samples
are stuffed inside fake 16 bit samples */ are stuffed inside fake 16 bit samples */
sample_format = SampleFormat::S16; sample_format = SampleFormat::S16;
break;
dsd_u32 = params.dsd_u32 && sample_format == SampleFormat::DSD; case DsdMode::U32:
if (dsd_u32)
/* after the conversion to DSD_U32, the DSD samples /* after the conversion to DSD_U32, the DSD samples
are stuffed inside fake 32 bit samples */ are stuffed inside fake 32 bit samples */
sample_format = SampleFormat::S32; sample_format = SampleFormat::S32;
break;
dop = params.dop && sample_format == SampleFormat::DSD; case DsdMode::DOP:
if (dop) {
dop_converter.Open(_channels); dop_converter.Open(_channels);
/* after the conversion to DoP, the DSD /* after the conversion to DoP, the DSD
samples are stuffed inside fake 24 bit samples */ samples are stuffed inside fake 24 bit samples */
sample_format = SampleFormat::S24_P32; sample_format = SampleFormat::S24_P32;
break;
} }
#endif #endif
...@@ -91,8 +99,16 @@ void ...@@ -91,8 +99,16 @@ void
PcmExport::Reset() noexcept PcmExport::Reset() noexcept
{ {
#ifdef ENABLE_DSD #ifdef ENABLE_DSD
if (dop) switch (dsd_mode) {
case DsdMode::NONE:
case DsdMode::U16:
case DsdMode::U32:
break;
case DsdMode::DOP:
dop_converter.Reset(); dop_converter.Reset();
break;
}
#endif #endif
} }
...@@ -104,18 +120,23 @@ PcmExport::GetFrameSize(const AudioFormat &audio_format) const noexcept ...@@ -104,18 +120,23 @@ PcmExport::GetFrameSize(const AudioFormat &audio_format) const noexcept
return audio_format.channels * 3; return audio_format.channels * 3;
#ifdef ENABLE_DSD #ifdef ENABLE_DSD
if (dsd_u16) switch (dsd_mode) {
case DsdMode::NONE:
break;
case DsdMode::U16:
return channels * 2; return channels * 2;
if (dsd_u32) case DsdMode::U32:
return channels * 4; return channels * 4;
if (dop) case DsdMode::DOP:
/* the DSD-over-USB draft says that DSD 1-bit samples /* the DSD-over-USB draft says that DSD 1-bit samples
are enclosed within 24 bit samples, and MPD's are enclosed within 24 bit samples, and MPD's
representation of 24 bit is padded to 32 bit (4 representation of 24 bit is padded to 32 bit (4
bytes per sample) */ bytes per sample) */
return channels * 4; return channels * 4;
}
#endif #endif
return audio_format.GetFrameSize(); return audio_format.GetFrameSize();
...@@ -125,20 +146,28 @@ unsigned ...@@ -125,20 +146,28 @@ unsigned
PcmExport::Params::CalcOutputSampleRate(unsigned sample_rate) const noexcept PcmExport::Params::CalcOutputSampleRate(unsigned sample_rate) const noexcept
{ {
#ifdef ENABLE_DSD #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 /* DSD_U16 combines two 8-bit "samples" in one 16-bit
"sample" */ "sample" */
sample_rate /= 2; sample_rate /= 2;
break;
if (dsd_u32) case DsdMode::U32:
/* DSD_U32 combines four 8-bit "samples" in one 32-bit /* DSD_U32 combines four 8-bit "samples" in one 32-bit
"sample" */ "sample" */
sample_rate /= 4; sample_rate /= 4;
break;
if (dop) case DsdMode::DOP:
/* DoP packs two 8-bit "samples" in one 24-bit /* DoP packs two 8-bit "samples" in one 24-bit
"sample" */ "sample" */
sample_rate /= 2; sample_rate /= 2;
break;
}
#endif #endif
return sample_rate; return sample_rate;
...@@ -148,14 +177,22 @@ unsigned ...@@ -148,14 +177,22 @@ unsigned
PcmExport::Params::CalcInputSampleRate(unsigned sample_rate) const noexcept PcmExport::Params::CalcInputSampleRate(unsigned sample_rate) const noexcept
{ {
#ifdef ENABLE_DSD #ifdef ENABLE_DSD
if (dsd_u16) switch (dsd_mode) {
case DsdMode::NONE:
break;
case DsdMode::U16:
sample_rate *= 2; sample_rate *= 2;
break;
if (dsd_u32) case DsdMode::U32:
sample_rate *= 4; sample_rate *= 4;
break;
if (dop) case DsdMode::DOP:
sample_rate *= 2; sample_rate *= 2;
break;
}
#endif #endif
return sample_rate; return sample_rate;
...@@ -169,19 +206,27 @@ PcmExport::Export(ConstBuffer<void> data) noexcept ...@@ -169,19 +206,27 @@ PcmExport::Export(ConstBuffer<void> data) noexcept
alsa_channel_order, channels); alsa_channel_order, channels);
#ifdef ENABLE_DSD #ifdef ENABLE_DSD
if (dsd_u16) switch (dsd_mode) {
case DsdMode::NONE:
break;
case DsdMode::U16:
data = Dsd8To16(dsd_buffer, channels, data = Dsd8To16(dsd_buffer, channels,
ConstBuffer<uint8_t>::FromVoid(data)) ConstBuffer<uint8_t>::FromVoid(data))
.ToVoid(); .ToVoid();
break;
if (dsd_u32) case DsdMode::U32:
data = Dsd8To32(dsd_buffer, channels, data = Dsd8To32(dsd_buffer, channels,
ConstBuffer<uint8_t>::FromVoid(data)) ConstBuffer<uint8_t>::FromVoid(data))
.ToVoid(); .ToVoid();
break;
if (dop) case DsdMode::DOP:
data = dop_converter.Convert(ConstBuffer<uint8_t>::FromVoid(data)) data = dop_converter.Convert(ConstBuffer<uint8_t>::FromVoid(data))
.ToVoid(); .ToVoid();
break;
}
#endif #endif
if (pack24) { if (pack24) {
...@@ -228,9 +273,17 @@ PcmExport::CalcSourceSize(size_t size) const noexcept ...@@ -228,9 +273,17 @@ PcmExport::CalcSourceSize(size_t size) const noexcept
size = (size / 3) * 4; size = (size / 3) * 4;
#ifdef ENABLE_DSD #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 */ /* DoP doubles the transport size */
size /= 2; size /= 2;
break;
}
#endif #endif
return size; return size;
......
...@@ -28,6 +28,8 @@ ...@@ -28,6 +28,8 @@
#include "Dop.hxx" #include "Dop.hxx"
#endif #endif
#include <stdint.h>
template<typename T> struct ConstBuffer; template<typename T> struct ConstBuffer;
struct AudioFormat; struct AudioFormat;
...@@ -88,22 +90,30 @@ class PcmExport { ...@@ -88,22 +90,30 @@ class PcmExport {
SampleFormat alsa_channel_order; SampleFormat alsa_channel_order;
#ifdef ENABLE_DSD #ifdef ENABLE_DSD
/** public:
* Convert DSD (U8) to DSD_U16? enum class DsdMode : uint8_t {
*/ NONE,
bool dsd_u16;
/** /**
* Convert DSD (U8) to DSD_U32? * Convert DSD (U8) to DSD_U16?
*/ */
bool dsd_u32; U16,
/** /**
* Convert DSD to DSD-over-PCM (DoP)? Input format must be * Convert DSD (U8) to DSD_U32?
* SampleFormat::DSD and output format must be */
* SampleFormat::S24_P32. U32,
*/
bool dop; /**
* 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 #endif
/** /**
...@@ -128,9 +138,7 @@ public: ...@@ -128,9 +138,7 @@ public:
struct Params { struct Params {
bool alsa_channel_order = false; bool alsa_channel_order = false;
#ifdef ENABLE_DSD #ifdef ENABLE_DSD
bool dsd_u16 = false; DsdMode dsd_mode = DsdMode::NONE;
bool dsd_u32 = false;
bool dop = false;
#endif #endif
bool shift8 = false; bool shift8 = false;
bool pack24 = false; bool pack24 = false;
......
...@@ -141,7 +141,7 @@ TEST(PcmTest, ExportDsdU16) ...@@ -141,7 +141,7 @@ TEST(PcmTest, ExportDsdU16)
}; };
PcmExport::Params params; PcmExport::Params params;
params.dsd_u16 = true; params.dsd_mode = PcmExport::DsdMode::U16;
EXPECT_EQ(params.CalcOutputSampleRate(705600u), 352800u); EXPECT_EQ(params.CalcOutputSampleRate(705600u), 352800u);
EXPECT_EQ(params.CalcInputSampleRate(352800u), 705600u); EXPECT_EQ(params.CalcInputSampleRate(352800u), 705600u);
...@@ -171,7 +171,7 @@ TEST(PcmTest, ExportDsdU32) ...@@ -171,7 +171,7 @@ TEST(PcmTest, ExportDsdU32)
}; };
PcmExport::Params params; PcmExport::Params params;
params.dsd_u32 = true; params.dsd_mode = PcmExport::DsdMode::U32;
EXPECT_EQ(params.CalcOutputSampleRate(705600u), 176400u); EXPECT_EQ(params.CalcOutputSampleRate(705600u), 176400u);
EXPECT_EQ(params.CalcInputSampleRate(176400u), 705600u); EXPECT_EQ(params.CalcInputSampleRate(176400u), 705600u);
...@@ -199,7 +199,7 @@ TEST(PcmTest, ExportDop) ...@@ -199,7 +199,7 @@ TEST(PcmTest, ExportDop)
}; };
PcmExport::Params params; PcmExport::Params params;
params.dop = true; params.dsd_mode = PcmExport::DsdMode::DOP;
EXPECT_EQ(params.CalcOutputSampleRate(705600u), 352800u); EXPECT_EQ(params.CalcOutputSampleRate(705600u), 352800u);
EXPECT_EQ(params.CalcInputSampleRate(352800u), 705600u); EXPECT_EQ(params.CalcInputSampleRate(352800u), 705600u);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment