audio_format: support packed 24 bit samples
This commit is contained in:
parent
da47afe7d1
commit
1abfcc56af
|
@ -279,6 +279,7 @@ src_mpd_SOURCES = \
|
|||
src/pcm_mix.c \
|
||||
src/pcm_byteswap.c \
|
||||
src/pcm_channels.c \
|
||||
src/pcm_pack.c \
|
||||
src/pcm_format.c \
|
||||
src/pcm_resample.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/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_pack.c \
|
||||
src/pcm_resample.c src/pcm_resample_fallback.c \
|
||||
src/audio_check.c \
|
||||
src/audio_format.c \
|
||||
|
@ -935,6 +937,7 @@ test_run_convert_SOURCES = test/run_convert.c \
|
|||
src/audio_parser.c \
|
||||
src/pcm_channels.c \
|
||||
src/pcm_format.c \
|
||||
src/pcm_pack.c \
|
||||
src/pcm_dither.c \
|
||||
src/pcm_byteswap.c \
|
||||
src/pcm_resample.c \
|
||||
|
|
1
NEWS
1
NEWS
|
@ -78,6 +78,7 @@ ver 0.16 (20??/??/??)
|
|||
* log: redirect stdout/stderr to /dev/null if syslog is used
|
||||
* set the close-on-exec flag on all file descriptors
|
||||
* pcm_volume, pcm_mix: implemented 32 bit support
|
||||
* support packed 24 bit samples
|
||||
* CUE sheet support
|
||||
* obey $(sysconfdir) for default mpd.conf location
|
||||
* build with large file support by default
|
||||
|
|
11
doc/user.xml
11
doc/user.xml
|
@ -313,6 +313,17 @@ cd mpd-version</programlisting>
|
|||
<parameter>*:*:*</parameter> is equal to not having
|
||||
a <varname>format</varname> specification.
|
||||
</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>
|
||||
</row>
|
||||
<row>
|
||||
|
|
|
@ -41,6 +41,9 @@ sample_format_to_string(enum sample_format format)
|
|||
case SAMPLE_FORMAT_S16:
|
||||
return "16";
|
||||
|
||||
case SAMPLE_FORMAT_S24:
|
||||
return "24_3";
|
||||
|
||||
case SAMPLE_FORMAT_S24_P32:
|
||||
return "24";
|
||||
|
||||
|
|
|
@ -29,6 +29,11 @@ enum sample_format {
|
|||
SAMPLE_FORMAT_S8,
|
||||
SAMPLE_FORMAT_S16,
|
||||
|
||||
/**
|
||||
* Signed 24 bit integer samples, without padding.
|
||||
*/
|
||||
SAMPLE_FORMAT_S24,
|
||||
|
||||
/**
|
||||
* Signed 24 bit integer samples, packed in 32 bit integers
|
||||
* (the most significant byte is filled with the sign bit).
|
||||
|
@ -156,6 +161,7 @@ audio_valid_sample_format(enum sample_format format)
|
|||
switch (format) {
|
||||
case SAMPLE_FORMAT_S8:
|
||||
case SAMPLE_FORMAT_S16:
|
||||
case SAMPLE_FORMAT_S24:
|
||||
case SAMPLE_FORMAT_S24_P32:
|
||||
case SAMPLE_FORMAT_S32:
|
||||
return true;
|
||||
|
@ -235,6 +241,9 @@ static inline unsigned audio_format_sample_size(const struct audio_format *af)
|
|||
case SAMPLE_FORMAT_S16:
|
||||
return 2;
|
||||
|
||||
case SAMPLE_FORMAT_S24:
|
||||
return 3;
|
||||
|
||||
case SAMPLE_FORMAT_S24_P32:
|
||||
case SAMPLE_FORMAT_S32:
|
||||
return 4;
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "audio_check.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/**
|
||||
|
@ -97,7 +98,11 @@ parse_sample_format(const char *src, bool mask,
|
|||
break;
|
||||
|
||||
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;
|
||||
|
||||
case 32:
|
||||
|
|
|
@ -101,6 +101,7 @@ flac_convert(void *dest,
|
|||
position, end);
|
||||
break;
|
||||
|
||||
case SAMPLE_FORMAT_S24:
|
||||
case SAMPLE_FORMAT_UNDEFINED:
|
||||
/* unreachable */
|
||||
assert(false);
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "pcm_channels.h"
|
||||
#include "pcm_format.h"
|
||||
#include "pcm_byteswap.h"
|
||||
#include "pcm_pack.h"
|
||||
#include "audio_format.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
@ -40,6 +41,7 @@ void pcm_convert_init(struct pcm_convert_state *state)
|
|||
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);
|
||||
}
|
||||
|
@ -49,6 +51,7 @@ void pcm_convert_deinit(struct pcm_convert_state *state)
|
|||
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);
|
||||
}
|
||||
|
@ -164,6 +167,45 @@ pcm_convert_24(struct pcm_convert_state *state,
|
|||
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,
|
||||
|
@ -234,6 +276,12 @@ 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,
|
||||
|
|
|
@ -39,6 +39,9 @@ struct pcm_convert_state {
|
|||
/** the buffer for converting the sample format */
|
||||
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 */
|
||||
struct pcm_buffer channels_buffer;
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "pcm_format.h"
|
||||
#include "pcm_dither.h"
|
||||
#include "pcm_buffer.h"
|
||||
#include "pcm_pack.h"
|
||||
|
||||
static void
|
||||
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);
|
||||
}
|
||||
|
||||
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 *
|
||||
pcm_convert_to_16(struct pcm_buffer *buffer, struct pcm_dither *dither,
|
||||
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;
|
||||
int16_t *dest;
|
||||
int32_t *dest32;
|
||||
|
||||
switch (src_format) {
|
||||
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;
|
||||
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:
|
||||
num_samples = src_size / 4;
|
||||
*dest_size_r = num_samples * sizeof(*dest);
|
||||
|
@ -158,6 +182,12 @@ pcm_convert_to_24(struct pcm_buffer *buffer,
|
|||
num_samples);
|
||||
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:
|
||||
*dest_size_r = src_size;
|
||||
return src;
|
||||
|
@ -235,6 +265,17 @@ pcm_convert_to_32(struct pcm_buffer *buffer,
|
|||
num_samples);
|
||||
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:
|
||||
num_samples = src_size / 4;
|
||||
*dest_size_r = num_samples * sizeof(*dest);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
Loading…
Reference in New Issue