From e119cdee3b0c0f093d20e5d4ca95badaad6bec9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 16 Feb 2022 13:28:52 +0200 Subject: [PATCH] audio-quantize: Switch dither PRNG from LCG to xorshift While this is slightly more expensive (~48% slower per random number) it does not cause any measurable difference when running through a complete audio conversion pipeline. On the other hand its random numbers are of much higher quality and on spectrograms for 32 bit to 24 bit conversion the difference is clearly visible. Part-of: --- .../gst-libs/gst/audio/audio-quantize.c | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/subprojects/gst-plugins-base/gst-libs/gst/audio/audio-quantize.c b/subprojects/gst-plugins-base/gst-libs/gst/audio/audio-quantize.c index 8c627173bc..e4c33ba6a1 100644 --- a/subprojects/gst-plugins-base/gst-libs/gst/audio/audio-quantize.c +++ b/subprojects/gst-plugins-base/gst-libs/gst/audio/audio-quantize.c @@ -54,6 +54,7 @@ struct _GstAudioQuantize /* last random number generated per channel for hifreq TPDF dither */ gpointer last_random; + guint32 random_state; /* contains the past quantization errors, error[channels][count] */ guint error_size; gpointer error_buf; @@ -92,27 +93,28 @@ gst_audio_quantize_quantize_int_none_none (GstAudioQuantize * quant, samples * quant->stride); } -/* This is the base function, implementing a linear congruential generator - * and returning a pseudo random number between 0 and 2^32 - 1. - */ +/* 32 bit xorshift PRNG, see https://en.wikipedia.org/wiki/Xorshift */ static inline guint32 -gst_fast_random_uint32 (void) +gst_fast_random_uint32 (guint32 * state) { - static guint32 state = 0xdeadbeef; - return (state = state * 1103515245 + 12345); + guint64 x = *state; + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + return (*state = x); } static inline gint32 -gst_fast_random_int32 (void) +gst_fast_random_int32 (guint32 * state) { - return (gint32) gst_fast_random_uint32 (); + return (gint32) gst_fast_random_uint32 (state); } /* Assuming dither == 2^n, * returns one of 2^(n+1) possible random values: * -dither <= retval < dither */ -#define RANDOM_INT_DITHER(dither) \ - (- dither + (gst_fast_random_int32 () & ((dither << 1) - 1))) +#define RANDOM_INT_DITHER(state, dither) \ + (- dither + (gst_fast_random_int32 (state) & ((dither << 1) - 1))) static void setup_dither_buf (GstAudioQuantize * quant, gint samples) @@ -144,13 +146,15 @@ setup_dither_buf (GstAudioQuantize * quant, gint samples) case GST_AUDIO_DITHER_RPDF: dither = 1 << (shift); for (i = 0; i < len; i++) - d[i] = bias + RANDOM_INT_DITHER (dither); + d[i] = bias + RANDOM_INT_DITHER (&quant->random_state, dither); break; case GST_AUDIO_DITHER_TPDF: dither = 1 << (shift - 1); for (i = 0; i < len; i++) - d[i] = bias + RANDOM_INT_DITHER (dither) + RANDOM_INT_DITHER (dither); + d[i] = + bias + RANDOM_INT_DITHER (&quant->random_state, + dither) + RANDOM_INT_DITHER (&quant->random_state, dither); break; case GST_AUDIO_DITHER_TPDF_HF: @@ -159,7 +163,7 @@ setup_dither_buf (GstAudioQuantize * quant, gint samples) dither = 1 << (shift - 1); for (i = 0; i < len; i++) { - tmp = RANDOM_INT_DITHER (dither); + tmp = RANDOM_INT_DITHER (&quant->random_state, dither); d[i] = bias + tmp - last_random[i % stride]; last_random[i % stride] = tmp; } @@ -369,6 +373,9 @@ gst_audio_quantize_setup_noise_shaping (GstAudioQuantize * quant) static void gst_audio_quantize_setup_dither (GstAudioQuantize * quant) { + /* Some non-zero number */ + quant->random_state = 0xc2d6038f; + switch (quant->dither) { case GST_AUDIO_DITHER_TPDF_HF: quant->last_random = g_new0 (gint32, quant->stride);