diff options
author | Max Kellermann <max@duempel.org> | 2011-10-08 15:36:29 +0200 |
---|---|---|
committer | Max Kellermann <max@duempel.org> | 2011-10-20 02:55:05 +0200 |
commit | 8465c5fe0e154b96edea992fdcb574060934b9e5 (patch) | |
tree | 243362cb3db60395f4bdb29170f096b73b3a8937 | |
parent | 42e248a8da0f2043e752166d4a34615c850384ec (diff) | |
download | mpd-8465c5fe0e154b96edea992fdcb574060934b9e5.tar.gz mpd-8465c5fe0e154b96edea992fdcb574060934b9e5.tar.xz mpd-8465c5fe0e154b96edea992fdcb574060934b9e5.zip |
pcm_format: implement conversion to float
-rw-r--r-- | src/pcm_convert.c | 69 | ||||
-rw-r--r-- | src/pcm_format.c | 135 | ||||
-rw-r--r-- | src/pcm_format.h | 15 |
3 files changed, 218 insertions, 1 deletions
diff --git a/src/pcm_convert.c b/src/pcm_convert.c index 58137fd0e..daf1cd234 100644 --- a/src/pcm_convert.c +++ b/src/pcm_convert.c @@ -24,6 +24,7 @@ #include "pcm_byteswap.h" #include "pcm_pack.h" #include "audio_format.h" +#include "glib_compat.h" #include <assert.h> #include <string.h> @@ -56,7 +57,6 @@ void pcm_convert_deinit(struct pcm_convert_state *state) pcm_buffer_deinit(&state->byteswap_buffer); } -G_GNUC_UNUSED static const void * pcm_convert_channels(struct pcm_buffer *buffer, enum sample_format format, uint8_t dest_channels, @@ -312,6 +312,67 @@ pcm_convert_32(struct pcm_convert_state *state, return buf; } +static const float * +pcm_convert_float(struct pcm_convert_state *state, + const struct audio_format *src_format, + const void *src_buffer, size_t src_size, + const struct audio_format *dest_format, size_t *dest_size_r, + GError **error_r) +{ + const float *buffer = src_buffer; + size_t size = src_size; + + assert(dest_format->format == SAMPLE_FORMAT_FLOAT); + + if (src_format->reverse_endian || dest_format->reverse_endian) { + g_set_error_literal(error_r, pcm_convert_quark(), 0, + "Reverse endian not supported"); + return NULL; + } + + /* 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, + src_format->format, + buffer, size, &size); + if (buffer == NULL) { + g_set_error(error_r, pcm_convert_quark(), 0, + "Conversion from %s to float is not implemented", + sample_format_to_string(src_format->format)); + return NULL; + } + + /* resample with float, because this is the best format for + libsamplerate */ + + if (src_format->sample_rate != dest_format->sample_rate) { + buffer = pcm_resample_float(&state->resample, + dest_format->channels, + src_format->sample_rate, + buffer, size, + dest_format->sample_rate, &size, + error_r); + if (buffer == NULL) + return NULL; + } + + *dest_size_r = size; + return buffer; +} + const void * pcm_convert(struct pcm_convert_state *state, const struct audio_format *src_format, @@ -359,6 +420,12 @@ pcm_convert(struct pcm_convert_state *state, dest_format, dest_size_r, error_r); + case SAMPLE_FORMAT_FLOAT: + return pcm_convert_float(state, + src_format, src, src_size, + dest_format, dest_size_r, + error_r); + default: g_set_error(error_r, pcm_convert_quark(), 0, "PCM conversion to %s is not implemented", diff --git a/src/pcm_format.c b/src/pcm_format.c index bb731b2b6..a20d8bb8d 100644 --- a/src/pcm_format.c +++ b/src/pcm_format.c @@ -418,3 +418,138 @@ pcm_convert_to_32(struct pcm_buffer *buffer, return NULL; } + +static void +pcm_convert_8_to_float(float *out, const int8_t *in, const int8_t *in_end) +{ + enum { in_bits = sizeof(*in) * 8 }; + static const float factor = 2.0f / (1 << in_bits); + while (in < in_end) + *out++ = (float)*in++ * factor; +} + +static void +pcm_convert_16_to_float(float *out, const int16_t *in, const int16_t *in_end) +{ + enum { in_bits = sizeof(*in) * 8 }; + static const float factor = 2.0f / (1 << in_bits); + while (in < in_end) + *out++ = (float)*in++ * factor; +} + +static void +pcm_convert_24_to_float(float *out, const int32_t *in, const int32_t *in_end) +{ + enum { in_bits = 24 }; + static const float factor = 2.0f / (1 << in_bits); + while (in < in_end) + *out++ = (float)*in++ * factor; +} + +static void +pcm_convert_32_to_float(float *out, const int32_t *in, const int32_t *in_end) +{ + enum { in_bits = sizeof(*in) * 8 }; + static const float factor = 0.5f / (1 << (in_bits - 2)); + while (in < in_end) + *out++ = (float)*in++ * factor; +} + +static float * +pcm_allocate_8_to_float(struct pcm_buffer *buffer, + const int8_t *src, size_t src_size, + size_t *dest_size_r) +{ + float *dest; + *dest_size_r = src_size / sizeof(*src) * sizeof(*dest); + dest = pcm_buffer_get(buffer, *dest_size_r); + pcm_convert_8_to_float(dest, src, pcm_end_pointer(src, src_size)); + return dest; +} + +static float * +pcm_allocate_16_to_float(struct pcm_buffer *buffer, + const int16_t *src, size_t src_size, + size_t *dest_size_r) +{ + float *dest; + *dest_size_r = src_size * 2; + assert(*dest_size_r == src_size / sizeof(*src) * sizeof(*dest)); + dest = pcm_buffer_get(buffer, *dest_size_r); + pcm_convert_16_to_float(dest, src, pcm_end_pointer(src, src_size)); + return dest; +} + +static float * +pcm_allocate_24_to_float(struct pcm_buffer *buffer, + const uint8_t *src, + size_t src_size, size_t *dest_size_r) +{ + /* convert to S24_P32 first */ + int32_t *tmp = pcm_allocate_24_to_24p32(buffer, src, src_size, + dest_size_r); + + /* convert to float in-place */ + float *dest = (float *)tmp; + pcm_convert_24_to_float(dest, tmp, pcm_end_pointer(tmp, *dest_size_r)); + return dest; +} + +static float * +pcm_allocate_24p32_to_float(struct pcm_buffer *buffer, + const int32_t *src, size_t src_size, + size_t *dest_size_r) +{ + *dest_size_r = src_size; + float *dest = pcm_buffer_get(buffer, *dest_size_r); + pcm_convert_24_to_float(dest, src, pcm_end_pointer(src, src_size)); + return dest; +} + +static float * +pcm_allocate_32_to_float(struct pcm_buffer *buffer, + const int32_t *src, size_t src_size, + size_t *dest_size_r) +{ + *dest_size_r = src_size; + float *dest = pcm_buffer_get(buffer, *dest_size_r); + pcm_convert_32_to_float(dest, src, pcm_end_pointer(src, src_size)); + return dest; +} + +const float * +pcm_convert_to_float(struct pcm_buffer *buffer, + enum sample_format src_format, const void *src, + size_t src_size, size_t *dest_size_r) +{ + switch (src_format) { + case SAMPLE_FORMAT_UNDEFINED: + break; + + case SAMPLE_FORMAT_S8: + return pcm_allocate_8_to_float(buffer, + src, src_size, dest_size_r); + + case SAMPLE_FORMAT_S16: + return pcm_allocate_16_to_float(buffer, + src, src_size, dest_size_r); + + case SAMPLE_FORMAT_S24: + return pcm_allocate_24_to_float(buffer, + src, src_size, dest_size_r); + + case SAMPLE_FORMAT_S24_P32: + return pcm_allocate_24p32_to_float(buffer, + src, src_size, dest_size_r); + + case SAMPLE_FORMAT_S32: + return pcm_allocate_32_to_float(buffer, + src, src_size, dest_size_r); + + case SAMPLE_FORMAT_FLOAT: + *dest_size_r = src_size; + return src; + } + + return NULL; +} diff --git a/src/pcm_format.h b/src/pcm_format.h index 54cc32020..48bcd0662 100644 --- a/src/pcm_format.h +++ b/src/pcm_format.h @@ -75,4 +75,19 @@ pcm_convert_to_32(struct pcm_buffer *buffer, enum sample_format src_format, const void *src, size_t src_size, size_t *dest_size_r); +/** + * Converts PCM samples to 32 bit floating point. + * + * @param buffer a pcm_buffer object + * @param bits the number of in the source buffer + * @param src the source PCM buffer + * @param src_size the size of #src in bytes + * @param dest_size_r returns the number of bytes of the destination buffer + * @return the destination buffer + */ +const float * +pcm_convert_to_float(struct pcm_buffer *buffer, + enum sample_format src_format, const void *src, + size_t src_size, size_t *dest_size_r); + #endif |