From c654c7630aad220a4935c34b076e107b6b0561a5 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 9 Apr 2013 01:24:52 +0200 Subject: pcm_*: move to src/pcm/ --- Makefile.am | 54 ++-- src/DecoderInternal.hxx | 2 +- src/Main.cxx | 2 +- src/MixerAll.cxx | 2 +- src/OutputThread.cxx | 2 +- src/PcmChannels.cxx | 290 -------------------- src/PcmChannels.hxx | 97 ------- src/PcmConvert.cxx | 326 ---------------------- src/PcmConvert.hxx | 115 -------- src/PcmDither.cxx | 89 ------ src/PcmDither.hxx | 44 --- src/PcmFormat.cxx | 500 ---------------------------------- src/PcmFormat.hxx | 93 ------- src/PcmMix.cxx | 211 -------------- src/PcmMix.hxx | 49 ---- src/PcmPrng.hxx | 33 --- src/PcmUtils.hxx | 66 ----- src/PcmVolume.cxx | 194 ------------- src/PcmVolume.hxx | 82 ------ src/decoder/FLACCommon.hxx | 2 +- src/dsd2pcm/dsd2pcm.c | 184 ------------- src/dsd2pcm/dsd2pcm.h | 64 ----- src/dsd2pcm/dsd2pcm.hpp | 39 --- src/dsd2pcm/info.txt | 38 --- src/dsd2pcm/main.cpp | 120 -------- src/dsd2pcm/noiseshape.c | 83 ------ src/dsd2pcm/noiseshape.h | 57 ---- src/dsd2pcm/noiseshape.hpp | 43 --- src/encoder/flac_encoder.c | 2 +- src/filter/ConvertFilterPlugin.cxx | 2 +- src/filter/NormalizeFilterPlugin.cxx | 2 +- src/filter/ReplayGainFilterPlugin.cxx | 4 +- src/filter/RouteFilterPlugin.cxx | 2 +- src/filter/VolumeFilterPlugin.cxx | 4 +- src/mixer/SoftwareMixerPlugin.cxx | 2 +- src/output/AlsaOutputPlugin.cxx | 2 +- src/output/OssOutputPlugin.cxx | 2 +- src/output/WinmmOutputPlugin.cxx | 2 +- src/output_internal.h | 2 +- src/pcm/PcmChannels.cxx | 290 ++++++++++++++++++++ src/pcm/PcmChannels.hxx | 97 +++++++ src/pcm/PcmConvert.cxx | 326 ++++++++++++++++++++++ src/pcm/PcmConvert.hxx | 115 ++++++++ src/pcm/PcmDither.cxx | 89 ++++++ src/pcm/PcmDither.hxx | 44 +++ src/pcm/PcmFormat.cxx | 500 ++++++++++++++++++++++++++++++++++ src/pcm/PcmFormat.hxx | 93 +++++++ src/pcm/PcmMix.cxx | 211 ++++++++++++++ src/pcm/PcmMix.hxx | 49 ++++ src/pcm/PcmPrng.hxx | 33 +++ src/pcm/PcmUtils.hxx | 66 +++++ src/pcm/PcmVolume.cxx | 194 +++++++++++++ src/pcm/PcmVolume.hxx | 82 ++++++ src/pcm/dsd2pcm/dsd2pcm.c | 184 +++++++++++++ src/pcm/dsd2pcm/dsd2pcm.h | 64 +++++ src/pcm/dsd2pcm/dsd2pcm.hpp | 39 +++ src/pcm/dsd2pcm/info.txt | 38 +++ src/pcm/dsd2pcm/main.cpp | 120 ++++++++ src/pcm/dsd2pcm/noiseshape.c | 83 ++++++ src/pcm/dsd2pcm/noiseshape.h | 57 ++++ src/pcm/dsd2pcm/noiseshape.hpp | 43 +++ src/pcm/pcm_buffer.c | 58 ++++ src/pcm/pcm_buffer.h | 85 ++++++ src/pcm/pcm_dsd.c | 85 ++++++ src/pcm/pcm_dsd.h | 52 ++++ src/pcm/pcm_dsd_usb.c | 97 +++++++ src/pcm/pcm_dsd_usb.h | 42 +++ src/pcm/pcm_export.c | 160 +++++++++++ src/pcm/pcm_export.h | 155 +++++++++++ src/pcm/pcm_pack.c | 77 ++++++ src/pcm/pcm_pack.h | 55 ++++ src/pcm/pcm_resample.c | 159 +++++++++++ src/pcm/pcm_resample.h | 164 +++++++++++ src/pcm/pcm_resample_fallback.c | 118 ++++++++ src/pcm/pcm_resample_internal.h | 97 +++++++ src/pcm/pcm_resample_libsamplerate.c | 312 +++++++++++++++++++++ src/pcm_buffer.c | 58 ---- src/pcm_buffer.h | 85 ------ src/pcm_dsd.c | 85 ------ src/pcm_dsd.h | 52 ---- src/pcm_dsd_usb.c | 97 ------- src/pcm_dsd_usb.h | 42 --- src/pcm_export.c | 160 ----------- src/pcm_export.h | 155 ----------- src/pcm_pack.c | 77 ------ src/pcm_pack.h | 55 ---- src/pcm_resample.c | 159 ----------- src/pcm_resample.h | 164 ----------- src/pcm_resample_fallback.c | 118 -------- src/pcm_resample_internal.h | 97 ------- src/pcm_resample_libsamplerate.c | 312 --------------------- test/read_mixer.cxx | 2 +- test/run_convert.cxx | 2 +- test/run_filter.cxx | 2 +- test/run_output.cxx | 2 +- test/software_volume.cxx | 2 +- test/test_pcm_channels.cxx | 4 +- test/test_pcm_dither.cxx | 2 +- test/test_pcm_format.cxx | 8 +- test/test_pcm_mix.cxx | 2 +- test/test_pcm_pack.cxx | 2 +- test/test_pcm_volume.cxx | 2 +- 102 files changed, 4593 insertions(+), 4593 deletions(-) delete mode 100644 src/PcmChannels.cxx delete mode 100644 src/PcmChannels.hxx delete mode 100644 src/PcmConvert.cxx delete mode 100644 src/PcmConvert.hxx delete mode 100644 src/PcmDither.cxx delete mode 100644 src/PcmDither.hxx delete mode 100644 src/PcmFormat.cxx delete mode 100644 src/PcmFormat.hxx delete mode 100644 src/PcmMix.cxx delete mode 100644 src/PcmMix.hxx delete mode 100644 src/PcmPrng.hxx delete mode 100644 src/PcmUtils.hxx delete mode 100644 src/PcmVolume.cxx delete mode 100644 src/PcmVolume.hxx delete mode 100644 src/dsd2pcm/dsd2pcm.c delete mode 100644 src/dsd2pcm/dsd2pcm.h delete mode 100644 src/dsd2pcm/dsd2pcm.hpp delete mode 100644 src/dsd2pcm/info.txt delete mode 100644 src/dsd2pcm/main.cpp delete mode 100644 src/dsd2pcm/noiseshape.c delete mode 100644 src/dsd2pcm/noiseshape.h delete mode 100644 src/dsd2pcm/noiseshape.hpp create mode 100644 src/pcm/PcmChannels.cxx create mode 100644 src/pcm/PcmChannels.hxx create mode 100644 src/pcm/PcmConvert.cxx create mode 100644 src/pcm/PcmConvert.hxx create mode 100644 src/pcm/PcmDither.cxx create mode 100644 src/pcm/PcmDither.hxx create mode 100644 src/pcm/PcmFormat.cxx create mode 100644 src/pcm/PcmFormat.hxx create mode 100644 src/pcm/PcmMix.cxx create mode 100644 src/pcm/PcmMix.hxx create mode 100644 src/pcm/PcmPrng.hxx create mode 100644 src/pcm/PcmUtils.hxx create mode 100644 src/pcm/PcmVolume.cxx create mode 100644 src/pcm/PcmVolume.hxx create mode 100644 src/pcm/dsd2pcm/dsd2pcm.c create mode 100644 src/pcm/dsd2pcm/dsd2pcm.h create mode 100644 src/pcm/dsd2pcm/dsd2pcm.hpp create mode 100644 src/pcm/dsd2pcm/info.txt create mode 100644 src/pcm/dsd2pcm/main.cpp create mode 100644 src/pcm/dsd2pcm/noiseshape.c create mode 100644 src/pcm/dsd2pcm/noiseshape.h create mode 100644 src/pcm/dsd2pcm/noiseshape.hpp create mode 100644 src/pcm/pcm_buffer.c create mode 100644 src/pcm/pcm_buffer.h create mode 100644 src/pcm/pcm_dsd.c create mode 100644 src/pcm/pcm_dsd.h create mode 100644 src/pcm/pcm_dsd_usb.c create mode 100644 src/pcm/pcm_dsd_usb.h create mode 100644 src/pcm/pcm_export.c create mode 100644 src/pcm/pcm_export.h create mode 100644 src/pcm/pcm_pack.c create mode 100644 src/pcm/pcm_pack.h create mode 100644 src/pcm/pcm_resample.c create mode 100644 src/pcm/pcm_resample.h create mode 100644 src/pcm/pcm_resample_fallback.c create mode 100644 src/pcm/pcm_resample_internal.h create mode 100644 src/pcm/pcm_resample_libsamplerate.c delete mode 100644 src/pcm_buffer.c delete mode 100644 src/pcm_buffer.h delete mode 100644 src/pcm_dsd.c delete mode 100644 src/pcm_dsd.h delete mode 100644 src/pcm_dsd_usb.c delete mode 100644 src/pcm_dsd_usb.h delete mode 100644 src/pcm_export.c delete mode 100644 src/pcm_export.h delete mode 100644 src/pcm_pack.c delete mode 100644 src/pcm_pack.h delete mode 100644 src/pcm_resample.c delete mode 100644 src/pcm_resample.h delete mode 100644 src/pcm_resample_fallback.c delete mode 100644 src/pcm_resample_internal.h delete mode 100644 src/pcm_resample_libsamplerate.c diff --git a/Makefile.am b/Makefile.am index 4c6b78e47..fafc96f4a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -315,23 +315,23 @@ libevent_a_SOURCES = \ # PCM library libpcm_a_SOURCES = \ - src/pcm_buffer.c src/pcm_buffer.h \ - src/pcm_export.c src/pcm_export.h \ - src/PcmConvert.cxx src/PcmConvert.hxx \ - src/dsd2pcm/dsd2pcm.c src/dsd2pcm/dsd2pcm.h \ - src/pcm_dsd.c src/pcm_dsd.h \ - src/pcm_dsd_usb.c src/pcm_dsd_usb.h \ - src/PcmVolume.cxx src/PcmVolume.hxx \ - src/PcmMix.cxx src/PcmMix.hxx \ - src/PcmChannels.cxx src/PcmChannels.hxx \ - src/pcm_pack.c src/pcm_pack.h \ - src/PcmFormat.cxx src/PcmFormat.hxx \ - src/pcm_resample.c src/pcm_resample.h \ - src/pcm_resample_fallback.c \ - src/pcm_resample_internal.h \ - src/PcmDither.cxx src/PcmDither.hxx \ - src/PcmPrng.hxx \ - src/PcmUtils.hxx + src/pcm/pcm_buffer.c src/pcm/pcm_buffer.h \ + src/pcm/pcm_export.c src/pcm/pcm_export.h \ + src/pcm/PcmConvert.cxx src/pcm/PcmConvert.hxx \ + src/pcm/dsd2pcm/dsd2pcm.c src/pcm/dsd2pcm/dsd2pcm.h \ + src/pcm/pcm_dsd.c src/pcm/pcm_dsd.h \ + src/pcm/pcm_dsd_usb.c src/pcm/pcm_dsd_usb.h \ + src/pcm/PcmVolume.cxx src/pcm/PcmVolume.hxx \ + src/pcm/PcmMix.cxx src/pcm/PcmMix.hxx \ + src/pcm/PcmChannels.cxx src/pcm/PcmChannels.hxx \ + src/pcm/pcm_pack.c src/pcm/pcm_pack.h \ + src/pcm/PcmFormat.cxx src/pcm/PcmFormat.hxx \ + src/pcm/pcm_resample.c src/pcm/pcm_resample.h \ + src/pcm/pcm_resample_fallback.c \ + src/pcm/pcm_resample_internal.h \ + src/pcm/PcmDither.cxx src/pcm/PcmDither.hxx \ + src/pcm/PcmPrng.hxx \ + src/pcm/PcmUtils.hxx libpcm_a_CPPFLAGS = $(AM_CPPFLAGS) \ $(SAMPLERATE_CFLAGS) @@ -340,7 +340,7 @@ PCM_LIBS = \ $(SAMPLERATE_LIBS) if HAVE_LIBSAMPLERATE -libpcm_a_SOURCES += src/pcm_resample_libsamplerate.c +libpcm_a_SOURCES += src/pcm/pcm_resample_libsamplerate.c endif # File system library @@ -1141,13 +1141,14 @@ test_dump_playlist_LDADD = \ libevent.a \ libfs.a \ libutil.a \ + libpcm.a \ $(GLIB_LIBS) test_dump_playlist_SOURCES = test/dump_playlist.cxx \ $(DECODER_SRC) \ src/IOThread.cxx \ src/Song.cxx src/Tag.cxx src/TagNames.c src/TagPool.cxx src/TagSave.cxx \ src/tag_handler.c src/TagFile.cxx \ - src/audio_check.c src/pcm_buffer.c \ + src/audio_check.c \ src/text_input_stream.c \ src/cue/CueParser.cxx src/cue/CueParser.hxx \ src/fd_util.c @@ -1262,12 +1263,12 @@ test_test_vorbis_encoder_SOURCES = test/test_vorbis_encoder.cxx \ src/audio_check.c \ src/audio_format.c \ src/AudioParser.cxx \ - src/pcm_buffer.c \ $(ENCODER_SRC) test_test_vorbis_encoder_CPPFLAGS = $(AM_CPPFLAGS) \ $(ENCODER_CFLAGS) test_test_vorbis_encoder_LDADD = $(MPD_LIBS) \ $(ENCODER_LIBS) \ + $(PCM_LIBS) \ libconf.a \ libfs.a \ libutil.a \ @@ -1291,7 +1292,6 @@ test_run_normalize_LDADD = \ $(GLIB_LIBS) test_run_convert_SOURCES = test/run_convert.cxx \ - src/dsd2pcm/dsd2pcm.c \ src/audio_format.c \ src/audio_check.c \ src/AudioParser.cxx @@ -1401,13 +1401,13 @@ test_test_queue_priority_LDADD = \ libutil.a \ $(GLIB_LIBS) -noinst_PROGRAMS += src/dsd2pcm/dsd2pcm +noinst_PROGRAMS += src/pcm/dsd2pcm/dsd2pcm -src_dsd2pcm_dsd2pcm_SOURCES = \ - src/dsd2pcm/dsd2pcm.c src/dsd2pcm/dsd2pcm.h \ - src/dsd2pcm/noiseshape.c src/dsd2pcm/noiseshape.h \ - src/dsd2pcm/main.cpp -src_dsd2pcm_dsd2pcm_LDADD = libutil.a +src_pcm_dsd2pcm_dsd2pcm_SOURCES = \ + src/pcm/dsd2pcm/dsd2pcm.c src/pcm/dsd2pcm/dsd2pcm.h \ + src/pcm/dsd2pcm/noiseshape.c src/pcm/dsd2pcm/noiseshape.h \ + src/pcm/dsd2pcm/main.cpp +src_pcm_dsd2pcm_dsd2pcm_LDADD = libutil.a endif diff --git a/src/DecoderInternal.hxx b/src/DecoderInternal.hxx index 3423e3f95..4cc8d3aa7 100644 --- a/src/DecoderInternal.hxx +++ b/src/DecoderInternal.hxx @@ -21,7 +21,7 @@ #define MPD_DECODER_INTERNAL_HXX #include "decoder_command.h" -#include "PcmConvert.hxx" +#include "pcm/PcmConvert.hxx" #include "replay_gain_info.h" struct input_stream; diff --git a/src/Main.cxx b/src/Main.cxx index 322f4d618..35eaea4ee 100644 --- a/src/Main.cxx +++ b/src/Main.cxx @@ -56,7 +56,7 @@ extern "C" { #include "daemon.h" #include "stats.h" -#include "pcm_resample.h" +#include "pcm/pcm_resample.h" } #include "mpd_error.h" diff --git a/src/MixerAll.cxx b/src/MixerAll.cxx index 00343a1a0..36c83abf8 100644 --- a/src/MixerAll.cxx +++ b/src/MixerAll.cxx @@ -23,7 +23,7 @@ #include "MixerInternal.hxx" #include "MixerList.hxx" #include "OutputAll.hxx" -#include "PcmVolume.hxx" +#include "pcm/PcmVolume.hxx" extern "C" { #include "output_internal.h" diff --git a/src/OutputThread.cxx b/src/OutputThread.cxx index f1ffe876f..34b3f50ba 100644 --- a/src/OutputThread.cxx +++ b/src/OutputThread.cxx @@ -20,7 +20,7 @@ #include "config.h" #include "OutputThread.hxx" #include "output_api.h" -#include "PcmMix.hxx" +#include "pcm/PcmMix.hxx" extern "C" { #include "output_internal.h" diff --git a/src/PcmChannels.cxx b/src/PcmChannels.cxx deleted file mode 100644 index eca6b2506..000000000 --- a/src/PcmChannels.cxx +++ /dev/null @@ -1,290 +0,0 @@ -/* - * 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 - -template -static void -MonoToStereo(D dest, S src, S end) -{ - while (src != end) { - const auto 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) - MonoToStereo(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_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) - MonoToStereo(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_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) - MonoToStereo(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_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) - MonoToStereo(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 deleted file mode 100644 index ede49cd81..000000000 --- a/src/PcmChannels.hxx +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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 deleted file mode 100644 index 9618b9642..000000000 --- a/src/PcmConvert.cxx +++ /dev/null @@ -1,326 +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 "PcmConvert.hxx" -#include "PcmChannels.hxx" -#include "PcmFormat.hxx" -#include "pcm_pack.h" -#include "audio_format.h" - -#include -#include -#include -#include - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "pcm" - -PcmConvert::PcmConvert() -{ - memset(this, 0, sizeof(*this)); - - pcm_dsd_init(&dsd); - pcm_resample_init(&resample); - - pcm_buffer_init(&format_buffer); - pcm_buffer_init(&channels_buffer); -} - -PcmConvert::~PcmConvert() -{ - pcm_dsd_deinit(&dsd); - pcm_resample_deinit(&resample); - - pcm_buffer_deinit(&format_buffer); - pcm_buffer_deinit(&channels_buffer); -} - -void -PcmConvert::Reset() -{ - pcm_dsd_reset(&dsd); - pcm_resample_reset(&resample); -} - -inline const int16_t * -PcmConvert::Convert16(const audio_format *src_format, - const void *src_buffer, size_t src_size, - const audio_format *dest_format, size_t *dest_size_r, - GError **error_r) -{ - const int16_t *buf; - size_t len; - - assert(dest_format->format == SAMPLE_FORMAT_S16); - - buf = pcm_convert_to_16(&format_buffer, dither, - sample_format(src_format->format), - src_buffer, src_size, - &len); - if (buf == NULL) { - g_set_error(error_r, pcm_convert_quark(), 0, - "Conversion from %s to 16 bit is not implemented", - sample_format_to_string(sample_format(src_format->format))); - return NULL; - } - - if (src_format->channels != dest_format->channels) { - buf = pcm_convert_channels_16(&channels_buffer, - dest_format->channels, - src_format->channels, - buf, len, &len); - if (buf == NULL) { - g_set_error(error_r, pcm_convert_quark(), 0, - "Conversion from %u to %u channels " - "is not implemented", - src_format->channels, - dest_format->channels); - return NULL; - } - } - - if (src_format->sample_rate != dest_format->sample_rate) { - buf = pcm_resample_16(&resample, - dest_format->channels, - src_format->sample_rate, buf, len, - dest_format->sample_rate, &len, - error_r); - if (buf == NULL) - return NULL; - } - - *dest_size_r = len; - return buf; -} - -inline const int32_t * -PcmConvert::Convert24(const audio_format *src_format, - const void *src_buffer, size_t src_size, - const audio_format *dest_format, size_t *dest_size_r, - GError **error_r) -{ - const int32_t *buf; - size_t len; - - assert(dest_format->format == SAMPLE_FORMAT_S24_P32); - - buf = pcm_convert_to_24(&format_buffer, - sample_format(src_format->format), - src_buffer, src_size, &len); - if (buf == NULL) { - g_set_error(error_r, pcm_convert_quark(), 0, - "Conversion from %s to 24 bit is not implemented", - sample_format_to_string(sample_format(src_format->format))); - return NULL; - } - - if (src_format->channels != dest_format->channels) { - buf = pcm_convert_channels_24(&channels_buffer, - dest_format->channels, - src_format->channels, - buf, len, &len); - if (buf == NULL) { - g_set_error(error_r, pcm_convert_quark(), 0, - "Conversion from %u to %u channels " - "is not implemented", - src_format->channels, - dest_format->channels); - return NULL; - } - } - - if (src_format->sample_rate != dest_format->sample_rate) { - buf = pcm_resample_24(&resample, - dest_format->channels, - src_format->sample_rate, buf, len, - dest_format->sample_rate, &len, - error_r); - if (buf == NULL) - return NULL; - } - - *dest_size_r = len; - return buf; -} - -inline const int32_t * -PcmConvert::Convert32(const audio_format *src_format, - const void *src_buffer, size_t src_size, - const audio_format *dest_format, size_t *dest_size_r, - GError **error_r) -{ - const int32_t *buf; - size_t len; - - assert(dest_format->format == SAMPLE_FORMAT_S32); - - buf = pcm_convert_to_32(&format_buffer, - sample_format(src_format->format), - src_buffer, src_size, &len); - if (buf == NULL) { - g_set_error(error_r, pcm_convert_quark(), 0, - "Conversion from %s to 32 bit is not implemented", - sample_format_to_string(sample_format(src_format->format))); - return NULL; - } - - if (src_format->channels != dest_format->channels) { - buf = pcm_convert_channels_32(&channels_buffer, - dest_format->channels, - src_format->channels, - buf, len, &len); - if (buf == NULL) { - g_set_error(error_r, pcm_convert_quark(), 0, - "Conversion from %u to %u channels " - "is not implemented", - src_format->channels, - dest_format->channels); - return NULL; - } - } - - if (src_format->sample_rate != dest_format->sample_rate) { - buf = pcm_resample_32(&resample, - dest_format->channels, - src_format->sample_rate, buf, len, - dest_format->sample_rate, &len, - error_r); - if (buf == NULL) - return buf; - } - - *dest_size_r = len; - return buf; -} - -inline const float * -PcmConvert::ConvertFloat(const audio_format *src_format, - const void *src_buffer, size_t src_size, - const audio_format *dest_format, size_t *dest_size_r, - GError **error_r) -{ - const float *buffer = (const float *)src_buffer; - size_t size = src_size; - - assert(dest_format->format == SAMPLE_FORMAT_FLOAT); - - /* convert to float now */ - - buffer = pcm_convert_to_float(&format_buffer, - sample_format(src_format->format), - buffer, size, &size); - if (buffer == NULL) { - g_set_error(error_r, pcm_convert_quark(), 0, - "Conversion from %s to float is not implemented", - sample_format_to_string(sample_format(src_format->format))); - return NULL; - } - - /* convert channels */ - - if (src_format->channels != dest_format->channels) { - buffer = pcm_convert_channels_float(&channels_buffer, - dest_format->channels, - src_format->channels, - buffer, size, &size); - if (buffer == NULL) { - g_set_error(error_r, pcm_convert_quark(), 0, - "Conversion from %u to %u channels " - "is not implemented", - src_format->channels, - dest_format->channels); - return NULL; - } - } - - /* resample with float, because this is the best format for - libsamplerate */ - - if (src_format->sample_rate != dest_format->sample_rate) { - buffer = pcm_resample_float(&resample, - dest_format->channels, - src_format->sample_rate, - buffer, size, - dest_format->sample_rate, &size, - error_r); - if (buffer == NULL) - return NULL; - } - - *dest_size_r = size; - return buffer; -} - -const void * -PcmConvert::Convert(const audio_format *src_format, - const void *src, size_t src_size, - const audio_format *dest_format, - size_t *dest_size_r, - GError **error_r) -{ - struct audio_format float_format; - if (src_format->format == SAMPLE_FORMAT_DSD) { - size_t f_size; - const float *f = pcm_dsd_to_float(&dsd, - src_format->channels, - false, (const uint8_t *)src, - src_size, &f_size); - if (f == NULL) { - g_set_error_literal(error_r, pcm_convert_quark(), 0, - "DSD to PCM conversion failed"); - return NULL; - } - - float_format = *src_format; - float_format.format = SAMPLE_FORMAT_FLOAT; - - src_format = &float_format; - src = f; - src_size = f_size; - } - - switch (sample_format(dest_format->format)) { - case SAMPLE_FORMAT_S16: - return Convert16(src_format, src, src_size, - dest_format, dest_size_r, - error_r); - - case SAMPLE_FORMAT_S24_P32: - return Convert24(src_format, src, src_size, - dest_format, dest_size_r, - error_r); - - case SAMPLE_FORMAT_S32: - return Convert32(src_format, src, src_size, - dest_format, dest_size_r, - error_r); - - case SAMPLE_FORMAT_FLOAT: - return ConvertFloat(src_format, src, src_size, - dest_format, dest_size_r, - error_r); - - default: - g_set_error(error_r, pcm_convert_quark(), 0, - "PCM conversion to %s is not implemented", - sample_format_to_string(sample_format(dest_format->format))); - return NULL; - } -} diff --git a/src/PcmConvert.hxx b/src/PcmConvert.hxx deleted file mode 100644 index f08188a9c..000000000 --- a/src/PcmConvert.hxx +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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 PCM_CONVERT_HXX -#define PCM_CONVERT_HXX - -#include "PcmDither.hxx" - -extern "C" { -#include "pcm_dsd.h" -#include "pcm_resample.h" -#include "pcm_buffer.h" -} - -#include - -struct audio_format; - -/** - * This object is statically allocated (within another struct), and - * holds buffer allocations and the state for all kinds of PCM - * conversions. - */ -class PcmConvert { - struct pcm_dsd dsd; - - struct pcm_resample_state resample; - - PcmDither dither; - - /** the buffer for converting the sample format */ - struct pcm_buffer format_buffer; - - /** the buffer for converting the channel count */ - struct pcm_buffer channels_buffer; - -public: - PcmConvert(); - ~PcmConvert(); - - - /** - * Reset the pcm_convert_state object. Use this at the - * boundary between two distinct songs and each time the - * format changes. - */ - void Reset(); - - /** - * Converts PCM data between two audio formats. - * - * @param src_format the source audio format - * @param src the source PCM buffer - * @param src_size the size of #src in bytes - * @param dest_format the requested destination audio format - * @param dest_size_r returns the number of bytes of the destination buffer - * @param error_r location to store the error occurring, or NULL to - * ignore errors - * @return the destination buffer, or NULL on error - */ - const void *Convert(const audio_format *src_format, - const void *src, size_t src_size, - const audio_format *dest_format, - size_t *dest_size_r, - GError **error_r); - -private: - const int16_t *Convert16(const audio_format *src_format, - const void *src_buffer, size_t src_size, - const audio_format *dest_format, - size_t *dest_size_r, - GError **error_r); - - const int32_t *Convert24(const audio_format *src_format, - const void *src_buffer, size_t src_size, - const audio_format *dest_format, - size_t *dest_size_r, - GError **error_r); - - const int32_t *Convert32(const audio_format *src_format, - const void *src_buffer, size_t src_size, - const audio_format *dest_format, - size_t *dest_size_r, - GError **error_r); - - const float *ConvertFloat(const audio_format *src_format, - const void *src_buffer, size_t src_size, - const audio_format *dest_format, - size_t *dest_size_r, - GError **error_r); -}; - -static inline GQuark -pcm_convert_quark(void) -{ - return g_quark_from_static_string("pcm_convert"); -} - -#endif diff --git a/src/PcmDither.cxx b/src/PcmDither.cxx deleted file mode 100644 index 98d0d443e..000000000 --- a/src/PcmDither.cxx +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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" - -inline int16_t -PcmDither::Dither24To16(int_fast32_t sample) -{ - constexpr unsigned from_bits = 24; - constexpr unsigned to_bits = 16; - constexpr unsigned scale_bits = from_bits - to_bits; - constexpr int_fast32_t round = 1 << (scale_bits - 1); - constexpr int_fast32_t mask = (1 << scale_bits) - 1; - constexpr int_fast32_t ONE = 1 << (from_bits - 1); - constexpr int_fast32_t MIN = -ONE; - constexpr int_fast32_t MAX = ONE - 1; - - sample += error[0] - error[1] + error[2]; - - error[2] = error[1]; - error[1] = error[0] / 2; - - /* round */ - int_fast32_t output = sample + round; - - int_fast32_t rnd = pcm_prng(random); - output += (rnd & mask) - (random & mask); - - 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; - - error[0] = sample - output; - - return (int16_t)(output >> scale_bits); -} - -void -PcmDither::Dither24To16(int16_t *dest, const int32_t *src, - const int32_t *src_end) -{ - while (src < src_end) - *dest++ = Dither24To16(*src++); -} - -inline int16_t -PcmDither::Dither32To16(int_fast32_t sample) -{ - return Dither24To16(sample >> 8); -} - -void -PcmDither::Dither32To16(int16_t *dest, const int32_t *src, - const int32_t *src_end) -{ - while (src < src_end) - *dest++ = Dither32To16(*src++); -} diff --git a/src/PcmDither.hxx b/src/PcmDither.hxx deleted file mode 100644 index 106382307..000000000 --- a/src/PcmDither.hxx +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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 - -class PcmDither { - int32_t error[3]; - int32_t random; - -public: - constexpr PcmDither() - :error{0, 0, 0}, random(0) {} - - void Dither24To16(int16_t *dest, const int32_t *src, - const int32_t *src_end); - - void Dither32To16(int16_t *dest, const int32_t *src, - const int32_t *src_end); - -private: - int16_t Dither24To16(int_fast32_t sample); - int16_t Dither32To16(int_fast32_t sample); -}; - -#endif diff --git a/src/PcmFormat.cxx b/src/PcmFormat.cxx deleted file mode 100644 index 1385d161b..000000000 --- a/src/PcmFormat.cxx +++ /dev/null @@ -1,500 +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 "PcmFormat.hxx" -#include "PcmDither.hxx" -#include "pcm_buffer.h" -#include "pcm_pack.h" -#include "PcmUtils.hxx" - -#include - -template -struct DefaultSampleBits { - typedef decltype(*S()) T; - typedef typename std::remove_reference::type U; - - static constexpr auto value = sizeof(U) * 8; -}; - -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(PcmDither &dither, - int16_t *out, const int32_t *in, const int32_t *in_end) -{ - dither.Dither24To16(out, in, in_end); -} - -static void -pcm_convert_32_to_16(PcmDither &dither, - int16_t *out, const int32_t *in, const int32_t *in_end) -{ - dither.Dither32To16(out, in, in_end); -} - -template::value> -static void -ConvertFromFloat(S dest, const float *src, const float *end) -{ - typedef decltype(*S()) T; - typedef typename std::remove_reference::type U; - - const float factor = 1 << (bits - 1); - - while (src != end) { - int sample(*src++ * factor); - *dest++ = PcmClamp(sample); - } -} - -template::value> -static void -ConvertFromFloat(S dest, const float *src, size_t size) -{ - ConvertFromFloat(dest, src, pcm_end_pointer(src, size)); -} - -template -static S * -AllocateFromFloat(pcm_buffer &buffer, const float *src, size_t src_size, - size_t *dest_size_r) -{ - constexpr size_t src_sample_size = sizeof(*src); - assert(src_size % src_sample_size == 0); - - const size_t num_samples = src_size / src_sample_size; - *dest_size_r = num_samples * sizeof(S); - S *dest = (S *)pcm_buffer_get(&buffer, *dest_size_r); - ConvertFromFloat(dest, src, src_size); - return dest; -} - -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, PcmDither &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, PcmDither &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) -{ - return AllocateFromFloat(*buffer, src, src_size, dest_size_r); -} - -const int16_t * -pcm_convert_to_16(struct pcm_buffer *buffer, PcmDither &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 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) -{ - return AllocateFromFloat(*buffer, src, src_size, - dest_size_r); -} - -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; -} - -template::value> -static void -ConvertToFloat(float *dest, S src, S end) -{ - constexpr float factor = 0.5 / (1 << (bits - 2)); - while (src != end) - *dest++ = float(*src++) * factor; - -} - -template::value> -static void -ConvertToFloat(float *dest, S src, size_t size) -{ - ConvertToFloat(dest, src, pcm_end_pointer(src, size)); -} - -template::value> -static float * -AllocateToFloat(pcm_buffer &buffer, S src, size_t src_size, - size_t *dest_size_r) -{ - constexpr size_t src_sample_size = sizeof(*S()); - assert(src_size % src_sample_size == 0); - - const size_t num_samples = src_size / src_sample_size; - *dest_size_r = num_samples * sizeof(float); - float *dest = (float *)pcm_buffer_get(&buffer, *dest_size_r); - ConvertToFloat(dest, src, src_size); - return dest; -} - -static float * -pcm_allocate_8_to_float(struct pcm_buffer *buffer, - const int8_t *src, size_t src_size, - size_t *dest_size_r) -{ - return AllocateToFloat(*buffer, src, src_size, dest_size_r); -} - -static float * -pcm_allocate_16_to_float(struct pcm_buffer *buffer, - const int16_t *src, size_t src_size, - size_t *dest_size_r) -{ - return AllocateToFloat(*buffer, src, src_size, dest_size_r); -} - -static float * -pcm_allocate_24p32_to_float(struct pcm_buffer *buffer, - const int32_t *src, size_t src_size, - size_t *dest_size_r) -{ - return AllocateToFloat - (*buffer, src, src_size, dest_size_r); -} - -static float * -pcm_allocate_32_to_float(struct pcm_buffer *buffer, - const int32_t *src, size_t src_size, - size_t *dest_size_r) -{ - return AllocateToFloat(*buffer, src, src_size, dest_size_r); -} - -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 deleted file mode 100644 index a5970b2d2..000000000 --- a/src/PcmFormat.hxx +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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; -class PcmDither; - -/** - * 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, PcmDither &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 deleted file mode 100644 index 8435c0c2a..000000000 --- a/src/PcmMix.cxx +++ /dev/null @@ -1,211 +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 "PcmMix.hxx" -#include "PcmVolume.hxx" -#include "PcmUtils.hxx" -#include "audio_format.h" - -#include - -template -static T -PcmAddVolume(T _a, T _b, int volume1, int volume2) -{ - U a(_a), b(_b); - - U c = ((a * volume1 + b * volume2) + - pcm_volume_dither() + PCM_VOLUME_1 / 2) - / PCM_VOLUME_1; - - return PcmClamp(c); -} - -template -static void -PcmAddVolume(T *a, const T *b, unsigned n, int volume1, int volume2) -{ - for (size_t i = 0; i != n; ++i) - a[i] = PcmAddVolume(a[i], b[i], volume1, volume2); -} - -template -static void -PcmAddVolumeVoid(void *a, const void *b, size_t size, int volume1, int volume2) -{ - constexpr size_t sample_size = sizeof(T); - assert(size % sample_size == 0); - - PcmAddVolume((T *)a, (const T *)b, size / sample_size, - volume1, volume2); -} - -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: - PcmAddVolumeVoid(buffer1, buffer2, size, - vol1, vol2); - return true; - - case SAMPLE_FORMAT_S16: - PcmAddVolumeVoid(buffer1, buffer2, size, - vol1, vol2); - return true; - - case SAMPLE_FORMAT_S24_P32: - PcmAddVolumeVoid(buffer1, buffer2, size, - vol1, vol2); - return true; - - case SAMPLE_FORMAT_S32: - PcmAddVolumeVoid(buffer1, buffer2, size, - 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; -} - -template -static T -PcmAdd(T _a, T _b) -{ - U a(_a), b(_b); - return PcmClamp(a + b); -} - -template -static void -PcmAdd(T *a, const T *b, unsigned n) -{ - for (size_t i = 0; i != n; ++i) - a[i] = PcmAdd(a[i], b[i]); -} - -template -static void -PcmAddVoid(void *a, const void *b, size_t size) -{ - constexpr size_t sample_size = sizeof(T); - assert(size % sample_size == 0); - - PcmAdd((T *)a, (const T *)b, size / sample_size); -} - -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: - PcmAddVoid(buffer1, buffer2, size); - return true; - - case SAMPLE_FORMAT_S16: - PcmAddVoid(buffer1, buffer2, size); - return true; - - case SAMPLE_FORMAT_S24_P32: - PcmAddVoid(buffer1, buffer2, size); - return true; - - case SAMPLE_FORMAT_S32: - PcmAddVoid(buffer1, buffer2, size); - 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 deleted file mode 100644 index bb7110d04..000000000 --- a/src/PcmMix.hxx +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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 deleted file mode 100644 index 0c823250d..000000000 --- a/src/PcmPrng.hxx +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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 deleted file mode 100644 index d77c4194a..000000000 --- a/src/PcmUtils.hxx +++ /dev/null @@ -1,66 +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 - -#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. - */ -template -gcc_const -static inline T -PcmClamp(U x) -{ - constexpr U MIN_VALUE = -(U(1) << (bits - 1)); - constexpr U MAX_VALUE = (U(1) << (bits - 1)) - 1; - - typedef std::numeric_limits limits; - static_assert(MIN_VALUE >= limits::min(), "out of range"); - static_assert(MAX_VALUE <= limits::max(), "out of range"); - - if (gcc_unlikely(x < MIN_VALUE)) - return T(MIN_VALUE); - - if (gcc_unlikely(x > MAX_VALUE)) - return T(MAX_VALUE); - - return T(x); -} - -#endif diff --git a/src/PcmVolume.cxx b/src/PcmVolume.cxx deleted file mode 100644 index 556ab9925..000000000 --- a/src/PcmVolume.cxx +++ /dev/null @@ -1,194 +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 "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++ = PcmClamp(sample); - } -} - -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++ = PcmClamp(sample); - } -} - -#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++ = PcmClamp(sample); - } -} - -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++ = PcmClamp(sample); -#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 deleted file mode 100644 index d3e6a5536..000000000 --- a/src/PcmVolume.hxx +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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/decoder/FLACCommon.hxx b/src/decoder/FLACCommon.hxx index b80372bbf..20f93c8c7 100644 --- a/src/decoder/FLACCommon.hxx +++ b/src/decoder/FLACCommon.hxx @@ -28,7 +28,7 @@ #include "decoder_api.h" extern "C" { -#include "pcm_buffer.h" +#include "pcm/pcm_buffer.h" } #include diff --git a/src/dsd2pcm/dsd2pcm.c b/src/dsd2pcm/dsd2pcm.c deleted file mode 100644 index 4c7640853..000000000 --- a/src/dsd2pcm/dsd2pcm.c +++ /dev/null @@ -1,184 +0,0 @@ -#include "util/bit_reverse.h" - -#include -#include - -#include "dsd2pcm.h" - -#define HTAPS 48 /* number of FIR constants */ -#define FIFOSIZE 16 /* must be a power of two */ -#define FIFOMASK (FIFOSIZE-1) /* bit mask for FIFO offsets */ -#define CTABLES ((HTAPS+7)/8) /* number of "8 MACs" lookup tables */ - -#if FIFOSIZE*8 < HTAPS*2 -#error "FIFOSIZE too small" -#endif - -/* - * Properties of this 96-tap lowpass filter when applied on a signal - * with sampling rate of 44100*64 Hz: - * - * () has a delay of 17 microseconds. - * - * () flat response up to 48 kHz - * - * () if you downsample afterwards by a factor of 8, the - * spectrum below 70 kHz is practically alias-free. - * - * () stopband rejection is about 160 dB - * - * The coefficient tables ("ctables") take only 6 Kibi Bytes and - * should fit into a modern processor's fast cache. - */ - -/* - * The 2nd half (48 coeffs) of a 96-tap symmetric lowpass filter - */ -static const double htaps[HTAPS] = { - 0.09950731974056658, - 0.09562845727714668, - 0.08819647126516944, - 0.07782552527068175, - 0.06534876523171299, - 0.05172629311427257, - 0.0379429484910187, - 0.02490921351762261, - 0.0133774746265897, - 0.003883043418804416, - -0.003284703416210726, - -0.008080250212687497, - -0.01067241812471033, - -0.01139427235000863, - -0.0106813877974587, - -0.009007905078766049, - -0.006828859761015335, - -0.004535184322001496, - -0.002425035959059578, - -0.0006922187080790708, - 0.0005700762133516592, - 0.001353838005269448, - 0.001713709169690937, - 0.001742046839472948, - 0.001545601648013235, - 0.001226696225277855, - 0.0008704322683580222, - 0.0005381636200535649, - 0.000266446345425276, - 7.002968738383528e-05, - -5.279407053811266e-05, - -0.0001140625650874684, - -0.0001304796361231895, - -0.0001189970287491285, - -9.396247155265073e-05, - -6.577634378272832e-05, - -4.07492895872535e-05, - -2.17407957554587e-05, - -9.163058931391722e-06, - -2.017460145032201e-06, - 1.249721855219005e-06, - 2.166655190537392e-06, - 1.930520892991082e-06, - 1.319400334374195e-06, - 7.410039764949091e-07, - 3.423230509967409e-07, - 1.244182214744588e-07, - 3.130441005359396e-08 -}; - -static float ctables[CTABLES][256]; -static int precalculated = 0; - -static void precalc(void) -{ - int t, e, m, k; - double acc; - if (precalculated) return; - for (t=0; t8) k=8; - for (e=0; e<256; ++e) { - acc = 0.0; - for (m=0; m> (7-m)) & 1)*2-1) * htaps[t*8+m]; - } - ctables[CTABLES-1-t][e] = (float)acc; - } - } - precalculated = 1; -} - -struct dsd2pcm_ctx_s -{ - unsigned char fifo[FIFOSIZE]; - unsigned fifopos; -}; - -extern dsd2pcm_ctx* dsd2pcm_init(void) -{ - dsd2pcm_ctx* ptr; - if (!precalculated) precalc(); - ptr = (dsd2pcm_ctx*) malloc(sizeof(dsd2pcm_ctx)); - if (ptr) dsd2pcm_reset(ptr); - return ptr; -} - -extern void dsd2pcm_destroy(dsd2pcm_ctx* ptr) -{ - free(ptr); -} - -extern dsd2pcm_ctx* dsd2pcm_clone(dsd2pcm_ctx* ptr) -{ - dsd2pcm_ctx* p2; - p2 = (dsd2pcm_ctx*) malloc(sizeof(dsd2pcm_ctx)); - if (p2) { - memcpy(p2,ptr,sizeof(dsd2pcm_ctx)); - } - return p2; -} - -extern void dsd2pcm_reset(dsd2pcm_ctx* ptr) -{ - int i; - for (i=0; ififo[i] = 0x69; /* my favorite silence pattern */ - ptr->fifopos = 0; - /* 0x69 = 01101001 - * This pattern "on repeat" makes a low energy 352.8 kHz tone - * and a high energy 1.0584 MHz tone which should be filtered - * out completely by any playback system --> silence - */ -} - -extern void dsd2pcm_translate( - dsd2pcm_ctx* ptr, - size_t samples, - const unsigned char *src, ptrdiff_t src_stride, - int lsbf, - float *dst, ptrdiff_t dst_stride) -{ - unsigned ffp; - unsigned i; - unsigned bite1, bite2; - unsigned char* p; - double acc; - ffp = ptr->fifopos; - lsbf = lsbf ? 1 : 0; - while (samples-- > 0) { - bite1 = *src & 0xFFu; - if (lsbf) bite1 = bit_reverse(bite1); - ptr->fifo[ffp] = bite1; src += src_stride; - p = ptr->fifo + ((ffp-CTABLES) & FIFOMASK); - *p = bit_reverse(*p); - acc = 0; - for (i=0; ififo[(ffp -i) & FIFOMASK] & 0xFF; - bite2 = ptr->fifo[(ffp-(CTABLES*2-1)+i) & FIFOMASK] & 0xFF; - acc += ctables[i][bite1] + ctables[i][bite2]; - } - *dst = (float)acc; dst += dst_stride; - ffp = (ffp + 1) & FIFOMASK; - } - ptr->fifopos = ffp; -} - diff --git a/src/dsd2pcm/dsd2pcm.h b/src/dsd2pcm/dsd2pcm.h deleted file mode 100644 index 80e8ce0cc..000000000 --- a/src/dsd2pcm/dsd2pcm.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef DSD2PCM_H_INCLUDED -#define DSD2PCM_H_INCLUDED - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -struct dsd2pcm_ctx_s; - -typedef struct dsd2pcm_ctx_s dsd2pcm_ctx; - -/** - * initializes a "dsd2pcm engine" for one channel - * (precomputes tables and allocates memory) - * - * This is the only function that is not thread-safe in terms of the - * POSIX thread-safety definition because it modifies global state - * (lookup tables are computed during the first call) - */ -extern dsd2pcm_ctx* dsd2pcm_init(void); - -/** - * deinitializes a "dsd2pcm engine" - * (releases memory, don't forget!) - */ -extern void dsd2pcm_destroy(dsd2pcm_ctx *ctx); - -/** - * clones the context and returns a pointer to the - * newly allocated copy - */ -extern dsd2pcm_ctx* dsd2pcm_clone(dsd2pcm_ctx *ctx); - -/** - * resets the internal state for a fresh new stream - */ -extern void dsd2pcm_reset(dsd2pcm_ctx *ctx); - -/** - * "translates" a stream of octets to a stream of floats - * (8:1 decimation) - * @param ctx -- pointer to abstract context (buffers) - * @param samples -- number of octets/samples to "translate" - * @param src -- pointer to first octet (input) - * @param src_stride -- src pointer increment - * @param lsbitfirst -- bitorder, 0=msb first, 1=lsbfirst - * @param dst -- pointer to first float (output) - * @param dst_stride -- dst pointer increment - */ -extern void dsd2pcm_translate(dsd2pcm_ctx *ctx, - size_t samples, - const unsigned char *src, ptrdiff_t src_stride, - int lsbitfirst, - float *dst, ptrdiff_t dst_stride); - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* include guard DSD2PCM_H_INCLUDED */ - diff --git a/src/dsd2pcm/dsd2pcm.hpp b/src/dsd2pcm/dsd2pcm.hpp deleted file mode 100644 index 8f3f55197..000000000 --- a/src/dsd2pcm/dsd2pcm.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef DSD2PCM_HXX_INCLUDED -#define DSD2PCM_HXX_INCLUDED - -#include -#include -#include "dsd2pcm.h" - -/** - * C++ PImpl Wrapper for the dsd2pcm C library - */ - -class dxd -{ - dsd2pcm_ctx *handle; -public: - dxd() : handle(dsd2pcm_init()) {} - - dxd(dxd const& x) : handle(dsd2pcm_clone(x.handle)) {} - - ~dxd() { dsd2pcm_destroy(handle); } - - friend void swap(dxd & a, dxd & b) - { std::swap(a.handle,b.handle); } - - dxd& operator=(dxd x) - { swap(*this,x); return *this; } - - void translate(size_t samples, - const unsigned char *src, ptrdiff_t src_stride, - bool lsbitfirst, - float *dst, ptrdiff_t dst_stride) - { - dsd2pcm_translate(handle,samples,src,src_stride, - lsbitfirst,dst,dst_stride); - } -}; - -#endif // DSD2PCM_HXX_INCLUDED - diff --git a/src/dsd2pcm/info.txt b/src/dsd2pcm/info.txt deleted file mode 100644 index 15ff29245..000000000 --- a/src/dsd2pcm/info.txt +++ /dev/null @@ -1,38 +0,0 @@ -You downloaded the source code for "dsd2pcm" which is a simple little -"filter" program, that takes a DSD data stream on stdin and converts -it to a PCM stream (352.8 kHz, either 16 or 24 bits) and writes it to -stdout. The code is split into three modules: - - (1) dsd2pcm - - This is where the 8:1 decimation magic happens. It's an - implementation of a symmetric 96-taps FIR lowpass filter - optimized for DSD inputs. If you feed this converter with - DSD64 you get a PCM stream at 352.8 kHz and floating point - samples. This module is independent and can be reused. - - (2) noiseshape - - A module for applying generic noise shaping filters. It's - used for the 16-bit output mode in "main" to preserve the - dynamic range. This module is independent and can be reused. - - (3) main.cpp (file contains the main function and handles I/O) - -The first two modules are pure C for maximum portability. In addition, -there are C++ wrapper headers for convenient use of these modules in -C++. The main application is a C++ application and makes use of the -C++ headers to access the functionality of the first two modules. - - -Under Linux this program is easily compiled by typing - - g++ *.c *.cpp -O3 -o dsd2pcm - -provided you have GCC installed. That's why I didn't bother writing -any makefiles. :-p - - -Cheers! -SG - diff --git a/src/dsd2pcm/main.cpp b/src/dsd2pcm/main.cpp deleted file mode 100644 index 0b58888a8..000000000 --- a/src/dsd2pcm/main.cpp +++ /dev/null @@ -1,120 +0,0 @@ -#include -#include -#include - -#include "dsd2pcm.hpp" -#include "noiseshape.hpp" - -namespace { - -const float my_ns_coeffs[] = { -// b1 b2 a1 a2 - -1.62666423, 0.79410094, 0.61367127, 0.23311013, // section 1 - -1.44870017, 0.54196219, 0.03373857, 0.70316556 // section 2 -}; - -const int my_ns_soscount = sizeof(my_ns_coeffs)/(sizeof(my_ns_coeffs[0])*4); - -inline long myround(float x) -{ - return static_cast(x + (x>=0 ? 0.5f : -0.5f)); -} - -template -struct id { typedef T type; }; - -template -inline T clip( - typename id::type min, - T v, - typename id::type max) -{ - if (vmax) return max; - return v; -} - -inline void write_intel16(unsigned char * ptr, unsigned word) -{ - ptr[0] = word & 0xFF; - ptr[1] = (word >> 8) & 0xFF; -} - -inline void write_intel24(unsigned char * ptr, unsigned long word) -{ - ptr[0] = word & 0xFF; - ptr[1] = (word >> 8) & 0xFF; - ptr[2] = (word >> 16) & 0xFF; -} - -} // anonymous namespace - -using std::vector; -using std::cin; -using std::cout; -using std::cerr; - -int main(int argc, char *argv[]) -{ - const int block = 16384; - int channels = -1; - int lsbitfirst = -1; - int bits = -1; - if (argc==4) { - if ('1'<=argv[1][0] && argv[1][0]<='9') channels = 1 + (argv[1][0]-'1'); - if (argv[2][0]=='m' || argv[2][0]=='M') lsbitfirst=0; - if (argv[2][0]=='l' || argv[2][0]=='L') lsbitfirst=1; - if (!strcmp(argv[3],"16")) bits = 16; - if (!strcmp(argv[3],"24")) bits = 24; - } - if (channels<1 || lsbitfirst<0 || bits<0) { - cerr << "\n" - "DSD2PCM filter (raw DSD64 --> 352 kHz raw PCM)\n" - "(c) 2009 Sebastian Gesemann\n\n" - "(filter as in \"reads data from stdin and writes to stdout\")\n\n" - "Syntax: dsd2pcm \n" - "channels = 1,2,3,...,9 (number of channels in DSD stream)\n" - "bitorder = L (lsb first), M (msb first) (DSD stream option)\n" - "bitdepth = 16 or 24 (intel byte order, output option)\n\n" - "Note: At 16 bits/sample a noise shaper kicks in that can preserve\n" - "a dynamic range of 135 dB below 30 kHz.\n\n"; - return 1; - } - int bytespersample = bits/8; - vector dxds (channels); - vector ns; - if (bits==16) { - ns.resize(channels, noise_shaper(my_ns_soscount, my_ns_coeffs) ); - } - vector dsd_data (block * channels); - vector float_data (block); - vector pcm_data (block * channels * bytespersample); - char * const dsd_in = reinterpret_cast(&dsd_data[0]); - char * const pcm_out = reinterpret_cast(&pcm_data[0]); - while (cin.read(dsd_in,block * channels)) { - for (int c=0; c -#include - -#include "noiseshape.h" - -extern int noise_shape_init( - noise_shape_ctx *ctx, - int sos_count, - const float *coeffs) -{ - int i; - ctx->sos_count = sos_count; - ctx->bbaa = coeffs; - ctx->t1 = (float*) malloc(sizeof(float)*sos_count); - if (!ctx->t1) goto escape1; - ctx->t2 = (float*) malloc(sizeof(float)*sos_count); - if (!ctx->t2) goto escape2; - for (i=0; it1[i] = 0.f; - ctx->t2[i] = 0.f; - } - return 0; -escape2: - free(ctx->t1); -escape1: - return -1; -} - -extern void noise_shape_destroy( - noise_shape_ctx *ctx) -{ - free(ctx->t1); - free(ctx->t2); -} - -extern int noise_shape_clone( - const noise_shape_ctx *from, - noise_shape_ctx *to) -{ - to->sos_count = from->sos_count; - to->bbaa = from->bbaa; - to->t1 = (float*) malloc(sizeof(float)*to->sos_count); - if (!to->t1) goto error1; - to->t2 = (float*) malloc(sizeof(float)*to->sos_count); - if (!to->t2) goto error2; - memcpy(to->t1,from->t1,sizeof(float)*to->sos_count); - memcpy(to->t2,from->t2,sizeof(float)*to->sos_count); - return 0; -error2: - free(to->t1); -error1: - return -1; -} - -extern float noise_shape_get(noise_shape_ctx *ctx) -{ - int i; - float acc; - const float *c; - acc = 0.0; - c = ctx->bbaa; - for (i=0; isos_count; ++i) { - float t1i = ctx->t1[i]; - float t2i = ctx->t2[i]; - ctx->t2[i] = acc -= t1i * c[2] + t2i * c[3]; - acc += t1i * c[0] + t2i * c[1]; - c += 4; - } - return acc; -} - -extern void noise_shape_update(noise_shape_ctx *ctx, float qerror) -{ - float *p; - int i; - for (i=0; isos_count; ++i) { - ctx->t2[i] += qerror; - } - p = ctx->t1; - ctx->t1 = ctx->t2; - ctx->t2 = p; -} - diff --git a/src/dsd2pcm/noiseshape.h b/src/dsd2pcm/noiseshape.h deleted file mode 100644 index 6075f0d88..000000000 --- a/src/dsd2pcm/noiseshape.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef NOISE_SHAPE_H_INCLUDED -#define NOISE_SHAPE_H_INCLUDED - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct noise_shape_ctx_s { - int sos_count; /* number of second order sections */ - const float *bbaa; /* filter coefficients, owned by user */ - float *t1, *t2; /* filter state, owned by ns library */ -} noise_shape_ctx; - -/** - * initializes a noise_shaper context - * returns an error code or 0 - */ -extern int noise_shape_init( - noise_shape_ctx *ctx, - int sos_count, - const float *coeffs); - -/** - * destroys a noise_shaper context - */ -extern void noise_shape_destroy( - noise_shape_ctx *ctx); - -/** - * initializes a noise_shaper context so that its state - * is a copy of a given context - * returns an error code or 0 - */ -extern int noise_shape_clone( - const noise_shape_ctx *from, noise_shape_ctx *to); - -/** - * computes the next "noise shaping sample". Note: This call - * alters the internal state. xxx_get and xxx_update must be - * called in an alternating manner. - */ -extern float noise_shape_get( - noise_shape_ctx *ctx); - -/** - * updates the noise shaper's state with the - * last quantization error - */ -extern void noise_shape_update( - noise_shape_ctx *ctx, float qerror); - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* NOISE_SHAPE_H_INCLUDED */ - diff --git a/src/dsd2pcm/noiseshape.hpp b/src/dsd2pcm/noiseshape.hpp deleted file mode 100644 index 1fc698b36..000000000 --- a/src/dsd2pcm/noiseshape.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef NOISE_SHAPE_HXX_INCLUDED -#define NOISE_SHAPE_HXX_INCLUDED - -#include -#include "noiseshape.h" - -/** - * C++ wrapper for the noiseshape C library - */ - -class noise_shaper -{ - noise_shape_ctx ctx; -public: - noise_shaper(int sos_count, const float *bbaa) - { - noise_shape_init(&ctx, sos_count, bbaa); - } - - noise_shaper(noise_shaper const& x) - { - noise_shape_clone(&x.ctx,&ctx); - } - - ~noise_shaper() - { noise_shape_destroy(&ctx); } - - noise_shaper& operator=(noise_shaper const& x) - { - if (this != &x) { - noise_shape_destroy(&ctx); - noise_shape_clone(&x.ctx,&ctx); - } - return *this; - } - - float get() { return noise_shape_get(&ctx); } - - void update(float error) { noise_shape_update(&ctx,error); } -}; - -#endif /* NOISE_SHAPE_HXX_INCLUDED */ - diff --git a/src/encoder/flac_encoder.c b/src/encoder/flac_encoder.c index db6503fb0..060d63318 100644 --- a/src/encoder/flac_encoder.c +++ b/src/encoder/flac_encoder.c @@ -21,7 +21,7 @@ #include "encoder_api.h" #include "encoder_plugin.h" #include "audio_format.h" -#include "pcm_buffer.h" +#include "pcm/pcm_buffer.h" #include "util/fifo_buffer.h" #include "util/growing_fifo.h" diff --git a/src/filter/ConvertFilterPlugin.cxx b/src/filter/ConvertFilterPlugin.cxx index 2c6907655..09a2c9848 100644 --- a/src/filter/ConvertFilterPlugin.cxx +++ b/src/filter/ConvertFilterPlugin.cxx @@ -23,7 +23,7 @@ #include "FilterInternal.hxx" #include "FilterRegistry.hxx" #include "conf.h" -#include "PcmConvert.hxx" +#include "pcm/PcmConvert.hxx" #include "util/Manual.hxx" #include "audio_format.h" #include "poison.h" diff --git a/src/filter/NormalizeFilterPlugin.cxx b/src/filter/NormalizeFilterPlugin.cxx index e18c5cdf9..082d6fc94 100644 --- a/src/filter/NormalizeFilterPlugin.cxx +++ b/src/filter/NormalizeFilterPlugin.cxx @@ -21,7 +21,7 @@ #include "FilterPlugin.hxx" #include "FilterInternal.hxx" #include "FilterRegistry.hxx" -#include "pcm_buffer.h" +#include "pcm/pcm_buffer.h" #include "audio_format.h" #include "AudioCompress/compress.h" diff --git a/src/filter/ReplayGainFilterPlugin.cxx b/src/filter/ReplayGainFilterPlugin.cxx index 1fa2269b4..fed474bd5 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 "MixerControl.hxx" -#include "PcmVolume.hxx" +#include "pcm/PcmVolume.hxx" extern "C" { -#include "pcm_buffer.h" +#include "pcm/pcm_buffer.h" } #include diff --git a/src/filter/RouteFilterPlugin.cxx b/src/filter/RouteFilterPlugin.cxx index 559578938..c6cf302b2 100644 --- a/src/filter/RouteFilterPlugin.cxx +++ b/src/filter/RouteFilterPlugin.cxx @@ -47,7 +47,7 @@ #include "FilterPlugin.hxx" #include "FilterInternal.hxx" #include "FilterRegistry.hxx" -#include "pcm_buffer.h" +#include "pcm/pcm_buffer.h" #include #include diff --git a/src/filter/VolumeFilterPlugin.cxx b/src/filter/VolumeFilterPlugin.cxx index 0689f5da5..660ad260e 100644 --- a/src/filter/VolumeFilterPlugin.cxx +++ b/src/filter/VolumeFilterPlugin.cxx @@ -23,8 +23,8 @@ #include "FilterInternal.hxx" #include "FilterRegistry.hxx" #include "conf.h" -#include "pcm_buffer.h" -#include "PcmVolume.hxx" +#include "pcm/pcm_buffer.h" +#include "pcm/PcmVolume.hxx" #include "audio_format.h" #include diff --git a/src/mixer/SoftwareMixerPlugin.cxx b/src/mixer/SoftwareMixerPlugin.cxx index 6c287ea07..ccadadb4d 100644 --- a/src/mixer/SoftwareMixerPlugin.cxx +++ b/src/mixer/SoftwareMixerPlugin.cxx @@ -23,7 +23,7 @@ #include "FilterPlugin.hxx" #include "FilterRegistry.hxx" #include "filter/VolumeFilterPlugin.hxx" -#include "PcmVolume.hxx" +#include "pcm/PcmVolume.hxx" #include #include diff --git a/src/output/AlsaOutputPlugin.cxx b/src/output/AlsaOutputPlugin.cxx index c87c4cb99..096bf8a3c 100644 --- a/src/output/AlsaOutputPlugin.cxx +++ b/src/output/AlsaOutputPlugin.cxx @@ -21,7 +21,7 @@ #include "AlsaOutputPlugin.hxx" #include "output_api.h" #include "MixerList.hxx" -#include "pcm_export.h" +#include "pcm/pcm_export.h" #include #include diff --git a/src/output/OssOutputPlugin.cxx b/src/output/OssOutputPlugin.cxx index 0111b13f6..1cf2d2f2e 100644 --- a/src/output/OssOutputPlugin.cxx +++ b/src/output/OssOutputPlugin.cxx @@ -52,7 +52,7 @@ #endif #ifdef AFMT_S24_PACKED -#include "pcm_export.h" +#include "pcm/pcm_export.h" #endif struct oss_data { diff --git a/src/output/WinmmOutputPlugin.cxx b/src/output/WinmmOutputPlugin.cxx index b9652fc0b..2386bfc61 100644 --- a/src/output/WinmmOutputPlugin.cxx +++ b/src/output/WinmmOutputPlugin.cxx @@ -20,7 +20,7 @@ #include "config.h" #include "WinmmOutputPlugin.hxx" #include "output_api.h" -#include "pcm_buffer.h" +#include "pcm/pcm_buffer.h" #include "MixerList.hxx" #include diff --git a/src/output_internal.h b/src/output_internal.h index 201962a72..8d0177786 100644 --- a/src/output_internal.h +++ b/src/output_internal.h @@ -21,7 +21,7 @@ #define MPD_OUTPUT_INTERNAL_H #include "audio_format.h" -#include "pcm_buffer.h" +#include "pcm/pcm_buffer.h" #include diff --git a/src/pcm/PcmChannels.cxx b/src/pcm/PcmChannels.cxx new file mode 100644 index 000000000..eca6b2506 --- /dev/null +++ b/src/pcm/PcmChannels.cxx @@ -0,0 +1,290 @@ +/* + * 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 + +template +static void +MonoToStereo(D dest, S src, S end) +{ + while (src != end) { + const auto 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) + MonoToStereo(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_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) + MonoToStereo(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_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) + MonoToStereo(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_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) + MonoToStereo(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/PcmChannels.hxx b/src/pcm/PcmChannels.hxx new file mode 100644 index 000000000..ede49cd81 --- /dev/null +++ b/src/pcm/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/pcm/PcmConvert.cxx b/src/pcm/PcmConvert.cxx new file mode 100644 index 000000000..9618b9642 --- /dev/null +++ b/src/pcm/PcmConvert.cxx @@ -0,0 +1,326 @@ +/* + * 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 "PcmConvert.hxx" +#include "PcmChannels.hxx" +#include "PcmFormat.hxx" +#include "pcm_pack.h" +#include "audio_format.h" + +#include +#include +#include +#include + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "pcm" + +PcmConvert::PcmConvert() +{ + memset(this, 0, sizeof(*this)); + + pcm_dsd_init(&dsd); + pcm_resample_init(&resample); + + pcm_buffer_init(&format_buffer); + pcm_buffer_init(&channels_buffer); +} + +PcmConvert::~PcmConvert() +{ + pcm_dsd_deinit(&dsd); + pcm_resample_deinit(&resample); + + pcm_buffer_deinit(&format_buffer); + pcm_buffer_deinit(&channels_buffer); +} + +void +PcmConvert::Reset() +{ + pcm_dsd_reset(&dsd); + pcm_resample_reset(&resample); +} + +inline const int16_t * +PcmConvert::Convert16(const audio_format *src_format, + const void *src_buffer, size_t src_size, + const audio_format *dest_format, size_t *dest_size_r, + GError **error_r) +{ + const int16_t *buf; + size_t len; + + assert(dest_format->format == SAMPLE_FORMAT_S16); + + buf = pcm_convert_to_16(&format_buffer, dither, + sample_format(src_format->format), + src_buffer, src_size, + &len); + if (buf == NULL) { + g_set_error(error_r, pcm_convert_quark(), 0, + "Conversion from %s to 16 bit is not implemented", + sample_format_to_string(sample_format(src_format->format))); + return NULL; + } + + if (src_format->channels != dest_format->channels) { + buf = pcm_convert_channels_16(&channels_buffer, + dest_format->channels, + src_format->channels, + buf, len, &len); + if (buf == NULL) { + g_set_error(error_r, pcm_convert_quark(), 0, + "Conversion from %u to %u channels " + "is not implemented", + src_format->channels, + dest_format->channels); + return NULL; + } + } + + if (src_format->sample_rate != dest_format->sample_rate) { + buf = pcm_resample_16(&resample, + dest_format->channels, + src_format->sample_rate, buf, len, + dest_format->sample_rate, &len, + error_r); + if (buf == NULL) + return NULL; + } + + *dest_size_r = len; + return buf; +} + +inline const int32_t * +PcmConvert::Convert24(const audio_format *src_format, + const void *src_buffer, size_t src_size, + const audio_format *dest_format, size_t *dest_size_r, + GError **error_r) +{ + const int32_t *buf; + size_t len; + + assert(dest_format->format == SAMPLE_FORMAT_S24_P32); + + buf = pcm_convert_to_24(&format_buffer, + sample_format(src_format->format), + src_buffer, src_size, &len); + if (buf == NULL) { + g_set_error(error_r, pcm_convert_quark(), 0, + "Conversion from %s to 24 bit is not implemented", + sample_format_to_string(sample_format(src_format->format))); + return NULL; + } + + if (src_format->channels != dest_format->channels) { + buf = pcm_convert_channels_24(&channels_buffer, + dest_format->channels, + src_format->channels, + buf, len, &len); + if (buf == NULL) { + g_set_error(error_r, pcm_convert_quark(), 0, + "Conversion from %u to %u channels " + "is not implemented", + src_format->channels, + dest_format->channels); + return NULL; + } + } + + if (src_format->sample_rate != dest_format->sample_rate) { + buf = pcm_resample_24(&resample, + dest_format->channels, + src_format->sample_rate, buf, len, + dest_format->sample_rate, &len, + error_r); + if (buf == NULL) + return NULL; + } + + *dest_size_r = len; + return buf; +} + +inline const int32_t * +PcmConvert::Convert32(const audio_format *src_format, + const void *src_buffer, size_t src_size, + const audio_format *dest_format, size_t *dest_size_r, + GError **error_r) +{ + const int32_t *buf; + size_t len; + + assert(dest_format->format == SAMPLE_FORMAT_S32); + + buf = pcm_convert_to_32(&format_buffer, + sample_format(src_format->format), + src_buffer, src_size, &len); + if (buf == NULL) { + g_set_error(error_r, pcm_convert_quark(), 0, + "Conversion from %s to 32 bit is not implemented", + sample_format_to_string(sample_format(src_format->format))); + return NULL; + } + + if (src_format->channels != dest_format->channels) { + buf = pcm_convert_channels_32(&channels_buffer, + dest_format->channels, + src_format->channels, + buf, len, &len); + if (buf == NULL) { + g_set_error(error_r, pcm_convert_quark(), 0, + "Conversion from %u to %u channels " + "is not implemented", + src_format->channels, + dest_format->channels); + return NULL; + } + } + + if (src_format->sample_rate != dest_format->sample_rate) { + buf = pcm_resample_32(&resample, + dest_format->channels, + src_format->sample_rate, buf, len, + dest_format->sample_rate, &len, + error_r); + if (buf == NULL) + return buf; + } + + *dest_size_r = len; + return buf; +} + +inline const float * +PcmConvert::ConvertFloat(const audio_format *src_format, + const void *src_buffer, size_t src_size, + const audio_format *dest_format, size_t *dest_size_r, + GError **error_r) +{ + const float *buffer = (const float *)src_buffer; + size_t size = src_size; + + assert(dest_format->format == SAMPLE_FORMAT_FLOAT); + + /* convert to float now */ + + buffer = pcm_convert_to_float(&format_buffer, + sample_format(src_format->format), + buffer, size, &size); + if (buffer == NULL) { + g_set_error(error_r, pcm_convert_quark(), 0, + "Conversion from %s to float is not implemented", + sample_format_to_string(sample_format(src_format->format))); + return NULL; + } + + /* convert channels */ + + if (src_format->channels != dest_format->channels) { + buffer = pcm_convert_channels_float(&channels_buffer, + dest_format->channels, + src_format->channels, + buffer, size, &size); + if (buffer == NULL) { + g_set_error(error_r, pcm_convert_quark(), 0, + "Conversion from %u to %u channels " + "is not implemented", + src_format->channels, + dest_format->channels); + return NULL; + } + } + + /* resample with float, because this is the best format for + libsamplerate */ + + if (src_format->sample_rate != dest_format->sample_rate) { + buffer = pcm_resample_float(&resample, + dest_format->channels, + src_format->sample_rate, + buffer, size, + dest_format->sample_rate, &size, + error_r); + if (buffer == NULL) + return NULL; + } + + *dest_size_r = size; + return buffer; +} + +const void * +PcmConvert::Convert(const audio_format *src_format, + const void *src, size_t src_size, + const audio_format *dest_format, + size_t *dest_size_r, + GError **error_r) +{ + struct audio_format float_format; + if (src_format->format == SAMPLE_FORMAT_DSD) { + size_t f_size; + const float *f = pcm_dsd_to_float(&dsd, + src_format->channels, + false, (const uint8_t *)src, + src_size, &f_size); + if (f == NULL) { + g_set_error_literal(error_r, pcm_convert_quark(), 0, + "DSD to PCM conversion failed"); + return NULL; + } + + float_format = *src_format; + float_format.format = SAMPLE_FORMAT_FLOAT; + + src_format = &float_format; + src = f; + src_size = f_size; + } + + switch (sample_format(dest_format->format)) { + case SAMPLE_FORMAT_S16: + return Convert16(src_format, src, src_size, + dest_format, dest_size_r, + error_r); + + case SAMPLE_FORMAT_S24_P32: + return Convert24(src_format, src, src_size, + dest_format, dest_size_r, + error_r); + + case SAMPLE_FORMAT_S32: + return Convert32(src_format, src, src_size, + dest_format, dest_size_r, + error_r); + + case SAMPLE_FORMAT_FLOAT: + return ConvertFloat(src_format, src, src_size, + dest_format, dest_size_r, + error_r); + + default: + g_set_error(error_r, pcm_convert_quark(), 0, + "PCM conversion to %s is not implemented", + sample_format_to_string(sample_format(dest_format->format))); + return NULL; + } +} diff --git a/src/pcm/PcmConvert.hxx b/src/pcm/PcmConvert.hxx new file mode 100644 index 000000000..f08188a9c --- /dev/null +++ b/src/pcm/PcmConvert.hxx @@ -0,0 +1,115 @@ +/* + * 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 PCM_CONVERT_HXX +#define PCM_CONVERT_HXX + +#include "PcmDither.hxx" + +extern "C" { +#include "pcm_dsd.h" +#include "pcm_resample.h" +#include "pcm_buffer.h" +} + +#include + +struct audio_format; + +/** + * This object is statically allocated (within another struct), and + * holds buffer allocations and the state for all kinds of PCM + * conversions. + */ +class PcmConvert { + struct pcm_dsd dsd; + + struct pcm_resample_state resample; + + PcmDither dither; + + /** the buffer for converting the sample format */ + struct pcm_buffer format_buffer; + + /** the buffer for converting the channel count */ + struct pcm_buffer channels_buffer; + +public: + PcmConvert(); + ~PcmConvert(); + + + /** + * Reset the pcm_convert_state object. Use this at the + * boundary between two distinct songs and each time the + * format changes. + */ + void Reset(); + + /** + * Converts PCM data between two audio formats. + * + * @param src_format the source audio format + * @param src the source PCM buffer + * @param src_size the size of #src in bytes + * @param dest_format the requested destination audio format + * @param dest_size_r returns the number of bytes of the destination buffer + * @param error_r location to store the error occurring, or NULL to + * ignore errors + * @return the destination buffer, or NULL on error + */ + const void *Convert(const audio_format *src_format, + const void *src, size_t src_size, + const audio_format *dest_format, + size_t *dest_size_r, + GError **error_r); + +private: + const int16_t *Convert16(const audio_format *src_format, + const void *src_buffer, size_t src_size, + const audio_format *dest_format, + size_t *dest_size_r, + GError **error_r); + + const int32_t *Convert24(const audio_format *src_format, + const void *src_buffer, size_t src_size, + const audio_format *dest_format, + size_t *dest_size_r, + GError **error_r); + + const int32_t *Convert32(const audio_format *src_format, + const void *src_buffer, size_t src_size, + const audio_format *dest_format, + size_t *dest_size_r, + GError **error_r); + + const float *ConvertFloat(const audio_format *src_format, + const void *src_buffer, size_t src_size, + const audio_format *dest_format, + size_t *dest_size_r, + GError **error_r); +}; + +static inline GQuark +pcm_convert_quark(void) +{ + return g_quark_from_static_string("pcm_convert"); +} + +#endif diff --git a/src/pcm/PcmDither.cxx b/src/pcm/PcmDither.cxx new file mode 100644 index 000000000..98d0d443e --- /dev/null +++ b/src/pcm/PcmDither.cxx @@ -0,0 +1,89 @@ +/* + * 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" + +inline int16_t +PcmDither::Dither24To16(int_fast32_t sample) +{ + constexpr unsigned from_bits = 24; + constexpr unsigned to_bits = 16; + constexpr unsigned scale_bits = from_bits - to_bits; + constexpr int_fast32_t round = 1 << (scale_bits - 1); + constexpr int_fast32_t mask = (1 << scale_bits) - 1; + constexpr int_fast32_t ONE = 1 << (from_bits - 1); + constexpr int_fast32_t MIN = -ONE; + constexpr int_fast32_t MAX = ONE - 1; + + sample += error[0] - error[1] + error[2]; + + error[2] = error[1]; + error[1] = error[0] / 2; + + /* round */ + int_fast32_t output = sample + round; + + int_fast32_t rnd = pcm_prng(random); + output += (rnd & mask) - (random & mask); + + 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; + + error[0] = sample - output; + + return (int16_t)(output >> scale_bits); +} + +void +PcmDither::Dither24To16(int16_t *dest, const int32_t *src, + const int32_t *src_end) +{ + while (src < src_end) + *dest++ = Dither24To16(*src++); +} + +inline int16_t +PcmDither::Dither32To16(int_fast32_t sample) +{ + return Dither24To16(sample >> 8); +} + +void +PcmDither::Dither32To16(int16_t *dest, const int32_t *src, + const int32_t *src_end) +{ + while (src < src_end) + *dest++ = Dither32To16(*src++); +} diff --git a/src/pcm/PcmDither.hxx b/src/pcm/PcmDither.hxx new file mode 100644 index 000000000..106382307 --- /dev/null +++ b/src/pcm/PcmDither.hxx @@ -0,0 +1,44 @@ +/* + * 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 + +class PcmDither { + int32_t error[3]; + int32_t random; + +public: + constexpr PcmDither() + :error{0, 0, 0}, random(0) {} + + void Dither24To16(int16_t *dest, const int32_t *src, + const int32_t *src_end); + + void Dither32To16(int16_t *dest, const int32_t *src, + const int32_t *src_end); + +private: + int16_t Dither24To16(int_fast32_t sample); + int16_t Dither32To16(int_fast32_t sample); +}; + +#endif diff --git a/src/pcm/PcmFormat.cxx b/src/pcm/PcmFormat.cxx new file mode 100644 index 000000000..1385d161b --- /dev/null +++ b/src/pcm/PcmFormat.cxx @@ -0,0 +1,500 @@ +/* + * 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" + +#include + +template +struct DefaultSampleBits { + typedef decltype(*S()) T; + typedef typename std::remove_reference::type U; + + static constexpr auto value = sizeof(U) * 8; +}; + +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(PcmDither &dither, + int16_t *out, const int32_t *in, const int32_t *in_end) +{ + dither.Dither24To16(out, in, in_end); +} + +static void +pcm_convert_32_to_16(PcmDither &dither, + int16_t *out, const int32_t *in, const int32_t *in_end) +{ + dither.Dither32To16(out, in, in_end); +} + +template::value> +static void +ConvertFromFloat(S dest, const float *src, const float *end) +{ + typedef decltype(*S()) T; + typedef typename std::remove_reference::type U; + + const float factor = 1 << (bits - 1); + + while (src != end) { + int sample(*src++ * factor); + *dest++ = PcmClamp(sample); + } +} + +template::value> +static void +ConvertFromFloat(S dest, const float *src, size_t size) +{ + ConvertFromFloat(dest, src, pcm_end_pointer(src, size)); +} + +template +static S * +AllocateFromFloat(pcm_buffer &buffer, const float *src, size_t src_size, + size_t *dest_size_r) +{ + constexpr size_t src_sample_size = sizeof(*src); + assert(src_size % src_sample_size == 0); + + const size_t num_samples = src_size / src_sample_size; + *dest_size_r = num_samples * sizeof(S); + S *dest = (S *)pcm_buffer_get(&buffer, *dest_size_r); + ConvertFromFloat(dest, src, src_size); + return dest; +} + +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, PcmDither &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, PcmDither &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) +{ + return AllocateFromFloat(*buffer, src, src_size, dest_size_r); +} + +const int16_t * +pcm_convert_to_16(struct pcm_buffer *buffer, PcmDither &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 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) +{ + return AllocateFromFloat(*buffer, src, src_size, + dest_size_r); +} + +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; +} + +template::value> +static void +ConvertToFloat(float *dest, S src, S end) +{ + constexpr float factor = 0.5 / (1 << (bits - 2)); + while (src != end) + *dest++ = float(*src++) * factor; + +} + +template::value> +static void +ConvertToFloat(float *dest, S src, size_t size) +{ + ConvertToFloat(dest, src, pcm_end_pointer(src, size)); +} + +template::value> +static float * +AllocateToFloat(pcm_buffer &buffer, S src, size_t src_size, + size_t *dest_size_r) +{ + constexpr size_t src_sample_size = sizeof(*S()); + assert(src_size % src_sample_size == 0); + + const size_t num_samples = src_size / src_sample_size; + *dest_size_r = num_samples * sizeof(float); + float *dest = (float *)pcm_buffer_get(&buffer, *dest_size_r); + ConvertToFloat(dest, src, src_size); + return dest; +} + +static float * +pcm_allocate_8_to_float(struct pcm_buffer *buffer, + const int8_t *src, size_t src_size, + size_t *dest_size_r) +{ + return AllocateToFloat(*buffer, src, src_size, dest_size_r); +} + +static float * +pcm_allocate_16_to_float(struct pcm_buffer *buffer, + const int16_t *src, size_t src_size, + size_t *dest_size_r) +{ + return AllocateToFloat(*buffer, src, src_size, dest_size_r); +} + +static float * +pcm_allocate_24p32_to_float(struct pcm_buffer *buffer, + const int32_t *src, size_t src_size, + size_t *dest_size_r) +{ + return AllocateToFloat + (*buffer, src, src_size, dest_size_r); +} + +static float * +pcm_allocate_32_to_float(struct pcm_buffer *buffer, + const int32_t *src, size_t src_size, + size_t *dest_size_r) +{ + return AllocateToFloat(*buffer, src, src_size, dest_size_r); +} + +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/pcm/PcmFormat.hxx b/src/pcm/PcmFormat.hxx new file mode 100644 index 000000000..a5970b2d2 --- /dev/null +++ b/src/pcm/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; +class PcmDither; + +/** + * 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, PcmDither &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/PcmMix.cxx b/src/pcm/PcmMix.cxx new file mode 100644 index 000000000..8435c0c2a --- /dev/null +++ b/src/pcm/PcmMix.cxx @@ -0,0 +1,211 @@ +/* + * 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 + +template +static T +PcmAddVolume(T _a, T _b, int volume1, int volume2) +{ + U a(_a), b(_b); + + U c = ((a * volume1 + b * volume2) + + pcm_volume_dither() + PCM_VOLUME_1 / 2) + / PCM_VOLUME_1; + + return PcmClamp(c); +} + +template +static void +PcmAddVolume(T *a, const T *b, unsigned n, int volume1, int volume2) +{ + for (size_t i = 0; i != n; ++i) + a[i] = PcmAddVolume(a[i], b[i], volume1, volume2); +} + +template +static void +PcmAddVolumeVoid(void *a, const void *b, size_t size, int volume1, int volume2) +{ + constexpr size_t sample_size = sizeof(T); + assert(size % sample_size == 0); + + PcmAddVolume((T *)a, (const T *)b, size / sample_size, + volume1, volume2); +} + +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: + PcmAddVolumeVoid(buffer1, buffer2, size, + vol1, vol2); + return true; + + case SAMPLE_FORMAT_S16: + PcmAddVolumeVoid(buffer1, buffer2, size, + vol1, vol2); + return true; + + case SAMPLE_FORMAT_S24_P32: + PcmAddVolumeVoid(buffer1, buffer2, size, + vol1, vol2); + return true; + + case SAMPLE_FORMAT_S32: + PcmAddVolumeVoid(buffer1, buffer2, size, + 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; +} + +template +static T +PcmAdd(T _a, T _b) +{ + U a(_a), b(_b); + return PcmClamp(a + b); +} + +template +static void +PcmAdd(T *a, const T *b, unsigned n) +{ + for (size_t i = 0; i != n; ++i) + a[i] = PcmAdd(a[i], b[i]); +} + +template +static void +PcmAddVoid(void *a, const void *b, size_t size) +{ + constexpr size_t sample_size = sizeof(T); + assert(size % sample_size == 0); + + PcmAdd((T *)a, (const T *)b, size / sample_size); +} + +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: + PcmAddVoid(buffer1, buffer2, size); + return true; + + case SAMPLE_FORMAT_S16: + PcmAddVoid(buffer1, buffer2, size); + return true; + + case SAMPLE_FORMAT_S24_P32: + PcmAddVoid(buffer1, buffer2, size); + return true; + + case SAMPLE_FORMAT_S32: + PcmAddVoid(buffer1, buffer2, size); + 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/pcm/PcmMix.hxx b/src/pcm/PcmMix.hxx new file mode 100644 index 000000000..bb7110d04 --- /dev/null +++ b/src/pcm/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/pcm/PcmPrng.hxx b/src/pcm/PcmPrng.hxx new file mode 100644 index 000000000..0c823250d --- /dev/null +++ b/src/pcm/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/pcm/PcmUtils.hxx b/src/pcm/PcmUtils.hxx new file mode 100644 index 000000000..d77c4194a --- /dev/null +++ b/src/pcm/PcmUtils.hxx @@ -0,0 +1,66 @@ +/* + * 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 + +#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. + */ +template +gcc_const +static inline T +PcmClamp(U x) +{ + constexpr U MIN_VALUE = -(U(1) << (bits - 1)); + constexpr U MAX_VALUE = (U(1) << (bits - 1)) - 1; + + typedef std::numeric_limits limits; + static_assert(MIN_VALUE >= limits::min(), "out of range"); + static_assert(MAX_VALUE <= limits::max(), "out of range"); + + if (gcc_unlikely(x < MIN_VALUE)) + return T(MIN_VALUE); + + if (gcc_unlikely(x > MAX_VALUE)) + return T(MAX_VALUE); + + return T(x); +} + +#endif diff --git a/src/pcm/PcmVolume.cxx b/src/pcm/PcmVolume.cxx new file mode 100644 index 000000000..556ab9925 --- /dev/null +++ b/src/pcm/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++ = PcmClamp(sample); + } +} + +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++ = PcmClamp(sample); + } +} + +#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++ = PcmClamp(sample); + } +} + +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++ = PcmClamp(sample); +#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/pcm/PcmVolume.hxx b/src/pcm/PcmVolume.hxx new file mode 100644 index 000000000..d3e6a5536 --- /dev/null +++ b/src/pcm/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/pcm/dsd2pcm/dsd2pcm.c b/src/pcm/dsd2pcm/dsd2pcm.c new file mode 100644 index 000000000..4c7640853 --- /dev/null +++ b/src/pcm/dsd2pcm/dsd2pcm.c @@ -0,0 +1,184 @@ +#include "util/bit_reverse.h" + +#include +#include + +#include "dsd2pcm.h" + +#define HTAPS 48 /* number of FIR constants */ +#define FIFOSIZE 16 /* must be a power of two */ +#define FIFOMASK (FIFOSIZE-1) /* bit mask for FIFO offsets */ +#define CTABLES ((HTAPS+7)/8) /* number of "8 MACs" lookup tables */ + +#if FIFOSIZE*8 < HTAPS*2 +#error "FIFOSIZE too small" +#endif + +/* + * Properties of this 96-tap lowpass filter when applied on a signal + * with sampling rate of 44100*64 Hz: + * + * () has a delay of 17 microseconds. + * + * () flat response up to 48 kHz + * + * () if you downsample afterwards by a factor of 8, the + * spectrum below 70 kHz is practically alias-free. + * + * () stopband rejection is about 160 dB + * + * The coefficient tables ("ctables") take only 6 Kibi Bytes and + * should fit into a modern processor's fast cache. + */ + +/* + * The 2nd half (48 coeffs) of a 96-tap symmetric lowpass filter + */ +static const double htaps[HTAPS] = { + 0.09950731974056658, + 0.09562845727714668, + 0.08819647126516944, + 0.07782552527068175, + 0.06534876523171299, + 0.05172629311427257, + 0.0379429484910187, + 0.02490921351762261, + 0.0133774746265897, + 0.003883043418804416, + -0.003284703416210726, + -0.008080250212687497, + -0.01067241812471033, + -0.01139427235000863, + -0.0106813877974587, + -0.009007905078766049, + -0.006828859761015335, + -0.004535184322001496, + -0.002425035959059578, + -0.0006922187080790708, + 0.0005700762133516592, + 0.001353838005269448, + 0.001713709169690937, + 0.001742046839472948, + 0.001545601648013235, + 0.001226696225277855, + 0.0008704322683580222, + 0.0005381636200535649, + 0.000266446345425276, + 7.002968738383528e-05, + -5.279407053811266e-05, + -0.0001140625650874684, + -0.0001304796361231895, + -0.0001189970287491285, + -9.396247155265073e-05, + -6.577634378272832e-05, + -4.07492895872535e-05, + -2.17407957554587e-05, + -9.163058931391722e-06, + -2.017460145032201e-06, + 1.249721855219005e-06, + 2.166655190537392e-06, + 1.930520892991082e-06, + 1.319400334374195e-06, + 7.410039764949091e-07, + 3.423230509967409e-07, + 1.244182214744588e-07, + 3.130441005359396e-08 +}; + +static float ctables[CTABLES][256]; +static int precalculated = 0; + +static void precalc(void) +{ + int t, e, m, k; + double acc; + if (precalculated) return; + for (t=0; t8) k=8; + for (e=0; e<256; ++e) { + acc = 0.0; + for (m=0; m> (7-m)) & 1)*2-1) * htaps[t*8+m]; + } + ctables[CTABLES-1-t][e] = (float)acc; + } + } + precalculated = 1; +} + +struct dsd2pcm_ctx_s +{ + unsigned char fifo[FIFOSIZE]; + unsigned fifopos; +}; + +extern dsd2pcm_ctx* dsd2pcm_init(void) +{ + dsd2pcm_ctx* ptr; + if (!precalculated) precalc(); + ptr = (dsd2pcm_ctx*) malloc(sizeof(dsd2pcm_ctx)); + if (ptr) dsd2pcm_reset(ptr); + return ptr; +} + +extern void dsd2pcm_destroy(dsd2pcm_ctx* ptr) +{ + free(ptr); +} + +extern dsd2pcm_ctx* dsd2pcm_clone(dsd2pcm_ctx* ptr) +{ + dsd2pcm_ctx* p2; + p2 = (dsd2pcm_ctx*) malloc(sizeof(dsd2pcm_ctx)); + if (p2) { + memcpy(p2,ptr,sizeof(dsd2pcm_ctx)); + } + return p2; +} + +extern void dsd2pcm_reset(dsd2pcm_ctx* ptr) +{ + int i; + for (i=0; ififo[i] = 0x69; /* my favorite silence pattern */ + ptr->fifopos = 0; + /* 0x69 = 01101001 + * This pattern "on repeat" makes a low energy 352.8 kHz tone + * and a high energy 1.0584 MHz tone which should be filtered + * out completely by any playback system --> silence + */ +} + +extern void dsd2pcm_translate( + dsd2pcm_ctx* ptr, + size_t samples, + const unsigned char *src, ptrdiff_t src_stride, + int lsbf, + float *dst, ptrdiff_t dst_stride) +{ + unsigned ffp; + unsigned i; + unsigned bite1, bite2; + unsigned char* p; + double acc; + ffp = ptr->fifopos; + lsbf = lsbf ? 1 : 0; + while (samples-- > 0) { + bite1 = *src & 0xFFu; + if (lsbf) bite1 = bit_reverse(bite1); + ptr->fifo[ffp] = bite1; src += src_stride; + p = ptr->fifo + ((ffp-CTABLES) & FIFOMASK); + *p = bit_reverse(*p); + acc = 0; + for (i=0; ififo[(ffp -i) & FIFOMASK] & 0xFF; + bite2 = ptr->fifo[(ffp-(CTABLES*2-1)+i) & FIFOMASK] & 0xFF; + acc += ctables[i][bite1] + ctables[i][bite2]; + } + *dst = (float)acc; dst += dst_stride; + ffp = (ffp + 1) & FIFOMASK; + } + ptr->fifopos = ffp; +} + diff --git a/src/pcm/dsd2pcm/dsd2pcm.h b/src/pcm/dsd2pcm/dsd2pcm.h new file mode 100644 index 000000000..80e8ce0cc --- /dev/null +++ b/src/pcm/dsd2pcm/dsd2pcm.h @@ -0,0 +1,64 @@ +#ifndef DSD2PCM_H_INCLUDED +#define DSD2PCM_H_INCLUDED + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct dsd2pcm_ctx_s; + +typedef struct dsd2pcm_ctx_s dsd2pcm_ctx; + +/** + * initializes a "dsd2pcm engine" for one channel + * (precomputes tables and allocates memory) + * + * This is the only function that is not thread-safe in terms of the + * POSIX thread-safety definition because it modifies global state + * (lookup tables are computed during the first call) + */ +extern dsd2pcm_ctx* dsd2pcm_init(void); + +/** + * deinitializes a "dsd2pcm engine" + * (releases memory, don't forget!) + */ +extern void dsd2pcm_destroy(dsd2pcm_ctx *ctx); + +/** + * clones the context and returns a pointer to the + * newly allocated copy + */ +extern dsd2pcm_ctx* dsd2pcm_clone(dsd2pcm_ctx *ctx); + +/** + * resets the internal state for a fresh new stream + */ +extern void dsd2pcm_reset(dsd2pcm_ctx *ctx); + +/** + * "translates" a stream of octets to a stream of floats + * (8:1 decimation) + * @param ctx -- pointer to abstract context (buffers) + * @param samples -- number of octets/samples to "translate" + * @param src -- pointer to first octet (input) + * @param src_stride -- src pointer increment + * @param lsbitfirst -- bitorder, 0=msb first, 1=lsbfirst + * @param dst -- pointer to first float (output) + * @param dst_stride -- dst pointer increment + */ +extern void dsd2pcm_translate(dsd2pcm_ctx *ctx, + size_t samples, + const unsigned char *src, ptrdiff_t src_stride, + int lsbitfirst, + float *dst, ptrdiff_t dst_stride); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* include guard DSD2PCM_H_INCLUDED */ + diff --git a/src/pcm/dsd2pcm/dsd2pcm.hpp b/src/pcm/dsd2pcm/dsd2pcm.hpp new file mode 100644 index 000000000..8f3f55197 --- /dev/null +++ b/src/pcm/dsd2pcm/dsd2pcm.hpp @@ -0,0 +1,39 @@ +#ifndef DSD2PCM_HXX_INCLUDED +#define DSD2PCM_HXX_INCLUDED + +#include +#include +#include "dsd2pcm.h" + +/** + * C++ PImpl Wrapper for the dsd2pcm C library + */ + +class dxd +{ + dsd2pcm_ctx *handle; +public: + dxd() : handle(dsd2pcm_init()) {} + + dxd(dxd const& x) : handle(dsd2pcm_clone(x.handle)) {} + + ~dxd() { dsd2pcm_destroy(handle); } + + friend void swap(dxd & a, dxd & b) + { std::swap(a.handle,b.handle); } + + dxd& operator=(dxd x) + { swap(*this,x); return *this; } + + void translate(size_t samples, + const unsigned char *src, ptrdiff_t src_stride, + bool lsbitfirst, + float *dst, ptrdiff_t dst_stride) + { + dsd2pcm_translate(handle,samples,src,src_stride, + lsbitfirst,dst,dst_stride); + } +}; + +#endif // DSD2PCM_HXX_INCLUDED + diff --git a/src/pcm/dsd2pcm/info.txt b/src/pcm/dsd2pcm/info.txt new file mode 100644 index 000000000..15ff29245 --- /dev/null +++ b/src/pcm/dsd2pcm/info.txt @@ -0,0 +1,38 @@ +You downloaded the source code for "dsd2pcm" which is a simple little +"filter" program, that takes a DSD data stream on stdin and converts +it to a PCM stream (352.8 kHz, either 16 or 24 bits) and writes it to +stdout. The code is split into three modules: + + (1) dsd2pcm + + This is where the 8:1 decimation magic happens. It's an + implementation of a symmetric 96-taps FIR lowpass filter + optimized for DSD inputs. If you feed this converter with + DSD64 you get a PCM stream at 352.8 kHz and floating point + samples. This module is independent and can be reused. + + (2) noiseshape + + A module for applying generic noise shaping filters. It's + used for the 16-bit output mode in "main" to preserve the + dynamic range. This module is independent and can be reused. + + (3) main.cpp (file contains the main function and handles I/O) + +The first two modules are pure C for maximum portability. In addition, +there are C++ wrapper headers for convenient use of these modules in +C++. The main application is a C++ application and makes use of the +C++ headers to access the functionality of the first two modules. + + +Under Linux this program is easily compiled by typing + + g++ *.c *.cpp -O3 -o dsd2pcm + +provided you have GCC installed. That's why I didn't bother writing +any makefiles. :-p + + +Cheers! +SG + diff --git a/src/pcm/dsd2pcm/main.cpp b/src/pcm/dsd2pcm/main.cpp new file mode 100644 index 000000000..0b58888a8 --- /dev/null +++ b/src/pcm/dsd2pcm/main.cpp @@ -0,0 +1,120 @@ +#include +#include +#include + +#include "dsd2pcm.hpp" +#include "noiseshape.hpp" + +namespace { + +const float my_ns_coeffs[] = { +// b1 b2 a1 a2 + -1.62666423, 0.79410094, 0.61367127, 0.23311013, // section 1 + -1.44870017, 0.54196219, 0.03373857, 0.70316556 // section 2 +}; + +const int my_ns_soscount = sizeof(my_ns_coeffs)/(sizeof(my_ns_coeffs[0])*4); + +inline long myround(float x) +{ + return static_cast(x + (x>=0 ? 0.5f : -0.5f)); +} + +template +struct id { typedef T type; }; + +template +inline T clip( + typename id::type min, + T v, + typename id::type max) +{ + if (vmax) return max; + return v; +} + +inline void write_intel16(unsigned char * ptr, unsigned word) +{ + ptr[0] = word & 0xFF; + ptr[1] = (word >> 8) & 0xFF; +} + +inline void write_intel24(unsigned char * ptr, unsigned long word) +{ + ptr[0] = word & 0xFF; + ptr[1] = (word >> 8) & 0xFF; + ptr[2] = (word >> 16) & 0xFF; +} + +} // anonymous namespace + +using std::vector; +using std::cin; +using std::cout; +using std::cerr; + +int main(int argc, char *argv[]) +{ + const int block = 16384; + int channels = -1; + int lsbitfirst = -1; + int bits = -1; + if (argc==4) { + if ('1'<=argv[1][0] && argv[1][0]<='9') channels = 1 + (argv[1][0]-'1'); + if (argv[2][0]=='m' || argv[2][0]=='M') lsbitfirst=0; + if (argv[2][0]=='l' || argv[2][0]=='L') lsbitfirst=1; + if (!strcmp(argv[3],"16")) bits = 16; + if (!strcmp(argv[3],"24")) bits = 24; + } + if (channels<1 || lsbitfirst<0 || bits<0) { + cerr << "\n" + "DSD2PCM filter (raw DSD64 --> 352 kHz raw PCM)\n" + "(c) 2009 Sebastian Gesemann\n\n" + "(filter as in \"reads data from stdin and writes to stdout\")\n\n" + "Syntax: dsd2pcm \n" + "channels = 1,2,3,...,9 (number of channels in DSD stream)\n" + "bitorder = L (lsb first), M (msb first) (DSD stream option)\n" + "bitdepth = 16 or 24 (intel byte order, output option)\n\n" + "Note: At 16 bits/sample a noise shaper kicks in that can preserve\n" + "a dynamic range of 135 dB below 30 kHz.\n\n"; + return 1; + } + int bytespersample = bits/8; + vector dxds (channels); + vector ns; + if (bits==16) { + ns.resize(channels, noise_shaper(my_ns_soscount, my_ns_coeffs) ); + } + vector dsd_data (block * channels); + vector float_data (block); + vector pcm_data (block * channels * bytespersample); + char * const dsd_in = reinterpret_cast(&dsd_data[0]); + char * const pcm_out = reinterpret_cast(&pcm_data[0]); + while (cin.read(dsd_in,block * channels)) { + for (int c=0; c +#include + +#include "noiseshape.h" + +extern int noise_shape_init( + noise_shape_ctx *ctx, + int sos_count, + const float *coeffs) +{ + int i; + ctx->sos_count = sos_count; + ctx->bbaa = coeffs; + ctx->t1 = (float*) malloc(sizeof(float)*sos_count); + if (!ctx->t1) goto escape1; + ctx->t2 = (float*) malloc(sizeof(float)*sos_count); + if (!ctx->t2) goto escape2; + for (i=0; it1[i] = 0.f; + ctx->t2[i] = 0.f; + } + return 0; +escape2: + free(ctx->t1); +escape1: + return -1; +} + +extern void noise_shape_destroy( + noise_shape_ctx *ctx) +{ + free(ctx->t1); + free(ctx->t2); +} + +extern int noise_shape_clone( + const noise_shape_ctx *from, + noise_shape_ctx *to) +{ + to->sos_count = from->sos_count; + to->bbaa = from->bbaa; + to->t1 = (float*) malloc(sizeof(float)*to->sos_count); + if (!to->t1) goto error1; + to->t2 = (float*) malloc(sizeof(float)*to->sos_count); + if (!to->t2) goto error2; + memcpy(to->t1,from->t1,sizeof(float)*to->sos_count); + memcpy(to->t2,from->t2,sizeof(float)*to->sos_count); + return 0; +error2: + free(to->t1); +error1: + return -1; +} + +extern float noise_shape_get(noise_shape_ctx *ctx) +{ + int i; + float acc; + const float *c; + acc = 0.0; + c = ctx->bbaa; + for (i=0; isos_count; ++i) { + float t1i = ctx->t1[i]; + float t2i = ctx->t2[i]; + ctx->t2[i] = acc -= t1i * c[2] + t2i * c[3]; + acc += t1i * c[0] + t2i * c[1]; + c += 4; + } + return acc; +} + +extern void noise_shape_update(noise_shape_ctx *ctx, float qerror) +{ + float *p; + int i; + for (i=0; isos_count; ++i) { + ctx->t2[i] += qerror; + } + p = ctx->t1; + ctx->t1 = ctx->t2; + ctx->t2 = p; +} + diff --git a/src/pcm/dsd2pcm/noiseshape.h b/src/pcm/dsd2pcm/noiseshape.h new file mode 100644 index 000000000..6075f0d88 --- /dev/null +++ b/src/pcm/dsd2pcm/noiseshape.h @@ -0,0 +1,57 @@ +#ifndef NOISE_SHAPE_H_INCLUDED +#define NOISE_SHAPE_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct noise_shape_ctx_s { + int sos_count; /* number of second order sections */ + const float *bbaa; /* filter coefficients, owned by user */ + float *t1, *t2; /* filter state, owned by ns library */ +} noise_shape_ctx; + +/** + * initializes a noise_shaper context + * returns an error code or 0 + */ +extern int noise_shape_init( + noise_shape_ctx *ctx, + int sos_count, + const float *coeffs); + +/** + * destroys a noise_shaper context + */ +extern void noise_shape_destroy( + noise_shape_ctx *ctx); + +/** + * initializes a noise_shaper context so that its state + * is a copy of a given context + * returns an error code or 0 + */ +extern int noise_shape_clone( + const noise_shape_ctx *from, noise_shape_ctx *to); + +/** + * computes the next "noise shaping sample". Note: This call + * alters the internal state. xxx_get and xxx_update must be + * called in an alternating manner. + */ +extern float noise_shape_get( + noise_shape_ctx *ctx); + +/** + * updates the noise shaper's state with the + * last quantization error + */ +extern void noise_shape_update( + noise_shape_ctx *ctx, float qerror); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* NOISE_SHAPE_H_INCLUDED */ + diff --git a/src/pcm/dsd2pcm/noiseshape.hpp b/src/pcm/dsd2pcm/noiseshape.hpp new file mode 100644 index 000000000..1fc698b36 --- /dev/null +++ b/src/pcm/dsd2pcm/noiseshape.hpp @@ -0,0 +1,43 @@ +#ifndef NOISE_SHAPE_HXX_INCLUDED +#define NOISE_SHAPE_HXX_INCLUDED + +#include +#include "noiseshape.h" + +/** + * C++ wrapper for the noiseshape C library + */ + +class noise_shaper +{ + noise_shape_ctx ctx; +public: + noise_shaper(int sos_count, const float *bbaa) + { + noise_shape_init(&ctx, sos_count, bbaa); + } + + noise_shaper(noise_shaper const& x) + { + noise_shape_clone(&x.ctx,&ctx); + } + + ~noise_shaper() + { noise_shape_destroy(&ctx); } + + noise_shaper& operator=(noise_shaper const& x) + { + if (this != &x) { + noise_shape_destroy(&ctx); + noise_shape_clone(&x.ctx,&ctx); + } + return *this; + } + + float get() { return noise_shape_get(&ctx); } + + void update(float error) { noise_shape_update(&ctx,error); } +}; + +#endif /* NOISE_SHAPE_HXX_INCLUDED */ + diff --git a/src/pcm/pcm_buffer.c b/src/pcm/pcm_buffer.c new file mode 100644 index 000000000..4b1eb875a --- /dev/null +++ b/src/pcm/pcm_buffer.c @@ -0,0 +1,58 @@ +/* + * 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_buffer.h" +#include "poison.h" + +/** + * Align the specified size to the next 8k boundary. + */ +G_GNUC_CONST +static size_t +align_8k(size_t size) +{ + return ((size - 1) | 0x1fff) + 1; +} + +void * +pcm_buffer_get(struct pcm_buffer *buffer, size_t size) +{ + assert(buffer != NULL); + + if (size == 0) + /* never return NULL, because NULL would be assumed to + be an error condition */ + size = 1; + + if (buffer->size < size) { + /* free the old buffer */ + g_free(buffer->buffer); + + buffer->size = align_8k(size); + buffer->buffer = g_malloc(buffer->size); + } else { + /* discard old buffer contents */ + poison_undefined(buffer->buffer, buffer->size); + } + + assert(buffer->size >= size); + + return buffer->buffer; +} diff --git a/src/pcm/pcm_buffer.h b/src/pcm/pcm_buffer.h new file mode 100644 index 000000000..5d6382d5e --- /dev/null +++ b/src/pcm/pcm_buffer.h @@ -0,0 +1,85 @@ +/* + * 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_BUFFER_H +#define PCM_BUFFER_H + +#include "check.h" + +#include + +#include + +/** + * Manager for a temporary buffer which grows as needed. We could + * allocate a new buffer every time pcm_convert() is called, but that + * would put too much stress on the allocator. + */ +struct pcm_buffer { + void *buffer; + + size_t size; +}; + +/** + * Initialize the buffer, but don't allocate anything yet. + */ +static inline void +pcm_buffer_init(struct pcm_buffer *buffer) +{ + assert(buffer != NULL); + + buffer->buffer = NULL; + buffer->size = 0; +} + +/** + * Free resources. This function may be called more than once. + */ +static inline void +pcm_buffer_deinit(struct pcm_buffer *buffer) +{ + assert(buffer != NULL); + + g_free(buffer->buffer); + + buffer->buffer = NULL; +} + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Get the buffer, and guarantee a minimum size. This buffer becomes + * invalid with the next pcm_buffer_get() call. + * + * This function will never return NULL, even if size is zero, because + * the PCM library uses the NULL return value to signal "error". An + * empty destination buffer is not always an error. + */ +G_GNUC_MALLOC +void * +pcm_buffer_get(struct pcm_buffer *buffer, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/pcm/pcm_dsd.c b/src/pcm/pcm_dsd.c new file mode 100644 index 000000000..76266b4cc --- /dev/null +++ b/src/pcm/pcm_dsd.c @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2003-2012 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_dsd.h" +#include "dsd2pcm/dsd2pcm.h" + +#include +#include + +void +pcm_dsd_init(struct pcm_dsd *dsd) +{ + pcm_buffer_init(&dsd->buffer); + + memset(dsd->dsd2pcm, 0, sizeof(dsd->dsd2pcm)); +} + +void +pcm_dsd_deinit(struct pcm_dsd *dsd) +{ + pcm_buffer_deinit(&dsd->buffer); + + for (unsigned i = 0; i < G_N_ELEMENTS(dsd->dsd2pcm); ++i) + if (dsd->dsd2pcm[i] != NULL) + dsd2pcm_destroy(dsd->dsd2pcm[i]); +} + +void +pcm_dsd_reset(struct pcm_dsd *dsd) +{ + for (unsigned i = 0; i < G_N_ELEMENTS(dsd->dsd2pcm); ++i) + if (dsd->dsd2pcm[i] != NULL) + dsd2pcm_reset(dsd->dsd2pcm[i]); +} + +const float * +pcm_dsd_to_float(struct pcm_dsd *dsd, unsigned channels, bool lsbfirst, + const uint8_t *src, size_t src_size, + size_t *dest_size_r) +{ + assert(dsd != NULL); + assert(src != NULL); + assert(src_size > 0); + assert(src_size % channels == 0); + assert(channels <= G_N_ELEMENTS(dsd->dsd2pcm)); + + const unsigned num_samples = src_size; + const unsigned num_frames = src_size / channels; + + float *dest; + const size_t dest_size = num_samples * sizeof(*dest); + *dest_size_r = dest_size; + dest = pcm_buffer_get(&dsd->buffer, dest_size); + + for (unsigned c = 0; c < channels; ++c) { + if (dsd->dsd2pcm[c] == NULL) { + dsd->dsd2pcm[c] = dsd2pcm_init(); + if (dsd->dsd2pcm[c] == NULL) + return NULL; + } + + dsd2pcm_translate(dsd->dsd2pcm[c], num_frames, + src + c, channels, + lsbfirst, dest + c, channels); + } + + return dest; +} diff --git a/src/pcm/pcm_dsd.h b/src/pcm/pcm_dsd.h new file mode 100644 index 000000000..85c2455aa --- /dev/null +++ b/src/pcm/pcm_dsd.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2003-2012 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_DSD_H +#define MPD_PCM_DSD_H + +#include "check.h" +#include "pcm_buffer.h" + +#include +#include + +/** + * Wrapper for the dsd2pcm library. + */ +struct pcm_dsd { + struct pcm_buffer buffer; + + struct dsd2pcm_ctx_s *dsd2pcm[32]; +}; + +void +pcm_dsd_init(struct pcm_dsd *dsd); + +void +pcm_dsd_deinit(struct pcm_dsd *dsd); + +void +pcm_dsd_reset(struct pcm_dsd *dsd); + +const float * +pcm_dsd_to_float(struct pcm_dsd *dsd, unsigned channels, bool lsbfirst, + const uint8_t *src, size_t src_size, + size_t *dest_size_r); + +#endif diff --git a/src/pcm/pcm_dsd_usb.c b/src/pcm/pcm_dsd_usb.c new file mode 100644 index 000000000..4b5e39f39 --- /dev/null +++ b/src/pcm/pcm_dsd_usb.c @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2003-2012 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_dsd_usb.h" +#include "pcm_buffer.h" +#include "audio_format.h" + +G_GNUC_CONST +static inline uint32_t +pcm_two_dsd_to_usb_marker1(uint8_t a, uint8_t b) +{ + return 0xff050000 | (a << 8) | b; +} + +G_GNUC_CONST +static inline uint32_t +pcm_two_dsd_to_usb_marker2(uint8_t a, uint8_t b) +{ + return 0xfffa0000 | (a << 8) | b; +} + + +const uint32_t * +pcm_dsd_to_usb(struct pcm_buffer *buffer, unsigned channels, + const uint8_t *src, size_t src_size, + size_t *dest_size_r) +{ + assert(buffer != NULL); + assert(audio_valid_channel_count(channels)); + assert(src != NULL); + assert(src_size > 0); + assert(src_size % channels == 0); + + const unsigned num_src_samples = src_size; + const unsigned num_src_frames = num_src_samples / channels; + + /* this rounds down and discards the last odd frame; not + elegant, but good enough for now */ + const unsigned num_frames = num_src_frames / 2; + const unsigned num_samples = num_frames * channels; + + const size_t dest_size = num_samples * 4; + *dest_size_r = dest_size; + uint32_t *const dest0 = pcm_buffer_get(buffer, dest_size), + *dest = dest0; + + for (unsigned i = num_frames / 2; i > 0; --i) { + for (unsigned c = channels; c > 0; --c) { + /* each 24 bit sample has 16 DSD sample bits + plus the magic 0x05 marker */ + + *dest++ = pcm_two_dsd_to_usb_marker1(src[0], src[channels]); + + /* seek the source pointer to the next + channel */ + ++src; + } + + /* skip the second byte of each channel, because we + have already copied it */ + src += channels; + + for (unsigned c = channels; c > 0; --c) { + /* each 24 bit sample has 16 DSD sample bits + plus the magic 0xfa marker */ + + *dest++ = pcm_two_dsd_to_usb_marker2(src[0], src[channels]); + + /* seek the source pointer to the next + channel */ + ++src; + } + + /* skip the second byte of each channel, because we + have already copied it */ + src += channels; + } + + return dest0; +} diff --git a/src/pcm/pcm_dsd_usb.h b/src/pcm/pcm_dsd_usb.h new file mode 100644 index 000000000..389358459 --- /dev/null +++ b/src/pcm/pcm_dsd_usb.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2003-2012 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_DSD_USB_H +#define MPD_PCM_DSD_USB_H + +#include "check.h" + +#include +#include +#include + +struct pcm_buffer; + +/** + * Pack DSD 1 bit samples into (padded) 24 bit PCM samples for + * playback over USB, according to the proposed standard by + * dCS and others: + * http://www.sonore.us/DoP_openStandard_1v1.pdf + */ +const uint32_t * +pcm_dsd_to_usb(struct pcm_buffer *buffer, unsigned channels, + const uint8_t *src, size_t src_size, + size_t *dest_size_r); + +#endif diff --git a/src/pcm/pcm_export.c b/src/pcm/pcm_export.c new file mode 100644 index 000000000..144ac71cd --- /dev/null +++ b/src/pcm/pcm_export.c @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2003-2012 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_export.h" +#include "pcm_dsd_usb.h" +#include "pcm_pack.h" +#include "util/byte_reverse.h" + +void +pcm_export_init(struct pcm_export_state *state) +{ + pcm_buffer_init(&state->reverse_buffer); + pcm_buffer_init(&state->pack_buffer); + pcm_buffer_init(&state->dsd_buffer); +} + +void pcm_export_deinit(struct pcm_export_state *state) +{ + pcm_buffer_deinit(&state->reverse_buffer); + pcm_buffer_deinit(&state->pack_buffer); + pcm_buffer_deinit(&state->dsd_buffer); +} + +void +pcm_export_open(struct pcm_export_state *state, + enum sample_format sample_format, unsigned channels, + bool dsd_usb, bool shift8, bool pack, bool reverse_endian) +{ + assert(audio_valid_sample_format(sample_format)); + assert(!dsd_usb || audio_valid_channel_count(channels)); + + state->channels = channels; + state->dsd_usb = dsd_usb && sample_format == SAMPLE_FORMAT_DSD; + if (state->dsd_usb) + /* after the conversion to DSD-over-USB, the DSD + samples are stuffed inside fake 24 bit samples */ + sample_format = SAMPLE_FORMAT_S24_P32; + + state->shift8 = shift8 && sample_format == SAMPLE_FORMAT_S24_P32; + state->pack24 = pack && sample_format == SAMPLE_FORMAT_S24_P32; + + assert(!state->shift8 || !state->pack24); + + state->reverse_endian = 0; + if (reverse_endian) { + size_t sample_size = state->pack24 + ? 3 + : sample_format_size(sample_format); + assert(sample_size <= 0xff); + + if (sample_size > 1) + state->reverse_endian = sample_size; + } +} + +size_t +pcm_export_frame_size(const struct pcm_export_state *state, + const struct audio_format *audio_format) +{ + assert(state != NULL); + assert(audio_format != NULL); + + if (state->pack24) + /* packed 24 bit samples (3 bytes per sample) */ + return audio_format->channels * 3; + + if (state->dsd_usb) + /* the DSD-over-USB draft says that DSD 1-bit samples + are enclosed within 24 bit samples, and MPD's + representation of 24 bit is padded to 32 bit (4 + bytes per sample) */ + return audio_format->channels * 4; + + return audio_format_frame_size(audio_format); +} + +const void * +pcm_export(struct pcm_export_state *state, const void *data, size_t size, + size_t *dest_size_r) +{ + if (state->dsd_usb) + data = pcm_dsd_to_usb(&state->dsd_buffer, state->channels, + data, size, &size); + + if (state->pack24) { + assert(size % 4 == 0); + + const size_t num_samples = size / 4; + const size_t dest_size = num_samples * 3; + + const uint8_t *src8 = data, *src_end8 = src8 + size; + uint8_t *dest = pcm_buffer_get(&state->pack_buffer, dest_size); + assert(dest != NULL); + + pcm_pack_24(dest, (const int32_t *)src8, + (const int32_t *)src_end8); + + data = dest; + size = dest_size; + } else if (state->shift8) { + assert(size % 4 == 0); + + const uint8_t *src8 = data, *src_end8 = src8 + size; + const uint32_t *src = (const uint32_t *)src8; + const uint32_t *const src_end = (const uint32_t *)src_end8; + + uint32_t *dest = pcm_buffer_get(&state->pack_buffer, size); + data = dest; + + while (src < src_end) + *dest++ = *src++ << 8; + } + + + if (state->reverse_endian > 0) { + assert(state->reverse_endian >= 2); + + void *dest = pcm_buffer_get(&state->reverse_buffer, size); + assert(dest != NULL); + + const uint8_t *src = data, *src_end = src + size; + reverse_bytes(dest, src, src_end, state->reverse_endian); + + data = dest; + } + + *dest_size_r = size; + return data; +} + +size_t +pcm_export_source_size(const struct pcm_export_state *state, size_t size) +{ + if (state->pack24) + /* 32 bit to 24 bit conversion (4 to 3 bytes) */ + size = (size / 3) * 4; + + if (state->dsd_usb) + /* DSD over USB doubles the transport size */ + size /= 2; + + return size; +} diff --git a/src/pcm/pcm_export.h b/src/pcm/pcm_export.h new file mode 100644 index 000000000..005db48e0 --- /dev/null +++ b/src/pcm/pcm_export.h @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2003-2012 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_EXPORT_H +#define PCM_EXPORT_H + +#include "check.h" +#include "pcm_buffer.h" +#include "audio_format.h" + +#include + +struct audio_format; + +/** + * An object that handles export of PCM samples to some instance + * outside of MPD. It has a few more options to tweak the binary + * representation which are not supported by the pcm_convert library. + */ +struct pcm_export_state { + /** + * The buffer is used to convert DSD samples to the + * DSD-over-USB format. + * + * @see #dsd_usb + */ + struct pcm_buffer dsd_buffer; + + /** + * The buffer is used to pack samples, removing padding. + * + * @see #pack24 + */ + struct pcm_buffer pack_buffer; + + /** + * The buffer is used to reverse the byte order. + * + * @see #reverse_endian + */ + struct pcm_buffer reverse_buffer; + + /** + * The number of channels. + */ + uint8_t channels; + + /** + * Convert DSD to DSD-over-USB? Input format must be + * SAMPLE_FORMAT_DSD and output format must be + * SAMPLE_FORMAT_S24_P32. + */ + bool dsd_usb; + + /** + * Convert (padded) 24 bit samples to 32 bit by shifting 8 + * bits to the left? + */ + bool shift8; + + /** + * Pack 24 bit samples? + */ + bool pack24; + + /** + * Export the samples in reverse byte order? A non-zero value + * means the option is enabled and represents the size of each + * sample (2 or bigger). + */ + uint8_t reverse_endian; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Initialize a #pcm_export_state object. + */ +void +pcm_export_init(struct pcm_export_state *state); + +/** + * Deinitialize a #pcm_export_state object and free allocated memory. + */ +void +pcm_export_deinit(struct pcm_export_state *state); + +/** + * Open the #pcm_export_state object. + * + * There is no "close" method. This function may be called multiple + * times to reuse the object, until pcm_export_deinit() is called. + * + * This function cannot fail. + * + * @param channels the number of channels; ignored unless dsd_usb is set + */ +void +pcm_export_open(struct pcm_export_state *state, + enum sample_format sample_format, unsigned channels, + bool dsd_usb, bool shift8, bool pack, bool reverse_endian); + +/** + * Calculate the size of one output frame. + */ +G_GNUC_PURE +size_t +pcm_export_frame_size(const struct pcm_export_state *state, + const struct audio_format *audio_format); + +/** + * Export a PCM buffer. + * + * @param state an initialized and open pcm_export_state object + * @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 (may be a pointer to the source buffer) + */ +const void * +pcm_export(struct pcm_export_state *state, const void *src, size_t src_size, + size_t *dest_size_r); + +/** + * Converts the number of consumed bytes from the pcm_export() + * destination buffer to the according number of bytes from the + * pcm_export() source buffer. + */ +G_GNUC_PURE +size_t +pcm_export_source_size(const struct pcm_export_state *state, size_t dest_size); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/pcm/pcm_pack.c b/src/pcm/pcm_pack.c new file mode 100644 index 000000000..921d880c0 --- /dev/null +++ b/src/pcm/pcm_pack.c @@ -0,0 +1,77 @@ +/* + * 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 "pcm_pack.h" + +#include + +static void +pack_sample(uint8_t *dest, const int32_t *src0) +{ + const uint8_t *src = (const uint8_t *)src0; + + if (G_BYTE_ORDER == G_BIG_ENDIAN) + ++src; + + *dest++ = *src++; + *dest++ = *src++; + *dest++ = *src++; +} + +void +pcm_pack_24(uint8_t *dest, const int32_t *src, const int32_t *src_end) +{ + /* duplicate loop to help the compiler's optimizer (constant + parameter to the pack_sample() inline function) */ + + while (src < src_end) { + pack_sample(dest, src++); + dest += 3; + } +} + +static void +unpack_sample(int32_t *dest0, const uint8_t *src) +{ + uint8_t *dest = (uint8_t *)dest0; + + if (G_BYTE_ORDER == G_BIG_ENDIAN) + /* extend the sign bit to the most fourth byte */ + *dest++ = *src & 0x80 ? 0xff : 0x00; + + *dest++ = *src++; + *dest++ = *src++; + *dest++ = *src; + + if (G_BYTE_ORDER == G_LITTLE_ENDIAN) + /* extend the sign bit to the most fourth byte */ + *dest++ = *src & 0x80 ? 0xff : 0x00; +} + +void +pcm_unpack_24(int32_t *dest, const uint8_t *src, const uint8_t *src_end) +{ + /* duplicate loop to help the compiler's optimizer (constant + parameter to the unpack_sample() inline function) */ + + while (src < src_end) { + unpack_sample(dest++, src); + src += 3; + } +} diff --git a/src/pcm/pcm_pack.h b/src/pcm/pcm_pack.h new file mode 100644 index 000000000..f3184b403 --- /dev/null +++ b/src/pcm/pcm_pack.h @@ -0,0 +1,55 @@ +/* + * 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. + */ + +/** \file + * + * Library for working with packed 24 bit samples. + */ + +#ifndef PCM_PACK_H +#define PCM_PACK_H + +#include +#include + +/** + * Converts padded 24 bit samples (4 bytes per sample) to packed 24 + * bit samples (3 bytes per sample). + * + * This function can be used to convert a buffer in-place. + * + * @param dest the destination buffer (array of triples) + * @param src the source buffer + * @param num_samples the number of samples to convert + */ +void +pcm_pack_24(uint8_t *dest, const int32_t *src, const int32_t *src_end); + +/** + * Converts packed 24 bit samples (3 bytes per sample) to padded 24 + * bit samples (4 bytes per sample). + * + * @param dest the destination buffer + * @param src the source buffer (array of triples) + * @param num_samples the number of samples to convert + */ +void +pcm_unpack_24(int32_t *dest, const uint8_t *src, const uint8_t *src_end); + +#endif diff --git a/src/pcm/pcm_resample.c b/src/pcm/pcm_resample.c new file mode 100644 index 000000000..4bc057a7e --- /dev/null +++ b/src/pcm/pcm_resample.c @@ -0,0 +1,159 @@ +/* + * 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_resample_internal.h" + +#ifdef HAVE_LIBSAMPLERATE +#include "conf.h" +#endif + +#include + +#ifdef HAVE_LIBSAMPLERATE +static bool lsr_enabled; +#endif + +#ifdef HAVE_LIBSAMPLERATE +static bool +pcm_resample_lsr_enabled(void) +{ + return lsr_enabled; +} +#endif + +bool +pcm_resample_global_init(GError **error_r) +{ +#ifdef HAVE_LIBSAMPLERATE + const char *converter = + config_get_string(CONF_SAMPLERATE_CONVERTER, ""); + + lsr_enabled = strcmp(converter, "internal") != 0; + if (lsr_enabled) + return pcm_resample_lsr_global_init(converter, error_r); + else + return true; +#else + (void)error_r; + return true; +#endif +} + +void pcm_resample_init(struct pcm_resample_state *state) +{ +#ifdef HAVE_LIBSAMPLERATE + if (pcm_resample_lsr_enabled()) + pcm_resample_lsr_init(state); + else +#endif + pcm_resample_fallback_init(state); +} + +void pcm_resample_deinit(struct pcm_resample_state *state) +{ +#ifdef HAVE_LIBSAMPLERATE + if (pcm_resample_lsr_enabled()) + pcm_resample_lsr_deinit(state); + else +#endif + pcm_resample_fallback_deinit(state); +} + +void +pcm_resample_reset(struct pcm_resample_state *state) +{ +#ifdef HAVE_LIBSAMPLERATE + pcm_resample_lsr_reset(state); +#else + (void)state; +#endif +} + +const float * +pcm_resample_float(struct pcm_resample_state *state, + unsigned channels, + unsigned src_rate, + const float *src_buffer, size_t src_size, + unsigned dest_rate, size_t *dest_size_r, + GError **error_r) +{ +#ifdef HAVE_LIBSAMPLERATE + if (pcm_resample_lsr_enabled()) + return pcm_resample_lsr_float(state, channels, + src_rate, src_buffer, src_size, + dest_rate, dest_size_r, + error_r); +#else + (void)error_r; +#endif + + /* sizeof(float)==sizeof(int32_t); the fallback resampler does + not do any math on the sample values, so this hack is + possible: */ + return (const float *) + pcm_resample_fallback_32(state, channels, + src_rate, (const int32_t *)src_buffer, + src_size, + dest_rate, dest_size_r); +} + +const int16_t * +pcm_resample_16(struct pcm_resample_state *state, + unsigned channels, + unsigned src_rate, const int16_t *src_buffer, size_t src_size, + unsigned dest_rate, size_t *dest_size_r, + GError **error_r) +{ +#ifdef HAVE_LIBSAMPLERATE + if (pcm_resample_lsr_enabled()) + return pcm_resample_lsr_16(state, channels, + src_rate, src_buffer, src_size, + dest_rate, dest_size_r, + error_r); +#else + (void)error_r; +#endif + + return pcm_resample_fallback_16(state, channels, + src_rate, src_buffer, src_size, + dest_rate, dest_size_r); +} + +const int32_t * +pcm_resample_32(struct pcm_resample_state *state, + unsigned channels, + unsigned src_rate, const int32_t *src_buffer, size_t src_size, + unsigned dest_rate, size_t *dest_size_r, + GError **error_r) +{ +#ifdef HAVE_LIBSAMPLERATE + if (pcm_resample_lsr_enabled()) + return pcm_resample_lsr_32(state, channels, + src_rate, src_buffer, src_size, + dest_rate, dest_size_r, + error_r); +#else + (void)error_r; +#endif + + return pcm_resample_fallback_32(state, channels, + src_rate, src_buffer, src_size, + dest_rate, dest_size_r); +} diff --git a/src/pcm/pcm_resample.h b/src/pcm/pcm_resample.h new file mode 100644 index 000000000..a49a24142 --- /dev/null +++ b/src/pcm/pcm_resample.h @@ -0,0 +1,164 @@ +/* + * 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_RESAMPLE_H +#define MPD_PCM_RESAMPLE_H + +#include "check.h" +#include "pcm_buffer.h" + +#include +#include +#include + +#ifdef HAVE_LIBSAMPLERATE +#include +#endif + +/** + * This object is statically allocated (within another struct), and + * holds buffer allocations and the state for the resampler. + */ +struct pcm_resample_state { +#ifdef HAVE_LIBSAMPLERATE + SRC_STATE *state; + SRC_DATA data; + + struct pcm_buffer in, out; + + struct { + unsigned src_rate; + unsigned dest_rate; + unsigned channels; + } prev; + + int error; +#endif + + struct pcm_buffer buffer; +}; + +bool +pcm_resample_global_init(GError **error_r); + +/** + * Initializes a pcm_resample_state object. + */ +void pcm_resample_init(struct pcm_resample_state *state); + +/** + * Deinitializes a pcm_resample_state object and frees allocated + * memory. + */ +void pcm_resample_deinit(struct pcm_resample_state *state); + +/** + * @see pcm_convert_reset() + */ +void +pcm_resample_reset(struct pcm_resample_state *state); + +/** + * Resamples 32 bit float data. + * + * @param state an initialized pcm_resample_state object + * @param channels the number of channels + * @param src_rate the source sample rate + * @param src the source PCM buffer + * @param src_size the size of #src in bytes + * @param dest_rate the requested destination sample rate + * @param dest_size_r returns the number of bytes of the destination buffer + * @return the destination buffer + */ +const float * +pcm_resample_float(struct pcm_resample_state *state, + unsigned channels, + unsigned src_rate, + const float *src_buffer, size_t src_size, + unsigned dest_rate, size_t *dest_size_r, + GError **error_r); + +/** + * Resamples 16 bit PCM data. + * + * @param state an initialized pcm_resample_state object + * @param channels the number of channels + * @param src_rate the source sample rate + * @param src the source PCM buffer + * @param src_size the size of #src in bytes + * @param dest_rate the requested destination sample rate + * @param dest_size_r returns the number of bytes of the destination buffer + * @return the destination buffer + */ +const int16_t * +pcm_resample_16(struct pcm_resample_state *state, + unsigned channels, + unsigned src_rate, + const int16_t *src_buffer, size_t src_size, + unsigned dest_rate, size_t *dest_size_r, + GError **error_r); + +/** + * Resamples 32 bit PCM data. + * + * @param state an initialized pcm_resample_state object + * @param channels the number of channels + * @param src_rate the source sample rate + * @param src the source PCM buffer + * @param src_size the size of #src in bytes + * @param dest_rate the requested destination sample rate + * @param dest_size_r returns the number of bytes of the destination buffer + * @return the destination buffer + */ +const int32_t * +pcm_resample_32(struct pcm_resample_state *state, + unsigned channels, + unsigned src_rate, + const int32_t *src_buffer, size_t src_size, + unsigned dest_rate, size_t *dest_size_r, + GError **error_r); + +/** + * Resamples 24 bit PCM data. + * + * @param state an initialized pcm_resample_state object + * @param channels the number of channels + * @param src_rate the source sample rate + * @param src the source PCM buffer + * @param src_size the size of #src in bytes + * @param dest_rate the requested destination sample rate + * @param dest_size_r returns the number of bytes of the destination buffer + * @return the destination buffer + */ +static inline const int32_t * +pcm_resample_24(struct pcm_resample_state *state, + unsigned channels, + unsigned src_rate, + const int32_t *src_buffer, size_t src_size, + unsigned dest_rate, size_t *dest_size_r, + GError **error_r) +{ + /* reuse the 32 bit code - the resampler code doesn't care if + the upper 8 bits are actually used */ + return pcm_resample_32(state, channels, + src_rate, src_buffer, src_size, + dest_rate, dest_size_r, error_r); +} + +#endif diff --git a/src/pcm/pcm_resample_fallback.c b/src/pcm/pcm_resample_fallback.c new file mode 100644 index 000000000..1d1dfdf59 --- /dev/null +++ b/src/pcm/pcm_resample_fallback.c @@ -0,0 +1,118 @@ +/* + * 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_resample_internal.h" + +#include + +void +pcm_resample_fallback_init(struct pcm_resample_state *state) +{ + pcm_buffer_init(&state->buffer); +} + +void +pcm_resample_fallback_deinit(struct pcm_resample_state *state) +{ + pcm_buffer_deinit(&state->buffer); +} + +/* resampling code blatantly ripped from ESD */ +const int16_t * +pcm_resample_fallback_16(struct pcm_resample_state *state, + unsigned channels, + unsigned src_rate, + const int16_t *src_buffer, size_t src_size, + unsigned dest_rate, + size_t *dest_size_r) +{ + unsigned src_pos, dest_pos = 0; + unsigned src_frames = src_size / channels / sizeof(*src_buffer); + unsigned dest_frames = + (src_frames * dest_rate + src_rate - 1) / src_rate; + unsigned dest_samples = dest_frames * channels; + size_t dest_size = dest_samples * sizeof(*src_buffer); + int16_t *dest_buffer = pcm_buffer_get(&state->buffer, dest_size); + + assert((src_size % (sizeof(*src_buffer) * channels)) == 0); + + switch (channels) { + case 1: + while (dest_pos < dest_samples) { + src_pos = dest_pos * src_rate / dest_rate; + + dest_buffer[dest_pos++] = src_buffer[src_pos]; + } + break; + case 2: + while (dest_pos < dest_samples) { + src_pos = dest_pos * src_rate / dest_rate; + src_pos &= ~1; + + dest_buffer[dest_pos++] = src_buffer[src_pos]; + dest_buffer[dest_pos++] = src_buffer[src_pos + 1]; + } + break; + } + + *dest_size_r = dest_size; + return dest_buffer; +} + +const int32_t * +pcm_resample_fallback_32(struct pcm_resample_state *state, + unsigned channels, + unsigned src_rate, + const int32_t *src_buffer, size_t src_size, + unsigned dest_rate, + size_t *dest_size_r) +{ + unsigned src_pos, dest_pos = 0; + unsigned src_frames = src_size / channels / sizeof(*src_buffer); + unsigned dest_frames = + (src_frames * dest_rate + src_rate - 1) / src_rate; + unsigned dest_samples = dest_frames * channels; + size_t dest_size = dest_samples * sizeof(*src_buffer); + int32_t *dest_buffer = pcm_buffer_get(&state->buffer, dest_size); + + assert((src_size % (sizeof(*src_buffer) * channels)) == 0); + + switch (channels) { + case 1: + while (dest_pos < dest_samples) { + src_pos = dest_pos * src_rate / dest_rate; + + dest_buffer[dest_pos++] = src_buffer[src_pos]; + } + break; + case 2: + while (dest_pos < dest_samples) { + src_pos = dest_pos * src_rate / dest_rate; + src_pos &= ~1; + + dest_buffer[dest_pos++] = src_buffer[src_pos]; + dest_buffer[dest_pos++] = src_buffer[src_pos + 1]; + } + break; + } + + *dest_size_r = dest_size; + return dest_buffer; +} diff --git a/src/pcm/pcm_resample_internal.h b/src/pcm/pcm_resample_internal.h new file mode 100644 index 000000000..a0e108d4b --- /dev/null +++ b/src/pcm/pcm_resample_internal.h @@ -0,0 +1,97 @@ +/* + * 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. + */ + +/** \file + * + * Internal declarations for the pcm_resample library. The "internal" + * resampler is called "fallback" in the MPD source, so the file name + * of this header is somewhat unrelated to it. + */ + +#ifndef MPD_PCM_RESAMPLE_INTERNAL_H +#define MPD_PCM_RESAMPLE_INTERNAL_H + +#include "check.h" +#include "pcm_resample.h" + +#ifdef HAVE_LIBSAMPLERATE + +bool +pcm_resample_lsr_global_init(const char *converter, GError **error_r); + +void +pcm_resample_lsr_init(struct pcm_resample_state *state); + +void +pcm_resample_lsr_deinit(struct pcm_resample_state *state); + +void +pcm_resample_lsr_reset(struct pcm_resample_state *state); + +const float * +pcm_resample_lsr_float(struct pcm_resample_state *state, + unsigned channels, + unsigned src_rate, + const float *src_buffer, size_t src_size, + unsigned dest_rate, size_t *dest_size_r, + GError **error_r); + +const int16_t * +pcm_resample_lsr_16(struct pcm_resample_state *state, + unsigned channels, + unsigned src_rate, + const int16_t *src_buffer, size_t src_size, + unsigned dest_rate, size_t *dest_size_r, + GError **error_r); + +const int32_t * +pcm_resample_lsr_32(struct pcm_resample_state *state, + unsigned channels, + unsigned src_rate, + const int32_t *src_buffer, + G_GNUC_UNUSED size_t src_size, + unsigned dest_rate, size_t *dest_size_r, + GError **error_r); + +#endif + +void +pcm_resample_fallback_init(struct pcm_resample_state *state); + +void +pcm_resample_fallback_deinit(struct pcm_resample_state *state); + +const int16_t * +pcm_resample_fallback_16(struct pcm_resample_state *state, + unsigned channels, + unsigned src_rate, + const int16_t *src_buffer, size_t src_size, + unsigned dest_rate, + size_t *dest_size_r); + +const int32_t * +pcm_resample_fallback_32(struct pcm_resample_state *state, + unsigned channels, + unsigned src_rate, + const int32_t *src_buffer, + G_GNUC_UNUSED size_t src_size, + unsigned dest_rate, + size_t *dest_size_r); + +#endif diff --git a/src/pcm/pcm_resample_libsamplerate.c b/src/pcm/pcm_resample_libsamplerate.c new file mode 100644 index 000000000..f957e5155 --- /dev/null +++ b/src/pcm/pcm_resample_libsamplerate.c @@ -0,0 +1,312 @@ +/* + * 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_resample_internal.h" +#include "conf.h" + +#include + +#include +#include +#include + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "pcm" + +static int lsr_converter = SRC_SINC_FASTEST; + +static inline GQuark +libsamplerate_quark(void) +{ + return g_quark_from_static_string("libsamplerate"); +} + +static bool +lsr_parse_converter(const char *s) +{ + assert(s != NULL); + + if (*s == 0) + return true; + + char *endptr; + long l = strtol(s, &endptr, 10); + if (*endptr == 0 && src_get_name(l) != NULL) { + lsr_converter = l; + return true; + } + + size_t length = strlen(s); + for (int i = 0;; ++i) { + const char *name = src_get_name(i); + if (name == NULL) + break; + + if (g_ascii_strncasecmp(s, name, length) == 0) { + lsr_converter = i; + return true; + } + } + + return false; +} + +bool +pcm_resample_lsr_global_init(const char *converter, GError **error_r) +{ + if (!lsr_parse_converter(converter)) { + g_set_error(error_r, libsamplerate_quark(), 0, + "unknown samplerate converter '%s'", converter); + return false; + } + + g_debug("libsamplerate converter '%s'", + src_get_name(lsr_converter)); + + return true; +} + +void +pcm_resample_lsr_init(struct pcm_resample_state *state) +{ + memset(state, 0, sizeof(*state)); + + pcm_buffer_init(&state->in); + pcm_buffer_init(&state->out); + pcm_buffer_init(&state->buffer); +} + +void +pcm_resample_lsr_deinit(struct pcm_resample_state *state) +{ + if (state->state != NULL) + state->state = src_delete(state->state); + + pcm_buffer_deinit(&state->in); + pcm_buffer_deinit(&state->out); + pcm_buffer_deinit(&state->buffer); +} + +void +pcm_resample_lsr_reset(struct pcm_resample_state *state) +{ + if (state->state != NULL) + src_reset(state->state); +} + +static bool +pcm_resample_set(struct pcm_resample_state *state, + unsigned channels, unsigned src_rate, unsigned dest_rate, + GError **error_r) +{ + int error; + SRC_DATA *data = &state->data; + + /* (re)set the state/ratio if the in or out format changed */ + if (channels == state->prev.channels && + src_rate == state->prev.src_rate && + dest_rate == state->prev.dest_rate) + return true; + + state->error = 0; + state->prev.channels = channels; + state->prev.src_rate = src_rate; + state->prev.dest_rate = dest_rate; + + if (state->state) + state->state = src_delete(state->state); + + state->state = src_new(lsr_converter, channels, &error); + if (!state->state) { + g_set_error(error_r, libsamplerate_quark(), state->error, + "libsamplerate initialization has failed: %s", + src_strerror(error)); + return false; + } + + data->src_ratio = (double)dest_rate / (double)src_rate; + g_debug("setting samplerate conversion ratio to %.2lf", + data->src_ratio); + src_set_ratio(state->state, data->src_ratio); + + return true; +} + +static bool +lsr_process(struct pcm_resample_state *state, GError **error_r) +{ + if (state->error == 0) + state->error = src_process(state->state, &state->data); + if (state->error) { + g_set_error(error_r, libsamplerate_quark(), state->error, + "libsamplerate has failed: %s", + src_strerror(state->error)); + return false; + } + + return true; +} + +static float * +deconst_float_buffer(const float *in) +{ + union { + const float *in; + float *out; + } u = { .in = in }; + return u.out; +} + +const float * +pcm_resample_lsr_float(struct pcm_resample_state *state, + unsigned channels, + unsigned src_rate, + const float *src_buffer, size_t src_size, + unsigned dest_rate, size_t *dest_size_r, + GError **error_r) +{ + assert((src_size % (sizeof(*src_buffer) * channels)) == 0); + + if (!pcm_resample_set(state, channels, src_rate, dest_rate, error_r)) + return NULL; + + SRC_DATA *data = &state->data; + data->input_frames = src_size / sizeof(*src_buffer) / channels; + data->data_in = deconst_float_buffer(src_buffer); + + data->output_frames = (src_size * dest_rate + src_rate - 1) / src_rate; + size_t data_out_size = data->output_frames * sizeof(float) * channels; + data->data_out = pcm_buffer_get(&state->out, data_out_size); + + if (!lsr_process(state, error_r)) + return NULL; + + *dest_size_r = data->output_frames_gen * + sizeof(*data->data_out) * channels; + return data->data_out; +} + +const int16_t * +pcm_resample_lsr_16(struct pcm_resample_state *state, + unsigned channels, + unsigned src_rate, + const int16_t *src_buffer, size_t src_size, + unsigned dest_rate, size_t *dest_size_r, + GError **error_r) +{ + bool success; + SRC_DATA *data = &state->data; + size_t data_in_size; + size_t data_out_size; + int16_t *dest_buffer; + + assert((src_size % (sizeof(*src_buffer) * channels)) == 0); + + success = pcm_resample_set(state, channels, src_rate, dest_rate, + error_r); + if (!success) + return NULL; + + data->input_frames = src_size / sizeof(*src_buffer) / channels; + data_in_size = data->input_frames * sizeof(float) * channels; + data->data_in = pcm_buffer_get(&state->in, data_in_size); + + data->output_frames = (src_size * dest_rate + src_rate - 1) / src_rate; + data_out_size = data->output_frames * sizeof(float) * channels; + data->data_out = pcm_buffer_get(&state->out, data_out_size); + + src_short_to_float_array(src_buffer, data->data_in, + data->input_frames * channels); + + if (!lsr_process(state, error_r)) + return NULL; + + *dest_size_r = data->output_frames_gen * + sizeof(*dest_buffer) * channels; + dest_buffer = pcm_buffer_get(&state->buffer, *dest_size_r); + src_float_to_short_array(data->data_out, dest_buffer, + data->output_frames_gen * channels); + + return dest_buffer; +} + +#ifdef HAVE_LIBSAMPLERATE_NOINT + +/* libsamplerate introduced these functions in v0.1.3 */ + +static void +src_int_to_float_array(const int *in, float *out, int len) +{ + while (len-- > 0) + *out++ = *in++ / (float)(1 << (24 - 1)); +} + +static void +src_float_to_int_array (const float *in, int *out, int len) +{ + while (len-- > 0) + *out++ = *in++ * (float)(1 << (24 - 1)); +} + +#endif + +const int32_t * +pcm_resample_lsr_32(struct pcm_resample_state *state, + unsigned channels, + unsigned src_rate, + const int32_t *src_buffer, size_t src_size, + unsigned dest_rate, size_t *dest_size_r, + GError **error_r) +{ + bool success; + SRC_DATA *data = &state->data; + size_t data_in_size; + size_t data_out_size; + int32_t *dest_buffer; + + assert((src_size % (sizeof(*src_buffer) * channels)) == 0); + + success = pcm_resample_set(state, channels, src_rate, dest_rate, + error_r); + if (!success) + return NULL; + + data->input_frames = src_size / sizeof(*src_buffer) / channels; + data_in_size = data->input_frames * sizeof(float) * channels; + data->data_in = pcm_buffer_get(&state->in, data_in_size); + + data->output_frames = (src_size * dest_rate + src_rate - 1) / src_rate; + data_out_size = data->output_frames * sizeof(float) * channels; + data->data_out = pcm_buffer_get(&state->out, data_out_size); + + src_int_to_float_array(src_buffer, data->data_in, + data->input_frames * channels); + + if (!lsr_process(state, error_r)) + return NULL; + + *dest_size_r = data->output_frames_gen * + sizeof(*dest_buffer) * channels; + dest_buffer = pcm_buffer_get(&state->buffer, *dest_size_r); + src_float_to_int_array(data->data_out, dest_buffer, + data->output_frames_gen * channels); + + return dest_buffer; +} diff --git a/src/pcm_buffer.c b/src/pcm_buffer.c deleted file mode 100644 index 4b1eb875a..000000000 --- a/src/pcm_buffer.c +++ /dev/null @@ -1,58 +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_buffer.h" -#include "poison.h" - -/** - * Align the specified size to the next 8k boundary. - */ -G_GNUC_CONST -static size_t -align_8k(size_t size) -{ - return ((size - 1) | 0x1fff) + 1; -} - -void * -pcm_buffer_get(struct pcm_buffer *buffer, size_t size) -{ - assert(buffer != NULL); - - if (size == 0) - /* never return NULL, because NULL would be assumed to - be an error condition */ - size = 1; - - if (buffer->size < size) { - /* free the old buffer */ - g_free(buffer->buffer); - - buffer->size = align_8k(size); - buffer->buffer = g_malloc(buffer->size); - } else { - /* discard old buffer contents */ - poison_undefined(buffer->buffer, buffer->size); - } - - assert(buffer->size >= size); - - return buffer->buffer; -} diff --git a/src/pcm_buffer.h b/src/pcm_buffer.h deleted file mode 100644 index 5d6382d5e..000000000 --- a/src/pcm_buffer.h +++ /dev/null @@ -1,85 +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_BUFFER_H -#define PCM_BUFFER_H - -#include "check.h" - -#include - -#include - -/** - * Manager for a temporary buffer which grows as needed. We could - * allocate a new buffer every time pcm_convert() is called, but that - * would put too much stress on the allocator. - */ -struct pcm_buffer { - void *buffer; - - size_t size; -}; - -/** - * Initialize the buffer, but don't allocate anything yet. - */ -static inline void -pcm_buffer_init(struct pcm_buffer *buffer) -{ - assert(buffer != NULL); - - buffer->buffer = NULL; - buffer->size = 0; -} - -/** - * Free resources. This function may be called more than once. - */ -static inline void -pcm_buffer_deinit(struct pcm_buffer *buffer) -{ - assert(buffer != NULL); - - g_free(buffer->buffer); - - buffer->buffer = NULL; -} - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Get the buffer, and guarantee a minimum size. This buffer becomes - * invalid with the next pcm_buffer_get() call. - * - * This function will never return NULL, even if size is zero, because - * the PCM library uses the NULL return value to signal "error". An - * empty destination buffer is not always an error. - */ -G_GNUC_MALLOC -void * -pcm_buffer_get(struct pcm_buffer *buffer, size_t size); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/pcm_dsd.c b/src/pcm_dsd.c deleted file mode 100644 index 76266b4cc..000000000 --- a/src/pcm_dsd.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2003-2012 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_dsd.h" -#include "dsd2pcm/dsd2pcm.h" - -#include -#include - -void -pcm_dsd_init(struct pcm_dsd *dsd) -{ - pcm_buffer_init(&dsd->buffer); - - memset(dsd->dsd2pcm, 0, sizeof(dsd->dsd2pcm)); -} - -void -pcm_dsd_deinit(struct pcm_dsd *dsd) -{ - pcm_buffer_deinit(&dsd->buffer); - - for (unsigned i = 0; i < G_N_ELEMENTS(dsd->dsd2pcm); ++i) - if (dsd->dsd2pcm[i] != NULL) - dsd2pcm_destroy(dsd->dsd2pcm[i]); -} - -void -pcm_dsd_reset(struct pcm_dsd *dsd) -{ - for (unsigned i = 0; i < G_N_ELEMENTS(dsd->dsd2pcm); ++i) - if (dsd->dsd2pcm[i] != NULL) - dsd2pcm_reset(dsd->dsd2pcm[i]); -} - -const float * -pcm_dsd_to_float(struct pcm_dsd *dsd, unsigned channels, bool lsbfirst, - const uint8_t *src, size_t src_size, - size_t *dest_size_r) -{ - assert(dsd != NULL); - assert(src != NULL); - assert(src_size > 0); - assert(src_size % channels == 0); - assert(channels <= G_N_ELEMENTS(dsd->dsd2pcm)); - - const unsigned num_samples = src_size; - const unsigned num_frames = src_size / channels; - - float *dest; - const size_t dest_size = num_samples * sizeof(*dest); - *dest_size_r = dest_size; - dest = pcm_buffer_get(&dsd->buffer, dest_size); - - for (unsigned c = 0; c < channels; ++c) { - if (dsd->dsd2pcm[c] == NULL) { - dsd->dsd2pcm[c] = dsd2pcm_init(); - if (dsd->dsd2pcm[c] == NULL) - return NULL; - } - - dsd2pcm_translate(dsd->dsd2pcm[c], num_frames, - src + c, channels, - lsbfirst, dest + c, channels); - } - - return dest; -} diff --git a/src/pcm_dsd.h b/src/pcm_dsd.h deleted file mode 100644 index 85c2455aa..000000000 --- a/src/pcm_dsd.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2003-2012 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_DSD_H -#define MPD_PCM_DSD_H - -#include "check.h" -#include "pcm_buffer.h" - -#include -#include - -/** - * Wrapper for the dsd2pcm library. - */ -struct pcm_dsd { - struct pcm_buffer buffer; - - struct dsd2pcm_ctx_s *dsd2pcm[32]; -}; - -void -pcm_dsd_init(struct pcm_dsd *dsd); - -void -pcm_dsd_deinit(struct pcm_dsd *dsd); - -void -pcm_dsd_reset(struct pcm_dsd *dsd); - -const float * -pcm_dsd_to_float(struct pcm_dsd *dsd, unsigned channels, bool lsbfirst, - const uint8_t *src, size_t src_size, - size_t *dest_size_r); - -#endif diff --git a/src/pcm_dsd_usb.c b/src/pcm_dsd_usb.c deleted file mode 100644 index 4b5e39f39..000000000 --- a/src/pcm_dsd_usb.c +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2003-2012 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_dsd_usb.h" -#include "pcm_buffer.h" -#include "audio_format.h" - -G_GNUC_CONST -static inline uint32_t -pcm_two_dsd_to_usb_marker1(uint8_t a, uint8_t b) -{ - return 0xff050000 | (a << 8) | b; -} - -G_GNUC_CONST -static inline uint32_t -pcm_two_dsd_to_usb_marker2(uint8_t a, uint8_t b) -{ - return 0xfffa0000 | (a << 8) | b; -} - - -const uint32_t * -pcm_dsd_to_usb(struct pcm_buffer *buffer, unsigned channels, - const uint8_t *src, size_t src_size, - size_t *dest_size_r) -{ - assert(buffer != NULL); - assert(audio_valid_channel_count(channels)); - assert(src != NULL); - assert(src_size > 0); - assert(src_size % channels == 0); - - const unsigned num_src_samples = src_size; - const unsigned num_src_frames = num_src_samples / channels; - - /* this rounds down and discards the last odd frame; not - elegant, but good enough for now */ - const unsigned num_frames = num_src_frames / 2; - const unsigned num_samples = num_frames * channels; - - const size_t dest_size = num_samples * 4; - *dest_size_r = dest_size; - uint32_t *const dest0 = pcm_buffer_get(buffer, dest_size), - *dest = dest0; - - for (unsigned i = num_frames / 2; i > 0; --i) { - for (unsigned c = channels; c > 0; --c) { - /* each 24 bit sample has 16 DSD sample bits - plus the magic 0x05 marker */ - - *dest++ = pcm_two_dsd_to_usb_marker1(src[0], src[channels]); - - /* seek the source pointer to the next - channel */ - ++src; - } - - /* skip the second byte of each channel, because we - have already copied it */ - src += channels; - - for (unsigned c = channels; c > 0; --c) { - /* each 24 bit sample has 16 DSD sample bits - plus the magic 0xfa marker */ - - *dest++ = pcm_two_dsd_to_usb_marker2(src[0], src[channels]); - - /* seek the source pointer to the next - channel */ - ++src; - } - - /* skip the second byte of each channel, because we - have already copied it */ - src += channels; - } - - return dest0; -} diff --git a/src/pcm_dsd_usb.h b/src/pcm_dsd_usb.h deleted file mode 100644 index 389358459..000000000 --- a/src/pcm_dsd_usb.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2003-2012 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_DSD_USB_H -#define MPD_PCM_DSD_USB_H - -#include "check.h" - -#include -#include -#include - -struct pcm_buffer; - -/** - * Pack DSD 1 bit samples into (padded) 24 bit PCM samples for - * playback over USB, according to the proposed standard by - * dCS and others: - * http://www.sonore.us/DoP_openStandard_1v1.pdf - */ -const uint32_t * -pcm_dsd_to_usb(struct pcm_buffer *buffer, unsigned channels, - const uint8_t *src, size_t src_size, - size_t *dest_size_r); - -#endif diff --git a/src/pcm_export.c b/src/pcm_export.c deleted file mode 100644 index 144ac71cd..000000000 --- a/src/pcm_export.c +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (C) 2003-2012 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_export.h" -#include "pcm_dsd_usb.h" -#include "pcm_pack.h" -#include "util/byte_reverse.h" - -void -pcm_export_init(struct pcm_export_state *state) -{ - pcm_buffer_init(&state->reverse_buffer); - pcm_buffer_init(&state->pack_buffer); - pcm_buffer_init(&state->dsd_buffer); -} - -void pcm_export_deinit(struct pcm_export_state *state) -{ - pcm_buffer_deinit(&state->reverse_buffer); - pcm_buffer_deinit(&state->pack_buffer); - pcm_buffer_deinit(&state->dsd_buffer); -} - -void -pcm_export_open(struct pcm_export_state *state, - enum sample_format sample_format, unsigned channels, - bool dsd_usb, bool shift8, bool pack, bool reverse_endian) -{ - assert(audio_valid_sample_format(sample_format)); - assert(!dsd_usb || audio_valid_channel_count(channels)); - - state->channels = channels; - state->dsd_usb = dsd_usb && sample_format == SAMPLE_FORMAT_DSD; - if (state->dsd_usb) - /* after the conversion to DSD-over-USB, the DSD - samples are stuffed inside fake 24 bit samples */ - sample_format = SAMPLE_FORMAT_S24_P32; - - state->shift8 = shift8 && sample_format == SAMPLE_FORMAT_S24_P32; - state->pack24 = pack && sample_format == SAMPLE_FORMAT_S24_P32; - - assert(!state->shift8 || !state->pack24); - - state->reverse_endian = 0; - if (reverse_endian) { - size_t sample_size = state->pack24 - ? 3 - : sample_format_size(sample_format); - assert(sample_size <= 0xff); - - if (sample_size > 1) - state->reverse_endian = sample_size; - } -} - -size_t -pcm_export_frame_size(const struct pcm_export_state *state, - const struct audio_format *audio_format) -{ - assert(state != NULL); - assert(audio_format != NULL); - - if (state->pack24) - /* packed 24 bit samples (3 bytes per sample) */ - return audio_format->channels * 3; - - if (state->dsd_usb) - /* the DSD-over-USB draft says that DSD 1-bit samples - are enclosed within 24 bit samples, and MPD's - representation of 24 bit is padded to 32 bit (4 - bytes per sample) */ - return audio_format->channels * 4; - - return audio_format_frame_size(audio_format); -} - -const void * -pcm_export(struct pcm_export_state *state, const void *data, size_t size, - size_t *dest_size_r) -{ - if (state->dsd_usb) - data = pcm_dsd_to_usb(&state->dsd_buffer, state->channels, - data, size, &size); - - if (state->pack24) { - assert(size % 4 == 0); - - const size_t num_samples = size / 4; - const size_t dest_size = num_samples * 3; - - const uint8_t *src8 = data, *src_end8 = src8 + size; - uint8_t *dest = pcm_buffer_get(&state->pack_buffer, dest_size); - assert(dest != NULL); - - pcm_pack_24(dest, (const int32_t *)src8, - (const int32_t *)src_end8); - - data = dest; - size = dest_size; - } else if (state->shift8) { - assert(size % 4 == 0); - - const uint8_t *src8 = data, *src_end8 = src8 + size; - const uint32_t *src = (const uint32_t *)src8; - const uint32_t *const src_end = (const uint32_t *)src_end8; - - uint32_t *dest = pcm_buffer_get(&state->pack_buffer, size); - data = dest; - - while (src < src_end) - *dest++ = *src++ << 8; - } - - - if (state->reverse_endian > 0) { - assert(state->reverse_endian >= 2); - - void *dest = pcm_buffer_get(&state->reverse_buffer, size); - assert(dest != NULL); - - const uint8_t *src = data, *src_end = src + size; - reverse_bytes(dest, src, src_end, state->reverse_endian); - - data = dest; - } - - *dest_size_r = size; - return data; -} - -size_t -pcm_export_source_size(const struct pcm_export_state *state, size_t size) -{ - if (state->pack24) - /* 32 bit to 24 bit conversion (4 to 3 bytes) */ - size = (size / 3) * 4; - - if (state->dsd_usb) - /* DSD over USB doubles the transport size */ - size /= 2; - - return size; -} diff --git a/src/pcm_export.h b/src/pcm_export.h deleted file mode 100644 index 005db48e0..000000000 --- a/src/pcm_export.h +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (C) 2003-2012 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_EXPORT_H -#define PCM_EXPORT_H - -#include "check.h" -#include "pcm_buffer.h" -#include "audio_format.h" - -#include - -struct audio_format; - -/** - * An object that handles export of PCM samples to some instance - * outside of MPD. It has a few more options to tweak the binary - * representation which are not supported by the pcm_convert library. - */ -struct pcm_export_state { - /** - * The buffer is used to convert DSD samples to the - * DSD-over-USB format. - * - * @see #dsd_usb - */ - struct pcm_buffer dsd_buffer; - - /** - * The buffer is used to pack samples, removing padding. - * - * @see #pack24 - */ - struct pcm_buffer pack_buffer; - - /** - * The buffer is used to reverse the byte order. - * - * @see #reverse_endian - */ - struct pcm_buffer reverse_buffer; - - /** - * The number of channels. - */ - uint8_t channels; - - /** - * Convert DSD to DSD-over-USB? Input format must be - * SAMPLE_FORMAT_DSD and output format must be - * SAMPLE_FORMAT_S24_P32. - */ - bool dsd_usb; - - /** - * Convert (padded) 24 bit samples to 32 bit by shifting 8 - * bits to the left? - */ - bool shift8; - - /** - * Pack 24 bit samples? - */ - bool pack24; - - /** - * Export the samples in reverse byte order? A non-zero value - * means the option is enabled and represents the size of each - * sample (2 or bigger). - */ - uint8_t reverse_endian; -}; - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Initialize a #pcm_export_state object. - */ -void -pcm_export_init(struct pcm_export_state *state); - -/** - * Deinitialize a #pcm_export_state object and free allocated memory. - */ -void -pcm_export_deinit(struct pcm_export_state *state); - -/** - * Open the #pcm_export_state object. - * - * There is no "close" method. This function may be called multiple - * times to reuse the object, until pcm_export_deinit() is called. - * - * This function cannot fail. - * - * @param channels the number of channels; ignored unless dsd_usb is set - */ -void -pcm_export_open(struct pcm_export_state *state, - enum sample_format sample_format, unsigned channels, - bool dsd_usb, bool shift8, bool pack, bool reverse_endian); - -/** - * Calculate the size of one output frame. - */ -G_GNUC_PURE -size_t -pcm_export_frame_size(const struct pcm_export_state *state, - const struct audio_format *audio_format); - -/** - * Export a PCM buffer. - * - * @param state an initialized and open pcm_export_state object - * @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 (may be a pointer to the source buffer) - */ -const void * -pcm_export(struct pcm_export_state *state, const void *src, size_t src_size, - size_t *dest_size_r); - -/** - * Converts the number of consumed bytes from the pcm_export() - * destination buffer to the according number of bytes from the - * pcm_export() source buffer. - */ -G_GNUC_PURE -size_t -pcm_export_source_size(const struct pcm_export_state *state, size_t dest_size); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/pcm_pack.c b/src/pcm_pack.c deleted file mode 100644 index 921d880c0..000000000 --- a/src/pcm_pack.c +++ /dev/null @@ -1,77 +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 "pcm_pack.h" - -#include - -static void -pack_sample(uint8_t *dest, const int32_t *src0) -{ - const uint8_t *src = (const uint8_t *)src0; - - if (G_BYTE_ORDER == G_BIG_ENDIAN) - ++src; - - *dest++ = *src++; - *dest++ = *src++; - *dest++ = *src++; -} - -void -pcm_pack_24(uint8_t *dest, const int32_t *src, const int32_t *src_end) -{ - /* duplicate loop to help the compiler's optimizer (constant - parameter to the pack_sample() inline function) */ - - while (src < src_end) { - pack_sample(dest, src++); - dest += 3; - } -} - -static void -unpack_sample(int32_t *dest0, const uint8_t *src) -{ - uint8_t *dest = (uint8_t *)dest0; - - if (G_BYTE_ORDER == G_BIG_ENDIAN) - /* extend the sign bit to the most fourth byte */ - *dest++ = *src & 0x80 ? 0xff : 0x00; - - *dest++ = *src++; - *dest++ = *src++; - *dest++ = *src; - - if (G_BYTE_ORDER == G_LITTLE_ENDIAN) - /* extend the sign bit to the most fourth byte */ - *dest++ = *src & 0x80 ? 0xff : 0x00; -} - -void -pcm_unpack_24(int32_t *dest, const uint8_t *src, const uint8_t *src_end) -{ - /* duplicate loop to help the compiler's optimizer (constant - parameter to the unpack_sample() inline function) */ - - while (src < src_end) { - unpack_sample(dest++, src); - src += 3; - } -} diff --git a/src/pcm_pack.h b/src/pcm_pack.h deleted file mode 100644 index f3184b403..000000000 --- a/src/pcm_pack.h +++ /dev/null @@ -1,55 +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. - */ - -/** \file - * - * Library for working with packed 24 bit samples. - */ - -#ifndef PCM_PACK_H -#define PCM_PACK_H - -#include -#include - -/** - * Converts padded 24 bit samples (4 bytes per sample) to packed 24 - * bit samples (3 bytes per sample). - * - * This function can be used to convert a buffer in-place. - * - * @param dest the destination buffer (array of triples) - * @param src the source buffer - * @param num_samples the number of samples to convert - */ -void -pcm_pack_24(uint8_t *dest, const int32_t *src, const int32_t *src_end); - -/** - * Converts packed 24 bit samples (3 bytes per sample) to padded 24 - * bit samples (4 bytes per sample). - * - * @param dest the destination buffer - * @param src the source buffer (array of triples) - * @param num_samples the number of samples to convert - */ -void -pcm_unpack_24(int32_t *dest, const uint8_t *src, const uint8_t *src_end); - -#endif diff --git a/src/pcm_resample.c b/src/pcm_resample.c deleted file mode 100644 index 4bc057a7e..000000000 --- a/src/pcm_resample.c +++ /dev/null @@ -1,159 +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_resample_internal.h" - -#ifdef HAVE_LIBSAMPLERATE -#include "conf.h" -#endif - -#include - -#ifdef HAVE_LIBSAMPLERATE -static bool lsr_enabled; -#endif - -#ifdef HAVE_LIBSAMPLERATE -static bool -pcm_resample_lsr_enabled(void) -{ - return lsr_enabled; -} -#endif - -bool -pcm_resample_global_init(GError **error_r) -{ -#ifdef HAVE_LIBSAMPLERATE - const char *converter = - config_get_string(CONF_SAMPLERATE_CONVERTER, ""); - - lsr_enabled = strcmp(converter, "internal") != 0; - if (lsr_enabled) - return pcm_resample_lsr_global_init(converter, error_r); - else - return true; -#else - (void)error_r; - return true; -#endif -} - -void pcm_resample_init(struct pcm_resample_state *state) -{ -#ifdef HAVE_LIBSAMPLERATE - if (pcm_resample_lsr_enabled()) - pcm_resample_lsr_init(state); - else -#endif - pcm_resample_fallback_init(state); -} - -void pcm_resample_deinit(struct pcm_resample_state *state) -{ -#ifdef HAVE_LIBSAMPLERATE - if (pcm_resample_lsr_enabled()) - pcm_resample_lsr_deinit(state); - else -#endif - pcm_resample_fallback_deinit(state); -} - -void -pcm_resample_reset(struct pcm_resample_state *state) -{ -#ifdef HAVE_LIBSAMPLERATE - pcm_resample_lsr_reset(state); -#else - (void)state; -#endif -} - -const float * -pcm_resample_float(struct pcm_resample_state *state, - unsigned channels, - unsigned src_rate, - const float *src_buffer, size_t src_size, - unsigned dest_rate, size_t *dest_size_r, - GError **error_r) -{ -#ifdef HAVE_LIBSAMPLERATE - if (pcm_resample_lsr_enabled()) - return pcm_resample_lsr_float(state, channels, - src_rate, src_buffer, src_size, - dest_rate, dest_size_r, - error_r); -#else - (void)error_r; -#endif - - /* sizeof(float)==sizeof(int32_t); the fallback resampler does - not do any math on the sample values, so this hack is - possible: */ - return (const float *) - pcm_resample_fallback_32(state, channels, - src_rate, (const int32_t *)src_buffer, - src_size, - dest_rate, dest_size_r); -} - -const int16_t * -pcm_resample_16(struct pcm_resample_state *state, - unsigned channels, - unsigned src_rate, const int16_t *src_buffer, size_t src_size, - unsigned dest_rate, size_t *dest_size_r, - GError **error_r) -{ -#ifdef HAVE_LIBSAMPLERATE - if (pcm_resample_lsr_enabled()) - return pcm_resample_lsr_16(state, channels, - src_rate, src_buffer, src_size, - dest_rate, dest_size_r, - error_r); -#else - (void)error_r; -#endif - - return pcm_resample_fallback_16(state, channels, - src_rate, src_buffer, src_size, - dest_rate, dest_size_r); -} - -const int32_t * -pcm_resample_32(struct pcm_resample_state *state, - unsigned channels, - unsigned src_rate, const int32_t *src_buffer, size_t src_size, - unsigned dest_rate, size_t *dest_size_r, - GError **error_r) -{ -#ifdef HAVE_LIBSAMPLERATE - if (pcm_resample_lsr_enabled()) - return pcm_resample_lsr_32(state, channels, - src_rate, src_buffer, src_size, - dest_rate, dest_size_r, - error_r); -#else - (void)error_r; -#endif - - return pcm_resample_fallback_32(state, channels, - src_rate, src_buffer, src_size, - dest_rate, dest_size_r); -} diff --git a/src/pcm_resample.h b/src/pcm_resample.h deleted file mode 100644 index a49a24142..000000000 --- a/src/pcm_resample.h +++ /dev/null @@ -1,164 +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_RESAMPLE_H -#define MPD_PCM_RESAMPLE_H - -#include "check.h" -#include "pcm_buffer.h" - -#include -#include -#include - -#ifdef HAVE_LIBSAMPLERATE -#include -#endif - -/** - * This object is statically allocated (within another struct), and - * holds buffer allocations and the state for the resampler. - */ -struct pcm_resample_state { -#ifdef HAVE_LIBSAMPLERATE - SRC_STATE *state; - SRC_DATA data; - - struct pcm_buffer in, out; - - struct { - unsigned src_rate; - unsigned dest_rate; - unsigned channels; - } prev; - - int error; -#endif - - struct pcm_buffer buffer; -}; - -bool -pcm_resample_global_init(GError **error_r); - -/** - * Initializes a pcm_resample_state object. - */ -void pcm_resample_init(struct pcm_resample_state *state); - -/** - * Deinitializes a pcm_resample_state object and frees allocated - * memory. - */ -void pcm_resample_deinit(struct pcm_resample_state *state); - -/** - * @see pcm_convert_reset() - */ -void -pcm_resample_reset(struct pcm_resample_state *state); - -/** - * Resamples 32 bit float data. - * - * @param state an initialized pcm_resample_state object - * @param channels the number of channels - * @param src_rate the source sample rate - * @param src the source PCM buffer - * @param src_size the size of #src in bytes - * @param dest_rate the requested destination sample rate - * @param dest_size_r returns the number of bytes of the destination buffer - * @return the destination buffer - */ -const float * -pcm_resample_float(struct pcm_resample_state *state, - unsigned channels, - unsigned src_rate, - const float *src_buffer, size_t src_size, - unsigned dest_rate, size_t *dest_size_r, - GError **error_r); - -/** - * Resamples 16 bit PCM data. - * - * @param state an initialized pcm_resample_state object - * @param channels the number of channels - * @param src_rate the source sample rate - * @param src the source PCM buffer - * @param src_size the size of #src in bytes - * @param dest_rate the requested destination sample rate - * @param dest_size_r returns the number of bytes of the destination buffer - * @return the destination buffer - */ -const int16_t * -pcm_resample_16(struct pcm_resample_state *state, - unsigned channels, - unsigned src_rate, - const int16_t *src_buffer, size_t src_size, - unsigned dest_rate, size_t *dest_size_r, - GError **error_r); - -/** - * Resamples 32 bit PCM data. - * - * @param state an initialized pcm_resample_state object - * @param channels the number of channels - * @param src_rate the source sample rate - * @param src the source PCM buffer - * @param src_size the size of #src in bytes - * @param dest_rate the requested destination sample rate - * @param dest_size_r returns the number of bytes of the destination buffer - * @return the destination buffer - */ -const int32_t * -pcm_resample_32(struct pcm_resample_state *state, - unsigned channels, - unsigned src_rate, - const int32_t *src_buffer, size_t src_size, - unsigned dest_rate, size_t *dest_size_r, - GError **error_r); - -/** - * Resamples 24 bit PCM data. - * - * @param state an initialized pcm_resample_state object - * @param channels the number of channels - * @param src_rate the source sample rate - * @param src the source PCM buffer - * @param src_size the size of #src in bytes - * @param dest_rate the requested destination sample rate - * @param dest_size_r returns the number of bytes of the destination buffer - * @return the destination buffer - */ -static inline const int32_t * -pcm_resample_24(struct pcm_resample_state *state, - unsigned channels, - unsigned src_rate, - const int32_t *src_buffer, size_t src_size, - unsigned dest_rate, size_t *dest_size_r, - GError **error_r) -{ - /* reuse the 32 bit code - the resampler code doesn't care if - the upper 8 bits are actually used */ - return pcm_resample_32(state, channels, - src_rate, src_buffer, src_size, - dest_rate, dest_size_r, error_r); -} - -#endif diff --git a/src/pcm_resample_fallback.c b/src/pcm_resample_fallback.c deleted file mode 100644 index 1d1dfdf59..000000000 --- a/src/pcm_resample_fallback.c +++ /dev/null @@ -1,118 +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_resample_internal.h" - -#include - -void -pcm_resample_fallback_init(struct pcm_resample_state *state) -{ - pcm_buffer_init(&state->buffer); -} - -void -pcm_resample_fallback_deinit(struct pcm_resample_state *state) -{ - pcm_buffer_deinit(&state->buffer); -} - -/* resampling code blatantly ripped from ESD */ -const int16_t * -pcm_resample_fallback_16(struct pcm_resample_state *state, - unsigned channels, - unsigned src_rate, - const int16_t *src_buffer, size_t src_size, - unsigned dest_rate, - size_t *dest_size_r) -{ - unsigned src_pos, dest_pos = 0; - unsigned src_frames = src_size / channels / sizeof(*src_buffer); - unsigned dest_frames = - (src_frames * dest_rate + src_rate - 1) / src_rate; - unsigned dest_samples = dest_frames * channels; - size_t dest_size = dest_samples * sizeof(*src_buffer); - int16_t *dest_buffer = pcm_buffer_get(&state->buffer, dest_size); - - assert((src_size % (sizeof(*src_buffer) * channels)) == 0); - - switch (channels) { - case 1: - while (dest_pos < dest_samples) { - src_pos = dest_pos * src_rate / dest_rate; - - dest_buffer[dest_pos++] = src_buffer[src_pos]; - } - break; - case 2: - while (dest_pos < dest_samples) { - src_pos = dest_pos * src_rate / dest_rate; - src_pos &= ~1; - - dest_buffer[dest_pos++] = src_buffer[src_pos]; - dest_buffer[dest_pos++] = src_buffer[src_pos + 1]; - } - break; - } - - *dest_size_r = dest_size; - return dest_buffer; -} - -const int32_t * -pcm_resample_fallback_32(struct pcm_resample_state *state, - unsigned channels, - unsigned src_rate, - const int32_t *src_buffer, size_t src_size, - unsigned dest_rate, - size_t *dest_size_r) -{ - unsigned src_pos, dest_pos = 0; - unsigned src_frames = src_size / channels / sizeof(*src_buffer); - unsigned dest_frames = - (src_frames * dest_rate + src_rate - 1) / src_rate; - unsigned dest_samples = dest_frames * channels; - size_t dest_size = dest_samples * sizeof(*src_buffer); - int32_t *dest_buffer = pcm_buffer_get(&state->buffer, dest_size); - - assert((src_size % (sizeof(*src_buffer) * channels)) == 0); - - switch (channels) { - case 1: - while (dest_pos < dest_samples) { - src_pos = dest_pos * src_rate / dest_rate; - - dest_buffer[dest_pos++] = src_buffer[src_pos]; - } - break; - case 2: - while (dest_pos < dest_samples) { - src_pos = dest_pos * src_rate / dest_rate; - src_pos &= ~1; - - dest_buffer[dest_pos++] = src_buffer[src_pos]; - dest_buffer[dest_pos++] = src_buffer[src_pos + 1]; - } - break; - } - - *dest_size_r = dest_size; - return dest_buffer; -} diff --git a/src/pcm_resample_internal.h b/src/pcm_resample_internal.h deleted file mode 100644 index a0e108d4b..000000000 --- a/src/pcm_resample_internal.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. - */ - -/** \file - * - * Internal declarations for the pcm_resample library. The "internal" - * resampler is called "fallback" in the MPD source, so the file name - * of this header is somewhat unrelated to it. - */ - -#ifndef MPD_PCM_RESAMPLE_INTERNAL_H -#define MPD_PCM_RESAMPLE_INTERNAL_H - -#include "check.h" -#include "pcm_resample.h" - -#ifdef HAVE_LIBSAMPLERATE - -bool -pcm_resample_lsr_global_init(const char *converter, GError **error_r); - -void -pcm_resample_lsr_init(struct pcm_resample_state *state); - -void -pcm_resample_lsr_deinit(struct pcm_resample_state *state); - -void -pcm_resample_lsr_reset(struct pcm_resample_state *state); - -const float * -pcm_resample_lsr_float(struct pcm_resample_state *state, - unsigned channels, - unsigned src_rate, - const float *src_buffer, size_t src_size, - unsigned dest_rate, size_t *dest_size_r, - GError **error_r); - -const int16_t * -pcm_resample_lsr_16(struct pcm_resample_state *state, - unsigned channels, - unsigned src_rate, - const int16_t *src_buffer, size_t src_size, - unsigned dest_rate, size_t *dest_size_r, - GError **error_r); - -const int32_t * -pcm_resample_lsr_32(struct pcm_resample_state *state, - unsigned channels, - unsigned src_rate, - const int32_t *src_buffer, - G_GNUC_UNUSED size_t src_size, - unsigned dest_rate, size_t *dest_size_r, - GError **error_r); - -#endif - -void -pcm_resample_fallback_init(struct pcm_resample_state *state); - -void -pcm_resample_fallback_deinit(struct pcm_resample_state *state); - -const int16_t * -pcm_resample_fallback_16(struct pcm_resample_state *state, - unsigned channels, - unsigned src_rate, - const int16_t *src_buffer, size_t src_size, - unsigned dest_rate, - size_t *dest_size_r); - -const int32_t * -pcm_resample_fallback_32(struct pcm_resample_state *state, - unsigned channels, - unsigned src_rate, - const int32_t *src_buffer, - G_GNUC_UNUSED size_t src_size, - unsigned dest_rate, - size_t *dest_size_r); - -#endif diff --git a/src/pcm_resample_libsamplerate.c b/src/pcm_resample_libsamplerate.c deleted file mode 100644 index f957e5155..000000000 --- a/src/pcm_resample_libsamplerate.c +++ /dev/null @@ -1,312 +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_resample_internal.h" -#include "conf.h" - -#include - -#include -#include -#include - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "pcm" - -static int lsr_converter = SRC_SINC_FASTEST; - -static inline GQuark -libsamplerate_quark(void) -{ - return g_quark_from_static_string("libsamplerate"); -} - -static bool -lsr_parse_converter(const char *s) -{ - assert(s != NULL); - - if (*s == 0) - return true; - - char *endptr; - long l = strtol(s, &endptr, 10); - if (*endptr == 0 && src_get_name(l) != NULL) { - lsr_converter = l; - return true; - } - - size_t length = strlen(s); - for (int i = 0;; ++i) { - const char *name = src_get_name(i); - if (name == NULL) - break; - - if (g_ascii_strncasecmp(s, name, length) == 0) { - lsr_converter = i; - return true; - } - } - - return false; -} - -bool -pcm_resample_lsr_global_init(const char *converter, GError **error_r) -{ - if (!lsr_parse_converter(converter)) { - g_set_error(error_r, libsamplerate_quark(), 0, - "unknown samplerate converter '%s'", converter); - return false; - } - - g_debug("libsamplerate converter '%s'", - src_get_name(lsr_converter)); - - return true; -} - -void -pcm_resample_lsr_init(struct pcm_resample_state *state) -{ - memset(state, 0, sizeof(*state)); - - pcm_buffer_init(&state->in); - pcm_buffer_init(&state->out); - pcm_buffer_init(&state->buffer); -} - -void -pcm_resample_lsr_deinit(struct pcm_resample_state *state) -{ - if (state->state != NULL) - state->state = src_delete(state->state); - - pcm_buffer_deinit(&state->in); - pcm_buffer_deinit(&state->out); - pcm_buffer_deinit(&state->buffer); -} - -void -pcm_resample_lsr_reset(struct pcm_resample_state *state) -{ - if (state->state != NULL) - src_reset(state->state); -} - -static bool -pcm_resample_set(struct pcm_resample_state *state, - unsigned channels, unsigned src_rate, unsigned dest_rate, - GError **error_r) -{ - int error; - SRC_DATA *data = &state->data; - - /* (re)set the state/ratio if the in or out format changed */ - if (channels == state->prev.channels && - src_rate == state->prev.src_rate && - dest_rate == state->prev.dest_rate) - return true; - - state->error = 0; - state->prev.channels = channels; - state->prev.src_rate = src_rate; - state->prev.dest_rate = dest_rate; - - if (state->state) - state->state = src_delete(state->state); - - state->state = src_new(lsr_converter, channels, &error); - if (!state->state) { - g_set_error(error_r, libsamplerate_quark(), state->error, - "libsamplerate initialization has failed: %s", - src_strerror(error)); - return false; - } - - data->src_ratio = (double)dest_rate / (double)src_rate; - g_debug("setting samplerate conversion ratio to %.2lf", - data->src_ratio); - src_set_ratio(state->state, data->src_ratio); - - return true; -} - -static bool -lsr_process(struct pcm_resample_state *state, GError **error_r) -{ - if (state->error == 0) - state->error = src_process(state->state, &state->data); - if (state->error) { - g_set_error(error_r, libsamplerate_quark(), state->error, - "libsamplerate has failed: %s", - src_strerror(state->error)); - return false; - } - - return true; -} - -static float * -deconst_float_buffer(const float *in) -{ - union { - const float *in; - float *out; - } u = { .in = in }; - return u.out; -} - -const float * -pcm_resample_lsr_float(struct pcm_resample_state *state, - unsigned channels, - unsigned src_rate, - const float *src_buffer, size_t src_size, - unsigned dest_rate, size_t *dest_size_r, - GError **error_r) -{ - assert((src_size % (sizeof(*src_buffer) * channels)) == 0); - - if (!pcm_resample_set(state, channels, src_rate, dest_rate, error_r)) - return NULL; - - SRC_DATA *data = &state->data; - data->input_frames = src_size / sizeof(*src_buffer) / channels; - data->data_in = deconst_float_buffer(src_buffer); - - data->output_frames = (src_size * dest_rate + src_rate - 1) / src_rate; - size_t data_out_size = data->output_frames * sizeof(float) * channels; - data->data_out = pcm_buffer_get(&state->out, data_out_size); - - if (!lsr_process(state, error_r)) - return NULL; - - *dest_size_r = data->output_frames_gen * - sizeof(*data->data_out) * channels; - return data->data_out; -} - -const int16_t * -pcm_resample_lsr_16(struct pcm_resample_state *state, - unsigned channels, - unsigned src_rate, - const int16_t *src_buffer, size_t src_size, - unsigned dest_rate, size_t *dest_size_r, - GError **error_r) -{ - bool success; - SRC_DATA *data = &state->data; - size_t data_in_size; - size_t data_out_size; - int16_t *dest_buffer; - - assert((src_size % (sizeof(*src_buffer) * channels)) == 0); - - success = pcm_resample_set(state, channels, src_rate, dest_rate, - error_r); - if (!success) - return NULL; - - data->input_frames = src_size / sizeof(*src_buffer) / channels; - data_in_size = data->input_frames * sizeof(float) * channels; - data->data_in = pcm_buffer_get(&state->in, data_in_size); - - data->output_frames = (src_size * dest_rate + src_rate - 1) / src_rate; - data_out_size = data->output_frames * sizeof(float) * channels; - data->data_out = pcm_buffer_get(&state->out, data_out_size); - - src_short_to_float_array(src_buffer, data->data_in, - data->input_frames * channels); - - if (!lsr_process(state, error_r)) - return NULL; - - *dest_size_r = data->output_frames_gen * - sizeof(*dest_buffer) * channels; - dest_buffer = pcm_buffer_get(&state->buffer, *dest_size_r); - src_float_to_short_array(data->data_out, dest_buffer, - data->output_frames_gen * channels); - - return dest_buffer; -} - -#ifdef HAVE_LIBSAMPLERATE_NOINT - -/* libsamplerate introduced these functions in v0.1.3 */ - -static void -src_int_to_float_array(const int *in, float *out, int len) -{ - while (len-- > 0) - *out++ = *in++ / (float)(1 << (24 - 1)); -} - -static void -src_float_to_int_array (const float *in, int *out, int len) -{ - while (len-- > 0) - *out++ = *in++ * (float)(1 << (24 - 1)); -} - -#endif - -const int32_t * -pcm_resample_lsr_32(struct pcm_resample_state *state, - unsigned channels, - unsigned src_rate, - const int32_t *src_buffer, size_t src_size, - unsigned dest_rate, size_t *dest_size_r, - GError **error_r) -{ - bool success; - SRC_DATA *data = &state->data; - size_t data_in_size; - size_t data_out_size; - int32_t *dest_buffer; - - assert((src_size % (sizeof(*src_buffer) * channels)) == 0); - - success = pcm_resample_set(state, channels, src_rate, dest_rate, - error_r); - if (!success) - return NULL; - - data->input_frames = src_size / sizeof(*src_buffer) / channels; - data_in_size = data->input_frames * sizeof(float) * channels; - data->data_in = pcm_buffer_get(&state->in, data_in_size); - - data->output_frames = (src_size * dest_rate + src_rate - 1) / src_rate; - data_out_size = data->output_frames * sizeof(float) * channels; - data->data_out = pcm_buffer_get(&state->out, data_out_size); - - src_int_to_float_array(src_buffer, data->data_in, - data->input_frames * channels); - - if (!lsr_process(state, error_r)) - return NULL; - - *dest_size_r = data->output_frames_gen * - sizeof(*dest_buffer) * channels; - dest_buffer = pcm_buffer_get(&state->buffer, *dest_size_r); - src_float_to_int_array(data->data_out, dest_buffer, - data->output_frames_gen * channels); - - return dest_buffer; -} diff --git a/test/read_mixer.cxx b/test/read_mixer.cxx index 45344a2f6..1dc0a4f43 100644 --- a/test/read_mixer.cxx +++ b/test/read_mixer.cxx @@ -21,7 +21,7 @@ #include "MixerControl.hxx" #include "MixerList.hxx" #include "FilterRegistry.hxx" -#include "PcmVolume.hxx" +#include "pcm/PcmVolume.hxx" #include "GlobalEvents.hxx" #include "Main.hxx" #include "event/Loop.hxx" diff --git a/test/run_convert.cxx b/test/run_convert.cxx index e66d0be2b..2b0214912 100644 --- a/test/run_convert.cxx +++ b/test/run_convert.cxx @@ -26,7 +26,7 @@ #include "config.h" #include "AudioParser.hxx" #include "audio_format.h" -#include "PcmConvert.hxx" +#include "pcm/PcmConvert.hxx" #include "conf.h" #include "util/fifo_buffer.h" #include "stdbin.h" diff --git a/test/run_filter.cxx b/test/run_filter.cxx index 9ea50ff55..0b8078c1a 100644 --- a/test/run_filter.cxx +++ b/test/run_filter.cxx @@ -24,7 +24,7 @@ #include "audio_format.h" #include "FilterPlugin.hxx" #include "FilterInternal.hxx" -#include "PcmVolume.hxx" +#include "pcm/PcmVolume.hxx" #include "MixerControl.hxx" #include "stdbin.h" diff --git a/test/run_output.cxx b/test/run_output.cxx index 5b6d54a11..e41fe1cad 100644 --- a/test/run_output.cxx +++ b/test/run_output.cxx @@ -27,7 +27,7 @@ #include "IOThread.hxx" #include "fs/Path.hxx" #include "AudioParser.hxx" -#include "PcmConvert.hxx" +#include "pcm/PcmConvert.hxx" extern "C" { #include "output_plugin.h" diff --git a/test/software_volume.cxx b/test/software_volume.cxx index 929932398..fee4eeff3 100644 --- a/test/software_volume.cxx +++ b/test/software_volume.cxx @@ -24,7 +24,7 @@ */ #include "config.h" -#include "PcmVolume.hxx" +#include "pcm/PcmVolume.hxx" #include "AudioParser.hxx" #include "audio_format.h" #include "stdbin.h" diff --git a/test/test_pcm_channels.cxx b/test/test_pcm_channels.cxx index 8e013d5af..1f9d02daf 100644 --- a/test/test_pcm_channels.cxx +++ b/test/test_pcm_channels.cxx @@ -20,8 +20,8 @@ #include "config.h" #include "test_pcm_all.hxx" #include "test_pcm_util.hxx" -#include "PcmChannels.hxx" -#include "pcm_buffer.h" +#include "pcm/PcmChannels.hxx" +#include "pcm/pcm_buffer.h" #include diff --git a/test/test_pcm_dither.cxx b/test/test_pcm_dither.cxx index 2fb976db5..5694c17f8 100644 --- a/test/test_pcm_dither.cxx +++ b/test/test_pcm_dither.cxx @@ -19,7 +19,7 @@ #include "test_pcm_all.hxx" #include "test_pcm_util.hxx" -#include "PcmDither.hxx" +#include "pcm/PcmDither.hxx" #include diff --git a/test/test_pcm_format.cxx b/test/test_pcm_format.cxx index f175a850c..f9b9516c9 100644 --- a/test/test_pcm_format.cxx +++ b/test/test_pcm_format.cxx @@ -20,10 +20,10 @@ #include "config.h" #include "test_pcm_all.hxx" #include "test_pcm_util.hxx" -#include "PcmFormat.hxx" -#include "PcmDither.hxx" -#include "PcmUtils.hxx" -#include "pcm_buffer.h" +#include "pcm/PcmFormat.hxx" +#include "pcm/PcmDither.hxx" +#include "pcm/PcmUtils.hxx" +#include "pcm/pcm_buffer.h" #include "audio_format.h" #include diff --git a/test/test_pcm_mix.cxx b/test/test_pcm_mix.cxx index a6e01d20f..b4d8486bf 100644 --- a/test/test_pcm_mix.cxx +++ b/test/test_pcm_mix.cxx @@ -20,7 +20,7 @@ #include "config.h" #include "test_pcm_all.hxx" #include "test_pcm_util.hxx" -#include "PcmMix.hxx" +#include "pcm/PcmMix.hxx" #include diff --git a/test/test_pcm_pack.cxx b/test/test_pcm_pack.cxx index e23acad58..313948838 100644 --- a/test/test_pcm_pack.cxx +++ b/test/test_pcm_pack.cxx @@ -21,7 +21,7 @@ #include "test_pcm_util.hxx" extern "C" { -#include "pcm_pack.h" +#include "pcm/pcm_pack.h" } #include diff --git a/test/test_pcm_volume.cxx b/test/test_pcm_volume.cxx index 1ab590490..ec3ac23b9 100644 --- a/test/test_pcm_volume.cxx +++ b/test/test_pcm_volume.cxx @@ -18,7 +18,7 @@ */ #include "test_pcm_all.hxx" -#include "PcmVolume.hxx" +#include "pcm/PcmVolume.hxx" #include "test_pcm_util.hxx" #include -- cgit v1.2.3