From 545685bc3209d9cfdf6c4b9aeee4715edd453dc1 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Sat, 8 Oct 2011 10:25:06 +0200 Subject: audio_format: basic support for floating point samples Support for conversion from float to 16, 24 and 32 bit integer samples. --- NEWS | 1 + doc/user.xml | 3 +- src/audio_format.c | 3 ++ src/audio_format.h | 8 +++++ src/audio_parser.c | 6 ++++ src/decoder/flac_pcm.c | 1 + src/output/alsa_output_plugin.c | 3 ++ src/output/oss_output_plugin.c | 1 + src/pcm_byteswap.c | 1 + src/pcm_format.c | 75 +++++++++++++++++++++++++++++++++++++++++ src/pcm_mix.c | 8 +++++ src/pcm_utils.h | 28 +++++++++++++++ src/pcm_volume.c | 4 +++ 13 files changed, 141 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index e3e7de967..ef6da2ed4 100644 --- a/NEWS +++ b/NEWS @@ -25,6 +25,7 @@ ver 0.17 (2011/??/??) * state_file: add option "restore_paused" * cue: show CUE track numbers * allow port specification in "bind_to_address" settings +* support floating point samples ver 0.16.5 (2010/10/09) diff --git a/doc/user.xml b/doc/user.xml index 394b8b0de..4bd849f96 100644 --- a/doc/user.xml +++ b/doc/user.xml @@ -332,7 +332,8 @@ cd mpd-version 24_3 (signed 24 bit integer samples, no padding, 3 bytes per sample), 32 (signed 32 bit integer - samples). + samples), f (32 bit floating + point, -1.0 to 1.0). diff --git a/src/audio_format.c b/src/audio_format.c index 458ec3b13..8c40457ec 100644 --- a/src/audio_format.c +++ b/src/audio_format.c @@ -68,6 +68,9 @@ sample_format_to_string(enum sample_format format) case SAMPLE_FORMAT_S32: return "32"; + + case SAMPLE_FORMAT_FLOAT: + return "f"; } /* unreachable */ diff --git a/src/audio_format.h b/src/audio_format.h index 71bd369e9..4f7dbfb1a 100644 --- a/src/audio_format.h +++ b/src/audio_format.h @@ -43,6 +43,12 @@ enum sample_format { SAMPLE_FORMAT_S24_P32, SAMPLE_FORMAT_S32, + + /** + * 32 bit floating point samples in the host's format. The + * range is -1.0f to +1.0f. + */ + SAMPLE_FORMAT_FLOAT, }; static const unsigned MAX_CHANNELS = 8; @@ -168,6 +174,7 @@ audio_valid_sample_format(enum sample_format format) case SAMPLE_FORMAT_S24: case SAMPLE_FORMAT_S24_P32: case SAMPLE_FORMAT_S32: + case SAMPLE_FORMAT_FLOAT: return true; case SAMPLE_FORMAT_UNDEFINED: @@ -241,6 +248,7 @@ sample_format_size(enum sample_format format) case SAMPLE_FORMAT_S24_P32: case SAMPLE_FORMAT_S32: + case SAMPLE_FORMAT_FLOAT: return 4; case SAMPLE_FORMAT_UNDEFINED: diff --git a/src/audio_parser.c b/src/audio_parser.c index 80bf9a5d7..8875239e1 100644 --- a/src/audio_parser.c +++ b/src/audio_parser.c @@ -81,6 +81,12 @@ parse_sample_format(const char *src, bool mask, return true; } + if (*src == 'f') { + *sample_format_r = SAMPLE_FORMAT_FLOAT; + *endptr_r = src + 1; + return true; + } + value = strtoul(src, &endptr, 10); if (endptr == src) { g_set_error(error_r, audio_parser_quark(), 0, diff --git a/src/decoder/flac_pcm.c b/src/decoder/flac_pcm.c index 3b56d50bd..c479ebfd4 100644 --- a/src/decoder/flac_pcm.c +++ b/src/decoder/flac_pcm.c @@ -102,6 +102,7 @@ flac_convert(void *dest, break; case SAMPLE_FORMAT_S24: + case SAMPLE_FORMAT_FLOAT: case SAMPLE_FORMAT_UNDEFINED: /* unreachable */ assert(false); diff --git a/src/output/alsa_output_plugin.c b/src/output/alsa_output_plugin.c index af6d1b184..f939ba5b4 100644 --- a/src/output/alsa_output_plugin.c +++ b/src/output/alsa_output_plugin.c @@ -212,6 +212,9 @@ get_bitformat(enum sample_format sample_format) case SAMPLE_FORMAT_S32: return SND_PCM_FORMAT_S32; + + case SAMPLE_FORMAT_FLOAT: + return SND_PCM_FORMAT_FLOAT; } assert(false); diff --git a/src/output/oss_output_plugin.c b/src/output/oss_output_plugin.c index c8fce84ea..46505873b 100644 --- a/src/output/oss_output_plugin.c +++ b/src/output/oss_output_plugin.c @@ -395,6 +395,7 @@ sample_format_to_oss(enum sample_format format) { switch (format) { case SAMPLE_FORMAT_UNDEFINED: + case SAMPLE_FORMAT_FLOAT: return AFMT_QUERY; case SAMPLE_FORMAT_S8: diff --git a/src/pcm_byteswap.c b/src/pcm_byteswap.c index fcd995ba2..a1315c475 100644 --- a/src/pcm_byteswap.c +++ b/src/pcm_byteswap.c @@ -69,6 +69,7 @@ pcm_byteswap(struct pcm_buffer *buffer, enum sample_format format, switch (format) { case SAMPLE_FORMAT_UNDEFINED: case SAMPLE_FORMAT_S24: + case SAMPLE_FORMAT_FLOAT: /* not implemented */ return NULL; diff --git a/src/pcm_format.c b/src/pcm_format.c index d22dfb896..bb731b2b6 100644 --- a/src/pcm_format.c +++ b/src/pcm_format.c @@ -46,6 +46,18 @@ pcm_convert_32_to_16(struct pcm_dither *dither, pcm_dither_32_to_16(dither, out, in, in_end); } +static void +pcm_convert_float_to_16(int16_t *out, const float *in, const float *in_end) +{ + const unsigned OUT_BITS = 16; + const float factor = 1 << (OUT_BITS - 1); + + while (in < in_end) { + int sample = *in++ * factor; + *out++ = pcm_clamp_16(sample); + } +} + static int16_t * pcm_allocate_8_to_16(struct pcm_buffer *buffer, const int8_t *src, size_t src_size, size_t *dest_size_r) @@ -112,6 +124,20 @@ pcm_allocate_32_to_16(struct pcm_buffer *buffer, struct pcm_dither *dither, return dest; } +static int16_t * +pcm_allocate_float_to_16(struct pcm_buffer *buffer, + const float *src, size_t src_size, + size_t *dest_size_r) +{ + int16_t *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_float_to_16(dest, src, + pcm_end_pointer(src, src_size)); + return dest; +} + const int16_t * pcm_convert_to_16(struct pcm_buffer *buffer, struct pcm_dither *dither, enum sample_format src_format, const void *src, @@ -142,6 +168,10 @@ pcm_convert_to_16(struct pcm_buffer *buffer, struct pcm_dither *dither, case SAMPLE_FORMAT_S32: return pcm_allocate_32_to_16(buffer, dither, src, src_size, dest_size_r); + + case SAMPLE_FORMAT_FLOAT: + return pcm_allocate_float_to_16(buffer, src, src_size, + dest_size_r); } return NULL; @@ -170,6 +200,18 @@ pcm_convert_32_to_24(int32_t *restrict out, *out++ = *in++ >> 8; } +static void +pcm_convert_float_to_24(int32_t *out, const float *in, const float *in_end) +{ + const unsigned OUT_BITS = 24; + const float factor = 1 << (OUT_BITS - 1); + + while (in < in_end) { + int sample = *in++ * factor; + *out++ = pcm_clamp_24(sample); + } +} + static int32_t * pcm_allocate_8_to_24(struct pcm_buffer *buffer, const int8_t *src, size_t src_size, size_t *dest_size_r) @@ -203,6 +245,17 @@ pcm_allocate_32_to_24(struct pcm_buffer *buffer, return dest; } +static int32_t * +pcm_allocate_float_to_24(struct pcm_buffer *buffer, + const float *src, size_t src_size, + size_t *dest_size_r) +{ + *dest_size_r = src_size; + int32_t *dest = pcm_buffer_get(buffer, *dest_size_r); + pcm_convert_float_to_24(dest, src, pcm_end_pointer(src, src_size)); + return dest; +} + const int32_t * pcm_convert_to_24(struct pcm_buffer *buffer, enum sample_format src_format, const void *src, @@ -233,6 +286,10 @@ pcm_convert_to_24(struct pcm_buffer *buffer, case SAMPLE_FORMAT_S32: return pcm_allocate_32_to_24(buffer, src, src_size, dest_size_r); + + case SAMPLE_FORMAT_FLOAT: + return pcm_allocate_float_to_24(buffer, src, src_size, + dest_size_r); } return NULL; @@ -309,6 +366,20 @@ pcm_allocate_24p32_to_32(struct pcm_buffer *buffer, return dest; } +static int32_t * +pcm_allocate_float_to_32(struct pcm_buffer *buffer, + const float *src, size_t src_size, + size_t *dest_size_r) +{ + /* convert to S24_P32 first */ + int32_t *dest = pcm_allocate_float_to_24(buffer, src, src_size, + dest_size_r); + + /* convert to 32 bit in-place */ + pcm_convert_24_to_32(dest, dest, pcm_end_pointer(dest, *dest_size_r)); + return dest; +} + const int32_t * pcm_convert_to_32(struct pcm_buffer *buffer, enum sample_format src_format, const void *src, @@ -339,6 +410,10 @@ pcm_convert_to_32(struct pcm_buffer *buffer, case SAMPLE_FORMAT_S32: *dest_size_r = src_size; return src; + + case SAMPLE_FORMAT_FLOAT: + return pcm_allocate_float_to_32(buffer, src, src_size, + dest_size_r); } return NULL; diff --git a/src/pcm_mix.c b/src/pcm_mix.c index ef81f44a4..b94089cfb 100644 --- a/src/pcm_mix.c +++ b/src/pcm_mix.c @@ -128,6 +128,10 @@ pcm_add_vol(void *buffer1, const void *buffer2, size_t size, pcm_add_vol_32((int32_t *)buffer1, (const int32_t *)buffer2, size / 4, vol1, vol2); return true; + + case SAMPLE_FORMAT_FLOAT: + /* XXX */ + return false; } /* unreachable */ @@ -216,6 +220,10 @@ pcm_add(void *buffer1, const void *buffer2, size_t size, case SAMPLE_FORMAT_S32: pcm_add_32((int32_t *)buffer1, (const int32_t *)buffer2, size / 4); return true; + + case SAMPLE_FORMAT_FLOAT: + /* XXX */ + return false; } /* unreachable */ diff --git a/src/pcm_utils.h b/src/pcm_utils.h index 001423b37..4ad896570 100644 --- a/src/pcm_utils.h +++ b/src/pcm_utils.h @@ -63,4 +63,32 @@ pcm_range_64(int64_t sample, unsigned bits) return sample; } +G_GNUC_CONST +static inline int16_t +pcm_clamp_16(int x) +{ + static const int32_t MIN_VALUE = G_MININT16; + static const int32_t MAX_VALUE = G_MAXINT16; + + if (G_UNLIKELY(x < MIN_VALUE)) + return MIN_VALUE; + if (G_UNLIKELY(x > MAX_VALUE)) + return MAX_VALUE; + return x; +} + +G_GNUC_CONST +static inline int32_t +pcm_clamp_24(int x) +{ + static const int32_t MIN_VALUE = -(1 << 23); + static const int32_t MAX_VALUE = (1 << 23) - 1; + + if (G_UNLIKELY(x < MIN_VALUE)) + return MIN_VALUE; + if (G_UNLIKELY(x > MAX_VALUE)) + return MAX_VALUE; + return x; +} + #endif diff --git a/src/pcm_volume.c b/src/pcm_volume.c index fd8670f4b..f732060b2 100644 --- a/src/pcm_volume.c +++ b/src/pcm_volume.c @@ -167,6 +167,10 @@ pcm_volume(void *buffer, size_t length, case SAMPLE_FORMAT_S32: pcm_volume_change_32(buffer, end, volume); return true; + + case SAMPLE_FORMAT_FLOAT: + /* XXX */ + return false; } /* unreachable */ -- cgit v1.2.3