diff options
author | Max Kellermann <max@duempel.org> | 2012-10-02 08:29:52 +0200 |
---|---|---|
committer | Max Kellermann <max@duempel.org> | 2012-10-02 08:39:12 +0200 |
commit | e166ddf46f16ca115372bc05a880e47deafc23c1 (patch) | |
tree | 285d5607f5b9e894d8815e043e6b01137bb96876 /src | |
parent | 31e1be75709e7ca69184b9277cfe13e71594b029 (diff) | |
download | mpd-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.c | 71 | ||||
-rw-r--r-- | src/pcm_channels.h | 17 | ||||
-rw-r--r-- | src/pcm_convert.c | 79 |
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 */ |