From 1b175025fecb1c10e6719d4ab79c188d473fccc4 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Thu, 31 Jan 2013 20:33:26 +0100 Subject: pcm_*: convert to C++ --- src/MixerAll.cxx | 2 +- src/OutputThread.cxx | 2 +- src/PcmChannels.cxx | 319 +++++++++++++++++++++ src/PcmChannels.hxx | 97 +++++++ src/PcmConvert.cxx | 8 +- src/PcmConvert.hxx | 3 +- src/PcmDither.cxx | 93 +++++++ src/PcmDither.hxx | 45 +++ src/PcmFormat.cxx | 503 ++++++++++++++++++++++++++++++++++ src/PcmFormat.hxx | 93 +++++++ src/PcmMix.cxx | 277 +++++++++++++++++++ src/PcmMix.hxx | 49 ++++ src/PcmPrng.hxx | 33 +++ src/PcmUtils.hxx | 95 +++++++ src/PcmVolume.cxx | 194 +++++++++++++ src/PcmVolume.hxx | 82 ++++++ src/filter/ReplayGainFilterPlugin.cxx | 2 +- src/filter/VolumeFilterPlugin.cxx | 2 +- src/mixer/SoftwareMixerPlugin.cxx | 2 +- src/pcm_channels.c | 317 --------------------- src/pcm_channels.h | 97 ------- src/pcm_dither.c | 93 ------- src/pcm_dither.h | 45 --- src/pcm_format.c | 487 -------------------------------- src/pcm_format.h | 93 ------- src/pcm_mix.c | 280 ------------------- src/pcm_mix.h | 50 ---- src/pcm_prng.h | 33 --- src/pcm_utils.h | 94 ------- src/pcm_volume.c | 190 ------------- src/pcm_volume.h | 90 ------ 31 files changed, 1889 insertions(+), 1881 deletions(-) create mode 100644 src/PcmChannels.cxx create mode 100644 src/PcmChannels.hxx create mode 100644 src/PcmDither.cxx create mode 100644 src/PcmDither.hxx create mode 100644 src/PcmFormat.cxx create mode 100644 src/PcmFormat.hxx create mode 100644 src/PcmMix.cxx create mode 100644 src/PcmMix.hxx create mode 100644 src/PcmPrng.hxx create mode 100644 src/PcmUtils.hxx create mode 100644 src/PcmVolume.cxx create mode 100644 src/PcmVolume.hxx delete mode 100644 src/pcm_channels.c delete mode 100644 src/pcm_channels.h delete mode 100644 src/pcm_dither.c delete mode 100644 src/pcm_dither.h delete mode 100644 src/pcm_format.c delete mode 100644 src/pcm_format.h delete mode 100644 src/pcm_mix.c delete mode 100644 src/pcm_mix.h delete mode 100644 src/pcm_prng.h delete mode 100644 src/pcm_utils.h delete mode 100644 src/pcm_volume.c delete mode 100644 src/pcm_volume.h (limited to 'src') diff --git a/src/MixerAll.cxx b/src/MixerAll.cxx index a214c1e24..b38005520 100644 --- a/src/MixerAll.cxx +++ b/src/MixerAll.cxx @@ -19,8 +19,8 @@ #include "config.h" #include "MixerAll.hxx" -#include "pcm_volume.h" #include "OutputAll.hxx" +#include "PcmVolume.hxx" extern "C" { #include "mixer_control.h" diff --git a/src/OutputThread.cxx b/src/OutputThread.cxx index fbbab98ea..c7b01d957 100644 --- a/src/OutputThread.cxx +++ b/src/OutputThread.cxx @@ -20,10 +20,10 @@ #include "config.h" #include "OutputThread.hxx" #include "output_api.h" +#include "PcmMix.hxx" extern "C" { #include "output_internal.h" -#include "pcm_mix.h" #include "filter_plugin.h" } diff --git a/src/PcmChannels.cxx b/src/PcmChannels.cxx new file mode 100644 index 000000000..3b100e46f --- /dev/null +++ b/src/PcmChannels.cxx @@ -0,0 +1,319 @@ +/* + * Copyright (C) 2003-2013 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 "config.h" +#include "PcmChannels.hxx" +#include "pcm_buffer.h" +#include "PcmUtils.hxx" + +#include + +static void +pcm_convert_channels_16_1_to_2(int16_t *restrict dest, + const int16_t *restrict src, + const int16_t *restrict src_end) +{ + while (src < src_end) { + int16_t value = *src++; + + *dest++ = value; + *dest++ = value; + } +} + +static void +pcm_convert_channels_16_2_to_1(int16_t *restrict dest, + const int16_t *restrict src, + const int16_t *restrict src_end) +{ + while (src < src_end) { + int32_t a = *src++, b = *src++; + + *dest++ = (a + b) / 2; + } +} + +static void +pcm_convert_channels_16_n_to_2(int16_t *restrict dest, + unsigned src_channels, + const int16_t *restrict src, + const int16_t *restrict src_end) +{ + unsigned c; + + assert(src_channels > 0); + + while (src < src_end) { + int32_t sum = 0; + int16_t value; + + for (c = 0; c < src_channels; ++c) + sum += *src++; + value = sum / (int)src_channels; + + /* XXX this is actually only mono ... */ + *dest++ = value; + *dest++ = value; + } +} + +const int16_t * +pcm_convert_channels_16(struct pcm_buffer *buffer, + unsigned dest_channels, + unsigned src_channels, const int16_t *src, + size_t src_size, size_t *dest_size_r) +{ + assert(src_size % (sizeof(*src) * src_channels) == 0); + + size_t dest_size = src_size / src_channels * dest_channels; + *dest_size_r = dest_size; + + int16_t *dest = (int16_t *)pcm_buffer_get(buffer, dest_size); + const int16_t *src_end = pcm_end_pointer(src, src_size); + + if (src_channels == 1 && dest_channels == 2) + pcm_convert_channels_16_1_to_2(dest, src, src_end); + else if (src_channels == 2 && dest_channels == 1) + pcm_convert_channels_16_2_to_1(dest, src, src_end); + else if (dest_channels == 2) + pcm_convert_channels_16_n_to_2(dest, src_channels, src, + src_end); + else + return NULL; + + return dest; +} + +static void +pcm_convert_channels_24_1_to_2(int32_t *restrict dest, + const int32_t *restrict src, + const int32_t *restrict src_end) +{ + while (src < src_end) { + int32_t value = *src++; + + *dest++ = value; + *dest++ = value; + } +} + +static void +pcm_convert_channels_24_2_to_1(int32_t *restrict dest, + const int32_t *restrict src, + const int32_t *restrict src_end) +{ + while (src < src_end) { + int32_t a = *src++, b = *src++; + + *dest++ = (a + b) / 2; + } +} + +static void +pcm_convert_channels_24_n_to_2(int32_t *restrict dest, + unsigned src_channels, + const int32_t *restrict src, + const int32_t *restrict src_end) +{ + unsigned c; + + assert(src_channels > 0); + + while (src < src_end) { + int32_t sum = 0; + int32_t value; + + for (c = 0; c < src_channels; ++c) + sum += *src++; + value = sum / (int)src_channels; + + /* XXX this is actually only mono ... */ + *dest++ = value; + *dest++ = value; + } +} + +const int32_t * +pcm_convert_channels_24(struct pcm_buffer *buffer, + unsigned dest_channels, + unsigned src_channels, const int32_t *src, + size_t src_size, size_t *dest_size_r) +{ + assert(src_size % (sizeof(*src) * src_channels) == 0); + + size_t dest_size = src_size / src_channels * dest_channels; + *dest_size_r = dest_size; + + int32_t *dest = (int32_t *)pcm_buffer_get(buffer, dest_size); + const int32_t *src_end = (const int32_t *) + pcm_end_pointer(src, src_size); + + if (src_channels == 1 && dest_channels == 2) + pcm_convert_channels_24_1_to_2(dest, src, src_end); + else if (src_channels == 2 && dest_channels == 1) + pcm_convert_channels_24_2_to_1(dest, src, src_end); + else if (dest_channels == 2) + pcm_convert_channels_24_n_to_2(dest, src_channels, src, + src_end); + else + return NULL; + + return dest; +} + +static void +pcm_convert_channels_32_1_to_2(int32_t *dest, const int32_t *src, + const int32_t *src_end) +{ + pcm_convert_channels_24_1_to_2(dest, src, src_end); +} + +static void +pcm_convert_channels_32_2_to_1(int32_t *restrict dest, + const int32_t *restrict src, + const int32_t *restrict src_end) +{ + while (src < src_end) { + int64_t a = *src++, b = *src++; + + *dest++ = (a + b) / 2; + } +} + +static void +pcm_convert_channels_32_n_to_2(int32_t *dest, + unsigned src_channels, const int32_t *src, + const int32_t *src_end) +{ + unsigned c; + + assert(src_channels > 0); + + while (src < src_end) { + int64_t sum = 0; + int32_t value; + + for (c = 0; c < src_channels; ++c) + sum += *src++; + value = sum / (int64_t)src_channels; + + /* XXX this is actually only mono ... */ + *dest++ = value; + *dest++ = value; + } +} + +const int32_t * +pcm_convert_channels_32(struct pcm_buffer *buffer, + unsigned dest_channels, + unsigned src_channels, const int32_t *src, + size_t src_size, size_t *dest_size_r) +{ + assert(src_size % (sizeof(*src) * src_channels) == 0); + + size_t dest_size = src_size / src_channels * dest_channels; + *dest_size_r = dest_size; + + int32_t *dest = (int32_t *)pcm_buffer_get(buffer, dest_size); + const int32_t *src_end = (const int32_t *) + pcm_end_pointer(src, src_size); + + if (src_channels == 1 && dest_channels == 2) + pcm_convert_channels_32_1_to_2(dest, src, src_end); + else if (src_channels == 2 && dest_channels == 1) + pcm_convert_channels_32_2_to_1(dest, src, src_end); + else if (dest_channels == 2) + pcm_convert_channels_32_n_to_2(dest, src_channels, src, + src_end); + else + return NULL; + + return dest; +} + +static void +pcm_convert_channels_float_1_to_2(float *dest, const float *src, + const float *src_end) +{ + pcm_convert_channels_24_1_to_2((int32_t *)dest, + (const int32_t *)src, + (const int32_t *)src_end); +} + +static void +pcm_convert_channels_float_2_to_1(float *restrict dest, + const float *restrict src, + const float *restrict src_end) +{ + while (src < src_end) { + double a = *src++, b = *src++; + + *dest++ = (a + b) / 2; + } +} + +static void +pcm_convert_channels_float_n_to_2(float *dest, + unsigned src_channels, const float *src, + const float *src_end) +{ + unsigned c; + + assert(src_channels > 0); + + while (src < src_end) { + double sum = 0; + float value; + + for (c = 0; c < src_channels; ++c) + sum += *src++; + value = sum / (double)src_channels; + + /* XXX this is actually only mono ... */ + *dest++ = value; + *dest++ = value; + } +} + +const float * +pcm_convert_channels_float(struct pcm_buffer *buffer, + unsigned dest_channels, + unsigned src_channels, const float *src, + size_t src_size, size_t *dest_size_r) +{ + assert(src_size % (sizeof(*src) * src_channels) == 0); + + size_t dest_size = src_size / src_channels * dest_channels; + *dest_size_r = dest_size; + + float *dest = (float *)pcm_buffer_get(buffer, dest_size); + const float *src_end = (const float *)pcm_end_pointer(src, src_size); + + if (src_channels == 1 && dest_channels == 2) + pcm_convert_channels_float_1_to_2(dest, src, src_end); + else if (src_channels == 2 && dest_channels == 1) + pcm_convert_channels_float_2_to_1(dest, src, src_end); + else if (dest_channels == 2) + pcm_convert_channels_float_n_to_2(dest, src_channels, src, + src_end); + else + return NULL; + + return dest; +} diff --git a/src/PcmChannels.hxx b/src/PcmChannels.hxx new file mode 100644 index 000000000..ede49cd81 --- /dev/null +++ b/src/PcmChannels.hxx @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2003-2013 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. + */ + +#ifndef MPD_PCM_CHANNELS_HXX +#define MPD_PCM_CHANNELS_HXX + +#include +#include + +struct pcm_buffer; + +/** + * Changes the number of channels in 16 bit PCM data. + * + * @param buffer the destination pcm_buffer object + * @param dest_channels the number of channels requested + * @param src_channels the number of channels in the source buffer + * @param src the source PCM buffer + * @param src_size the number of bytes in #src + * @param dest_size_r returns the number of bytes of the destination buffer + * @return the destination buffer + */ +const int16_t * +pcm_convert_channels_16(struct pcm_buffer *buffer, + unsigned dest_channels, + unsigned src_channels, const int16_t *src, + size_t src_size, size_t *dest_size_r); + +/** + * Changes the number of channels in 24 bit PCM data (aligned at 32 + * bit boundaries). + * + * @param buffer the destination pcm_buffer object + * @param dest_channels the number of channels requested + * @param src_channels the number of channels in the source buffer + * @param src the source PCM buffer + * @param src_size the number of bytes in #src + * @param dest_size_r returns the number of bytes of the destination buffer + * @return the destination buffer + */ +const int32_t * +pcm_convert_channels_24(struct pcm_buffer *buffer, + unsigned dest_channels, + unsigned src_channels, const int32_t *src, + size_t src_size, size_t *dest_size_r); + +/** + * Changes the number of channels in 32 bit PCM data. + * + * @param buffer the destination pcm_buffer object + * @param dest_channels the number of channels requested + * @param src_channels the number of channels in the source buffer + * @param src the source PCM buffer + * @param src_size the number of bytes in #src + * @param dest_size_r returns the number of bytes of the destination buffer + * @return the destination buffer + */ +const int32_t * +pcm_convert_channels_32(struct pcm_buffer *buffer, + unsigned dest_channels, + unsigned src_channels, const int32_t *src, + size_t src_size, size_t *dest_size_r); + +/** + * Changes the number of channels in 32 bit float PCM data. + * + * @param buffer the destination pcm_buffer object + * @param dest_channels the number of channels requested + * @param src_channels the number of channels in the source buffer + * @param src the source PCM buffer + * @param src_size the number of bytes in #src + * @param dest_size_r returns the number of bytes of the destination buffer + * @return the destination buffer + */ +const float * +pcm_convert_channels_float(struct pcm_buffer *buffer, + unsigned dest_channels, + unsigned src_channels, const float *src, + size_t src_size, size_t *dest_size_r); + +#endif diff --git a/src/PcmConvert.cxx b/src/PcmConvert.cxx index 175584c83..ed260d074 100644 --- a/src/PcmConvert.cxx +++ b/src/PcmConvert.cxx @@ -19,12 +19,8 @@ #include "config.h" #include "PcmConvert.hxx" - -extern "C" { -#include "pcm_channels.h" -#include "pcm_format.h" -} - +#include "PcmChannels.hxx" +#include "PcmFormat.hxx" #include "pcm_pack.h" #include "audio_format.h" diff --git a/src/PcmConvert.hxx b/src/PcmConvert.hxx index bec30af45..d76ecc125 100644 --- a/src/PcmConvert.hxx +++ b/src/PcmConvert.hxx @@ -20,10 +20,11 @@ #ifndef PCM_CONVERT_HXX #define PCM_CONVERT_HXX +#include "PcmDither.hxx" + extern "C" { #include "pcm_dsd.h" #include "pcm_resample.h" -#include "pcm_dither.h" #include "pcm_buffer.h" } diff --git a/src/PcmDither.cxx b/src/PcmDither.cxx new file mode 100644 index 000000000..e18119082 --- /dev/null +++ b/src/PcmDither.cxx @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2003-2013 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 "config.h" +#include "PcmDither.hxx" +#include "PcmPrng.hxx" + +static int16_t +pcm_dither_sample_24_to_16(int32_t sample, struct pcm_dither *dither) +{ + int32_t output, rnd; + + enum { + from_bits = 24, + to_bits = 16, + scale_bits = from_bits - to_bits, + round = 1 << (scale_bits - 1), + mask = (1 << scale_bits) - 1, + ONE = 1 << (from_bits - 1), + MIN = -ONE, + MAX = ONE - 1 + }; + + sample += dither->error[0] - dither->error[1] + dither->error[2]; + + dither->error[2] = dither->error[1]; + dither->error[1] = dither->error[0] / 2; + + /* round */ + output = sample + round; + + rnd = pcm_prng(dither->random); + output += (rnd & mask) - (dither->random & mask); + + dither->random = rnd; + + /* clip */ + if (output > MAX) { + output = MAX; + + if (sample > MAX) + sample = MAX; + } else if (output < MIN) { + output = MIN; + + if (sample < MIN) + sample = MIN; + } + + output &= ~mask; + + dither->error[0] = sample - output; + + return (int16_t)(output >> scale_bits); +} + +void +pcm_dither_24_to_16(struct pcm_dither *dither, + int16_t *dest, const int32_t *src, const int32_t *src_end) +{ + while (src < src_end) + *dest++ = pcm_dither_sample_24_to_16(*src++, dither); +} + +static int16_t +pcm_dither_sample_32_to_16(int32_t sample, struct pcm_dither *dither) +{ + return pcm_dither_sample_24_to_16(sample >> 8, dither); +} + +void +pcm_dither_32_to_16(struct pcm_dither *dither, + int16_t *dest, const int32_t *src, const int32_t *src_end) +{ + while (src < src_end) + *dest++ = pcm_dither_sample_32_to_16(*src++, dither); +} diff --git a/src/PcmDither.hxx b/src/PcmDither.hxx new file mode 100644 index 000000000..59affa088 --- /dev/null +++ b/src/PcmDither.hxx @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2003-2013 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. + */ + +#ifndef MPD_PCM_DITHER_HXX +#define MPD_PCM_DITHER_HXX + +#include + +struct pcm_dither { + int32_t error[3]; + int32_t random; +}; + +static inline void +pcm_dither_24_init(struct pcm_dither *dither) +{ + dither->error[0] = dither->error[1] = dither->error[2] = 0; + dither->random = 0; +} + +void +pcm_dither_24_to_16(struct pcm_dither *dither, + int16_t *dest, const int32_t *src, const int32_t *src_end); + +void +pcm_dither_32_to_16(struct pcm_dither *dither, + int16_t *dest, const int32_t *src, const int32_t *src_end); + +#endif diff --git a/src/PcmFormat.cxx b/src/PcmFormat.cxx new file mode 100644 index 000000000..cf601684a --- /dev/null +++ b/src/PcmFormat.cxx @@ -0,0 +1,503 @@ +/* + * Copyright (C) 2003-2011 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 "config.h" +#include "PcmFormat.hxx" +#include "PcmDither.hxx" +#include "pcm_buffer.h" +#include "pcm_pack.h" +#include "PcmUtils.hxx" + +static void +pcm_convert_8_to_16(int16_t *out, const int8_t *in, const int8_t *in_end) +{ + while (in < in_end) { + *out++ = *in++ << 8; + } +} + +static void +pcm_convert_24_to_16(struct pcm_dither *dither, + int16_t *out, const int32_t *in, const int32_t *in_end) +{ + pcm_dither_24_to_16(dither, out, in, in_end); +} + +static void +pcm_convert_32_to_16(struct pcm_dither *dither, + int16_t *out, const int32_t *in, const int32_t *in_end) +{ + pcm_dither_32_to_16(dither, out, in, in_end); +} + +static void +pcm_convert_float_to_16(int16_t *out, const float *in, const float *in_end) +{ + const unsigned OUT_BITS = 16; + const float factor = 1 << (OUT_BITS - 1); + + while (in < in_end) { + int sample = *in++ * factor; + *out++ = pcm_clamp_16(sample); + } +} + +static int16_t * +pcm_allocate_8_to_16(struct pcm_buffer *buffer, + const int8_t *src, size_t src_size, size_t *dest_size_r) +{ + int16_t *dest; + *dest_size_r = src_size / sizeof(*src) * sizeof(*dest); + dest = (int16_t *)pcm_buffer_get(buffer, *dest_size_r); + pcm_convert_8_to_16(dest, src, pcm_end_pointer(src, src_size)); + return dest; +} + +static int16_t * +pcm_allocate_24p32_to_16(struct pcm_buffer *buffer, struct pcm_dither *dither, + const int32_t *src, size_t src_size, + size_t *dest_size_r) +{ + int16_t *dest; + *dest_size_r = src_size / 2; + assert(*dest_size_r == src_size / sizeof(*src) * sizeof(*dest)); + dest = (int16_t *)pcm_buffer_get(buffer, *dest_size_r); + pcm_convert_24_to_16(dither, dest, src, + pcm_end_pointer(src, src_size)); + return dest; +} + +static int16_t * +pcm_allocate_32_to_16(struct pcm_buffer *buffer, struct pcm_dither *dither, + const int32_t *src, size_t src_size, + size_t *dest_size_r) +{ + int16_t *dest; + *dest_size_r = src_size / 2; + assert(*dest_size_r == src_size / sizeof(*src) * sizeof(*dest)); + dest = (int16_t *)pcm_buffer_get(buffer, *dest_size_r); + pcm_convert_32_to_16(dither, dest, src, + pcm_end_pointer(src, src_size)); + return dest; +} + +static int16_t * +pcm_allocate_float_to_16(struct pcm_buffer *buffer, + const float *src, size_t src_size, + size_t *dest_size_r) +{ + int16_t *dest; + *dest_size_r = src_size / 2; + assert(*dest_size_r == src_size / sizeof(*src) * sizeof(*dest)); + dest = (int16_t *)pcm_buffer_get(buffer, *dest_size_r); + pcm_convert_float_to_16(dest, src, + pcm_end_pointer(src, src_size)); + 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, + size_t src_size, size_t *dest_size_r) +{ + assert(src_size % sample_format_size(src_format) == 0); + + switch (src_format) { + case SAMPLE_FORMAT_UNDEFINED: + case SAMPLE_FORMAT_DSD: + break; + + case SAMPLE_FORMAT_S8: + return pcm_allocate_8_to_16(buffer, + (const int8_t *)src, src_size, + dest_size_r); + + case SAMPLE_FORMAT_S16: + *dest_size_r = src_size; + return (const int16_t *)src; + + case SAMPLE_FORMAT_S24_P32: + return pcm_allocate_24p32_to_16(buffer, dither, + (const int32_t *)src, src_size, + dest_size_r); + + case SAMPLE_FORMAT_S32: + return pcm_allocate_32_to_16(buffer, dither, + (const int32_t *)src, src_size, + dest_size_r); + + case SAMPLE_FORMAT_FLOAT: + return pcm_allocate_float_to_16(buffer, + (const float *)src, src_size, + dest_size_r); + } + + return NULL; +} + +static void +pcm_convert_8_to_24(int32_t *out, const int8_t *in, const int8_t *in_end) +{ + while (in < in_end) + *out++ = *in++ << 16; +} + +static void +pcm_convert_16_to_24(int32_t *out, const int16_t *in, const int16_t *in_end) +{ + while (in < in_end) + *out++ = *in++ << 8; +} + +static void +pcm_convert_32_to_24(int32_t *restrict out, + const int32_t *restrict in, + const int32_t *restrict in_end) +{ + while (in < in_end) + *out++ = *in++ >> 8; +} + +static void +pcm_convert_float_to_24(int32_t *out, const float *in, const float *in_end) +{ + const unsigned OUT_BITS = 24; + const float factor = 1 << (OUT_BITS - 1); + + while (in < in_end) { + int sample = *in++ * factor; + *out++ = pcm_clamp_24(sample); + } +} + +static int32_t * +pcm_allocate_8_to_24(struct pcm_buffer *buffer, + const int8_t *src, size_t src_size, size_t *dest_size_r) +{ + int32_t *dest; + *dest_size_r = src_size / sizeof(*src) * sizeof(*dest); + dest = (int32_t *)pcm_buffer_get(buffer, *dest_size_r); + pcm_convert_8_to_24(dest, src, pcm_end_pointer(src, src_size)); + return dest; +} + +static int32_t * +pcm_allocate_16_to_24(struct pcm_buffer *buffer, + const int16_t *src, size_t src_size, size_t *dest_size_r) +{ + int32_t *dest; + *dest_size_r = src_size * 2; + assert(*dest_size_r == src_size / sizeof(*src) * sizeof(*dest)); + dest = (int32_t *)pcm_buffer_get(buffer, *dest_size_r); + pcm_convert_16_to_24(dest, src, pcm_end_pointer(src, src_size)); + return dest; +} + +static int32_t * +pcm_allocate_32_to_24(struct pcm_buffer *buffer, + const int32_t *src, size_t src_size, size_t *dest_size_r) +{ + *dest_size_r = src_size; + int32_t *dest = (int32_t *)pcm_buffer_get(buffer, *dest_size_r); + pcm_convert_32_to_24(dest, src, pcm_end_pointer(src, src_size)); + return dest; +} + +static int32_t * +pcm_allocate_float_to_24(struct pcm_buffer *buffer, + const float *src, size_t src_size, + size_t *dest_size_r) +{ + *dest_size_r = src_size; + int32_t *dest = (int32_t *)pcm_buffer_get(buffer, *dest_size_r); + pcm_convert_float_to_24(dest, src, pcm_end_pointer(src, src_size)); + return dest; +} + +const int32_t * +pcm_convert_to_24(struct pcm_buffer *buffer, + enum sample_format src_format, const void *src, + size_t src_size, size_t *dest_size_r) +{ + assert(src_size % sample_format_size(src_format) == 0); + + switch (src_format) { + case SAMPLE_FORMAT_UNDEFINED: + case SAMPLE_FORMAT_DSD: + break; + + case SAMPLE_FORMAT_S8: + return pcm_allocate_8_to_24(buffer, + (const int8_t *)src, src_size, + dest_size_r); + + case SAMPLE_FORMAT_S16: + return pcm_allocate_16_to_24(buffer, + (const int16_t *)src, src_size, + dest_size_r); + + case SAMPLE_FORMAT_S24_P32: + *dest_size_r = src_size; + return (const int32_t *)src; + + case SAMPLE_FORMAT_S32: + return pcm_allocate_32_to_24(buffer, + (const int32_t *)src, src_size, + dest_size_r); + + case SAMPLE_FORMAT_FLOAT: + return pcm_allocate_float_to_24(buffer, + (const float *)src, src_size, + dest_size_r); + } + + return NULL; +} + +static void +pcm_convert_8_to_32(int32_t *out, const int8_t *in, const int8_t *in_end) +{ + while (in < in_end) + *out++ = *in++ << 24; +} + +static void +pcm_convert_16_to_32(int32_t *out, const int16_t *in, const int16_t *in_end) +{ + while (in < in_end) + *out++ = *in++ << 16; +} + +static void +pcm_convert_24_to_32(int32_t *restrict out, + const int32_t *restrict in, + const int32_t *restrict in_end) +{ + while (in < in_end) + *out++ = *in++ << 8; +} + +static int32_t * +pcm_allocate_8_to_32(struct pcm_buffer *buffer, + const int8_t *src, size_t src_size, size_t *dest_size_r) +{ + int32_t *dest; + *dest_size_r = src_size / sizeof(*src) * sizeof(*dest); + dest = (int32_t *)pcm_buffer_get(buffer, *dest_size_r); + pcm_convert_8_to_32(dest, src, pcm_end_pointer(src, src_size)); + return dest; +} + +static int32_t * +pcm_allocate_16_to_32(struct pcm_buffer *buffer, + const int16_t *src, size_t src_size, size_t *dest_size_r) +{ + int32_t *dest; + *dest_size_r = src_size * 2; + assert(*dest_size_r == src_size / sizeof(*src) * sizeof(*dest)); + dest = (int32_t *)pcm_buffer_get(buffer, *dest_size_r); + pcm_convert_16_to_32(dest, src, pcm_end_pointer(src, src_size)); + return dest; +} + +static int32_t * +pcm_allocate_24p32_to_32(struct pcm_buffer *buffer, + const int32_t *src, size_t src_size, + size_t *dest_size_r) +{ + *dest_size_r = src_size; + int32_t *dest = (int32_t *)pcm_buffer_get(buffer, *dest_size_r); + pcm_convert_24_to_32(dest, src, pcm_end_pointer(src, src_size)); + return dest; +} + +static int32_t * +pcm_allocate_float_to_32(struct pcm_buffer *buffer, + const float *src, size_t src_size, + size_t *dest_size_r) +{ + /* convert to S24_P32 first */ + int32_t *dest = pcm_allocate_float_to_24(buffer, src, src_size, + dest_size_r); + + /* convert to 32 bit in-place */ + pcm_convert_24_to_32(dest, dest, pcm_end_pointer(dest, *dest_size_r)); + return dest; +} + +const int32_t * +pcm_convert_to_32(struct pcm_buffer *buffer, + enum sample_format src_format, const void *src, + size_t src_size, size_t *dest_size_r) +{ + assert(src_size % sample_format_size(src_format) == 0); + + switch (src_format) { + case SAMPLE_FORMAT_UNDEFINED: + case SAMPLE_FORMAT_DSD: + break; + + case SAMPLE_FORMAT_S8: + return pcm_allocate_8_to_32(buffer, + (const int8_t *)src, src_size, + dest_size_r); + + case SAMPLE_FORMAT_S16: + return pcm_allocate_16_to_32(buffer, + (const int16_t *)src, src_size, + dest_size_r); + + case SAMPLE_FORMAT_S24_P32: + return pcm_allocate_24p32_to_32(buffer, + (const int32_t *)src, src_size, + dest_size_r); + + case SAMPLE_FORMAT_S32: + *dest_size_r = src_size; + return (const int32_t *)src; + + case SAMPLE_FORMAT_FLOAT: + return pcm_allocate_float_to_32(buffer, + (const float *)src, src_size, + dest_size_r); + } + + return NULL; +} + +static void +pcm_convert_8_to_float(float *out, const int8_t *in, const int8_t *in_end) +{ + enum { in_bits = sizeof(*in) * 8 }; + static const float factor = 2.0f / (1 << in_bits); + while (in < in_end) + *out++ = (float)*in++ * factor; +} + +static void +pcm_convert_16_to_float(float *out, const int16_t *in, const int16_t *in_end) +{ + enum { in_bits = sizeof(*in) * 8 }; + static const float factor = 2.0f / (1 << in_bits); + while (in < in_end) + *out++ = (float)*in++ * factor; +} + +static void +pcm_convert_24_to_float(float *out, const int32_t *in, const int32_t *in_end) +{ + enum { in_bits = 24 }; + static const float factor = 2.0f / (1 << in_bits); + while (in < in_end) + *out++ = (float)*in++ * factor; +} + +static void +pcm_convert_32_to_float(float *out, const int32_t *in, const int32_t *in_end) +{ + enum { in_bits = sizeof(*in) * 8 }; + static const float factor = 0.5f / (1 << (in_bits - 2)); + while (in < in_end) + *out++ = (float)*in++ * factor; +} + +static float * +pcm_allocate_8_to_float(struct pcm_buffer *buffer, + const int8_t *src, size_t src_size, + size_t *dest_size_r) +{ + float *dest; + *dest_size_r = src_size / sizeof(*src) * sizeof(*dest); + dest = (float *)pcm_buffer_get(buffer, *dest_size_r); + pcm_convert_8_to_float(dest, src, pcm_end_pointer(src, src_size)); + return dest; +} + +static float * +pcm_allocate_16_to_float(struct pcm_buffer *buffer, + const int16_t *src, size_t src_size, + size_t *dest_size_r) +{ + float *dest; + *dest_size_r = src_size * 2; + assert(*dest_size_r == src_size / sizeof(*src) * sizeof(*dest)); + dest = (float *)pcm_buffer_get(buffer, *dest_size_r); + pcm_convert_16_to_float(dest, src, pcm_end_pointer(src, src_size)); + return dest; +} + +static float * +pcm_allocate_24p32_to_float(struct pcm_buffer *buffer, + const int32_t *src, size_t src_size, + size_t *dest_size_r) +{ + *dest_size_r = src_size; + float *dest = (float *)pcm_buffer_get(buffer, *dest_size_r); + pcm_convert_24_to_float(dest, src, pcm_end_pointer(src, src_size)); + return dest; +} + +static float * +pcm_allocate_32_to_float(struct pcm_buffer *buffer, + const int32_t *src, size_t src_size, + size_t *dest_size_r) +{ + *dest_size_r = src_size; + float *dest = (float *)pcm_buffer_get(buffer, *dest_size_r); + pcm_convert_32_to_float(dest, src, pcm_end_pointer(src, src_size)); + return dest; +} + +const float * +pcm_convert_to_float(struct pcm_buffer *buffer, + enum sample_format src_format, const void *src, + size_t src_size, size_t *dest_size_r) +{ + switch (src_format) { + case SAMPLE_FORMAT_UNDEFINED: + case SAMPLE_FORMAT_DSD: + break; + + case SAMPLE_FORMAT_S8: + return pcm_allocate_8_to_float(buffer, + (const int8_t *)src, src_size, + dest_size_r); + + case SAMPLE_FORMAT_S16: + return pcm_allocate_16_to_float(buffer, + (const int16_t *)src, src_size, + dest_size_r); + + case SAMPLE_FORMAT_S24_P32: + return pcm_allocate_24p32_to_float(buffer, + (const int32_t *)src, src_size, + dest_size_r); + + case SAMPLE_FORMAT_S32: + return pcm_allocate_32_to_float(buffer, + (const int32_t *)src, src_size, + dest_size_r); + + case SAMPLE_FORMAT_FLOAT: + *dest_size_r = src_size; + return (const float *)src; + } + + return NULL; +} diff --git a/src/PcmFormat.hxx b/src/PcmFormat.hxx new file mode 100644 index 000000000..d739f60d1 --- /dev/null +++ b/src/PcmFormat.hxx @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2003-2013 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. + */ + +#ifndef MPD_PCM_FORMAT_HXX +#define MPD_PCM_FORMAT_HXX + +#include "audio_format.h" + +#include +#include + +struct pcm_buffer; +struct pcm_dither; + +/** + * Converts PCM samples to 16 bit. If the source format is 24 bit, + * then dithering is applied. + * + * @param buffer a pcm_buffer object + * @param dither a pcm_dither object for 24-to-16 conversion + * @param bits the number of in the source buffer + * @param src the source PCM buffer + * @param src_size the size of #src in bytes + * @param dest_size_r returns the number of bytes of the destination buffer + * @return the destination buffer + */ +const int16_t * +pcm_convert_to_16(struct pcm_buffer *buffer, struct pcm_dither *dither, + enum sample_format src_format, const void *src, + size_t src_size, size_t *dest_size_r); + +/** + * Converts PCM samples to 24 bit (32 bit alignment). + * + * @param buffer a pcm_buffer object + * @param bits the number of in the source buffer + * @param src the source PCM buffer + * @param src_size the size of #src in bytes + * @param dest_size_r returns the number of bytes of the destination buffer + * @return the destination buffer + */ +const int32_t * +pcm_convert_to_24(struct pcm_buffer *buffer, + enum sample_format src_format, const void *src, + size_t src_size, size_t *dest_size_r); + +/** + * Converts PCM samples to 32 bit. + * + * @param buffer a pcm_buffer object + * @param bits the number of in the source buffer + * @param src the source PCM buffer + * @param src_size the size of #src in bytes + * @param dest_size_r returns the number of bytes of the destination buffer + * @return the destination buffer + */ +const int32_t * +pcm_convert_to_32(struct pcm_buffer *buffer, + enum sample_format src_format, const void *src, + size_t src_size, size_t *dest_size_r); + +/** + * Converts PCM samples to 32 bit floating point. + * + * @param buffer a pcm_buffer object + * @param bits the number of in the source buffer + * @param src the source PCM buffer + * @param src_size the size of #src in bytes + * @param dest_size_r returns the number of bytes of the destination buffer + * @return the destination buffer + */ +const float * +pcm_convert_to_float(struct pcm_buffer *buffer, + enum sample_format src_format, const void *src, + size_t src_size, size_t *dest_size_r); + +#endif diff --git a/src/PcmMix.cxx b/src/PcmMix.cxx new file mode 100644 index 000000000..4f8f3882c --- /dev/null +++ b/src/PcmMix.cxx @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2003-2011 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 "config.h" +#include "PcmMix.hxx" +#include "PcmVolume.hxx" +#include "PcmUtils.hxx" +#include "audio_format.h" + +#include + +static void +pcm_add_vol_8(int8_t *buffer1, const int8_t *buffer2, + unsigned num_samples, int volume1, int volume2) +{ + while (num_samples > 0) { + int32_t sample1 = *buffer1; + int32_t sample2 = *buffer2++; + + sample1 = ((sample1 * volume1 + sample2 * volume2) + + pcm_volume_dither() + PCM_VOLUME_1 / 2) + / PCM_VOLUME_1; + + *buffer1++ = pcm_range(sample1, 8); + --num_samples; + } +} + +static void +pcm_add_vol_16(int16_t *buffer1, const int16_t *buffer2, + unsigned num_samples, int volume1, int volume2) +{ + while (num_samples > 0) { + int32_t sample1 = *buffer1; + int32_t sample2 = *buffer2++; + + sample1 = ((sample1 * volume1 + sample2 * volume2) + + pcm_volume_dither() + PCM_VOLUME_1 / 2) + / PCM_VOLUME_1; + + *buffer1++ = pcm_range(sample1, 16); + --num_samples; + } +} + +static void +pcm_add_vol_24(int32_t *buffer1, const int32_t *buffer2, + unsigned num_samples, unsigned volume1, unsigned volume2) +{ + while (num_samples > 0) { + int64_t sample1 = *buffer1; + int64_t sample2 = *buffer2++; + + sample1 = ((sample1 * volume1 + sample2 * volume2) + + pcm_volume_dither() + PCM_VOLUME_1 / 2) + / PCM_VOLUME_1; + + *buffer1++ = pcm_range(sample1, 24); + --num_samples; + } +} + +static void +pcm_add_vol_32(int32_t *buffer1, const int32_t *buffer2, + unsigned num_samples, unsigned volume1, unsigned volume2) +{ + while (num_samples > 0) { + int64_t sample1 = *buffer1; + int64_t sample2 = *buffer2++; + + sample1 = ((sample1 * volume1 + sample2 * volume2) + + pcm_volume_dither() + PCM_VOLUME_1 / 2) + / PCM_VOLUME_1; + + *buffer1++ = pcm_range_64(sample1, 32); + --num_samples; + } +} + +static void +pcm_add_vol_float(float *buffer1, const float *buffer2, + unsigned num_samples, float volume1, float volume2) +{ + while (num_samples > 0) { + float sample1 = *buffer1; + float sample2 = *buffer2++; + + sample1 = (sample1 * volume1 + sample2 * volume2); + *buffer1++ = sample1; + --num_samples; + } +} + +static bool +pcm_add_vol(void *buffer1, const void *buffer2, size_t size, + int vol1, int vol2, + enum sample_format format) +{ + switch (format) { + case SAMPLE_FORMAT_UNDEFINED: + case SAMPLE_FORMAT_DSD: + /* not implemented */ + return false; + + case SAMPLE_FORMAT_S8: + pcm_add_vol_8((int8_t *)buffer1, (const int8_t *)buffer2, + size, vol1, vol2); + return true; + + case SAMPLE_FORMAT_S16: + pcm_add_vol_16((int16_t *)buffer1, (const int16_t *)buffer2, + size / 2, vol1, vol2); + return true; + + case SAMPLE_FORMAT_S24_P32: + pcm_add_vol_24((int32_t *)buffer1, (const int32_t *)buffer2, + size / 4, vol1, vol2); + return true; + + case SAMPLE_FORMAT_S32: + pcm_add_vol_32((int32_t *)buffer1, (const int32_t *)buffer2, + size / 4, vol1, vol2); + return true; + + case SAMPLE_FORMAT_FLOAT: + pcm_add_vol_float((float *)buffer1, (const float *)buffer2, + size / 4, + pcm_volume_to_float(vol1), + pcm_volume_to_float(vol2)); + return true; + } + + /* unreachable */ + assert(false); + return false; +} + +static void +pcm_add_8(int8_t *buffer1, const int8_t *buffer2, unsigned num_samples) +{ + while (num_samples > 0) { + int32_t sample1 = *buffer1; + int32_t sample2 = *buffer2++; + + sample1 += sample2; + + *buffer1++ = pcm_range(sample1, 8); + --num_samples; + } +} + +static void +pcm_add_16(int16_t *buffer1, const int16_t *buffer2, unsigned num_samples) +{ + while (num_samples > 0) { + int32_t sample1 = *buffer1; + int32_t sample2 = *buffer2++; + + sample1 += sample2; + + *buffer1++ = pcm_range(sample1, 16); + --num_samples; + } +} + +static void +pcm_add_24(int32_t *buffer1, const int32_t *buffer2, unsigned num_samples) +{ + while (num_samples > 0) { + int64_t sample1 = *buffer1; + int64_t sample2 = *buffer2++; + + sample1 += sample2; + + *buffer1++ = pcm_range(sample1, 24); + --num_samples; + } +} + +static void +pcm_add_32(int32_t *buffer1, const int32_t *buffer2, unsigned num_samples) +{ + while (num_samples > 0) { + int64_t sample1 = *buffer1; + int64_t sample2 = *buffer2++; + + sample1 += sample2; + + *buffer1++ = pcm_range_64(sample1, 32); + --num_samples; + } +} + +static void +pcm_add_float(float *buffer1, const float *buffer2, unsigned num_samples) +{ + while (num_samples > 0) { + float sample1 = *buffer1; + float sample2 = *buffer2++; + *buffer1++ = sample1 + sample2; + --num_samples; + } +} + +static bool +pcm_add(void *buffer1, const void *buffer2, size_t size, + enum sample_format format) +{ + switch (format) { + case SAMPLE_FORMAT_UNDEFINED: + case SAMPLE_FORMAT_DSD: + /* not implemented */ + return false; + + case SAMPLE_FORMAT_S8: + pcm_add_8((int8_t *)buffer1, (const int8_t *)buffer2, size); + return true; + + case SAMPLE_FORMAT_S16: + pcm_add_16((int16_t *)buffer1, (const int16_t *)buffer2, size / 2); + return true; + + case SAMPLE_FORMAT_S24_P32: + pcm_add_24((int32_t *)buffer1, (const int32_t *)buffer2, size / 4); + return true; + + case SAMPLE_FORMAT_S32: + pcm_add_32((int32_t *)buffer1, (const int32_t *)buffer2, size / 4); + return true; + + case SAMPLE_FORMAT_FLOAT: + pcm_add_float((float *)buffer1, (const float *)buffer2, + size / 4); + return true; + } + + /* unreachable */ + assert(false); + return false; +} + +bool +pcm_mix(void *buffer1, const void *buffer2, size_t size, + enum sample_format format, float portion1) +{ + int vol1; + float s; + + /* portion1 is between 0.0 and 1.0 for crossfading, MixRamp uses NaN + * to signal mixing rather than fading */ + if (isnan(portion1)) + return pcm_add(buffer1, buffer2, size, format); + + s = sin(M_PI_2 * portion1); + s *= s; + + vol1 = s * PCM_VOLUME_1 + 0.5; + vol1 = vol1 > PCM_VOLUME_1 ? PCM_VOLUME_1 : (vol1 < 0 ? 0 : vol1); + + return pcm_add_vol(buffer1, buffer2, size, vol1, PCM_VOLUME_1 - vol1, format); +} diff --git a/src/PcmMix.hxx b/src/PcmMix.hxx new file mode 100644 index 000000000..bb7110d04 --- /dev/null +++ b/src/PcmMix.hxx @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2003-2013 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. + */ + +#ifndef MPD_PCM_MIX_HXX +#define MPD_PCM_MIX_HXX + +#include "audio_format.h" +#include "gcc.h" + +#include + +/* + * Linearly mixes two PCM buffers. Both must have the same length and + * the same audio format. The formula is: + * + * s1 := s1 * portion1 + s2 * (1 - portion1) + * + * @param buffer1 the first PCM buffer, and the destination buffer + * @param buffer2 the second PCM buffer + * @param size the size of both buffers in bytes + * @param format the sample format of both buffers + * @param portion1 a number between 0.0 and 1.0 specifying the portion + * of the first buffer in the mix; portion2 = (1.0 - portion1). The value + * NaN is used by the MixRamp code to specify that simple addition is required. + * + * @return true on success, false if the format is not supported + */ +gcc_warn_unused_result +bool +pcm_mix(void *buffer1, const void *buffer2, size_t size, + enum sample_format format, float portion1); + +#endif diff --git a/src/PcmPrng.hxx b/src/PcmPrng.hxx new file mode 100644 index 000000000..0c823250d --- /dev/null +++ b/src/PcmPrng.hxx @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2003-2013 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. + */ + +#ifndef MPD_PCM_PRNG_HXX +#define MPD_PCM_PRNG_HXX + +/** + * A very simple linear congruential PRNG. It's good enough for PCM + * dithering. + */ +static unsigned long +pcm_prng(unsigned long state) +{ + return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL; +} + +#endif diff --git a/src/PcmUtils.hxx b/src/PcmUtils.hxx new file mode 100644 index 000000000..a95af7e68 --- /dev/null +++ b/src/PcmUtils.hxx @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2003-2011 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. + */ + +#ifndef MPD_PCM_UTILS_H +#define MPD_PCM_UTILS_H + +#include "gcc.h" + +#include + +/** + * Add a byte count to the specified pointer. This is a utility + * function to convert a source pointer and a byte count to an "end" + * pointer for use in loops. + */ +template +static inline const T * +pcm_end_pointer(const T *p, size_t size) +{ + return (const T *)((const uint8_t *)p + size); +} + +/** + * Check if the value is within the range of the provided bit size, + * and caps it if necessary. + */ +static inline int32_t +pcm_range(int32_t sample, unsigned bits) +{ + if (gcc_unlikely(sample < (-1 << (bits - 1)))) + return -1 << (bits - 1); + if (gcc_unlikely(sample >= (1 << (bits - 1)))) + return (1 << (bits - 1)) - 1; + return sample; +} + +/** + * Check if the value is within the range of the provided bit size, + * and caps it if necessary. + */ +static inline int64_t +pcm_range_64(int64_t sample, unsigned bits) +{ + if (gcc_unlikely(sample < ((int64_t)-1 << (bits - 1)))) + return (int64_t)-1 << (bits - 1); + if (gcc_unlikely(sample >= ((int64_t)1 << (bits - 1)))) + return ((int64_t)1 << (bits - 1)) - 1; + return sample; +} + +gcc_const +static inline int16_t +pcm_clamp_16(int x) +{ + static const int32_t MIN_VALUE = -(1 << 15); + static const int32_t MAX_VALUE = (1 << 15) - 1; + + if (gcc_unlikely(x < MIN_VALUE)) + return MIN_VALUE; + if (gcc_unlikely(x > MAX_VALUE)) + return MAX_VALUE; + return x; +} + +gcc_const +static inline int32_t +pcm_clamp_24(int x) +{ + static const int32_t MIN_VALUE = -(1 << 23); + static const int32_t MAX_VALUE = (1 << 23) - 1; + + if (gcc_unlikely(x < MIN_VALUE)) + return MIN_VALUE; + if (gcc_unlikely(x > MAX_VALUE)) + return MAX_VALUE; + return x; +} + +#endif diff --git a/src/PcmVolume.cxx b/src/PcmVolume.cxx new file mode 100644 index 000000000..e19469f4a --- /dev/null +++ b/src/PcmVolume.cxx @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2003-2011 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 "config.h" +#include "PcmVolume.hxx" +#include "PcmUtils.hxx" +#include "audio_format.h" + +#include + +#include +#include + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "pcm_volume" + +static void +pcm_volume_change_8(int8_t *buffer, const int8_t *end, int volume) +{ + while (buffer < end) { + int32_t sample = *buffer; + + sample = (sample * volume + pcm_volume_dither() + + PCM_VOLUME_1 / 2) + / PCM_VOLUME_1; + + *buffer++ = pcm_range(sample, 8); + } +} + +static void +pcm_volume_change_16(int16_t *buffer, const int16_t *end, int volume) +{ + while (buffer < end) { + int32_t sample = *buffer; + + sample = (sample * volume + pcm_volume_dither() + + PCM_VOLUME_1 / 2) + / PCM_VOLUME_1; + + *buffer++ = pcm_range(sample, 16); + } +} + +#ifdef __i386__ +/** + * Optimized volume function for i386. Use the EDX:EAX 2*32 bit + * multiplication result instead of emulating 64 bit multiplication. + */ +static inline int32_t +pcm_volume_sample_24(int32_t sample, int32_t volume, G_GNUC_UNUSED int32_t dither) +{ + int32_t result; + + asm(/* edx:eax = sample * volume */ + "imul %2\n" + + /* "add %3, %1\n" dithering disabled for now, because we + have no overflow check - is dithering really important + here? */ + + /* eax = edx:eax / PCM_VOLUME_1 */ + "sal $22, %%edx\n" + "shr $10, %1\n" + "or %%edx, %1\n" + + : "=a"(result) + : "0"(sample), "r"(volume) /* , "r"(dither) */ + : "edx" + ); + + return result; +} +#endif + +static void +pcm_volume_change_24(int32_t *buffer, const int32_t *end, int volume) +{ + while (buffer < end) { +#ifdef __i386__ + /* assembly version for i386 */ + int32_t sample = *buffer; + + sample = pcm_volume_sample_24(sample, volume, + pcm_volume_dither()); +#else + /* portable version */ + int64_t sample = *buffer; + + sample = (sample * volume + pcm_volume_dither() + + PCM_VOLUME_1 / 2) + / PCM_VOLUME_1; +#endif + *buffer++ = pcm_range(sample, 24); + } +} + +static void +pcm_volume_change_32(int32_t *buffer, const int32_t *end, int volume) +{ + while (buffer < end) { +#ifdef __i386__ + /* assembly version for i386 */ + int32_t sample = *buffer; + + *buffer++ = pcm_volume_sample_24(sample, volume, 0); +#else + /* portable version */ + int64_t sample = *buffer; + + sample = (sample * volume + pcm_volume_dither() + + PCM_VOLUME_1 / 2) + / PCM_VOLUME_1; + *buffer++ = pcm_range_64(sample, 32); +#endif + } +} + +static void +pcm_volume_change_float(float *buffer, const float *end, float volume) +{ + while (buffer < end) { + float sample = *buffer; + sample *= volume; + *buffer++ = sample; + } +} + +bool +pcm_volume(void *buffer, size_t length, + enum sample_format format, + int volume) +{ + if (volume == PCM_VOLUME_1) + return true; + + if (volume <= 0) { + memset(buffer, 0, length); + return true; + } + + const void *end = pcm_end_pointer(buffer, length); + switch (format) { + case SAMPLE_FORMAT_UNDEFINED: + case SAMPLE_FORMAT_DSD: + /* not implemented */ + return false; + + case SAMPLE_FORMAT_S8: + pcm_volume_change_8((int8_t *)buffer, (const int8_t *)end, + volume); + return true; + + case SAMPLE_FORMAT_S16: + pcm_volume_change_16((int16_t *)buffer, (const int16_t *)end, + volume); + return true; + + case SAMPLE_FORMAT_S24_P32: + pcm_volume_change_24((int32_t *)buffer, (const int32_t *)end, + volume); + return true; + + case SAMPLE_FORMAT_S32: + pcm_volume_change_32((int32_t *)buffer, (const int32_t *)end, + volume); + return true; + + case SAMPLE_FORMAT_FLOAT: + pcm_volume_change_float((float *)buffer, (const float *)end, + pcm_volume_to_float(volume)); + return true; + } + + /* unreachable */ + assert(false); + return false; +} diff --git a/src/PcmVolume.hxx b/src/PcmVolume.hxx new file mode 100644 index 000000000..d3e6a5536 --- /dev/null +++ b/src/PcmVolume.hxx @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2003-2013 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. + */ + +#ifndef MPD_PCM_VOLUME_HXX +#define MPD_PCM_VOLUME_HXX + +#include "PcmPrng.hxx" +#include "audio_format.h" + +#include +#include +#include + +enum { + /** this value means "100% volume" */ + PCM_VOLUME_1 = 1024, +}; + +struct audio_format; + +/** + * Converts a float value (0.0 = silence, 1.0 = 100% volume) to an + * integer volume value (1000 = 100%). + */ +static inline int +pcm_float_to_volume(float volume) +{ + return volume * PCM_VOLUME_1 + 0.5; +} + +static inline float +pcm_volume_to_float(int volume) +{ + return (float)volume / (float)PCM_VOLUME_1; +} + +/** + * Returns the next volume dithering number, between -511 and +511. + * This number is taken from a global PRNG, see pcm_prng(). + */ +static inline int +pcm_volume_dither(void) +{ + static unsigned long state; + uint32_t r; + + r = state = pcm_prng(state); + + return (r & 511) - ((r >> 9) & 511); +} + +/** + * Adjust the volume of the specified PCM buffer. + * + * @param buffer the PCM buffer + * @param length the length of the PCM buffer + * @param format the sample format of the PCM buffer + * @param volume the volume between 0 and #PCM_VOLUME_1 + * @return true on success, false if the audio format is not supported + */ +bool +pcm_volume(void *buffer, size_t length, + enum sample_format format, + int volume); + +#endif diff --git a/src/filter/ReplayGainFilterPlugin.cxx b/src/filter/ReplayGainFilterPlugin.cxx index f4b1f92ee..9fb412be7 100644 --- a/src/filter/ReplayGainFilterPlugin.cxx +++ b/src/filter/ReplayGainFilterPlugin.cxx @@ -26,10 +26,10 @@ #include "replay_gain_info.h" #include "replay_gain_config.h" #include "mixer_control.h" +#include "PcmVolume.hxx" extern "C" { #include "pcm_buffer.h" -#include "pcm_volume.h" } #include diff --git a/src/filter/VolumeFilterPlugin.cxx b/src/filter/VolumeFilterPlugin.cxx index 764d2453f..5066ebbfa 100644 --- a/src/filter/VolumeFilterPlugin.cxx +++ b/src/filter/VolumeFilterPlugin.cxx @@ -24,7 +24,7 @@ #include "filter_registry.h" #include "conf.h" #include "pcm_buffer.h" -#include "pcm_volume.h" +#include "PcmVolume.hxx" #include "audio_format.h" #include diff --git a/src/mixer/SoftwareMixerPlugin.cxx b/src/mixer/SoftwareMixerPlugin.cxx index 45f564c9d..c06ce18ea 100644 --- a/src/mixer/SoftwareMixerPlugin.cxx +++ b/src/mixer/SoftwareMixerPlugin.cxx @@ -23,7 +23,7 @@ #include "filter_plugin.h" #include "filter_registry.h" #include "filter/VolumeFilterPlugin.hxx" -#include "pcm_volume.h" +#include "PcmVolume.hxx" #include #include diff --git a/src/pcm_channels.c b/src/pcm_channels.c deleted file mode 100644 index 9d166a437..000000000 --- a/src/pcm_channels.c +++ /dev/null @@ -1,317 +0,0 @@ -/* - * Copyright (C) 2003-2011 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 "config.h" -#include "pcm_channels.h" -#include "pcm_buffer.h" -#include "pcm_utils.h" - -#include - -static void -pcm_convert_channels_16_1_to_2(int16_t *restrict dest, - const int16_t *restrict src, - const int16_t *restrict src_end) -{ - while (src < src_end) { - int16_t value = *src++; - - *dest++ = value; - *dest++ = value; - } -} - -static void -pcm_convert_channels_16_2_to_1(int16_t *restrict dest, - const int16_t *restrict src, - const int16_t *restrict src_end) -{ - while (src < src_end) { - int32_t a = *src++, b = *src++; - - *dest++ = (a + b) / 2; - } -} - -static void -pcm_convert_channels_16_n_to_2(int16_t *restrict dest, - unsigned src_channels, - const int16_t *restrict src, - const int16_t *restrict src_end) -{ - unsigned c; - - assert(src_channels > 0); - - while (src < src_end) { - int32_t sum = 0; - int16_t value; - - for (c = 0; c < src_channels; ++c) - sum += *src++; - value = sum / (int)src_channels; - - /* XXX this is actually only mono ... */ - *dest++ = value; - *dest++ = value; - } -} - -const int16_t * -pcm_convert_channels_16(struct pcm_buffer *buffer, - unsigned dest_channels, - unsigned src_channels, const int16_t *src, - size_t src_size, size_t *dest_size_r) -{ - assert(src_size % (sizeof(*src) * src_channels) == 0); - - size_t dest_size = src_size / src_channels * dest_channels; - *dest_size_r = dest_size; - - int16_t *dest = pcm_buffer_get(buffer, dest_size); - const int16_t *src_end = pcm_end_pointer(src, src_size); - - if (src_channels == 1 && dest_channels == 2) - pcm_convert_channels_16_1_to_2(dest, src, src_end); - else if (src_channels == 2 && dest_channels == 1) - pcm_convert_channels_16_2_to_1(dest, src, src_end); - else if (dest_channels == 2) - pcm_convert_channels_16_n_to_2(dest, src_channels, src, - src_end); - else - return NULL; - - return dest; -} - -static void -pcm_convert_channels_24_1_to_2(int32_t *restrict dest, - const int32_t *restrict src, - const int32_t *restrict src_end) -{ - while (src < src_end) { - int32_t value = *src++; - - *dest++ = value; - *dest++ = value; - } -} - -static void -pcm_convert_channels_24_2_to_1(int32_t *restrict dest, - const int32_t *restrict src, - const int32_t *restrict src_end) -{ - while (src < src_end) { - int32_t a = *src++, b = *src++; - - *dest++ = (a + b) / 2; - } -} - -static void -pcm_convert_channels_24_n_to_2(int32_t *restrict dest, - unsigned src_channels, - const int32_t *restrict src, - const int32_t *restrict src_end) -{ - unsigned c; - - assert(src_channels > 0); - - while (src < src_end) { - int32_t sum = 0; - int32_t value; - - for (c = 0; c < src_channels; ++c) - sum += *src++; - value = sum / (int)src_channels; - - /* XXX this is actually only mono ... */ - *dest++ = value; - *dest++ = value; - } -} - -const int32_t * -pcm_convert_channels_24(struct pcm_buffer *buffer, - unsigned dest_channels, - unsigned src_channels, const int32_t *src, - size_t src_size, size_t *dest_size_r) -{ - assert(src_size % (sizeof(*src) * src_channels) == 0); - - size_t dest_size = src_size / src_channels * dest_channels; - *dest_size_r = dest_size; - - int32_t *dest = pcm_buffer_get(buffer, dest_size); - const int32_t *src_end = pcm_end_pointer(src, src_size); - - if (src_channels == 1 && dest_channels == 2) - pcm_convert_channels_24_1_to_2(dest, src, src_end); - else if (src_channels == 2 && dest_channels == 1) - pcm_convert_channels_24_2_to_1(dest, src, src_end); - else if (dest_channels == 2) - pcm_convert_channels_24_n_to_2(dest, src_channels, src, - src_end); - else - return NULL; - - return dest; -} - -static void -pcm_convert_channels_32_1_to_2(int32_t *dest, const int32_t *src, - const int32_t *src_end) -{ - pcm_convert_channels_24_1_to_2(dest, src, src_end); -} - -static void -pcm_convert_channels_32_2_to_1(int32_t *restrict dest, - const int32_t *restrict src, - const int32_t *restrict src_end) -{ - while (src < src_end) { - int64_t a = *src++, b = *src++; - - *dest++ = (a + b) / 2; - } -} - -static void -pcm_convert_channels_32_n_to_2(int32_t *dest, - unsigned src_channels, const int32_t *src, - const int32_t *src_end) -{ - unsigned c; - - assert(src_channels > 0); - - while (src < src_end) { - int64_t sum = 0; - int32_t value; - - for (c = 0; c < src_channels; ++c) - sum += *src++; - value = sum / (int64_t)src_channels; - - /* XXX this is actually only mono ... */ - *dest++ = value; - *dest++ = value; - } -} - -const int32_t * -pcm_convert_channels_32(struct pcm_buffer *buffer, - unsigned dest_channels, - unsigned src_channels, const int32_t *src, - size_t src_size, size_t *dest_size_r) -{ - assert(src_size % (sizeof(*src) * src_channels) == 0); - - size_t dest_size = src_size / src_channels * dest_channels; - *dest_size_r = dest_size; - - int32_t *dest = pcm_buffer_get(buffer, dest_size); - const int32_t *src_end = pcm_end_pointer(src, src_size); - - if (src_channels == 1 && dest_channels == 2) - pcm_convert_channels_32_1_to_2(dest, src, src_end); - else if (src_channels == 2 && dest_channels == 1) - pcm_convert_channels_32_2_to_1(dest, src, src_end); - else if (dest_channels == 2) - pcm_convert_channels_32_n_to_2(dest, src_channels, src, - src_end); - else - return NULL; - - return dest; -} - -static void -pcm_convert_channels_float_1_to_2(float *dest, const float *src, - const float *src_end) -{ - pcm_convert_channels_24_1_to_2((int32_t *)dest, - (const int32_t *)src, - (const int32_t *)src_end); -} - -static void -pcm_convert_channels_float_2_to_1(float *restrict dest, - const float *restrict src, - const float *restrict src_end) -{ - while (src < src_end) { - double a = *src++, b = *src++; - - *dest++ = (a + b) / 2; - } -} - -static void -pcm_convert_channels_float_n_to_2(float *dest, - unsigned src_channels, const float *src, - const float *src_end) -{ - unsigned c; - - assert(src_channels > 0); - - while (src < src_end) { - double sum = 0; - float value; - - for (c = 0; c < src_channels; ++c) - sum += *src++; - value = sum / (double)src_channels; - - /* XXX this is actually only mono ... */ - *dest++ = value; - *dest++ = value; - } -} - -const float * -pcm_convert_channels_float(struct pcm_buffer *buffer, - unsigned dest_channels, - unsigned src_channels, const float *src, - size_t src_size, size_t *dest_size_r) -{ - assert(src_size % (sizeof(*src) * src_channels) == 0); - - size_t dest_size = src_size / src_channels * dest_channels; - *dest_size_r = dest_size; - - float *dest = pcm_buffer_get(buffer, dest_size); - const float *src_end = pcm_end_pointer(src, src_size); - - if (src_channels == 1 && dest_channels == 2) - pcm_convert_channels_float_1_to_2(dest, src, src_end); - else if (src_channels == 2 && dest_channels == 1) - pcm_convert_channels_float_2_to_1(dest, src, src_end); - else if (dest_channels == 2) - pcm_convert_channels_float_n_to_2(dest, src_channels, src, - src_end); - else - return NULL; - - return dest; -} diff --git a/src/pcm_channels.h b/src/pcm_channels.h deleted file mode 100644 index 6da00316d..000000000 --- a/src/pcm_channels.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2003-2011 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. - */ - -#ifndef MPD_PCM_CHANNELS_H -#define MPD_PCM_CHANNELS_H - -#include -#include - -struct pcm_buffer; - -/** - * Changes the number of channels in 16 bit PCM data. - * - * @param buffer the destination pcm_buffer object - * @param dest_channels the number of channels requested - * @param src_channels the number of channels in the source buffer - * @param src the source PCM buffer - * @param src_size the number of bytes in #src - * @param dest_size_r returns the number of bytes of the destination buffer - * @return the destination buffer - */ -const int16_t * -pcm_convert_channels_16(struct pcm_buffer *buffer, - unsigned dest_channels, - unsigned src_channels, const int16_t *src, - size_t src_size, size_t *dest_size_r); - -/** - * Changes the number of channels in 24 bit PCM data (aligned at 32 - * bit boundaries). - * - * @param buffer the destination pcm_buffer object - * @param dest_channels the number of channels requested - * @param src_channels the number of channels in the source buffer - * @param src the source PCM buffer - * @param src_size the number of bytes in #src - * @param dest_size_r returns the number of bytes of the destination buffer - * @return the destination buffer - */ -const int32_t * -pcm_convert_channels_24(struct pcm_buffer *buffer, - unsigned dest_channels, - unsigned src_channels, const int32_t *src, - size_t src_size, size_t *dest_size_r); - -/** - * Changes the number of channels in 32 bit PCM data. - * - * @param buffer the destination pcm_buffer object - * @param dest_channels the number of channels requested - * @param src_channels the number of channels in the source buffer - * @param src the source PCM buffer - * @param src_size the number of bytes in #src - * @param dest_size_r returns the number of bytes of the destination buffer - * @return the destination buffer - */ -const int32_t * -pcm_convert_channels_32(struct pcm_buffer *buffer, - unsigned dest_channels, - unsigned src_channels, const int32_t *src, - size_t src_size, size_t *dest_size_r); - -/** - * Changes the number of channels in 32 bit float PCM data. - * - * @param buffer the destination pcm_buffer object - * @param dest_channels the number of channels requested - * @param src_channels the number of channels in the source buffer - * @param src the source PCM buffer - * @param src_size the number of bytes in #src - * @param dest_size_r returns the number of bytes of the destination buffer - * @return the destination buffer - */ -const float * -pcm_convert_channels_float(struct pcm_buffer *buffer, - unsigned dest_channels, - unsigned src_channels, const float *src, - size_t src_size, size_t *dest_size_r); - -#endif diff --git a/src/pcm_dither.c b/src/pcm_dither.c deleted file mode 100644 index 4811946c8..000000000 --- a/src/pcm_dither.c +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2003-2011 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 "config.h" -#include "pcm_dither.h" -#include "pcm_prng.h" - -static int16_t -pcm_dither_sample_24_to_16(int32_t sample, struct pcm_dither *dither) -{ - int32_t output, rnd; - - enum { - from_bits = 24, - to_bits = 16, - scale_bits = from_bits - to_bits, - round = 1 << (scale_bits - 1), - mask = (1 << scale_bits) - 1, - ONE = 1 << (from_bits - 1), - MIN = -ONE, - MAX = ONE - 1 - }; - - sample += dither->error[0] - dither->error[1] + dither->error[2]; - - dither->error[2] = dither->error[1]; - dither->error[1] = dither->error[0] / 2; - - /* round */ - output = sample + round; - - rnd = pcm_prng(dither->random); - output += (rnd & mask) - (dither->random & mask); - - dither->random = rnd; - - /* clip */ - if (output > MAX) { - output = MAX; - - if (sample > MAX) - sample = MAX; - } else if (output < MIN) { - output = MIN; - - if (sample < MIN) - sample = MIN; - } - - output &= ~mask; - - dither->error[0] = sample - output; - - return (int16_t)(output >> scale_bits); -} - -void -pcm_dither_24_to_16(struct pcm_dither *dither, - int16_t *dest, const int32_t *src, const int32_t *src_end) -{ - while (src < src_end) - *dest++ = pcm_dither_sample_24_to_16(*src++, dither); -} - -static int16_t -pcm_dither_sample_32_to_16(int32_t sample, struct pcm_dither *dither) -{ - return pcm_dither_sample_24_to_16(sample >> 8, dither); -} - -void -pcm_dither_32_to_16(struct pcm_dither *dither, - int16_t *dest, const int32_t *src, const int32_t *src_end) -{ - while (src < src_end) - *dest++ = pcm_dither_sample_32_to_16(*src++, dither); -} diff --git a/src/pcm_dither.h b/src/pcm_dither.h deleted file mode 100644 index 046dea21e..000000000 --- a/src/pcm_dither.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2003-2011 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. - */ - -#ifndef MPD_PCM_DITHER_H -#define MPD_PCM_DITHER_H - -#include - -struct pcm_dither { - int32_t error[3]; - int32_t random; -}; - -static inline void -pcm_dither_24_init(struct pcm_dither *dither) -{ - dither->error[0] = dither->error[1] = dither->error[2] = 0; - dither->random = 0; -} - -void -pcm_dither_24_to_16(struct pcm_dither *dither, - int16_t *dest, const int32_t *src, const int32_t *src_end); - -void -pcm_dither_32_to_16(struct pcm_dither *dither, - int16_t *dest, const int32_t *src, const int32_t *src_end); - -#endif diff --git a/src/pcm_format.c b/src/pcm_format.c deleted file mode 100644 index d3ea3acb0..000000000 --- a/src/pcm_format.c +++ /dev/null @@ -1,487 +0,0 @@ -/* - * Copyright (C) 2003-2011 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 "config.h" -#include "pcm_format.h" -#include "pcm_dither.h" -#include "pcm_buffer.h" -#include "pcm_pack.h" -#include "pcm_utils.h" - -static void -pcm_convert_8_to_16(int16_t *out, const int8_t *in, const int8_t *in_end) -{ - while (in < in_end) { - *out++ = *in++ << 8; - } -} - -static void -pcm_convert_24_to_16(struct pcm_dither *dither, - int16_t *out, const int32_t *in, const int32_t *in_end) -{ - pcm_dither_24_to_16(dither, out, in, in_end); -} - -static void -pcm_convert_32_to_16(struct pcm_dither *dither, - int16_t *out, const int32_t *in, const int32_t *in_end) -{ - pcm_dither_32_to_16(dither, out, in, in_end); -} - -static void -pcm_convert_float_to_16(int16_t *out, const float *in, const float *in_end) -{ - const unsigned OUT_BITS = 16; - const float factor = 1 << (OUT_BITS - 1); - - while (in < in_end) { - int sample = *in++ * factor; - *out++ = pcm_clamp_16(sample); - } -} - -static int16_t * -pcm_allocate_8_to_16(struct pcm_buffer *buffer, - const int8_t *src, size_t src_size, size_t *dest_size_r) -{ - int16_t *dest; - *dest_size_r = src_size / sizeof(*src) * sizeof(*dest); - dest = pcm_buffer_get(buffer, *dest_size_r); - pcm_convert_8_to_16(dest, src, pcm_end_pointer(src, src_size)); - return dest; -} - -static int16_t * -pcm_allocate_24p32_to_16(struct pcm_buffer *buffer, struct pcm_dither *dither, - const int32_t *src, size_t src_size, - size_t *dest_size_r) -{ - int16_t *dest; - *dest_size_r = src_size / 2; - assert(*dest_size_r == src_size / sizeof(*src) * sizeof(*dest)); - dest = pcm_buffer_get(buffer, *dest_size_r); - pcm_convert_24_to_16(dither, dest, src, - pcm_end_pointer(src, src_size)); - return dest; -} - -static int16_t * -pcm_allocate_32_to_16(struct pcm_buffer *buffer, struct pcm_dither *dither, - const int32_t *src, size_t src_size, - size_t *dest_size_r) -{ - int16_t *dest; - *dest_size_r = src_size / 2; - assert(*dest_size_r == src_size / sizeof(*src) * sizeof(*dest)); - dest = pcm_buffer_get(buffer, *dest_size_r); - pcm_convert_32_to_16(dither, dest, src, - pcm_end_pointer(src, src_size)); - return dest; -} - -static int16_t * -pcm_allocate_float_to_16(struct pcm_buffer *buffer, - const float *src, size_t src_size, - size_t *dest_size_r) -{ - int16_t *dest; - *dest_size_r = src_size / 2; - assert(*dest_size_r == src_size / sizeof(*src) * sizeof(*dest)); - dest = pcm_buffer_get(buffer, *dest_size_r); - pcm_convert_float_to_16(dest, src, - pcm_end_pointer(src, src_size)); - 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, - size_t src_size, size_t *dest_size_r) -{ - assert(src_size % sample_format_size(src_format) == 0); - - switch (src_format) { - case SAMPLE_FORMAT_UNDEFINED: - case SAMPLE_FORMAT_DSD: - break; - - case SAMPLE_FORMAT_S8: - return pcm_allocate_8_to_16(buffer, - src, src_size, dest_size_r); - - case SAMPLE_FORMAT_S16: - *dest_size_r = src_size; - return src; - - case SAMPLE_FORMAT_S24_P32: - return pcm_allocate_24p32_to_16(buffer, dither, src, src_size, - dest_size_r); - - case SAMPLE_FORMAT_S32: - return pcm_allocate_32_to_16(buffer, dither, src, src_size, - dest_size_r); - - case SAMPLE_FORMAT_FLOAT: - return pcm_allocate_float_to_16(buffer, src, src_size, - dest_size_r); - } - - return NULL; -} - -static void -pcm_convert_8_to_24(int32_t *out, const int8_t *in, const int8_t *in_end) -{ - while (in < in_end) - *out++ = *in++ << 16; -} - -static void -pcm_convert_16_to_24(int32_t *out, const int16_t *in, const int16_t *in_end) -{ - while (in < in_end) - *out++ = *in++ << 8; -} - -static void -pcm_convert_32_to_24(int32_t *restrict out, - const int32_t *restrict in, - const int32_t *restrict in_end) -{ - while (in < in_end) - *out++ = *in++ >> 8; -} - -static void -pcm_convert_float_to_24(int32_t *out, const float *in, const float *in_end) -{ - const unsigned OUT_BITS = 24; - const float factor = 1 << (OUT_BITS - 1); - - while (in < in_end) { - int sample = *in++ * factor; - *out++ = pcm_clamp_24(sample); - } -} - -static int32_t * -pcm_allocate_8_to_24(struct pcm_buffer *buffer, - const int8_t *src, size_t src_size, size_t *dest_size_r) -{ - int32_t *dest; - *dest_size_r = src_size / sizeof(*src) * sizeof(*dest); - dest = pcm_buffer_get(buffer, *dest_size_r); - pcm_convert_8_to_24(dest, src, pcm_end_pointer(src, src_size)); - return dest; -} - -static int32_t * -pcm_allocate_16_to_24(struct pcm_buffer *buffer, - const int16_t *src, size_t src_size, size_t *dest_size_r) -{ - int32_t *dest; - *dest_size_r = src_size * 2; - assert(*dest_size_r == src_size / sizeof(*src) * sizeof(*dest)); - dest = pcm_buffer_get(buffer, *dest_size_r); - pcm_convert_16_to_24(dest, src, pcm_end_pointer(src, src_size)); - return dest; -} - -static int32_t * -pcm_allocate_32_to_24(struct pcm_buffer *buffer, - const int32_t *src, size_t src_size, size_t *dest_size_r) -{ - *dest_size_r = src_size; - int32_t *dest = pcm_buffer_get(buffer, *dest_size_r); - pcm_convert_32_to_24(dest, src, pcm_end_pointer(src, src_size)); - return dest; -} - -static int32_t * -pcm_allocate_float_to_24(struct pcm_buffer *buffer, - const float *src, size_t src_size, - size_t *dest_size_r) -{ - *dest_size_r = src_size; - int32_t *dest = pcm_buffer_get(buffer, *dest_size_r); - pcm_convert_float_to_24(dest, src, pcm_end_pointer(src, src_size)); - return dest; -} - -const int32_t * -pcm_convert_to_24(struct pcm_buffer *buffer, - enum sample_format src_format, const void *src, - size_t src_size, size_t *dest_size_r) -{ - assert(src_size % sample_format_size(src_format) == 0); - - switch (src_format) { - case SAMPLE_FORMAT_UNDEFINED: - case SAMPLE_FORMAT_DSD: - break; - - case SAMPLE_FORMAT_S8: - return pcm_allocate_8_to_24(buffer, - src, src_size, dest_size_r); - - case SAMPLE_FORMAT_S16: - return pcm_allocate_16_to_24(buffer, - src, src_size, dest_size_r); - - case SAMPLE_FORMAT_S24_P32: - *dest_size_r = src_size; - return src; - - case SAMPLE_FORMAT_S32: - return pcm_allocate_32_to_24(buffer, src, src_size, - dest_size_r); - - case SAMPLE_FORMAT_FLOAT: - return pcm_allocate_float_to_24(buffer, src, src_size, - dest_size_r); - } - - return NULL; -} - -static void -pcm_convert_8_to_32(int32_t *out, const int8_t *in, const int8_t *in_end) -{ - while (in < in_end) - *out++ = *in++ << 24; -} - -static void -pcm_convert_16_to_32(int32_t *out, const int16_t *in, const int16_t *in_end) -{ - while (in < in_end) - *out++ = *in++ << 16; -} - -static void -pcm_convert_24_to_32(int32_t *restrict out, - const int32_t *restrict in, - const int32_t *restrict in_end) -{ - while (in < in_end) - *out++ = *in++ << 8; -} - -static int32_t * -pcm_allocate_8_to_32(struct pcm_buffer *buffer, - const int8_t *src, size_t src_size, size_t *dest_size_r) -{ - int32_t *dest; - *dest_size_r = src_size / sizeof(*src) * sizeof(*dest); - dest = pcm_buffer_get(buffer, *dest_size_r); - pcm_convert_8_to_32(dest, src, pcm_end_pointer(src, src_size)); - return dest; -} - -static int32_t * -pcm_allocate_16_to_32(struct pcm_buffer *buffer, - const int16_t *src, size_t src_size, size_t *dest_size_r) -{ - int32_t *dest; - *dest_size_r = src_size * 2; - assert(*dest_size_r == src_size / sizeof(*src) * sizeof(*dest)); - dest = pcm_buffer_get(buffer, *dest_size_r); - pcm_convert_16_to_32(dest, src, pcm_end_pointer(src, src_size)); - return dest; -} - -static int32_t * -pcm_allocate_24p32_to_32(struct pcm_buffer *buffer, - const int32_t *src, size_t src_size, - size_t *dest_size_r) -{ - *dest_size_r = src_size; - int32_t *dest = pcm_buffer_get(buffer, *dest_size_r); - pcm_convert_24_to_32(dest, src, pcm_end_pointer(src, src_size)); - return dest; -} - -static int32_t * -pcm_allocate_float_to_32(struct pcm_buffer *buffer, - const float *src, size_t src_size, - size_t *dest_size_r) -{ - /* convert to S24_P32 first */ - int32_t *dest = pcm_allocate_float_to_24(buffer, src, src_size, - dest_size_r); - - /* convert to 32 bit in-place */ - pcm_convert_24_to_32(dest, dest, pcm_end_pointer(dest, *dest_size_r)); - return dest; -} - -const int32_t * -pcm_convert_to_32(struct pcm_buffer *buffer, - enum sample_format src_format, const void *src, - size_t src_size, size_t *dest_size_r) -{ - assert(src_size % sample_format_size(src_format) == 0); - - switch (src_format) { - case SAMPLE_FORMAT_UNDEFINED: - case SAMPLE_FORMAT_DSD: - break; - - case SAMPLE_FORMAT_S8: - return pcm_allocate_8_to_32(buffer, src, src_size, - dest_size_r); - - case SAMPLE_FORMAT_S16: - return pcm_allocate_16_to_32(buffer, src, src_size, - dest_size_r); - - case SAMPLE_FORMAT_S24_P32: - return pcm_allocate_24p32_to_32(buffer, src, src_size, - dest_size_r); - - case SAMPLE_FORMAT_S32: - *dest_size_r = src_size; - return src; - - case SAMPLE_FORMAT_FLOAT: - return pcm_allocate_float_to_32(buffer, src, src_size, - dest_size_r); - } - - return NULL; -} - -static void -pcm_convert_8_to_float(float *out, const int8_t *in, const int8_t *in_end) -{ - enum { in_bits = sizeof(*in) * 8 }; - static const float factor = 2.0f / (1 << in_bits); - while (in < in_end) - *out++ = (float)*in++ * factor; -} - -static void -pcm_convert_16_to_float(float *out, const int16_t *in, const int16_t *in_end) -{ - enum { in_bits = sizeof(*in) * 8 }; - static const float factor = 2.0f / (1 << in_bits); - while (in < in_end) - *out++ = (float)*in++ * factor; -} - -static void -pcm_convert_24_to_float(float *out, const int32_t *in, const int32_t *in_end) -{ - enum { in_bits = 24 }; - static const float factor = 2.0f / (1 << in_bits); - while (in < in_end) - *out++ = (float)*in++ * factor; -} - -static void -pcm_convert_32_to_float(float *out, const int32_t *in, const int32_t *in_end) -{ - enum { in_bits = sizeof(*in) * 8 }; - static const float factor = 0.5f / (1 << (in_bits - 2)); - while (in < in_end) - *out++ = (float)*in++ * factor; -} - -static float * -pcm_allocate_8_to_float(struct pcm_buffer *buffer, - const int8_t *src, size_t src_size, - size_t *dest_size_r) -{ - float *dest; - *dest_size_r = src_size / sizeof(*src) * sizeof(*dest); - dest = pcm_buffer_get(buffer, *dest_size_r); - pcm_convert_8_to_float(dest, src, pcm_end_pointer(src, src_size)); - return dest; -} - -static float * -pcm_allocate_16_to_float(struct pcm_buffer *buffer, - const int16_t *src, size_t src_size, - size_t *dest_size_r) -{ - float *dest; - *dest_size_r = src_size * 2; - assert(*dest_size_r == src_size / sizeof(*src) * sizeof(*dest)); - dest = pcm_buffer_get(buffer, *dest_size_r); - pcm_convert_16_to_float(dest, src, pcm_end_pointer(src, src_size)); - return dest; -} - -static float * -pcm_allocate_24p32_to_float(struct pcm_buffer *buffer, - const int32_t *src, size_t src_size, - size_t *dest_size_r) -{ - *dest_size_r = src_size; - float *dest = pcm_buffer_get(buffer, *dest_size_r); - pcm_convert_24_to_float(dest, src, pcm_end_pointer(src, src_size)); - return dest; -} - -static float * -pcm_allocate_32_to_float(struct pcm_buffer *buffer, - const int32_t *src, size_t src_size, - size_t *dest_size_r) -{ - *dest_size_r = src_size; - float *dest = pcm_buffer_get(buffer, *dest_size_r); - pcm_convert_32_to_float(dest, src, pcm_end_pointer(src, src_size)); - return dest; -} - -const float * -pcm_convert_to_float(struct pcm_buffer *buffer, - enum sample_format src_format, const void *src, - size_t src_size, size_t *dest_size_r) -{ - switch (src_format) { - case SAMPLE_FORMAT_UNDEFINED: - case SAMPLE_FORMAT_DSD: - break; - - case SAMPLE_FORMAT_S8: - return pcm_allocate_8_to_float(buffer, - src, src_size, dest_size_r); - - case SAMPLE_FORMAT_S16: - return pcm_allocate_16_to_float(buffer, - src, src_size, dest_size_r); - - case SAMPLE_FORMAT_S24_P32: - return pcm_allocate_24p32_to_float(buffer, - src, src_size, dest_size_r); - - case SAMPLE_FORMAT_S32: - return pcm_allocate_32_to_float(buffer, - src, src_size, dest_size_r); - - case SAMPLE_FORMAT_FLOAT: - *dest_size_r = src_size; - return src; - } - - return NULL; -} diff --git a/src/pcm_format.h b/src/pcm_format.h deleted file mode 100644 index 48bcd0662..000000000 --- a/src/pcm_format.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2003-2011 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. - */ - -#ifndef PCM_FORMAT_H -#define PCM_FORMAT_H - -#include "audio_format.h" - -#include -#include - -struct pcm_buffer; -struct pcm_dither; - -/** - * Converts PCM samples to 16 bit. If the source format is 24 bit, - * then dithering is applied. - * - * @param buffer a pcm_buffer object - * @param dither a pcm_dither object for 24-to-16 conversion - * @param bits the number of in the source buffer - * @param src the source PCM buffer - * @param src_size the size of #src in bytes - * @param dest_size_r returns the number of bytes of the destination buffer - * @return the destination buffer - */ -const int16_t * -pcm_convert_to_16(struct pcm_buffer *buffer, struct pcm_dither *dither, - enum sample_format src_format, const void *src, - size_t src_size, size_t *dest_size_r); - -/** - * Converts PCM samples to 24 bit (32 bit alignment). - * - * @param buffer a pcm_buffer object - * @param bits the number of in the source buffer - * @param src the source PCM buffer - * @param src_size the size of #src in bytes - * @param dest_size_r returns the number of bytes of the destination buffer - * @return the destination buffer - */ -const int32_t * -pcm_convert_to_24(struct pcm_buffer *buffer, - enum sample_format src_format, const void *src, - size_t src_size, size_t *dest_size_r); - -/** - * Converts PCM samples to 32 bit. - * - * @param buffer a pcm_buffer object - * @param bits the number of in the source buffer - * @param src the source PCM buffer - * @param src_size the size of #src in bytes - * @param dest_size_r returns the number of bytes of the destination buffer - * @return the destination buffer - */ -const int32_t * -pcm_convert_to_32(struct pcm_buffer *buffer, - enum sample_format src_format, const void *src, - size_t src_size, size_t *dest_size_r); - -/** - * Converts PCM samples to 32 bit floating point. - * - * @param buffer a pcm_buffer object - * @param bits the number of in the source buffer - * @param src the source PCM buffer - * @param src_size the size of #src in bytes - * @param dest_size_r returns the number of bytes of the destination buffer - * @return the destination buffer - */ -const float * -pcm_convert_to_float(struct pcm_buffer *buffer, - enum sample_format src_format, const void *src, - size_t src_size, size_t *dest_size_r); - -#endif diff --git a/src/pcm_mix.c b/src/pcm_mix.c deleted file mode 100644 index 6c6d1b4ab..000000000 --- a/src/pcm_mix.c +++ /dev/null @@ -1,280 +0,0 @@ -/* - * Copyright (C) 2003-2011 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 "config.h" -#include "pcm_mix.h" -#include "pcm_volume.h" -#include "pcm_utils.h" -#include "audio_format.h" - -#include - -#include - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "pcm" - -static void -pcm_add_vol_8(int8_t *buffer1, const int8_t *buffer2, - unsigned num_samples, int volume1, int volume2) -{ - while (num_samples > 0) { - int32_t sample1 = *buffer1; - int32_t sample2 = *buffer2++; - - sample1 = ((sample1 * volume1 + sample2 * volume2) + - pcm_volume_dither() + PCM_VOLUME_1 / 2) - / PCM_VOLUME_1; - - *buffer1++ = pcm_range(sample1, 8); - --num_samples; - } -} - -static void -pcm_add_vol_16(int16_t *buffer1, const int16_t *buffer2, - unsigned num_samples, int volume1, int volume2) -{ - while (num_samples > 0) { - int32_t sample1 = *buffer1; - int32_t sample2 = *buffer2++; - - sample1 = ((sample1 * volume1 + sample2 * volume2) + - pcm_volume_dither() + PCM_VOLUME_1 / 2) - / PCM_VOLUME_1; - - *buffer1++ = pcm_range(sample1, 16); - --num_samples; - } -} - -static void -pcm_add_vol_24(int32_t *buffer1, const int32_t *buffer2, - unsigned num_samples, unsigned volume1, unsigned volume2) -{ - while (num_samples > 0) { - int64_t sample1 = *buffer1; - int64_t sample2 = *buffer2++; - - sample1 = ((sample1 * volume1 + sample2 * volume2) + - pcm_volume_dither() + PCM_VOLUME_1 / 2) - / PCM_VOLUME_1; - - *buffer1++ = pcm_range(sample1, 24); - --num_samples; - } -} - -static void -pcm_add_vol_32(int32_t *buffer1, const int32_t *buffer2, - unsigned num_samples, unsigned volume1, unsigned volume2) -{ - while (num_samples > 0) { - int64_t sample1 = *buffer1; - int64_t sample2 = *buffer2++; - - sample1 = ((sample1 * volume1 + sample2 * volume2) + - pcm_volume_dither() + PCM_VOLUME_1 / 2) - / PCM_VOLUME_1; - - *buffer1++ = pcm_range_64(sample1, 32); - --num_samples; - } -} - -static void -pcm_add_vol_float(float *buffer1, const float *buffer2, - unsigned num_samples, float volume1, float volume2) -{ - while (num_samples > 0) { - float sample1 = *buffer1; - float sample2 = *buffer2++; - - sample1 = (sample1 * volume1 + sample2 * volume2); - *buffer1++ = sample1; - --num_samples; - } -} - -static bool -pcm_add_vol(void *buffer1, const void *buffer2, size_t size, - int vol1, int vol2, - enum sample_format format) -{ - switch (format) { - case SAMPLE_FORMAT_UNDEFINED: - case SAMPLE_FORMAT_DSD: - /* not implemented */ - return false; - - case SAMPLE_FORMAT_S8: - pcm_add_vol_8((int8_t *)buffer1, (const int8_t *)buffer2, - size, vol1, vol2); - return true; - - case SAMPLE_FORMAT_S16: - pcm_add_vol_16((int16_t *)buffer1, (const int16_t *)buffer2, - size / 2, vol1, vol2); - return true; - - case SAMPLE_FORMAT_S24_P32: - pcm_add_vol_24((int32_t *)buffer1, (const int32_t *)buffer2, - size / 4, vol1, vol2); - return true; - - case SAMPLE_FORMAT_S32: - pcm_add_vol_32((int32_t *)buffer1, (const int32_t *)buffer2, - size / 4, vol1, vol2); - return true; - - case SAMPLE_FORMAT_FLOAT: - pcm_add_vol_float(buffer1, buffer2, size / 4, - pcm_volume_to_float(vol1), - pcm_volume_to_float(vol2)); - return true; - } - - /* unreachable */ - assert(false); - return false; -} - -static void -pcm_add_8(int8_t *buffer1, const int8_t *buffer2, unsigned num_samples) -{ - while (num_samples > 0) { - int32_t sample1 = *buffer1; - int32_t sample2 = *buffer2++; - - sample1 += sample2; - - *buffer1++ = pcm_range(sample1, 8); - --num_samples; - } -} - -static void -pcm_add_16(int16_t *buffer1, const int16_t *buffer2, unsigned num_samples) -{ - while (num_samples > 0) { - int32_t sample1 = *buffer1; - int32_t sample2 = *buffer2++; - - sample1 += sample2; - - *buffer1++ = pcm_range(sample1, 16); - --num_samples; - } -} - -static void -pcm_add_24(int32_t *buffer1, const int32_t *buffer2, unsigned num_samples) -{ - while (num_samples > 0) { - int64_t sample1 = *buffer1; - int64_t sample2 = *buffer2++; - - sample1 += sample2; - - *buffer1++ = pcm_range(sample1, 24); - --num_samples; - } -} - -static void -pcm_add_32(int32_t *buffer1, const int32_t *buffer2, unsigned num_samples) -{ - while (num_samples > 0) { - int64_t sample1 = *buffer1; - int64_t sample2 = *buffer2++; - - sample1 += sample2; - - *buffer1++ = pcm_range_64(sample1, 32); - --num_samples; - } -} - -static void -pcm_add_float(float *buffer1, const float *buffer2, unsigned num_samples) -{ - while (num_samples > 0) { - float sample1 = *buffer1; - float sample2 = *buffer2++; - *buffer1++ = sample1 + sample2; - --num_samples; - } -} - -static bool -pcm_add(void *buffer1, const void *buffer2, size_t size, - enum sample_format format) -{ - switch (format) { - case SAMPLE_FORMAT_UNDEFINED: - case SAMPLE_FORMAT_DSD: - /* not implemented */ - return false; - - case SAMPLE_FORMAT_S8: - pcm_add_8((int8_t *)buffer1, (const int8_t *)buffer2, size); - return true; - - case SAMPLE_FORMAT_S16: - pcm_add_16((int16_t *)buffer1, (const int16_t *)buffer2, size / 2); - return true; - - case SAMPLE_FORMAT_S24_P32: - pcm_add_24((int32_t *)buffer1, (const int32_t *)buffer2, size / 4); - return true; - - case SAMPLE_FORMAT_S32: - pcm_add_32((int32_t *)buffer1, (const int32_t *)buffer2, size / 4); - return true; - - case SAMPLE_FORMAT_FLOAT: - pcm_add_float(buffer1, buffer2, size / 4); - return true; - } - - /* unreachable */ - assert(false); - return false; -} - -bool -pcm_mix(void *buffer1, const void *buffer2, size_t size, - enum sample_format format, float portion1) -{ - int vol1; - float s; - - /* portion1 is between 0.0 and 1.0 for crossfading, MixRamp uses NaN - * to signal mixing rather than fading */ - if (isnan(portion1)) - return pcm_add(buffer1, buffer2, size, format); - - s = sin(M_PI_2 * portion1); - s *= s; - - vol1 = s * PCM_VOLUME_1 + 0.5; - vol1 = vol1 > PCM_VOLUME_1 ? PCM_VOLUME_1 : (vol1 < 0 ? 0 : vol1); - - return pcm_add_vol(buffer1, buffer2, size, vol1, PCM_VOLUME_1 - vol1, format); -} diff --git a/src/pcm_mix.h b/src/pcm_mix.h deleted file mode 100644 index 0cf557680..000000000 --- a/src/pcm_mix.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2003-2011 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. - */ - -#ifndef PCM_MIX_H -#define PCM_MIX_H - -#include "audio_format.h" -#include "gcc.h" - -#include -#include - -/* - * Linearly mixes two PCM buffers. Both must have the same length and - * the same audio format. The formula is: - * - * s1 := s1 * portion1 + s2 * (1 - portion1) - * - * @param buffer1 the first PCM buffer, and the destination buffer - * @param buffer2 the second PCM buffer - * @param size the size of both buffers in bytes - * @param format the sample format of both buffers - * @param portion1 a number between 0.0 and 1.0 specifying the portion - * of the first buffer in the mix; portion2 = (1.0 - portion1). The value - * NaN is used by the MixRamp code to specify that simple addition is required. - * - * @return true on success, false if the format is not supported - */ -gcc_warn_unused_result -bool -pcm_mix(void *buffer1, const void *buffer2, size_t size, - enum sample_format format, float portion1); - -#endif diff --git a/src/pcm_prng.h b/src/pcm_prng.h deleted file mode 100644 index 457ba4b66..000000000 --- a/src/pcm_prng.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2003-2011 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. - */ - -#ifndef PCM_PRNG_H -#define PCM_PRNG_H - -/** - * A very simple linear congruential PRNG. It's good enough for PCM - * dithering. - */ -static unsigned long -pcm_prng(unsigned long state) -{ - return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL; -} - -#endif diff --git a/src/pcm_utils.h b/src/pcm_utils.h deleted file mode 100644 index 21df95e48..000000000 --- a/src/pcm_utils.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2003-2011 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. - */ - -#ifndef MPD_PCM_UTILS_H -#define MPD_PCM_UTILS_H - -#include "gcc.h" - -#include - -/** - * Add a byte count to the specified pointer. This is a utility - * function to convert a source pointer and a byte count to an "end" - * pointer for use in loops. - */ -static inline const void * -pcm_end_pointer(const void *p, size_t size) -{ - return (const char *)p + size; -} - -/** - * Check if the value is within the range of the provided bit size, - * and caps it if necessary. - */ -static inline int32_t -pcm_range(int32_t sample, unsigned bits) -{ - if (gcc_unlikely(sample < (-1 << (bits - 1)))) - return -1 << (bits - 1); - if (gcc_unlikely(sample >= (1 << (bits - 1)))) - return (1 << (bits - 1)) - 1; - return sample; -} - -/** - * Check if the value is within the range of the provided bit size, - * and caps it if necessary. - */ -static inline int64_t -pcm_range_64(int64_t sample, unsigned bits) -{ - if (gcc_unlikely(sample < ((int64_t)-1 << (bits - 1)))) - return (int64_t)-1 << (bits - 1); - if (gcc_unlikely(sample >= ((int64_t)1 << (bits - 1)))) - return ((int64_t)1 << (bits - 1)) - 1; - return sample; -} - -gcc_const -static inline int16_t -pcm_clamp_16(int x) -{ - static const int32_t MIN_VALUE = -(1 << 15); - static const int32_t MAX_VALUE = (1 << 15) - 1; - - if (gcc_unlikely(x < MIN_VALUE)) - return MIN_VALUE; - if (gcc_unlikely(x > MAX_VALUE)) - return MAX_VALUE; - return x; -} - -gcc_const -static inline int32_t -pcm_clamp_24(int x) -{ - static const int32_t MIN_VALUE = -(1 << 23); - static const int32_t MAX_VALUE = (1 << 23) - 1; - - if (gcc_unlikely(x < MIN_VALUE)) - return MIN_VALUE; - if (gcc_unlikely(x > MAX_VALUE)) - return MAX_VALUE; - return x; -} - -#endif diff --git a/src/pcm_volume.c b/src/pcm_volume.c deleted file mode 100644 index 49c86026f..000000000 --- a/src/pcm_volume.c +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (C) 2003-2011 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 "config.h" -#include "pcm_volume.h" -#include "pcm_utils.h" -#include "audio_format.h" - -#include - -#include -#include - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "pcm_volume" - -static void -pcm_volume_change_8(int8_t *buffer, const int8_t *end, int volume) -{ - while (buffer < end) { - int32_t sample = *buffer; - - sample = (sample * volume + pcm_volume_dither() + - PCM_VOLUME_1 / 2) - / PCM_VOLUME_1; - - *buffer++ = pcm_range(sample, 8); - } -} - -static void -pcm_volume_change_16(int16_t *buffer, const int16_t *end, int volume) -{ - while (buffer < end) { - int32_t sample = *buffer; - - sample = (sample * volume + pcm_volume_dither() + - PCM_VOLUME_1 / 2) - / PCM_VOLUME_1; - - *buffer++ = pcm_range(sample, 16); - } -} - -#ifdef __i386__ -/** - * Optimized volume function for i386. Use the EDX:EAX 2*32 bit - * multiplication result instead of emulating 64 bit multiplication. - */ -static inline int32_t -pcm_volume_sample_24(int32_t sample, int32_t volume, G_GNUC_UNUSED int32_t dither) -{ - int32_t result; - - asm(/* edx:eax = sample * volume */ - "imul %2\n" - - /* "add %3, %1\n" dithering disabled for now, because we - have no overflow check - is dithering really important - here? */ - - /* eax = edx:eax / PCM_VOLUME_1 */ - "sal $22, %%edx\n" - "shr $10, %1\n" - "or %%edx, %1\n" - - : "=a"(result) - : "0"(sample), "r"(volume) /* , "r"(dither) */ - : "edx" - ); - - return result; -} -#endif - -static void -pcm_volume_change_24(int32_t *buffer, const int32_t *end, int volume) -{ - while (buffer < end) { -#ifdef __i386__ - /* assembly version for i386 */ - int32_t sample = *buffer; - - sample = pcm_volume_sample_24(sample, volume, - pcm_volume_dither()); -#else - /* portable version */ - int64_t sample = *buffer; - - sample = (sample * volume + pcm_volume_dither() + - PCM_VOLUME_1 / 2) - / PCM_VOLUME_1; -#endif - *buffer++ = pcm_range(sample, 24); - } -} - -static void -pcm_volume_change_32(int32_t *buffer, const int32_t *end, int volume) -{ - while (buffer < end) { -#ifdef __i386__ - /* assembly version for i386 */ - int32_t sample = *buffer; - - *buffer++ = pcm_volume_sample_24(sample, volume, 0); -#else - /* portable version */ - int64_t sample = *buffer; - - sample = (sample * volume + pcm_volume_dither() + - PCM_VOLUME_1 / 2) - / PCM_VOLUME_1; - *buffer++ = pcm_range_64(sample, 32); -#endif - } -} - -static void -pcm_volume_change_float(float *buffer, const float *end, float volume) -{ - while (buffer < end) { - float sample = *buffer; - sample *= volume; - *buffer++ = sample; - } -} - -bool -pcm_volume(void *buffer, size_t length, - enum sample_format format, - int volume) -{ - if (volume == PCM_VOLUME_1) - return true; - - if (volume <= 0) { - memset(buffer, 0, length); - return true; - } - - const void *end = pcm_end_pointer(buffer, length); - switch (format) { - case SAMPLE_FORMAT_UNDEFINED: - case SAMPLE_FORMAT_DSD: - /* not implemented */ - return false; - - case SAMPLE_FORMAT_S8: - pcm_volume_change_8(buffer, end, volume); - return true; - - case SAMPLE_FORMAT_S16: - pcm_volume_change_16(buffer, end, volume); - return true; - - case SAMPLE_FORMAT_S24_P32: - pcm_volume_change_24(buffer, end, volume); - return true; - - case SAMPLE_FORMAT_S32: - pcm_volume_change_32(buffer, end, volume); - return true; - - case SAMPLE_FORMAT_FLOAT: - pcm_volume_change_float(buffer, end, - pcm_volume_to_float(volume)); - return true; - } - - /* unreachable */ - assert(false); - return false; -} diff --git a/src/pcm_volume.h b/src/pcm_volume.h deleted file mode 100644 index c161a72c7..000000000 --- a/src/pcm_volume.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2003-2011 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. - */ - -#ifndef PCM_VOLUME_H -#define PCM_VOLUME_H - -#include "pcm_prng.h" -#include "audio_format.h" - -#include -#include -#include - -enum { - /** this value means "100% volume" */ - PCM_VOLUME_1 = 1024, -}; - -struct audio_format; - -/** - * Converts a float value (0.0 = silence, 1.0 = 100% volume) to an - * integer volume value (1000 = 100%). - */ -static inline int -pcm_float_to_volume(float volume) -{ - return volume * PCM_VOLUME_1 + 0.5; -} - -static inline float -pcm_volume_to_float(int volume) -{ - return (float)volume / (float)PCM_VOLUME_1; -} - -/** - * Returns the next volume dithering number, between -511 and +511. - * This number is taken from a global PRNG, see pcm_prng(). - */ -static inline int -pcm_volume_dither(void) -{ - static unsigned long state; - uint32_t r; - - r = state = pcm_prng(state); - - return (r & 511) - ((r >> 9) & 511); -} - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Adjust the volume of the specified PCM buffer. - * - * @param buffer the PCM buffer - * @param length the length of the PCM buffer - * @param format the sample format of the PCM buffer - * @param volume the volume between 0 and #PCM_VOLUME_1 - * @return true on success, false if the audio format is not supported - */ -bool -pcm_volume(void *buffer, size_t length, - enum sample_format format, - int volume); - -#ifdef __cplusplus -} -#endif - -#endif -- cgit v1.2.3