pcm_channels: support floating point samples

This commit is contained in:
Max Kellermann 2012-10-02 08:29:52 +02:00
parent 31e1be7570
commit e166ddf46f
3 changed files with 105 additions and 62 deletions

View File

@ -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;
}

View File

@ -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

View File

@ -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 */