aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMax Kellermann <max@duempel.org>2012-03-20 01:01:12 +0100
committerMax Kellermann <max@duempel.org>2012-03-22 01:14:51 +0100
commit81208d78acb260c7c2e6debc765042dd5e98f862 (patch)
tree32993048254599ac9d5dfb26a0ac182863d5db73 /src
parentda8b01771ff425dd30dca1cb8a8fe943d2ecc90b (diff)
pcm_dsd: implement DSD to 24 bit USB conversion
Implements the dCS suggested standard: http://www.dcsltd.co.uk/page/assets/DSDoverUSB.pdf
Diffstat (limited to 'src')
-rw-r--r--src/pcm_convert.c35
-rw-r--r--src/pcm_dsd_usb.c74
-rw-r--r--src/pcm_dsd_usb.h41
3 files changed, 150 insertions, 0 deletions
diff --git a/src/pcm_convert.c b/src/pcm_convert.c
index 03172b61..f19a9522 100644
--- a/src/pcm_convert.c
+++ b/src/pcm_convert.c
@@ -22,6 +22,7 @@
#include "pcm_channels.h"
#include "pcm_format.h"
#include "pcm_pack.h"
+#include "pcm_dsd_usb.h"
#include "audio_format.h"
#include "glib_compat.h"
@@ -325,6 +326,40 @@ pcm_convert(struct pcm_convert_state *state,
size_t *dest_size_r,
GError **error_r)
{
+ struct audio_format usb_format;
+
+ if (src_format->format == SAMPLE_FORMAT_DSD &&
+ dest_format->format == SAMPLE_FORMAT_DSD_OVER_USB) {
+ size_t u_size;
+ const uint32_t *u = pcm_dsd_to_usb(&state->dsd.buffer,
+ src_format->channels,
+ src, src_size,
+ &u_size);
+ if (u == NULL) {
+ g_set_error_literal(error_r, pcm_convert_quark(), 0,
+ "DSD to USB conversion failed");
+ return NULL;
+ }
+
+ usb_format = *src_format;
+ usb_format.format = SAMPLE_FORMAT_DSD_OVER_USB;
+
+ /* each DSD-over-USB sample contains 2 DSD bytes (16
+ DSD bits), which means the sample rate must be
+ halved; this is not the real 1 bit sample rate, but
+ MPD's point of view */
+ usb_format.sample_rate = usb_format.sample_rate / 2;
+
+ if (audio_format_equals(&usb_format, dest_format)) {
+ *dest_size_r = u_size;
+ return u;
+ }
+
+ src_format = &usb_format;
+ src = u;
+ src_size = u_size;
+ }
+
struct audio_format float_format;
if (src_format->format == SAMPLE_FORMAT_DSD) {
size_t f_size;
diff --git a/src/pcm_dsd_usb.c b/src/pcm_dsd_usb.c
new file mode 100644
index 00000000..976420c0
--- /dev/null
+++ b/src/pcm_dsd_usb.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "pcm_dsd_usb.h"
+#include "pcm_buffer.h"
+#include "audio_format.h"
+
+G_GNUC_CONST
+static inline uint32_t
+pcm_two_dsd_to_usb(uint8_t a, uint8_t b)
+{
+ return 0xffaa0000 | (a << 8) | b;
+}
+
+const uint32_t *
+pcm_dsd_to_usb(struct pcm_buffer *buffer, unsigned channels,
+ const uint8_t *src, size_t src_size,
+ size_t *dest_size_r)
+{
+ assert(buffer != NULL);
+ assert(audio_valid_channel_count(channels));
+ assert(src != NULL);
+ assert(src_size > 0);
+ assert(src_size % channels == 0);
+
+ const unsigned num_src_samples = src_size;
+ const unsigned num_src_frames = num_src_samples / channels;
+
+ /* this rounds down and discards the last odd frame; not
+ elegant, but good enough for now */
+ const unsigned num_frames = num_src_frames / 2;
+ const unsigned num_samples = num_frames * channels;
+
+ const size_t dest_size = num_samples * 4;
+ *dest_size_r = dest_size;
+ uint32_t *const dest0 = pcm_buffer_get(buffer, dest_size),
+ *dest = dest0;
+
+ for (unsigned i = num_frames; i > 0; --i) {
+ for (unsigned c = channels; c > 0; --c) {
+ /* each 24 bit sample has 16 DSD sample bits
+ plus the magic 0xaa marker */
+
+ *dest++ = pcm_two_dsd_to_usb(src[0], src[channels]);
+
+ /* seek the source pointer to the next
+ channel */
+ ++src;
+ }
+
+ /* skip the second byte of each channel, because we
+ have already copied it */
+ src += channels;
+ }
+
+ return dest0;
+}
diff --git a/src/pcm_dsd_usb.h b/src/pcm_dsd_usb.h
new file mode 100644
index 00000000..3e7ad8fa
--- /dev/null
+++ b/src/pcm_dsd_usb.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_PCM_DSD_USB_H
+#define MPD_PCM_DSD_USB_H
+
+#include "check.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stddef.h>
+
+struct pcm_buffer;
+
+/**
+ * Pack DSD 1 bit samples into (padded) 24 bit PCM samples for
+ * playback over USB, according to the dCS suggested standard:
+ * http://www.dcsltd.co.uk/page/assets/DSDoverUSB.pdf
+ */
+const uint32_t *
+pcm_dsd_to_usb(struct pcm_buffer *buffer, unsigned channels,
+ const uint8_t *src, size_t src_size,
+ size_t *dest_size_r);
+
+#endif