aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--NEWS1
-rw-r--r--src/decoder/plugins/DsfDecoderPlugin.cxx56
2 files changed, 47 insertions, 10 deletions
diff --git a/NEWS b/NEWS
index 9c9e02eae..ca9653f55 100644
--- a/NEWS
+++ b/NEWS
@@ -43,6 +43,7 @@ ver 0.19 (not yet released)
- audiofile: log libaudiofile errors
- dsdiff, dsf: report bit rate
- dsf: support DSD512
+ - dsf: support multi-channel files
- dsf: fix noise at end of malformed file
- sndfile: support scanning remote files
- sndfile: support tags "comment", "album", "track", "genre"
diff --git a/src/decoder/plugins/DsfDecoderPlugin.cxx b/src/decoder/plugins/DsfDecoderPlugin.cxx
index 8c6c3a03d..953096a45 100644
--- a/src/decoder/plugins/DsfDecoderPlugin.cxx
+++ b/src/decoder/plugins/DsfDecoderPlugin.cxx
@@ -39,6 +39,8 @@
#include "tag/TagHandler.hxx"
#include "Log.hxx"
+#include <string.h>
+
static constexpr unsigned DSF_BLOCK_SIZE = 4096;
struct DsfMetaData {
@@ -188,6 +190,13 @@ bit_reverse_buffer(uint8_t *p, uint8_t *end)
*p = bit_reverse(*p);
}
+static void
+InterleaveDsfBlockMono(uint8_t *gcc_restrict dest,
+ const uint8_t *gcc_restrict src)
+{
+ memcpy(dest, src, DSF_BLOCK_SIZE);
+}
+
/**
* DSF data is build up of alternating 4096 blocks of DSD samples for left and
* right. Convert the buffer holding 1 block of 4096 DSD left samples and 1
@@ -195,7 +204,8 @@ bit_reverse_buffer(uint8_t *p, uint8_t *end)
* order.
*/
static void
-dsf_to_pcm_order(uint8_t *gcc_restrict dest, const uint8_t *gcc_restrict src)
+InterleaveDsfBlockStereo(uint8_t *gcc_restrict dest,
+ const uint8_t *gcc_restrict src)
{
for (size_t i = 0; i < DSF_BLOCK_SIZE; ++i) {
dest[2 * i] = src[i];
@@ -203,6 +213,36 @@ dsf_to_pcm_order(uint8_t *gcc_restrict dest, const uint8_t *gcc_restrict src)
}
}
+static void
+InterleaveDsfBlockChannel(uint8_t *gcc_restrict dest,
+ const uint8_t *gcc_restrict src,
+ unsigned channels)
+{
+ for (size_t i = 0; i < DSF_BLOCK_SIZE; ++i, dest += channels, ++src)
+ *dest = *src;
+}
+
+static void
+InterleaveDsfBlockGeneric(uint8_t *gcc_restrict dest,
+ const uint8_t *gcc_restrict src,
+ unsigned channels)
+{
+ for (unsigned c = 0; c < channels; ++c, ++dest, src += DSF_BLOCK_SIZE)
+ InterleaveDsfBlockChannel(dest, src, channels);
+}
+
+static void
+InterleaveDsfBlock(uint8_t *gcc_restrict dest, const uint8_t *gcc_restrict src,
+ unsigned channels)
+{
+ if (channels == 1)
+ InterleaveDsfBlockMono(dest, src);
+ else if (channels == 2)
+ InterleaveDsfBlockStereo(dest, src);
+ else
+ InterleaveDsfBlockGeneric(dest, src, channels);
+}
+
/**
* Decode one complete DSF 'data' chunk i.e. a complete song
*/
@@ -212,14 +252,10 @@ dsf_decode_chunk(Decoder &decoder, InputStream &is,
offset_type chunk_size,
bool bitreverse)
{
- uint8_t buffer[DSF_BLOCK_SIZE * 2];
+ /* worst-case buffer size */
+ uint8_t buffer[MAX_CHANNELS * DSF_BLOCK_SIZE];
- const size_t sample_size = sizeof(buffer[0]);
- const size_t frame_size = channels * sample_size;
- const unsigned buffer_frames = sizeof(buffer) / frame_size;
- const unsigned buffer_samples = buffer_frames * frame_size;
- const size_t buffer_size = buffer_samples * sample_size;
- const size_t block_size = buffer_size;
+ const size_t block_size = channels * DSF_BLOCK_SIZE;
while (chunk_size >= block_size) {
chunk_size -= block_size;
@@ -230,8 +266,8 @@ dsf_decode_chunk(Decoder &decoder, InputStream &is,
if (bitreverse)
bit_reverse_buffer(buffer, buffer + block_size);
- uint8_t interleaved_buffer[DSF_BLOCK_SIZE * 2];
- dsf_to_pcm_order(interleaved_buffer, buffer);
+ uint8_t interleaved_buffer[MAX_CHANNELS * DSF_BLOCK_SIZE];
+ InterleaveDsfBlock(interleaved_buffer, buffer, channels);
const auto cmd = decoder_data(decoder, is,
interleaved_buffer, block_size,