aboutsummaryrefslogtreecommitdiffstats
path: root/src/pcm_utils.c
diff options
context:
space:
mode:
authorMax Kellermann <max@duempel.org>2008-10-23 20:11:37 +0200
committerMax Kellermann <max@duempel.org>2008-10-23 20:11:37 +0200
commit4eadb0f7aafe18d5e6032e202444afcaf8955eec (patch)
tree0a746f1f4d1e737d52f8efdfbacb6b581a1788e1 /src/pcm_utils.c
parentec37633f1ceb43487a79513af625e1e43da3ffc7 (diff)
downloadmpd-4eadb0f7aafe18d5e6032e202444afcaf8955eec.tar.gz
mpd-4eadb0f7aafe18d5e6032e202444afcaf8955eec.tar.xz
mpd-4eadb0f7aafe18d5e6032e202444afcaf8955eec.zip
pcm_utils: added 24 bit conversion functions
24 bit output is as important as 16 bit output. Provide a pcm_convert() implementation which can convert to 24 bit with as little quality loss as possible.
Diffstat (limited to 'src/pcm_utils.c')
-rw-r--r--src/pcm_utils.c108
1 files changed, 108 insertions, 0 deletions
diff --git a/src/pcm_utils.c b/src/pcm_utils.c
index 7fa95aa5e..c06c7e1ab 100644
--- a/src/pcm_utils.c
+++ b/src/pcm_utils.c
@@ -278,6 +278,68 @@ pcm_convert_to_16(struct pcm_convert_state *convert,
return NULL;
}
+static void
+pcm_convert_8_to_24(int32_t *out, const int8_t *in,
+ unsigned num_samples)
+{
+ while (num_samples > 0) {
+ *out++ = *in++ << 16;
+ --num_samples;
+ }
+}
+
+static void
+pcm_convert_16_to_24(int32_t *out, const int16_t *in,
+ unsigned num_samples)
+{
+ while (num_samples > 0) {
+ *out++ = *in++ << 8;
+ --num_samples;
+ }
+}
+
+static const int32_t *
+pcm_convert_to_24(uint8_t bits, const void *src,
+ size_t src_size, size_t *dest_size_r)
+{
+ static int32_t *buf;
+ static size_t len;
+ unsigned num_samples;
+
+ switch (bits) {
+ case 8:
+ num_samples = src_size;
+ *dest_size_r = src_size * 4;
+ if (*dest_size_r > len) {
+ len = *dest_size_r;
+ buf = xrealloc(buf, len);
+ }
+
+ pcm_convert_8_to_24(buf, (const int8_t *)src,
+ num_samples);
+ return buf;
+
+ case 16:
+ num_samples = src_size / 2;
+ *dest_size_r = num_samples * 4;
+ if (*dest_size_r > len) {
+ len = *dest_size_r;
+ buf = xrealloc(buf, len);
+ }
+
+ pcm_convert_16_to_24(buf, (const int16_t *)src,
+ num_samples);
+ return buf;
+
+ case 24:
+ *dest_size_r = src_size;
+ return src;
+ }
+
+ ERROR("only 8 or 24 bits are supported for conversion!\n");
+ return NULL;
+}
+
static size_t
pcm_convert_16(const struct audio_format *src_format,
const void *src_buffer, size_t src_size,
@@ -320,6 +382,48 @@ pcm_convert_16(const struct audio_format *src_format,
return len;
}
+static size_t
+pcm_convert_24(const struct audio_format *src_format,
+ const void *src_buffer, size_t src_size,
+ const struct audio_format *dest_format,
+ int32_t *dest_buffer,
+ struct pcm_convert_state *state)
+{
+ const int32_t *buf;
+ size_t len;
+ size_t dest_size = pcm_convert_size(src_format, src_size, dest_format);
+
+ assert(dest_format->bits == 24);
+
+ buf = pcm_convert_to_24(src_format->bits,
+ src_buffer, src_size, &len);
+ if (!buf)
+ exit(EXIT_FAILURE);
+
+ if (src_format->channels != dest_format->channels) {
+ buf = pcm_convert_channels_24(dest_format->channels,
+ src_format->channels,
+ buf, len, &len);
+ if (!buf)
+ exit(EXIT_FAILURE);
+ }
+
+ if (src_format->sample_rate == dest_format->sample_rate) {
+ assert(dest_size >= len);
+ memcpy(dest_buffer, buf, len);
+ } else {
+ len = pcm_resample_24(dest_format->channels,
+ src_format->sample_rate, buf, len,
+ dest_format->sample_rate,
+ (int32_t*)dest_buffer, dest_size,
+ &state->resample);
+ if (len == 0)
+ exit(EXIT_FAILURE);
+ }
+
+ return len;
+}
+
/* outFormat bits must be 16 and channels must be 1 or 2! */
size_t pcm_convert(const struct audio_format *inFormat,
const char *src, size_t src_size,
@@ -332,6 +436,10 @@ size_t pcm_convert(const struct audio_format *inFormat,
return pcm_convert_16(inFormat, src, src_size,
outFormat, (int16_t*)outBuffer,
convState);
+ case 24:
+ return pcm_convert_24(inFormat, src, src_size,
+ outFormat, (int32_t*)outBuffer,
+ convState);
default:
FATAL("cannot convert to %u bit\n", outFormat->bits);