aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMax Kellermann <max@duempel.org>2012-10-02 08:29:52 +0200
committerMax Kellermann <max@duempel.org>2012-10-02 08:39:12 +0200
commite166ddf46f16ca115372bc05a880e47deafc23c1 (patch)
tree285d5607f5b9e894d8815e043e6b01137bb96876 /src
parent31e1be75709e7ca69184b9277cfe13e71594b029 (diff)
downloadmpd-e166ddf46f16ca115372bc05a880e47deafc23c1.tar.gz
mpd-e166ddf46f16ca115372bc05a880e47deafc23c1.tar.xz
mpd-e166ddf46f16ca115372bc05a880e47deafc23c1.zip
pcm_channels: support floating point samples
Diffstat (limited to 'src')
-rw-r--r--src/pcm_channels.c71
-rw-r--r--src/pcm_channels.h17
-rw-r--r--src/pcm_convert.c79
3 files changed, 105 insertions, 62 deletions
diff --git a/src/pcm_channels.c b/src/pcm_channels.c
index ec2bd69a5..9d166a437 100644
--- a/src/pcm_channels.c
+++ b/src/pcm_channels.c
@@ -244,3 +244,74 @@ pcm_convert_channels_32(struct pcm_buffer *buffer,
return dest;
}
+
+static void
+pcm_convert_channels_float_1_to_2(float *dest, const float *src,
+ const float *src_end)
+{
+ pcm_convert_channels_24_1_to_2((int32_t *)dest,
+ (const int32_t *)src,
+ (const int32_t *)src_end);
+}
+
+static void
+pcm_convert_channels_float_2_to_1(float *restrict dest,
+ const float *restrict src,
+ const float *restrict src_end)
+{
+ while (src < src_end) {
+ double a = *src++, b = *src++;
+
+ *dest++ = (a + b) / 2;
+ }
+}
+
+static void
+pcm_convert_channels_float_n_to_2(float *dest,
+ unsigned src_channels, const float *src,
+ const float *src_end)
+{
+ unsigned c;
+
+ assert(src_channels > 0);
+
+ while (src < src_end) {
+ double sum = 0;
+ float value;
+
+ for (c = 0; c < src_channels; ++c)
+ sum += *src++;
+ value = sum / (double)src_channels;
+
+ /* XXX this is actually only mono ... */
+ *dest++ = value;
+ *dest++ = value;
+ }
+}
+
+const float *
+pcm_convert_channels_float(struct pcm_buffer *buffer,
+ unsigned dest_channels,
+ unsigned src_channels, const float *src,
+ size_t src_size, size_t *dest_size_r)
+{
+ assert(src_size % (sizeof(*src) * src_channels) == 0);
+
+ size_t dest_size = src_size / src_channels * dest_channels;
+ *dest_size_r = dest_size;
+
+ float *dest = pcm_buffer_get(buffer, dest_size);
+ const float *src_end = pcm_end_pointer(src, src_size);
+
+ if (src_channels == 1 && dest_channels == 2)
+ pcm_convert_channels_float_1_to_2(dest, src, src_end);
+ else if (src_channels == 2 && dest_channels == 1)
+ pcm_convert_channels_float_2_to_1(dest, src, src_end);
+ else if (dest_channels == 2)
+ pcm_convert_channels_float_n_to_2(dest, src_channels, src,
+ src_end);
+ else
+ return NULL;
+
+ return dest;
+}
diff --git a/src/pcm_channels.h b/src/pcm_channels.h
index 1e4a0991f..6da00316d 100644
--- a/src/pcm_channels.h
+++ b/src/pcm_channels.h
@@ -77,4 +77,21 @@ pcm_convert_channels_32(struct pcm_buffer *buffer,
unsigned src_channels, const int32_t *src,
size_t src_size, size_t *dest_size_r);
+/**
+ * Changes the number of channels in 32 bit float PCM data.
+ *
+ * @param buffer the destination pcm_buffer object
+ * @param dest_channels the number of channels requested
+ * @param src_channels the number of channels in the source buffer
+ * @param src the source PCM buffer
+ * @param src_size the number of bytes in #src
+ * @param dest_size_r returns the number of bytes of the destination buffer
+ * @return the destination buffer
+ */
+const float *
+pcm_convert_channels_float(struct pcm_buffer *buffer,
+ unsigned dest_channels,
+ unsigned src_channels, const float *src,
+ size_t src_size, size_t *dest_size_r);
+
#endif
diff --git a/src/pcm_convert.c b/src/pcm_convert.c
index 63f9a1b98..32425143a 100644
--- a/src/pcm_convert.c
+++ b/src/pcm_convert.c
@@ -61,55 +61,6 @@ pcm_convert_reset(struct pcm_convert_state *state)
pcm_resample_reset(&state->resample);
}
-static const void *
-pcm_convert_channels(struct pcm_buffer *buffer, enum sample_format format,
- uint8_t dest_channels,
- uint8_t src_channels, const void *src,
- size_t src_size, size_t *dest_size_r,
- GError **error_r)
-{
- const void *dest = NULL;
-
- switch (format) {
- case SAMPLE_FORMAT_UNDEFINED:
- case SAMPLE_FORMAT_S8:
- case SAMPLE_FORMAT_FLOAT:
- case SAMPLE_FORMAT_DSD:
- g_set_error(error_r, pcm_convert_quark(), 0,
- "Channel conversion not implemented for format '%s'",
- sample_format_to_string(format));
- return NULL;
-
- case SAMPLE_FORMAT_S16:
- dest = pcm_convert_channels_16(buffer, dest_channels,
- src_channels, src,
- src_size, dest_size_r);
- break;
-
- case SAMPLE_FORMAT_S24_P32:
- dest = pcm_convert_channels_24(buffer, dest_channels,
- src_channels, src,
- src_size, dest_size_r);
- break;
-
- case SAMPLE_FORMAT_S32:
- dest = pcm_convert_channels_32(buffer, dest_channels,
- src_channels, src,
- src_size, dest_size_r);
- break;
- }
-
- if (dest == NULL) {
- g_set_error(error_r, pcm_convert_quark(), 0,
- "Conversion from %u to %u channels "
- "is not implemented",
- src_channels, dest_channels);
- return NULL;
- }
-
- return dest;
-}
-
static const int16_t *
pcm_convert_16(struct pcm_convert_state *state,
const struct audio_format *src_format,
@@ -273,19 +224,6 @@ pcm_convert_float(struct pcm_convert_state *state,
assert(dest_format->format == SAMPLE_FORMAT_FLOAT);
- /* convert channels first, hoping the source format is
- supported (float is not) */
-
- if (dest_format->channels != src_format->channels) {
- buffer = pcm_convert_channels(&state->channels_buffer,
- src_format->format,
- dest_format->channels,
- src_format->channels,
- buffer, size, &size, error_r);
- if (buffer == NULL)
- return NULL;
- }
-
/* convert to float now */
buffer = pcm_convert_to_float(&state->format_buffer,
@@ -298,6 +236,23 @@ pcm_convert_float(struct pcm_convert_state *state,
return NULL;
}
+ /* convert channels */
+
+ if (src_format->channels != dest_format->channels) {
+ buffer = pcm_convert_channels_float(&state->channels_buffer,
+ dest_format->channels,
+ src_format->channels,
+ buffer, size, &size);
+ if (buffer == NULL) {
+ g_set_error(error_r, pcm_convert_quark(), 0,
+ "Conversion from %u to %u channels "
+ "is not implemented",
+ src_format->channels,
+ dest_format->channels);
+ return NULL;
+ }
+ }
+
/* resample with float, because this is the best format for
libsamplerate */