aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMax Kellermann <max@duempel.org>2012-03-27 00:03:44 +0200
committerMax Kellermann <max@duempel.org>2012-03-27 01:17:11 +0200
commitebfdd37451cea0151de74005d83db5ac31fcaf77 (patch)
treee87c20dc2951cdb617d78201d14e0b65476d2b2c
parentf6d6110aaaf6ba3e252323c2bf6360ea9782dff2 (diff)
downloadmpd-ebfdd37451cea0151de74005d83db5ac31fcaf77.tar.gz
mpd-ebfdd37451cea0151de74005d83db5ac31fcaf77.tar.xz
mpd-ebfdd37451cea0151de74005d83db5ac31fcaf77.zip
pcm_export: support DSD to DSD-over-USB conversion
Prepare for removing SAMPLE_FORMAT_DSD_OVER_USB.
-rw-r--r--src/output/alsa_output_plugin.c8
-rw-r--r--src/output/oss_output_plugin.c10
-rw-r--r--src/pcm_export.c31
-rw-r--r--src/pcm_export.h35
4 files changed, 74 insertions, 10 deletions
diff --git a/src/output/alsa_output_plugin.c b/src/output/alsa_output_plugin.c
index 21c3b1d22..d131003e3 100644
--- a/src/output/alsa_output_plugin.c
+++ b/src/output/alsa_output_plugin.c
@@ -633,8 +633,9 @@ alsa_setup_or_dsd(struct alsa_data *ad, struct audio_format *audio_format,
if (!success)
return false;
- pcm_export_open(&ad->export, audio_format->format,
- packed, reverse_endian);
+ pcm_export_open(&ad->export,
+ audio_format->format, audio_format->channels,
+ false, packed, reverse_endian);
return true;
}
@@ -777,7 +778,8 @@ alsa_play(struct audio_output *ao, const void *chunk, size_t size,
if (ret > 0) {
ad->period_position = (ad->period_position + ret)
% ad->period_frames;
- return ret * ad->in_frame_size;
+ return pcm_export_source_size(&ad->export,
+ ret * ad->in_frame_size);
}
if (ret < 0 && ret != -EAGAIN && ret != -EINTR &&
diff --git a/src/output/oss_output_plugin.c b/src/output/oss_output_plugin.c
index 0724ed4c2..85bdc37dc 100644
--- a/src/output/oss_output_plugin.c
+++ b/src/output/oss_output_plugin.c
@@ -540,7 +540,7 @@ oss_probe_sample_format(int fd, enum sample_format sample_format,
*oss_format_r = oss_format;
#ifdef AFMT_S24_PACKED
- pcm_export_open(export, sample_format,
+ pcm_export_open(export, sample_format, 0, false,
oss_format == AFMT_S24_PACKED,
oss_format == AFMT_S24_PACKED &&
G_BYTE_ORDER != G_LITTLE_ENDIAN);
@@ -755,8 +755,12 @@ oss_output_play(struct audio_output *ao, const void *chunk, size_t size,
while (true) {
ret = write(od->fd, chunk, size);
- if (ret > 0)
- return (size_t)ret;
+ if (ret > 0) {
+#ifdef AFMT_S24_PACKED
+ ret = pcm_export_source_size(&od->export, ret);
+#endif
+ return ret;
+ }
if (ret < 0 && errno != EINTR) {
g_set_error(error, oss_output_quark(), errno,
diff --git a/src/pcm_export.c b/src/pcm_export.c
index 134acc18f..66ed6d351 100644
--- a/src/pcm_export.c
+++ b/src/pcm_export.c
@@ -19,6 +19,7 @@
#include "config.h"
#include "pcm_export.h"
+#include "pcm_dsd_usb.h"
#include "pcm_pack.h"
#include "util/byte_reverse.h"
@@ -27,19 +28,31 @@ pcm_export_init(struct pcm_export_state *state)
{
pcm_buffer_init(&state->reverse_buffer);
pcm_buffer_init(&state->pack_buffer);
+ pcm_buffer_init(&state->dsd_buffer);
}
void pcm_export_deinit(struct pcm_export_state *state)
{
pcm_buffer_deinit(&state->reverse_buffer);
pcm_buffer_deinit(&state->pack_buffer);
+ pcm_buffer_deinit(&state->dsd_buffer);
}
void
pcm_export_open(struct pcm_export_state *state,
- enum sample_format sample_format,
- bool pack, bool reverse_endian)
+ enum sample_format sample_format, unsigned channels,
+ bool dsd_usb, bool pack, bool reverse_endian)
{
+ assert(audio_valid_sample_format(sample_format));
+ assert(!dsd_usb || audio_valid_channel_count(channels));
+
+ state->channels = channels;
+ state->dsd_usb = dsd_usb && sample_format == SAMPLE_FORMAT_DSD;
+ if (state->dsd_usb)
+ /* after the conversion to DSD-over-USB, the DSD
+ samples are stuffed inside fake 24 bit samples */
+ sample_format = SAMPLE_FORMAT_S24_P32;
+
state->pack24 = pack && (sample_format == SAMPLE_FORMAT_S24_P32 || sample_format == SAMPLE_FORMAT_DSD_OVER_USB);
state->reverse_endian = 0;
@@ -58,6 +71,10 @@ const void *
pcm_export(struct pcm_export_state *state, const void *data, size_t size,
size_t *dest_size_r)
{
+ if (state->dsd_usb)
+ data = pcm_dsd_to_usb(&state->dsd_buffer, state->channels,
+ data, size, &size);
+
if (state->pack24) {
assert(size % 4 == 0);
@@ -90,3 +107,13 @@ pcm_export(struct pcm_export_state *state, const void *data, size_t size,
*dest_size_r = size;
return data;
}
+
+size_t
+pcm_export_source_size(const struct pcm_export_state *state, size_t size)
+{
+ if (state->dsd_usb)
+ /* DSD over USB doubles the transport size */
+ size /= 2;
+
+ return size;
+}
diff --git a/src/pcm_export.h b/src/pcm_export.h
index 7ce35c096..c132c7169 100644
--- a/src/pcm_export.h
+++ b/src/pcm_export.h
@@ -35,6 +35,14 @@ struct audio_format;
*/
struct pcm_export_state {
/**
+ * The buffer is used to convert DSD samples to the
+ * DSD-over-USB format.
+ *
+ * @see #dsd_usb
+ */
+ struct pcm_buffer dsd_buffer;
+
+ /**
* The buffer is used to pack samples, removing padding.
*
* @see #pack24
@@ -49,6 +57,18 @@ struct pcm_export_state {
struct pcm_buffer reverse_buffer;
/**
+ * The number of channels.
+ */
+ uint8_t channels;
+
+ /**
+ * Convert DSD to DSD-over-USB? Input format must be
+ * SAMPLE_FORMAT_DSD and output format must be
+ * SAMPLE_FORMAT_S24_P32.
+ */
+ bool dsd_usb;
+
+ /**
* Pack 24 bit samples?
*/
bool pack24;
@@ -80,11 +100,13 @@ pcm_export_deinit(struct pcm_export_state *state);
* times to reuse the object, until pcm_export_deinit() is called.
*
* This function cannot fail.
+ *
+ * @param channels the number of channels; ignored unless dsd_usb is set
*/
void
pcm_export_open(struct pcm_export_state *state,
- enum sample_format sample_format,
- bool pack, bool reverse_endian);
+ enum sample_format sample_format, unsigned channels,
+ bool dsd_usb, bool pack, bool reverse_endian);
/**
* Export a PCM buffer.
@@ -99,4 +121,13 @@ const void *
pcm_export(struct pcm_export_state *state, const void *src, size_t src_size,
size_t *dest_size_r);
+/**
+ * Converts the number of consumed bytes from the pcm_export()
+ * destination buffer to the according number of bytes from the
+ * pcm_export() source buffer.
+ */
+G_GNUC_PURE
+size_t
+pcm_export_source_size(const struct pcm_export_state *state, size_t dest_size);
+
#endif