Commit ebfdd374 authored by Max Kellermann's avatar Max Kellermann

pcm_export: support DSD to DSD-over-USB conversion

Prepare for removing SAMPLE_FORMAT_DSD_OVER_USB.
parent f6d6110a
...@@ -633,8 +633,9 @@ alsa_setup_or_dsd(struct alsa_data *ad, struct audio_format *audio_format, ...@@ -633,8 +633,9 @@ alsa_setup_or_dsd(struct alsa_data *ad, struct audio_format *audio_format,
if (!success) if (!success)
return false; return false;
pcm_export_open(&ad->export, audio_format->format, pcm_export_open(&ad->export,
packed, reverse_endian); audio_format->format, audio_format->channels,
false, packed, reverse_endian);
return true; return true;
} }
...@@ -777,7 +778,8 @@ alsa_play(struct audio_output *ao, const void *chunk, size_t size, ...@@ -777,7 +778,8 @@ alsa_play(struct audio_output *ao, const void *chunk, size_t size,
if (ret > 0) { if (ret > 0) {
ad->period_position = (ad->period_position + ret) ad->period_position = (ad->period_position + ret)
% ad->period_frames; % 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 && if (ret < 0 && ret != -EAGAIN && ret != -EINTR &&
......
...@@ -540,7 +540,7 @@ oss_probe_sample_format(int fd, enum sample_format sample_format, ...@@ -540,7 +540,7 @@ oss_probe_sample_format(int fd, enum sample_format sample_format,
*oss_format_r = oss_format; *oss_format_r = oss_format;
#ifdef AFMT_S24_PACKED #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,
oss_format == AFMT_S24_PACKED && oss_format == AFMT_S24_PACKED &&
G_BYTE_ORDER != G_LITTLE_ENDIAN); G_BYTE_ORDER != G_LITTLE_ENDIAN);
...@@ -755,8 +755,12 @@ oss_output_play(struct audio_output *ao, const void *chunk, size_t size, ...@@ -755,8 +755,12 @@ oss_output_play(struct audio_output *ao, const void *chunk, size_t size,
while (true) { while (true) {
ret = write(od->fd, chunk, size); ret = write(od->fd, chunk, size);
if (ret > 0) if (ret > 0) {
return (size_t)ret; #ifdef AFMT_S24_PACKED
ret = pcm_export_source_size(&od->export, ret);
#endif
return ret;
}
if (ret < 0 && errno != EINTR) { if (ret < 0 && errno != EINTR) {
g_set_error(error, oss_output_quark(), errno, g_set_error(error, oss_output_quark(), errno,
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "config.h" #include "config.h"
#include "pcm_export.h" #include "pcm_export.h"
#include "pcm_dsd_usb.h"
#include "pcm_pack.h" #include "pcm_pack.h"
#include "util/byte_reverse.h" #include "util/byte_reverse.h"
...@@ -27,19 +28,31 @@ pcm_export_init(struct pcm_export_state *state) ...@@ -27,19 +28,31 @@ pcm_export_init(struct pcm_export_state *state)
{ {
pcm_buffer_init(&state->reverse_buffer); pcm_buffer_init(&state->reverse_buffer);
pcm_buffer_init(&state->pack_buffer); pcm_buffer_init(&state->pack_buffer);
pcm_buffer_init(&state->dsd_buffer);
} }
void pcm_export_deinit(struct pcm_export_state *state) void pcm_export_deinit(struct pcm_export_state *state)
{ {
pcm_buffer_deinit(&state->reverse_buffer); pcm_buffer_deinit(&state->reverse_buffer);
pcm_buffer_deinit(&state->pack_buffer); pcm_buffer_deinit(&state->pack_buffer);
pcm_buffer_deinit(&state->dsd_buffer);
} }
void void
pcm_export_open(struct pcm_export_state *state, pcm_export_open(struct pcm_export_state *state,
enum sample_format sample_format, enum sample_format sample_format, unsigned channels,
bool pack, bool reverse_endian) 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->pack24 = pack && (sample_format == SAMPLE_FORMAT_S24_P32 || sample_format == SAMPLE_FORMAT_DSD_OVER_USB);
state->reverse_endian = 0; state->reverse_endian = 0;
...@@ -58,6 +71,10 @@ const void * ...@@ -58,6 +71,10 @@ const void *
pcm_export(struct pcm_export_state *state, const void *data, size_t size, pcm_export(struct pcm_export_state *state, const void *data, size_t size,
size_t *dest_size_r) 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) { if (state->pack24) {
assert(size % 4 == 0); assert(size % 4 == 0);
...@@ -90,3 +107,13 @@ pcm_export(struct pcm_export_state *state, const void *data, size_t size, ...@@ -90,3 +107,13 @@ pcm_export(struct pcm_export_state *state, const void *data, size_t size,
*dest_size_r = size; *dest_size_r = size;
return data; 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;
}
...@@ -35,6 +35,14 @@ struct audio_format; ...@@ -35,6 +35,14 @@ struct audio_format;
*/ */
struct pcm_export_state { 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. * The buffer is used to pack samples, removing padding.
* *
* @see #pack24 * @see #pack24
...@@ -49,6 +57,18 @@ struct pcm_export_state { ...@@ -49,6 +57,18 @@ struct pcm_export_state {
struct pcm_buffer reverse_buffer; 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? * Pack 24 bit samples?
*/ */
bool pack24; bool pack24;
...@@ -80,11 +100,13 @@ pcm_export_deinit(struct pcm_export_state *state); ...@@ -80,11 +100,13 @@ pcm_export_deinit(struct pcm_export_state *state);
* times to reuse the object, until pcm_export_deinit() is called. * times to reuse the object, until pcm_export_deinit() is called.
* *
* This function cannot fail. * This function cannot fail.
*
* @param channels the number of channels; ignored unless dsd_usb is set
*/ */
void void
pcm_export_open(struct pcm_export_state *state, pcm_export_open(struct pcm_export_state *state,
enum sample_format sample_format, enum sample_format sample_format, unsigned channels,
bool pack, bool reverse_endian); bool dsd_usb, bool pack, bool reverse_endian);
/** /**
* Export a PCM buffer. * Export a PCM buffer.
...@@ -99,4 +121,13 @@ const void * ...@@ -99,4 +121,13 @@ const void *
pcm_export(struct pcm_export_state *state, const void *src, size_t src_size, pcm_export(struct pcm_export_state *state, const void *src, size_t src_size,
size_t *dest_size_r); 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 #endif
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