Commit 1abfcc56 authored by Max Kellermann's avatar Max Kellermann

audio_format: support packed 24 bit samples

parent da47afe7
......@@ -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 \
......
......@@ -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
......
......@@ -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";
......
......@@ -30,6 +30,11 @@ enum sample_format {
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,6 +98,10 @@ parse_sample_format(const char *src, bool mask,
break;
case 24:
if (memcmp(endptr, "_3", 2) == 0) {
sample_format = SAMPLE_FORMAT_S24;
endptr += 2;
} else
sample_format = SAMPLE_FORMAT_S24_P32;
break;
......
......@@ -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);
......
/*
* 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;
}
}
}
/*
* 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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment