aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Kellermann <max@duempel.org>2010-01-15 09:23:36 +0100
committerMax Kellermann <max@duempel.org>2010-01-16 23:44:54 +0100
commit1abfcc56af7de73c2088d7971f744778b5842ed8 (patch)
treecee3e0021348270f86402b9cb7b016e4b820827d
parentda47afe7d1aa6b59bf04764d0bd7d0b91dfac94b (diff)
audio_format: support packed 24 bit samples
-rw-r--r--Makefile.am3
-rw-r--r--NEWS1
-rw-r--r--doc/user.xml11
-rw-r--r--src/audio_format.c3
-rw-r--r--src/audio_format.h9
-rw-r--r--src/audio_parser.c7
-rw-r--r--src/decoder/flac_pcm.c1
-rw-r--r--src/pcm_convert.c48
-rw-r--r--src/pcm_convert.h3
-rw-r--r--src/pcm_format.c41
-rw-r--r--src/pcm_pack.c93
-rw-r--r--src/pcm_pack.h59
12 files changed, 278 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am
index f9e6a86f..1197483e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -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 \
diff --git a/NEWS b/NEWS
index fae16e33..0702c943 100644
--- a/NEWS
+++ b/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
diff --git a/doc/user.xml b/doc/user.xml
index 09868eb3..15b20d52 100644
--- a/doc/user.xml
+++ b/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>
diff --git a/src/audio_format.c b/src/audio_format.c
index cdb0f25b..13403fbc 100644
--- a/src/audio_format.c
+++ b/src/audio_format.c
@@ -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";
diff --git a/src/audio_format.h b/src/audio_format.h
index 7cd606b6..dd32731c 100644
--- a/src/audio_format.h
+++ b/src/audio_format.h
@@ -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;
diff --git a/src/audio_parser.c b/src/audio_parser.c
index 60318fc9..039ffa1a 100644
--- a/src/audio_parser.c
+++ b/src/audio_parser.c
@@ -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:
diff --git a/src/decoder/flac_pcm.c b/src/decoder/flac_pcm.c
index 3130ba35..bf6e2612 100644
--- a/src/decoder/flac_pcm.c
+++ b/src/decoder/flac_pcm.c
@@ -101,6 +101,7 @@ flac_convert(void *dest,
position, end);
break;
+ case SAMPLE_FORMAT_S24:
case SAMPLE_FORMAT_UNDEFINED:
/* unreachable */
assert(false);
diff --git a/src/pcm_convert.c b/src/pcm_convert.c
index 06e4d05a..5fe89b53 100644
--- a/src/pcm_convert.c
+++ b/src/pcm_convert.c
@@ -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,
diff --git a/src/pcm_convert.h b/src/pcm_convert.h
index a940f216..01ba2c78 100644
--- a/src/pcm_convert.h
+++ b/src/pcm_convert.h
@@ -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;
diff --git a/src/pcm_format.c b/src/pcm_format.c
index 54682354..3fd76a98 100644
--- a/src/pcm_format.c
+++ b/src/pcm_format.c
@@ -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);
diff --git a/src/pcm_pack.c b/src/pcm_pack.c
new file mode 100644
index 00000000..9af0ab1e
--- /dev/null
+++ b/src/pcm_pack.c
@@ -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;
+ }
+ }
+}
diff --git a/src/pcm_pack.h b/src/pcm_pack.h
new file mode 100644
index 00000000..3c99eaa3
--- /dev/null
+++ b/src/pcm_pack.h
@@ -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