Commit 58bb866e authored by Max Kellermann's avatar Max Kellermann

decoder/HybridDSD: add code comments

parent b2ec5d0f
...@@ -2766,12 +2766,16 @@ run</programlisting> ...@@ -2766,12 +2766,16 @@ run</programlisting>
<title><varname>hybrid_dsd</varname></title> <title><varname>hybrid_dsd</varname></title>
<para> <para>
Hybrid-DSD is a MP4 container file <ulink
(<filename>*.m4a</filename>) which contains both ALAC and url="http://dsdmaster.blogspot.de/p/bitperfect-introduces-hybrid-dsd-file.html">Hybrid-DSD</ulink>
DSD data. It is disabled by default, and works only if you is a MP4 container file (<filename>*.m4a</filename>) which
explicitly enable it. Without this plugin, the ALAC parts contains both ALAC and DSD data. It is disabled by default,
gets handled by the <link linkend="ffmpeg_decoder">FFmpeg and works only if you explicitly enable it. Without this
decoder plugin</link>. plugin, the ALAC parts gets handled by the <link
linkend="ffmpeg_decoder">FFmpeg decoder plugin</link>. This
plugin should be enabled only if you have a bit-perfect
playback path to a DSD-capable DAC; for everybody else,
playing back the ALAC copy of the file is better.
</para> </para>
</section> </section>
......
...@@ -36,6 +36,9 @@ namespace { ...@@ -36,6 +36,9 @@ namespace {
static bool static bool
InitHybridDsdDecoder(const ConfigBlock &block) InitHybridDsdDecoder(const ConfigBlock &block)
{ {
/* this plugin is disabled by default because for people
without a DSD DAC, the PCM (=ALAC) part of the file is
better */
if (block.GetBlockParam("enabled") == nullptr) { if (block.GetBlockParam("enabled") == nullptr) {
LogInfo(hybrid_dsd_domain, LogInfo(hybrid_dsd_domain,
"The Hybrid DSD decoder is disabled because it was not explicitly enabled"); "The Hybrid DSD decoder is disabled because it was not explicitly enabled");
...@@ -45,6 +48,10 @@ InitHybridDsdDecoder(const ConfigBlock &block) ...@@ -45,6 +48,10 @@ InitHybridDsdDecoder(const ConfigBlock &block)
return true; return true;
} }
/**
* This exception gets thrown by FindHybridDsdData() to indicate a
* file format error.
*/
struct UnsupportedFile {}; struct UnsupportedFile {};
struct Mp4ChunkHeader { struct Mp4ChunkHeader {
...@@ -98,6 +105,7 @@ static std::pair<AudioFormat, offset_type> ...@@ -98,6 +105,7 @@ static std::pair<AudioFormat, offset_type>
FindHybridDsdData(DecoderClient &client, InputStream &input) FindHybridDsdData(DecoderClient &client, InputStream &input)
{ {
auto audio_format = AudioFormat::Undefined(); auto audio_format = AudioFormat::Undefined();
bool found_version = false;
while (true) { while (true) {
auto header = ReadHeader(client, input); auto header = ReadHeader(client, input);
...@@ -107,12 +115,13 @@ FindHybridDsdData(DecoderClient &client, InputStream &input) ...@@ -107,12 +115,13 @@ FindHybridDsdData(DecoderClient &client, InputStream &input)
size_t remaining = header_size - sizeof(header); size_t remaining = header_size - sizeof(header);
if (memcmp(header.type, "bphv", 4) == 0) { if (memcmp(header.type, "bphv", 4) == 0) {
/* ? */ /* version; this plugin knows only version
1 */
if (remaining != 4 || ReadBE32(client, input) != 1) if (remaining != 4 || ReadBE32(client, input) != 1)
throw UnsupportedFile(); throw UnsupportedFile();
remaining -= 4; remaining -= 4;
audio_format.format = SampleFormat::DSD; found_version = true;
} else if (memcmp(header.type, "bphc", 4) == 0) { } else if (memcmp(header.type, "bphc", 4) == 0) {
/* channel count */ /* channel count */
if (remaining != 4) if (remaining != 4)
...@@ -139,18 +148,23 @@ FindHybridDsdData(DecoderClient &client, InputStream &input) ...@@ -139,18 +148,23 @@ FindHybridDsdData(DecoderClient &client, InputStream &input)
audio_format.sample_rate = sample_rate; audio_format.sample_rate = sample_rate;
} else if (memcmp(header.type, "bphf", 4) == 0) { } else if (memcmp(header.type, "bphf", 4) == 0) {
/* ? */ /* format: 0 = plain DSD; 1 = DST compressed
(only plain DSD is understood by this
plugin) */
if (remaining != 4 || ReadBE32(client, input) != 0) if (remaining != 4 || ReadBE32(client, input) != 0)
throw UnsupportedFile(); throw UnsupportedFile();
remaining -= 4; remaining -= 4;
audio_format.format = SampleFormat::DSD;
} else if (memcmp(header.type, "bphd", 4) == 0) { } else if (memcmp(header.type, "bphd", 4) == 0) {
/* the actual DSD data */ /* the actual DSD data */
if (!audio_format.IsValid()) if (!found_version || !audio_format.IsValid())
throw UnsupportedFile(); throw UnsupportedFile();
return std::make_pair(audio_format, remaining); return std::make_pair(audio_format, remaining);
} }
/* skip this chunk payload */
input.LockSkip(remaining); input.LockSkip(remaining);
} }
} }
...@@ -175,6 +189,8 @@ HybridDsdDecode(DecoderClient &client, InputStream &input) ...@@ -175,6 +189,8 @@ HybridDsdDecode(DecoderClient &client, InputStream &input)
frame_size = result.first.GetFrameSize(); frame_size = result.first.GetFrameSize();
remaining_bytes = result.second; remaining_bytes = result.second;
} catch (UnsupportedFile) { } catch (UnsupportedFile) {
/* not a Hybrid-DSD file; let the next decoder plugin
(e.g. FFmpeg) handle it */
return; return;
} }
...@@ -195,6 +211,7 @@ HybridDsdDecode(DecoderClient &client, InputStream &input) ...@@ -195,6 +211,7 @@ HybridDsdDecode(DecoderClient &client, InputStream &input)
break; break;
} }
/* fill the buffer */
auto w = buffer.Write(); auto w = buffer.Write();
if (!w.empty()) { if (!w.empty()) {
if (remaining_bytes < (1<<30ull) && if (remaining_bytes < (1<<30ull) &&
...@@ -210,6 +227,7 @@ HybridDsdDecode(DecoderClient &client, InputStream &input) ...@@ -210,6 +227,7 @@ HybridDsdDecode(DecoderClient &client, InputStream &input)
buffer.Append(nbytes); buffer.Append(nbytes);
} }
/* submit the buffer to our client */
auto r = buffer.Read(); auto r = buffer.Read();
auto n_frames = r.size / frame_size; auto n_frames = r.size / frame_size;
if (n_frames > 0) { if (n_frames > 0) {
...@@ -233,6 +251,8 @@ const struct DecoderPlugin hybrid_dsd_decoder_plugin = { ...@@ -233,6 +251,8 @@ const struct DecoderPlugin hybrid_dsd_decoder_plugin = {
HybridDsdDecode, HybridDsdDecode,
nullptr, nullptr,
nullptr, nullptr,
/* no scan method here; the FFmpeg plugin will do that for us,
and we only do the decoding */
nullptr, nullptr,
nullptr, nullptr,
hybrid_dsd_suffixes, hybrid_dsd_suffixes,
......
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