audio_format: support packed 24 bit samples

This commit is contained in:
Max Kellermann 2010-01-15 09:23:36 +01:00
parent da47afe7d1
commit 1abfcc56af
12 changed files with 278 additions and 1 deletions

View File

@ -279,6 +279,7 @@ src_mpd_SOURCES = \
src/pcm_mix.c \ src/pcm_mix.c \
src/pcm_byteswap.c \ src/pcm_byteswap.c \
src/pcm_channels.c \ src/pcm_channels.c \
src/pcm_pack.c \
src/pcm_format.c \ src/pcm_format.c \
src/pcm_resample.c \ src/pcm_resample.c \
src/pcm_resample_fallback.c \ src/pcm_resample_fallback.c \
@ -888,6 +889,7 @@ test_run_filter_SOURCES = test/run_filter.c \
src/conf.c src/tokenizer.c src/utils.c \ src/conf.c src/tokenizer.c src/utils.c \
src/pcm_volume.c src/pcm_convert.c src/pcm_byteswap.c \ src/pcm_volume.c src/pcm_convert.c src/pcm_byteswap.c \
src/pcm_format.c src/pcm_channels.c src/pcm_dither.c \ src/pcm_format.c src/pcm_channels.c src/pcm_dither.c \
src/pcm_pack.c \
src/pcm_resample.c src/pcm_resample_fallback.c \ src/pcm_resample.c src/pcm_resample_fallback.c \
src/audio_check.c \ src/audio_check.c \
src/audio_format.c \ src/audio_format.c \
@ -935,6 +937,7 @@ test_run_convert_SOURCES = test/run_convert.c \
src/audio_parser.c \ src/audio_parser.c \
src/pcm_channels.c \ src/pcm_channels.c \
src/pcm_format.c \ src/pcm_format.c \
src/pcm_pack.c \
src/pcm_dither.c \ src/pcm_dither.c \
src/pcm_byteswap.c \ src/pcm_byteswap.c \
src/pcm_resample.c \ src/pcm_resample.c \

1
NEWS
View File

@ -78,6 +78,7 @@ ver 0.16 (20??/??/??)
* log: redirect stdout/stderr to /dev/null if syslog is used * log: redirect stdout/stderr to /dev/null if syslog is used
* set the close-on-exec flag on all file descriptors * set the close-on-exec flag on all file descriptors
* pcm_volume, pcm_mix: implemented 32 bit support * pcm_volume, pcm_mix: implemented 32 bit support
* support packed 24 bit samples
* CUE sheet support * CUE sheet support
* obey $(sysconfdir) for default mpd.conf location * obey $(sysconfdir) for default mpd.conf location
* build with large file support by default * build with large file support by default

View File

@ -313,6 +313,17 @@ cd mpd-version</programlisting>
<parameter>*:*:*</parameter> is equal to not having <parameter>*:*:*</parameter> is equal to not having
a <varname>format</varname> specification. a <varname>format</varname> specification.
</para> </para>
<para>
The following values are valid for
<varname>bits</varname>: <varname>8</varname>
(signed 8 bit integer samples),
<varname>16</varname>, <varname>24</varname> (signed
24 bit integer samples padded to 32 bit),
<varname>24_3</varname> (signed 24 bit integer
samples, no padding, 3 bytes per sample),
<varname>32</varname> (signed 32 bit integer
samples).
</para>
</entry> </entry>
</row> </row>
<row> <row>

View File

@ -41,6 +41,9 @@ sample_format_to_string(enum sample_format format)
case SAMPLE_FORMAT_S16: case SAMPLE_FORMAT_S16:
return "16"; return "16";
case SAMPLE_FORMAT_S24:
return "24_3";
case SAMPLE_FORMAT_S24_P32: case SAMPLE_FORMAT_S24_P32:
return "24"; return "24";

View File

