audio_format: basic support for floating point samples
Support for conversion from float to 16, 24 and 32 bit integer samples.
This commit is contained in:
parent
13ad2b4dc2
commit
545685bc32
1
NEWS
1
NEWS
@ -25,6 +25,7 @@ ver 0.17 (2011/??/??)
|
|||||||
* state_file: add option "restore_paused"
|
* state_file: add option "restore_paused"
|
||||||
* cue: show CUE track numbers
|
* cue: show CUE track numbers
|
||||||
* allow port specification in "bind_to_address" settings
|
* allow port specification in "bind_to_address" settings
|
||||||
|
* support floating point samples
|
||||||
|
|
||||||
|
|
||||||
ver 0.16.5 (2010/10/09)
|
ver 0.16.5 (2010/10/09)
|
||||||
|
@ -332,7 +332,8 @@ cd mpd-version</programlisting>
|
|||||||
<varname>24_3</varname> (signed 24 bit integer
|
<varname>24_3</varname> (signed 24 bit integer
|
||||||
samples, no padding, 3 bytes per sample),
|
samples, no padding, 3 bytes per sample),
|
||||||
<varname>32</varname> (signed 32 bit integer
|
<varname>32</varname> (signed 32 bit integer
|
||||||
samples).
|
samples), <varname>f</varname> (32 bit floating
|
||||||
|
point, -1.0 to 1.0).
|
||||||
</para>
|
</para>
|
||||||
</entry>
|
</entry>
|
||||||
</row>
|
</row>
|
||||||
|
@ -68,6 +68,9 @@ sample_format_to_string(enum sample_format format)
|
|||||||
|
|
||||||
case SAMPLE_FORMAT_S32:
|
case SAMPLE_FORMAT_S32:
|
||||||
return "32";
|
return "32";
|
||||||
|
|
||||||
|
case SAMPLE_FORMAT_FLOAT:
|
||||||
|
return "f";
|
||||||
}
|
}
|
||||||
|
|
||||||
/* unreachable */
|
/* unreachable */
|
||||||
|
@ -43,6 +43,12 @@ enum sample_format {
|
|||||||
SAMPLE_FORMAT_S24_P32,
|
SAMPLE_FORMAT_S24_P32,
|
||||||
|
|
||||||
SAMPLE_FORMAT_S32,
|
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;
|
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:
|
||||||
case SAMPLE_FORMAT_S24_P32:
|
case SAMPLE_FORMAT_S24_P32:
|
||||||
case SAMPLE_FORMAT_S32:
|
case SAMPLE_FORMAT_S32:
|
||||||
|
case SAMPLE_FORMAT_FLOAT:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case SAMPLE_FORMAT_UNDEFINED:
|
case SAMPLE_FORMAT_UNDEFINED:
|
||||||
@ -241,6 +248,7 @@ sample_format_size(enum sample_format format)
|
|||||||
|
|
||||||
case SAMPLE_FORMAT_S24_P32:
|
case SAMPLE_FORMAT_S24_P32:
|
||||||
case SAMPLE_FORMAT_S32:
|
case SAMPLE_FORMAT_S32:
|
||||||
|
case SAMPLE_FORMAT_FLOAT:
|
||||||
return 4;
|
return 4;
|
||||||
|
|
||||||
case SAMPLE_FORMAT_UNDEFINED:
|
case SAMPLE_FORMAT_UNDEFINED:
|
||||||
|
@ -81,6 +81,12 @@ parse_sample_format(const char *src, bool mask,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (*src == 'f') {
|
||||||
|
*sample_format_r = SAMPLE_FORMAT_FLOAT;
|
||||||
|
*endptr_r = src + 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
value = strtoul(src, &endptr, 10);
|
value = strtoul(src, &endptr, 10);
|
||||||
if (endptr == src) {
|
if (endptr == src) {
|
||||||
g_set_error(error_r, audio_parser_quark(), 0,
|
g_set_error(error_r, audio_parser_quark(), 0,
|
||||||
|
@ -102,6 +102,7 @@ flac_convert(void *dest,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case SAMPLE_FORMAT_S24:
|
case SAMPLE_FORMAT_S24:
|
||||||
|
case SAMPLE_FORMAT_FLOAT:
|
||||||
case SAMPLE_FORMAT_UNDEFINED:
|
case SAMPLE_FORMAT_UNDEFINED:
|
||||||
/* unreachable */
|
/* unreachable */
|
||||||
assert(false);
|
assert(false);
|
||||||
|
@ -212,6 +212,9 @@ get_bitformat(enum sample_format sample_format)
|
|||||||
|
|
||||||
case SAMPLE_FORMAT_S32:
|
case SAMPLE_FORMAT_S32:
|
||||||
return SND_PCM_FORMAT_S32;
|
return SND_PCM_FORMAT_S32;
|
||||||
|
|
||||||
|
case SAMPLE_FORMAT_FLOAT:
|
||||||
|
return SND_PCM_FORMAT_FLOAT;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(false);
|
assert(false);
|
||||||
|
@ -395,6 +395,7 @@ sample_format_to_oss(enum sample_format format)
|
|||||||
{
|
{
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case SAMPLE_FORMAT_UNDEFINED:
|
case SAMPLE_FORMAT_UNDEFINED:
|
||||||
|
case SAMPLE_FORMAT_FLOAT:
|
||||||
return AFMT_QUERY;
|
return AFMT_QUERY;
|
||||||
|
|
||||||
case SAMPLE_FORMAT_S8:
|
case SAMPLE_FORMAT_S8:
|
||||||
|
@ -69,6 +69,7 @@ pcm_byteswap(struct pcm_buffer *buffer, enum sample_format format,
|
|||||||
switch (format) {
|
switch (format) {
|
||||||
case SAMPLE_FORMAT_UNDEFINED:
|
case SAMPLE_FORMAT_UNDEFINED:
|
||||||
case SAMPLE_FORMAT_S24:
|
case SAMPLE_FORMAT_S24:
|
||||||
|
case SAMPLE_FORMAT_FLOAT:
|
||||||
/* not implemented */
|
/* not implemented */
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
@ -46,6 +46,18 @@ pcm_convert_32_to_16(struct pcm_dither *dither,
|
|||||||
pcm_dither_32_to_16(dither, out, in, in_end);
|
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 *
|
static int16_t *
|
||||||
pcm_allocate_8_to_16(struct pcm_buffer *buffer,
|
pcm_allocate_8_to_16(struct pcm_buffer *buffer,
|
||||||
const int8_t *src, size_t src_size, size_t *dest_size_r)
|
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;
|
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 *
|
const int16_t *
|
||||||
pcm_convert_to_16(struct pcm_buffer *buffer, struct pcm_dither *dither,
|
pcm_convert_to_16(struct pcm_buffer *buffer, struct pcm_dither *dither,
|
||||||
enum sample_format src_format, const void *src,
|
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:
|
case SAMPLE_FORMAT_S32:
|
||||||
return pcm_allocate_32_to_16(buffer, dither, src, src_size,
|
return pcm_allocate_32_to_16(buffer, dither, src, src_size,
|
||||||
dest_size_r);
|
dest_size_r);
|
||||||
|
|
||||||
|
case SAMPLE_FORMAT_FLOAT:
|
||||||
|
return pcm_allocate_float_to_16(buffer, src, src_size,
|
||||||
|
dest_size_r);
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -170,6 +200,18 @@ pcm_convert_32_to_24(int32_t *restrict out,
|
|||||||
*out++ = *in++ >> 8;
|
*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 *
|
static int32_t *
|
||||||
pcm_allocate_8_to_24(struct pcm_buffer *buffer,
|
pcm_allocate_8_to_24(struct pcm_buffer *buffer,
|
||||||
const int8_t *src, size_t src_size, size_t *dest_size_r)
|
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;
|
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 *
|
const int32_t *
|
||||||
pcm_convert_to_24(struct pcm_buffer *buffer,
|
pcm_convert_to_24(struct pcm_buffer *buffer,
|
||||||
enum sample_format src_format, const void *src,
|
enum sample_format src_format, const void *src,
|
||||||
@ -233,6 +286,10 @@ pcm_convert_to_24(struct pcm_buffer *buffer,
|
|||||||
case SAMPLE_FORMAT_S32:
|
case SAMPLE_FORMAT_S32:
|
||||||
return pcm_allocate_32_to_24(buffer, src, src_size,
|
return pcm_allocate_32_to_24(buffer, src, src_size,
|
||||||
dest_size_r);
|
dest_size_r);
|
||||||
|
|
||||||
|
case SAMPLE_FORMAT_FLOAT:
|
||||||
|
return pcm_allocate_float_to_24(buffer, src, src_size,
|
||||||
|
dest_size_r);
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -309,6 +366,20 @@ pcm_allocate_24p32_to_32(struct pcm_buffer *buffer,
|
|||||||
return dest;
|
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 *
|
const int32_t *
|
||||||
pcm_convert_to_32(struct pcm_buffer *buffer,
|
pcm_convert_to_32(struct pcm_buffer *buffer,
|
||||||
enum sample_format src_format, const void *src,
|
enum sample_format src_format, const void *src,
|
||||||
@ -339,6 +410,10 @@ pcm_convert_to_32(struct pcm_buffer *buffer,
|
|||||||
case SAMPLE_FORMAT_S32:
|
case SAMPLE_FORMAT_S32:
|
||||||
*dest_size_r = src_size;
|
*dest_size_r = src_size;
|
||||||
return src;
|
return src;
|
||||||
|
|
||||||
|
case SAMPLE_FORMAT_FLOAT:
|
||||||
|
return pcm_allocate_float_to_32(buffer, src, src_size,
|
||||||
|
dest_size_r);
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -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,
|
pcm_add_vol_32((int32_t *)buffer1, (const int32_t *)buffer2,
|
||||||
size / 4, vol1, vol2);
|
size / 4, vol1, vol2);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
case SAMPLE_FORMAT_FLOAT:
|
||||||
|
/* XXX */
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* unreachable */
|
/* unreachable */
|
||||||
@ -216,6 +220,10 @@ pcm_add(void *buffer1, const void *buffer2, size_t size,
|
|||||||
case SAMPLE_FORMAT_S32:
|
case SAMPLE_FORMAT_S32:
|
||||||
pcm_add_32((int32_t *)buffer1, (const int32_t *)buffer2, size / 4);
|
pcm_add_32((int32_t *)buffer1, (const int32_t *)buffer2, size / 4);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
case SAMPLE_FORMAT_FLOAT:
|
||||||
|
/* XXX */
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* unreachable */
|
/* unreachable */
|
||||||
|
@ -63,4 +63,32 @@ pcm_range_64(int64_t sample, unsigned bits)
|
|||||||
return sample;
|
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
|
#endif
|
||||||
|
@ -167,6 +167,10 @@ pcm_volume(void *buffer, size_t length,
|
|||||||
case SAMPLE_FORMAT_S32:
|
case SAMPLE_FORMAT_S32:
|
||||||
pcm_volume_change_32(buffer, end, volume);
|
pcm_volume_change_32(buffer, end, volume);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
case SAMPLE_FORMAT_FLOAT:
|
||||||
|
/* XXX */
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* unreachable */
|
/* unreachable */
|
||||||
|
Loading…
Reference in New Issue
Block a user