diff options
Diffstat (limited to 'src/pcm_convert.c')
-rw-r--r-- | src/pcm_convert.c | 208 |
1 files changed, 142 insertions, 66 deletions
diff --git a/src/pcm_convert.c b/src/pcm_convert.c index 7bd4d7215..63f9a1b98 100644 --- a/src/pcm_convert.c +++ b/src/pcm_convert.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2010 The Music Player Daemon Project + * Copyright (C) 2003-2011 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,9 +21,9 @@ #include "pcm_convert.h" #include "pcm_channels.h" #include "pcm_format.h" -#include "pcm_byteswap.h" #include "pcm_pack.h" #include "audio_format.h" +#include "glib_compat.h" #include <assert.h> #include <string.h> @@ -37,23 +37,77 @@ void pcm_convert_init(struct pcm_convert_state *state) { memset(state, 0, sizeof(*state)); + pcm_dsd_init(&state->dsd); pcm_resample_init(&state->resample); pcm_dither_24_init(&state->dither); pcm_buffer_init(&state->format_buffer); - pcm_buffer_init(&state->pack_buffer); pcm_buffer_init(&state->channels_buffer); - pcm_buffer_init(&state->byteswap_buffer); } void pcm_convert_deinit(struct pcm_convert_state *state) { + pcm_dsd_deinit(&state->dsd); pcm_resample_deinit(&state->resample); pcm_buffer_deinit(&state->format_buffer); - pcm_buffer_deinit(&state->pack_buffer); pcm_buffer_deinit(&state->channels_buffer); - pcm_buffer_deinit(&state->byteswap_buffer); +} + +void +pcm_convert_reset(struct pcm_convert_state *state) +{ + pcm_dsd_reset(&state->dsd); + 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 * @@ -103,11 +157,6 @@ pcm_convert_16(struct pcm_convert_state *state, return NULL; } - if (dest_format->reverse_endian) { - buf = pcm_byteswap_16(&state->byteswap_buffer, buf, len); - assert(buf != NULL); - } - *dest_size_r = len; return buf; } @@ -158,54 +207,10 @@ pcm_convert_24(struct pcm_convert_state *state, return NULL; } - if (dest_format->reverse_endian) { - buf = pcm_byteswap_32(&state->byteswap_buffer, buf, len); - assert(buf != NULL); - } - *dest_size_r = len; return buf; } -/** - * Convert to 24 bit packed samples (aka S24_3LE / S24_3BE). - */ -static const void * -pcm_convert_24_packed(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) -{ - assert(dest_format->format == SAMPLE_FORMAT_S24); - - /* use the normal 24 bit conversion first */ - - struct audio_format audio_format; - audio_format_init(&audio_format, dest_format->sample_rate, - SAMPLE_FORMAT_S24_P32, dest_format->channels); - - const int32_t *buffer; - size_t buffer_size; - - buffer = pcm_convert_24(state, src_format, src_buffer, src_size, - &audio_format, &buffer_size, error_r); - if (buffer == NULL) - return NULL; - - /* now convert to packed 24 bit */ - - unsigned num_samples = buffer_size / 4; - size_t dest_size = num_samples * 3; - - uint8_t *dest = pcm_buffer_get(&state->pack_buffer, dest_size); - pcm_pack_24(dest, buffer, num_samples, dest_format->reverse_endian); - - *dest_size_r = dest_size; - return dest; -} - static const int32_t * pcm_convert_32(struct pcm_convert_state *state, const struct audio_format *src_format, @@ -252,15 +257,65 @@ pcm_convert_32(struct pcm_convert_state *state, return buf; } - if (dest_format->reverse_endian) { - buf = pcm_byteswap_32(&state->byteswap_buffer, buf, len); - assert(buf != NULL); - } - *dest_size_r = len; 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); + + /* 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, @@ -269,6 +324,27 @@ pcm_convert(struct pcm_convert_state *state, size_t *dest_size_r, GError **error_r) { + struct audio_format float_format; + if (src_format->format == SAMPLE_FORMAT_DSD) { + size_t f_size; + const float *f = pcm_dsd_to_float(&state->dsd, + src_format->channels, + false, src, src_size, + &f_size); + if (f == NULL) { + g_set_error_literal(error_r, pcm_convert_quark(), 0, + "DSD to PCM conversion failed"); + return NULL; + } + + float_format = *src_format; + float_format.format = SAMPLE_FORMAT_FLOAT; + + src_format = &float_format; + src = f; + src_size = f_size; + } + switch (dest_format->format) { case SAMPLE_FORMAT_S16: return pcm_convert_16(state, @@ -276,12 +352,6 @@ pcm_convert(struct pcm_convert_state *state, dest_format, dest_size_r, error_r); - case SAMPLE_FORMAT_S24: - return pcm_convert_24_packed(state, - src_format, src, src_size, - dest_format, dest_size_r, - error_r); - case SAMPLE_FORMAT_S24_P32: return pcm_convert_24(state, src_format, src, src_size, @@ -294,6 +364,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", |