@ -29,6 +29,11 @@ enum sample_format {
SAMPLE_FORMAT_S8, SAMPLE_FORMAT_S8,
SAMPLE_FORMAT_S16, SAMPLE_FORMAT_S16,
/**
* Signed 24 bit integer samples, without padding.
*/
SAMPLE_FORMAT_S24,
/** /**
* Signed 24 bit integer samples, packed in 32 bit integers * Signed 24 bit integer samples, packed in 32 bit integers
* (the most significant byte is filled with the sign bit). * (the most significant byte is filled with the sign bit).
@ -156,6 +161,7 @@ audio_valid_sample_format(enum sample_format format)
switch (format) { switch (format) {
case SAMPLE_FORMAT_S8: case SAMPLE_FORMAT_S8:
case SAMPLE_FORMAT_S16: case SAMPLE_FORMAT_S16:
case SAMPLE_FORMAT_S24:
case SAMPLE_FORMAT_S24_P32: case SAMPLE_FORMAT_S24_P32:
case SAMPLE_FORMAT_S32: case SAMPLE_FORMAT_S32:
return true; return true;
@ -235,6 +241,9 @@ static inline unsigned audio_format_sample_size(const struct audio_format *af)
case SAMPLE_FORMAT_S16: case SAMPLE_FORMAT_S16:
return 2; return 2;
case SAMPLE_FORMAT_S24:
return 3;
case SAMPLE_FORMAT_S24_P32: case SAMPLE_FORMAT_S24_P32:
case SAMPLE_FORMAT_S32: case SAMPLE_FORMAT_S32:
return 4; return 4;

View File

@ -28,6 +28,7 @@
#include "audio_check.h" #include "audio_check.h"
#include <assert.h> #include <assert.h>
#include <string.h>
#include <stdlib.h> #include <stdlib.h>
/** /**
@ -97,7 +98,11 @@ parse_sample_format(const char *src, bool mask,
break; break;
case 24: case 24:
sample_format = SAMPLE_FORMAT_S24_P32; if (memcmp(endptr, "_3", 2) == 0) {
sample_format = SAMPLE_FORMAT_S24;
endptr += 2;
} else
sample_format = SAMPLE_FORMAT_S24_P32;
break; break;
case 32: case 32:

View File

@ -101,6 +101,7 @@ flac_convert(void *dest,
position, end); position, end);
break; break;
case SAMPLE_FORMAT_S24:
case SAMPLE_FORMAT_UNDEFINED: case SAMPLE_FORMAT_UNDEFINED:
/* unreachable */ /* unreachable */
assert(false); assert(false);

View File

@ -22,6 +22,7 @@
#include "pcm_channels.h" #include "pcm_channels.h"
#include "pcm_format.h" #include "pcm_format.h"
#include "pcm_byteswap.h" #include "pcm_byteswap.h"
#include "pcm_pack.h"
#include "audio_format.h" #include "audio_format.h"
#include <assert.h> #include <assert.h>
@ -40,6 +41,7 @@ void pcm_convert_init(struct pcm_convert_state *state)
pcm_dither_24_init(&state->dither); pcm_dither_24_init(&state->dither);
pcm_buffer_init(&state->format_buffer); pcm_buffer_init(&state->format_buffer);
pcm_buffer_init(&state->pack_buffer);
pcm_buffer_init(&state->channels_buffer); pcm_buffer_init(&state->channels_buffer);
pcm_buffer_init(&state->byteswap_buffer); pcm_buffer_init(&state->byteswap_buffer);
} }
@ -49,6 +51,7 @@ void pcm_convert_deinit(struct pcm_convert_state *state)
pcm_resample_deinit(&state->resample); pcm_resample_deinit(&state->resample);
pcm_buffer_deinit(&state->format_buffer); pcm_buffer_deinit(&state->format_buffer);
pcm_buffer_deinit(&state->pack_buffer);
pcm_buffer_deinit(&state->channels_buffer); pcm_buffer_deinit(&state->channels_buffer);
pcm_buffer_deinit(&state->byteswap_buffer); pcm_buffer_deinit(&state->byteswap_buffer);
} }
@ -164,6 +167,45 @@ pcm_convert_24(struct pcm_convert_state *state,
return buf; 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 * static const int32_t *
pcm_convert_32(struct pcm_convert_state *state, pcm_convert_32(struct pcm_convert_state *state,
const struct audio_format *src_format, const struct audio_format *src_format,
@ -234,6 +276,12 @@ pcm_convert(struct pcm_convert_state *state,
dest_format, dest_size_r, dest_format, dest_size_r,
error_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: case SAMPLE_FORMAT_S24_P32:
return pcm_convert_24(state, return pcm_convert_24(state,
src_format, src, src_size, src_format, src, src_size,

View File

@ -39,6 +39,9 @@ struct pcm_convert_state {
/** the buffer for converting the sample format */ /** the buffer for converting the sample format */
struct pcm_buffer format_buffer; struct pcm_buffer format_buffer;
/** the buffer for converting to/from packed samples */
struct pcm_buffer pack_buffer;
/** the buffer for converting the channel count */ /** the buffer for converting the channel count */
struct pcm_buffer channels_buffer; struct pcm_buffer channels_buffer;

View File

@ -21,6 +21,7 @@
#include "pcm_format.h" #include "pcm_format.h"
#include "pcm_dither.h" #include "pcm_dither.h"
#include "pcm_buffer.h" #include "pcm_buffer.h"
#include "pcm_pack.h"
static void static void
pcm_convert_8_to_16(int16_t *out, const int8_t *in, pcm_convert_8_to_16(int16_t *out, const int8_t *in,
@ -48,6 +49,15 @@ pcm_convert_32_to_16(struct pcm_dither *dither,
pcm_dither_32_to_16(dither, out, in, num_samples); pcm_dither_32_to_16(dither, out, in, num_samples);
} }
static int32_t *
pcm_convert_24_to_24p32(struct pcm_buffer *buffer, const uint8_t *src,
unsigned num_samples)
{
int32_t *dest = pcm_buffer_get(buffer, num_samples * 4);
pcm_unpack_24(dest, src, num_samples, false);
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,
@ -55,6 +65,7 @@ pcm_convert_to_16(struct pcm_buffer *buffer, struct pcm_dither *dither,
{ {
unsigned num_samples; unsigned num_samples;
int16_t *dest; int16_t *dest;
int32_t *dest32;
switch (src_format) { switch (src_format) {
case SAMPLE_FORMAT_UNDEFINED: case SAMPLE_FORMAT_UNDEFINED:
@ -74,6 +85,19 @@ pcm_convert_to_16(struct pcm_buffer *buffer, struct pcm_dither *dither,
*dest_size_r = src_size; *dest_size_r = src_size;
return src; return src;
case SAMPLE_FORMAT_S24:
/* convert to S24_P32 first */
num_samples = src_size / 3;
dest32 = pcm_convert_24_to_24p32(buffer, src, num_samples);
dest = (int16_t *)dest32;
/* convert to 16 bit in-place */
*dest_size_r = num_samples * sizeof(*dest);
pcm_convert_24_to_16(dither, dest, dest32,
num_samples);
return dest;
case SAMPLE_FORMAT_S24_P32: case SAMPLE_FORMAT_S24_P32:
num_samples = src_size / 4; num_samples = src_size / 4;
*dest_size_r = num_samples * sizeof(*dest); *dest_size_r = num_samples * sizeof(*dest);
@ -158,6 +182,12 @@ pcm_convert_to_24(struct pcm_buffer *buffer,
num_samples); num_samples);
return dest; return dest;
case SAMPLE_FORMAT_S24:
num_samples = src_size / 3;
*dest_size_r = num_samples * sizeof(*dest);
return pcm_convert_24_to_24p32(buffer, src, num_samples);
case SAMPLE_FORMAT_S24_P32: case SAMPLE_FORMAT_S24_P32:
*dest_size_r = src_size; *dest_size_r = src_size;
return src; return src;
@ -235,6 +265,17 @@ pcm_convert_to_32(struct pcm_buffer *buffer,
num_samples); num_samples);
return dest; return dest;
case SAMPLE_FORMAT_S24:
/* convert to S24_P32 first */
num_samples = src_size / 3;
dest = pcm_convert_24_to_24p32(buffer, src, num_samples);
/* convert to 32 bit in-place */
*dest_size_r = num_samples * sizeof(*dest);
pcm_convert_24_to_32(dest, dest, num_samples);
return dest;
case SAMPLE_FORMAT_S24_P32: case SAMPLE_FORMAT_S24_P32:
num_samples = src_size / 4; num_samples = src_size / 4;
*dest_size_r = num_samples * sizeof(*dest); *dest_size_r = num_samples * sizeof(*dest);

93
src/pcm_pack.c Normal file
View File

@ -0,0 +1,93 @@
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "pcm_pack.h"
#include <glib.h>
static void
pack_sample(uint8_t *dest, const int32_t *src0, bool reverse_endian)
{
const uint8_t *src = (const uint8_t *)src0;
if ((G_BYTE_ORDER == G_BIG_ENDIAN) != reverse_endian)
++src;
*dest++ = *src++;
*dest++ = *src++;
*dest++ = *src++;
}
void
pcm_pack_24(uint8_t *dest, const int32_t *src, unsigned num_samples,
bool reverse_endian)
{
/* duplicate loop to help the compiler's optimizer (constant
parameter to the pack_sample() inline function) */
if (G_LIKELY(!reverse_endian)) {
while (num_samples-- > 0) {
pack_sample(dest, src++, false);
dest += 3;
}
} else {
while (num_samples-- > 0) {
pack_sample(dest, src++, true);
dest += 3;
}
}
}
static void
unpack_sample(int32_t *dest0, const uint8_t *src, bool reverse_endian)
{
uint8_t *dest = (uint8_t *)dest0;
if ((G_BYTE_ORDER == G_BIG_ENDIAN) != reverse_endian)
/* extend the sign bit to the most fourth byte */
*dest++ = *src & 0x80 ? 0xff : 0x00;
*dest++ = *src++;
*dest++ = *src++;
*dest++ = *src;
if ((G_BYTE_ORDER == G_LITTLE_ENDIAN) != reverse_endian)
/* extend the sign bit to the most fourth byte */
*dest++ = *src & 0x80 ? 0xff : 0x00;
}
void
pcm_unpack_24(int32_t *dest, const uint8_t *src, unsigned num_samples,
bool reverse_endian)
{
/* duplicate loop to help the compiler's optimizer (constant
parameter to the unpack_sample() inline function) */
if (G_LIKELY(!reverse_endian)) {
while (num_samples-- > 0) {
unpack_sample(dest++, src, false);
src += 3;
}
} else {
while (num_samples-- > 0) {
unpack_sample(dest++, src, true);
src += 3;
}
}
}

59
src/pcm_pack.h Normal file
View File

@ -0,0 +1,59 @@
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/** \file
*
* Library for working with packed 24 bit samples.
*/
#ifndef PCM_PACK_H
#define PCM_PACK_H
#include <stdbool.h>
#include <stdint.h>
/**
* Converts padded 24 bit samples (4 bytes per sample) to packed 24
* bit samples (3 bytes per sample).
*
* This function can be used to convert a buffer in-place.
*
* @param dest the destination buffer (array of triples)
* @param src the source buffer
* @param num_samples the number of samples to convert
* @param reverse_endian is src and dest in non-host byte order?
*/
void
pcm_pack_24(uint8_t *dest, const int32_t *src, unsigned num_samples,
bool reverse_endian);
/**
* Converts packed 24 bit samples (3 bytes per sample) to padded 24
* bit samples (4 bytes per sample).
*
* @param dest the destination buffer
* @param src the source buffer (array of triples)
* @param num_samples the number of samples to convert
* @param reverse_endian is src and dest in non-host byte order?
*/
void
pcm_unpack_24(int32_t *dest, const uint8_t *src, unsigned num_samples,
bool reverse_endian);
#endif