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: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1729>
This commit is contained in:
Sebastian Dröge 2022-02-16 13:28:52 +02:00 committed by GStreamer Marge Bot
parent d6ec88c775
commit e119cdee3b

View file

@ -54,6 +54,7 @@ struct _GstAudioQuantize
/* last random number generated per channel for hifreq TPDF dither */ /* last random number generated per channel for hifreq TPDF dither */
gpointer last_random; gpointer last_random;
guint32 random_state;
/* contains the past quantization errors, error[channels][count] */ /* contains the past quantization errors, error[channels][count] */
guint error_size; guint error_size;
gpointer error_buf; gpointer error_buf;
@ -92,27 +93,28 @@ gst_audio_quantize_quantize_int_none_none (GstAudioQuantize * quant,
samples * quant->stride); samples * quant->stride);
} }
/* This is the base function, implementing a linear congruential generator /* 32 bit xorshift PRNG, see https://en.wikipedia.org/wiki/Xorshift */
* and returning a pseudo random number between 0 and 2^32 - 1.
*/
static inline guint32 static inline guint32
gst_fast_random_uint32 (void) gst_fast_random_uint32 (guint32 * state)
{ {
static guint32 state = 0xdeadbeef; guint64 x = *state;
return (state = state * 1103515245 + 12345); x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
return (*state = x);
} }
static inline gint32 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, /* Assuming dither == 2^n,
* returns one of 2^(n+1) possible random values: * returns one of 2^(n+1) possible random values:
* -dither <= retval < dither */ * -dither <= retval < dither */
#define RANDOM_INT_DITHER(dither) \ #define RANDOM_INT_DITHER(state, dither) \
(- dither + (gst_fast_random_int32 () & ((dither << 1) - 1))) (- dither + (gst_fast_random_int32 (state) & ((dither << 1) - 1)))
static void static void
setup_dither_buf (GstAudioQuantize * quant, gint samples) setup_dither_buf (GstAudioQuantize * quant, gint samples)
@ -144,13 +146,15 @@ setup_dither_buf (GstAudioQuantize * quant, gint samples)
case GST_AUDIO_DITHER_RPDF: case GST_AUDIO_DITHER_RPDF:
dither = 1 << (shift); dither = 1 << (shift);
for (i = 0; i < len; i++) for (i = 0; i < len; i++)
d[i] = bias + RANDOM_INT_DITHER (dither); d[i] = bias + RANDOM_INT_DITHER (&quant->random_state, dither);
break; break;
case GST_AUDIO_DITHER_TPDF: case GST_AUDIO_DITHER_TPDF:
dither = 1 << (shift - 1); dither = 1 << (shift - 1);
for (i = 0; i < len; i++) 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; break;
case GST_AUDIO_DITHER_TPDF_HF: case GST_AUDIO_DITHER_TPDF_HF:
@ -159,7 +163,7 @@ setup_dither_buf (GstAudioQuantize * quant, gint samples)
dither = 1 << (shift - 1); dither = 1 << (shift - 1);
for (i = 0; i < len; i++) { 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]; d[i] = bias + tmp - last_random[i % stride];
last_random[i % stride] = tmp; last_random[i % stride] = tmp;
} }
@ -369,6 +373,9 @@ gst_audio_quantize_setup_noise_shaping (GstAudioQuantize * quant)
static void static void
gst_audio_quantize_setup_dither (GstAudioQuantize * quant) gst_audio_quantize_setup_dither (GstAudioQuantize * quant)
{ {
/* Some non-zero number */
quant->random_state = 0xc2d6038f;
switch (quant->dither) { switch (quant->dither) {
case GST_AUDIO_DITHER_TPDF_HF: case GST_AUDIO_DITHER_TPDF_HF:
quant->last_random = g_new0 (gint32, quant->stride); quant->last_random = g_new0 (gint32, quant->stride);