From da29298d4dbe8dddade7cf241dd1af267470f2a8 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Sat, 28 Dec 2013 17:42:03 +0100 Subject: pcm/PcmMix: improved dithering Use the existing PcmDither library. --- src/OutputInternal.hxx | 6 ++++++ src/OutputThread.cxx | 2 +- src/pcm/PcmMix.cxx | 45 ++++++++++++++++++++++++++++----------------- src/pcm/PcmMix.hxx | 4 +++- test/test_pcm_mix.cxx | 16 ++++++++++------ 5 files changed, 48 insertions(+), 25 deletions(-) diff --git a/src/OutputInternal.hxx b/src/OutputInternal.hxx index f05073bd7..ab1c1242e 100644 --- a/src/OutputInternal.hxx +++ b/src/OutputInternal.hxx @@ -22,6 +22,7 @@ #include "AudioFormat.hxx" #include "pcm/PcmBuffer.hxx" +#include "pcm/PcmDither.hxx" #include "thread/Mutex.hxx" #include "thread/Cond.hxx" #include "thread/Thread.hxx" @@ -171,6 +172,11 @@ struct audio_output { */ PcmBuffer cross_fade_buffer; + /** + * The dithering state for cross-fading two streams. + */ + PcmDither cross_fade_dither; + /** * The filter object of this audio output. This is an * instance of chain_filter_plugin. diff --git a/src/OutputThread.cxx b/src/OutputThread.cxx index 1a8c70196..62833e360 100644 --- a/src/OutputThread.cxx +++ b/src/OutputThread.cxx @@ -392,7 +392,7 @@ ao_filter_chunk(struct audio_output *ao, const struct music_chunk *chunk, void *dest = ao->cross_fade_buffer.Get(other_length); memcpy(dest, other_data, other_length); - if (!pcm_mix(dest, data, length, + if (!pcm_mix(ao->cross_fade_dither, dest, data, length, ao->in_audio_format.format, 1.0 - chunk->mix_ratio)) { FormatError(output_domain, diff --git a/src/pcm/PcmMix.cxx b/src/pcm/PcmMix.cxx index caf7fe516..c1ffd8633 100644 --- a/src/pcm/PcmMix.cxx +++ b/src/pcm/PcmMix.cxx @@ -25,41 +25,47 @@ #include "Traits.hxx" #include "util/Clamp.hxx" +#include "PcmDither.cxx" // including the .cxx file to get inlined templates + #include #include template> static typename Traits::value_type -PcmAddVolume(typename Traits::value_type _a, typename Traits::value_type _b, +PcmAddVolume(PcmDither &dither, + typename Traits::value_type _a, typename Traits::value_type _b, int volume1, int volume2) { typename Traits::long_type a(_a), b(_b); + typename Traits::long_type c(a * volume1 + b * volume2); - typename Traits::value_type c = ((a * volume1 + b * volume2) + - pcm_volume_dither() + PCM_VOLUME_1S / 2) - / PCM_VOLUME_1S; - - return PcmClamp(c); + return dither.DitherShift(c); } template> static void -PcmAddVolume(typename Traits::pointer_type a, +PcmAddVolume(PcmDither &dither, + typename Traits::pointer_type a, typename Traits::const_pointer_type b, size_t n, int volume1, int volume2) { for (size_t i = 0; i != n; ++i) - a[i] = PcmAddVolume(a[i], b[i], volume1, volume2); + a[i] = PcmAddVolume(dither, a[i], b[i], + volume1, volume2); } template> static void -PcmAddVolumeVoid(void *a, const void *b, size_t size, int volume1, int volume2) +PcmAddVolumeVoid(PcmDither &dither, + void *a, const void *b, size_t size, int volume1, int volume2) { constexpr size_t sample_size = Traits::SAMPLE_SIZE; assert(size % sample_size == 0); - PcmAddVolume(typename Traits::pointer_type(a), + PcmAddVolume(dither, + typename Traits::pointer_type(a), typename Traits::const_pointer_type(b), size / sample_size, volume1, volume2); @@ -80,7 +86,7 @@ pcm_add_vol_float(float *buffer1, const float *buffer2, } static bool -pcm_add_vol(void *buffer1, const void *buffer2, size_t size, +pcm_add_vol(PcmDither &dither, void *buffer1, const void *buffer2, size_t size, int vol1, int vol2, SampleFormat format) { @@ -91,22 +97,26 @@ pcm_add_vol(void *buffer1, const void *buffer2, size_t size, return false; case SampleFormat::S8: - PcmAddVolumeVoid(buffer1, buffer2, size, + PcmAddVolumeVoid(dither, + buffer1, buffer2, size, vol1, vol2); return true; case SampleFormat::S16: - PcmAddVolumeVoid(buffer1, buffer2, size, + PcmAddVolumeVoid(dither, + buffer1, buffer2, size, vol1, vol2); return true; case SampleFormat::S24_P32: - PcmAddVolumeVoid(buffer1, buffer2, size, + PcmAddVolumeVoid(dither, + buffer1, buffer2, size, vol1, vol2); return true; case SampleFormat::S32: - PcmAddVolumeVoid(buffer1, buffer2, size, + PcmAddVolumeVoid(dither, + buffer1, buffer2, size, vol1, vol2); return true; @@ -201,7 +211,7 @@ pcm_add(void *buffer1, const void *buffer2, size_t size, } bool -pcm_mix(void *buffer1, const void *buffer2, size_t size, +pcm_mix(PcmDither &dither, void *buffer1, const void *buffer2, size_t size, SampleFormat format, float portion1) { float s; @@ -217,5 +227,6 @@ pcm_mix(void *buffer1, const void *buffer2, size_t size, int vol1 = s * PCM_VOLUME_1S + 0.5; vol1 = Clamp(vol1, 0, PCM_VOLUME_1S); - return pcm_add_vol(buffer1, buffer2, size, vol1, PCM_VOLUME_1S - vol1, format); + return pcm_add_vol(dither, buffer1, buffer2, size, + vol1, PCM_VOLUME_1S - vol1, format); } diff --git a/src/pcm/PcmMix.hxx b/src/pcm/PcmMix.hxx index 637c88f8a..a6657a979 100644 --- a/src/pcm/PcmMix.hxx +++ b/src/pcm/PcmMix.hxx @@ -25,6 +25,8 @@ #include +class PcmDither; + /* * Linearly mixes two PCM buffers. Both must have the same length and * the same audio format. The formula is: @@ -44,7 +46,7 @@ */ gcc_warn_unused_result bool -pcm_mix(void *buffer1, const void *buffer2, size_t size, +pcm_mix(PcmDither &dither, void *buffer1, const void *buffer2, size_t size, SampleFormat format, float portion1); #endif diff --git a/test/test_pcm_mix.cxx b/test/test_pcm_mix.cxx index 2a8a11388..542c4de80 100644 --- a/test/test_pcm_mix.cxx +++ b/test/test_pcm_mix.cxx @@ -21,6 +21,7 @@ #include "test_pcm_all.hxx" #include "test_pcm_util.hxx" #include "pcm/PcmMix.hxx" +#include "pcm/PcmDither.hxx" template> static void @@ -30,23 +31,26 @@ TestPcmMix(G g=G()) const auto src1 = TestDataBuffer(g); const auto src2 = TestDataBuffer(g); + PcmDither dither; + /* portion1=1.0: result must be equal to src1 */ auto result = src1; - bool success = pcm_mix(result.begin(), src2.begin(), sizeof(result), + bool success = pcm_mix(dither, + result.begin(), src2.begin(), sizeof(result), format, 1.0); CPPUNIT_ASSERT(success); - AssertEqualWithTolerance(result, src1, 1); + AssertEqualWithTolerance(result, src1, 3); /* portion1=0.0: result must be equal to src2 */ result = src1; - success = pcm_mix(result.begin(), src2.begin(), sizeof(result), + success = pcm_mix(dither, result.begin(), src2.begin(), sizeof(result), format, 0.0); CPPUNIT_ASSERT(success); - AssertEqualWithTolerance(result, src2, 1); + AssertEqualWithTolerance(result, src2, 3); /* portion1=0.5 */ result = src1; - success = pcm_mix(result.begin(), src2.begin(), sizeof(result), + success = pcm_mix(dither, result.begin(), src2.begin(), sizeof(result), format, 0.5); CPPUNIT_ASSERT(success); @@ -54,7 +58,7 @@ TestPcmMix(G g=G()) for (unsigned i = 0; i < N; ++i) expected[i] = (int64_t(src1[i]) + int64_t(src2[i])) / 2; - AssertEqualWithTolerance(result, expected, 1); + AssertEqualWithTolerance(result, expected, 3); } void -- cgit v1.2.3