pcm/Export: convert the DSD bools to an enum

These options are exclusive.
This commit is contained in:
Max Kellermann 2019-06-17 22:20:28 +02:00
parent c75dc4a647
commit 28e07e900f
6 changed files with 113 additions and 53 deletions

View File

@ -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,
: 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,
: 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;
} }

View File

@ -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)
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

View File

@ -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)
#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 =

View File

@ -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
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
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
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
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) {
sample_rate *= 2; case DsdMode::NONE:
break;
if (dsd_u32) case DsdMode::U16:
sample_rate *= 2;
break;
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
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
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;

View File

@ -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 {
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:
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;

View File

@ -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)
}; };
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)
}; };
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);