Coverage Report

Created: 2025-11-16 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gstreamer/subprojects/gst-plugins-base/gst-libs/gst/audio/audio-quantize.c
Line
Count
Source
1
/* GStreamer
2
 * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
3
 *           (C) 2015 Wim Taymans <wim.taymans@gmail.com>
4
 *
5
 * gstaudioquantize.c: quantizes audio to the target format and optionally
6
 *                     applies dithering and noise shaping.
7
 *
8
 * This library is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU Library General Public
10
 * License as published by the Free Software Foundation; either
11
 * version 2 of the License, or (at your option) any later version.
12
 *
13
 * This library is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
 * Library General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Library General Public
19
 * License along with this library; if not, write to the
20
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21
 * Boston, MA 02110-1301, USA.
22
 */
23
24
/* TODO: - Maybe drop 5-pole noise shaping and use coefficients
25
 *         generated by dmaker
26
 *         http://shibatch.sf.net
27
 */
28
#ifdef HAVE_CONFIG_H
29
#include "config.h"
30
#endif
31
32
#include <gst/gst.h>
33
#include <string.h>
34
#include <math.h>
35
36
#include "gstaudiopack.h"
37
#include "audio-quantize.h"
38
39
typedef void (*QuantizeFunc) (GstAudioQuantize * quant, const gpointer src,
40
    gpointer dst, gint count);
41
42
struct _GstAudioQuantize
43
{
44
  GstAudioDitherMethod dither;
45
  GstAudioNoiseShapingMethod ns;
46
  GstAudioQuantizeFlags flags;
47
  GstAudioFormat format;
48
  guint quantizer;
49
  guint stride;
50
  guint blocks;
51
52
  guint shift;
53
  guint32 mask, bias;
54
55
  /* last random number generated per channel for hifreq TPDF dither */
56
  gpointer last_random;
57
  guint32 random_state;
58
  /* contains the past quantization errors, error[channels][count] */
59
  guint error_size;
60
  gpointer error_buf;
61
  /* buffer with dither values */
62
  guint dither_size;
63
  gpointer dither_buf;
64
  /* noise shaping coefficients */
65
  gpointer coeffs;
66
  gint n_coeffs;
67
68
  QuantizeFunc quantize;
69
};
70
71
#define ADDSS(res,val) \
72
0
        if (val > 0 && res > 0 && G_MAXINT32 - res <= val){             \
73
0
          res = G_MAXINT32;                                             \
74
0
        } else if (val < 0 && res < 0 && G_MININT32 - res >= val){      \
75
0
          res = G_MININT32;                                             \
76
0
        } else                                                          \
77
0
          res += val;
78
79
static void
80
gst_audio_quantize_quantize_memcpy (GstAudioQuantize * quant,
81
    const gpointer src, gpointer dst, gint samples)
82
0
{
83
0
  if (src != dst)
84
0
    memcpy (dst, src, samples * sizeof (gint32) * quant->stride);
85
0
}
86
87
/* Quantize functions for gint32 as intermediate format */
88
static void
89
gst_audio_quantize_quantize_int_none_none (GstAudioQuantize * quant,
90
    const gpointer src, gpointer dst, gint samples)
91
0
{
92
0
  audio_orc_int_bias (dst, src, quant->bias, ~quant->mask,
93
0
      samples * quant->stride);
94
0
}
95
96
/* 32 bit xorshift PRNG, see https://en.wikipedia.org/wiki/Xorshift */
97
static inline guint32
98
gst_fast_random_uint32 (guint32 * state)
99
0
{
100
0
  guint64 x = *state;
101
0
  x ^= x << 13;
102
0
  x ^= x >> 17;
103
0
  x ^= x << 5;
104
0
  return (*state = x);
105
0
}
106
107
static inline gint32
108
gst_fast_random_int32 (guint32 * state)
109
0
{
110
0
  return (gint32) gst_fast_random_uint32 (state);
111
0
}
112
113
/* Assuming dither == 2^n,
114
 * returns one of 2^(n+1) possible random values:
115
 * -dither <= retval < dither */
116
#define RANDOM_INT_DITHER(state, dither)                                       \
117
0
  (- dither + (gst_fast_random_int32 (state) & ((dither << 1) - 1)))
118
119
static void
120
setup_dither_buf (GstAudioQuantize * quant, gint samples)
121
0
{
122
0
  gboolean need_init = FALSE;
123
0
  gint stride = quant->stride;
124
0
  gint i, len = samples * stride;
125
0
  guint shift = quant->shift;
126
0
  guint32 bias;
127
0
  gint32 dither, *d;
128
129
0
  if (quant->dither_size < len) {
130
0
    quant->dither_size = len;
131
0
    quant->dither_buf = g_realloc (quant->dither_buf, len * sizeof (gint32));
132
0
    need_init = TRUE;
133
0
  }
134
135
0
  bias = quant->bias;
136
0
  d = quant->dither_buf;
137
138
0
  switch (quant->dither) {
139
0
    case GST_AUDIO_DITHER_NONE:
140
0
      if (need_init) {
141
0
        for (i = 0; i < len; i++)
142
0
          d[i] = 0;
143
0
      }
144
0
      break;
145
146
0
    case GST_AUDIO_DITHER_RPDF:
147
0
      dither = 1 << (shift);
148
0
      for (i = 0; i < len; i++)
149
0
        d[i] = bias + RANDOM_INT_DITHER (&quant->random_state, dither);
150
0
      break;
151
152
0
    case GST_AUDIO_DITHER_TPDF:
153
0
      dither = 1 << (shift - 1);
154
0
      for (i = 0; i < len; i++)
155
0
        d[i] =
156
0
            bias + RANDOM_INT_DITHER (&quant->random_state,
157
0
            dither) + RANDOM_INT_DITHER (&quant->random_state, dither);
158
0
      break;
159
160
0
    case GST_AUDIO_DITHER_TPDF_HF:
161
0
    {
162
0
      gint32 tmp, *last_random = quant->last_random;
163
164
0
      dither = 1 << (shift - 1);
165
0
      for (i = 0; i < len; i++) {
166
0
        tmp = RANDOM_INT_DITHER (&quant->random_state, dither);
167
0
        d[i] = bias + tmp - last_random[i % stride];
168
0
        last_random[i % stride] = tmp;
169
0
      }
170
0
      break;
171
0
    }
172
0
  }
173
0
}
174
175
static void
176
gst_audio_quantize_quantize_int_dither_none (GstAudioQuantize * quant,
177
    const gpointer src, gpointer dst, gint samples)
178
0
{
179
0
  setup_dither_buf (quant, samples);
180
181
0
  audio_orc_int_dither (dst, src, quant->dither_buf, ~quant->mask,
182
0
      samples * quant->stride);
183
0
}
184
185
static void
186
setup_error_buf (GstAudioQuantize * quant, gint samples, gint extra)
187
0
{
188
0
  gint stride = quant->stride;
189
0
  gint len = (samples + extra) * stride;
190
191
0
  if (quant->error_size < len) {
192
0
    quant->error_buf = g_realloc (quant->error_buf, len * sizeof (gint32));
193
0
    if (quant->error_size == 0)
194
0
      memset ((gint32 *) quant->error_buf, 0, stride * extra * sizeof (gint32));
195
0
    quant->error_size = len;
196
0
  }
197
0
}
198
199
static void
200
gst_audio_quantize_quantize_int_dither_feedback (GstAudioQuantize * quant,
201
    const gpointer src, gpointer dst, gint samples)
202
0
{
203
0
  guint32 mask;
204
0
  gint i, len, stride;
205
0
  const gint32 *s = src;
206
0
  gint32 *dith, *d = dst, v, o, *e, err;
207
208
0
  setup_dither_buf (quant, samples);
209
0
  setup_error_buf (quant, samples, 1);
210
211
0
  stride = quant->stride;
212
0
  len = samples * stride;
213
0
  dith = quant->dither_buf;
214
0
  e = quant->error_buf;
215
0
  mask = ~quant->mask;
216
217
0
  for (i = 0; i < len; i++) {
218
0
    o = v = s[i];
219
    /* add dither */
220
0
    err = dith[i];
221
    /* remove error */
222
0
    err -= e[i];
223
0
    ADDSS (v, err);
224
0
    v &= mask;
225
    /* store new error */
226
0
    e[i + stride] = e[i] + (v - o);
227
    /* store result */
228
0
    d[i] = v;
229
0
  }
230
0
  memmove (e, &e[len], sizeof (gint32) * stride);
231
0
}
232
233
0
#define SHIFT 10
234
0
#define REDUCE 8
235
0
#define RROUND (1<<(REDUCE-1))
236
0
#define SREDUCE 2
237
0
#define SROUND (1<<(SREDUCE-1))
238
239
static void
240
gst_audio_quantize_quantize_int_dither_noise_shape (GstAudioQuantize * quant,
241
    const gpointer src, gpointer dst, gint samples)
242
0
{
243
0
  guint32 mask;
244
0
  gint i, j, k, len, stride, nc;
245
0
  const gint32 *s = src;
246
0
  gint32 *c, *dith, *d = dst, v, o, *e, err;
247
248
0
  nc = quant->n_coeffs;
249
250
0
  setup_dither_buf (quant, samples);
251
0
  setup_error_buf (quant, samples, nc);
252
253
0
  stride = quant->stride;
254
0
  len = samples * stride;
255
0
  dith = quant->dither_buf;
256
0
  e = quant->error_buf;
257
0
  c = quant->coeffs;
258
0
  mask = ~quant->mask;
259
260
0
  for (i = 0; i < len; i++) {
261
0
    v = s[i];
262
    /* combine and remove error */
263
0
    err = 0;
264
0
    for (j = 0, k = i; j < nc; j++, k += stride)
265
0
      err -= e[k] * c[j];
266
0
    err = (err + SROUND) >> (SREDUCE);
267
0
    ADDSS (v, err);
268
0
    o = v;
269
    /* add dither */
270
0
    err = dith[i];
271
0
    ADDSS (v, err);
272
    /* quantize */
273
0
    v &= mask;
274
    /* store new error with reduced precision */
275
0
    e[k] = (v - o + RROUND) >> REDUCE;
276
    /* store result */
277
0
    d[i] = v;
278
0
  }
279
0
  memmove (e, &e[len], sizeof (gint32) * stride * nc);
280
0
}
281
282
0
#define MAKE_QUANTIZE_FUNC_NAME(name)                                   \
283
0
gst_audio_quantize_quantize_##name
284
285
static const QuantizeFunc quantize_funcs[] = {
286
  (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_none_none),
287
  (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_feedback),
288
  (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_noise_shape),
289
  (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_noise_shape),
290
  (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_noise_shape),
291
  (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_none),
292
  (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_feedback),
293
  (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_noise_shape),
294
  (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_noise_shape),
295
  (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_noise_shape),
296
  (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_none),
297
  (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_feedback),
298
  (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_noise_shape),
299
  (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_noise_shape),
300
  (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_noise_shape),
301
  (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_none),
302
  (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_feedback),
303
  (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_noise_shape),
304
  (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_noise_shape),
305
  (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_noise_shape),
306
};
307
308
/* Same as error feedback but also add 1/2 of the previous error value.
309
 * This moves the noise a bit more into the higher frequencies. */
310
static const gdouble ns_simple_coeffs[] = {
311
  -0.5, 1.0
312
};
313
314
/* Noise shaping coefficients from[1], moves most power of the
315
 * error noise into inaudible frequency ranges.
316
 *
317
 * [1]
318
 * "Minimally Audible Noise Shaping", Stanley P. Lipshitz,
319
 * John Vanderkooy, and Robert A. Wannamaker,
320
 * J. Audio Eng. Soc., Vol. 39, No. 11, November 1991. */
321
322
static const gdouble ns_medium_coeffs[] = {
323
  0.6149, -1.590, 1.959, -2.165, 2.033
324
};
325
326
/* Noise shaping coefficients by David Schleef, moves most power of the
327
 * error noise into inaudible frequency ranges */
328
static const gdouble ns_high_coeffs[] = {
329
  -0.340122, 0.876066, -1.72008, 2.61339, -3.31399, 3.27918, -2.92975, 2.08484,
330
};
331
332
333
static void
334
gst_audio_quantize_setup_noise_shaping (GstAudioQuantize * quant)
335
0
{
336
0
  gint i, n_coeffs = 0;
337
0
  gint32 *q;
338
0
  const gdouble *coeffs;
339
340
0
  switch (quant->ns) {
341
0
    case GST_AUDIO_NOISE_SHAPING_HIGH:
342
0
      n_coeffs = 8;
343
0
      coeffs = ns_high_coeffs;
344
0
      break;
345
346
0
    case GST_AUDIO_NOISE_SHAPING_MEDIUM:
347
0
      n_coeffs = 5;
348
0
      coeffs = ns_medium_coeffs;
349
0
      break;
350
351
0
    case GST_AUDIO_NOISE_SHAPING_SIMPLE:
352
0
      n_coeffs = 2;
353
0
      coeffs = ns_simple_coeffs;
354
0
      break;
355
356
0
    case GST_AUDIO_NOISE_SHAPING_ERROR_FEEDBACK:
357
0
      break;
358
359
0
    case GST_AUDIO_NOISE_SHAPING_NONE:
360
0
    default:
361
0
      break;
362
0
  }
363
364
0
  if (n_coeffs) {
365
0
    quant->n_coeffs = n_coeffs;
366
0
    q = quant->coeffs = g_new0 (gint32, n_coeffs);
367
0
    for (i = 0; i < n_coeffs; i++)
368
0
      q[i] = floor (coeffs[i] * (1 << SHIFT) + 0.5);
369
0
  }
370
0
  return;
371
0
}
372
373
static void
374
gst_audio_quantize_setup_dither (GstAudioQuantize * quant)
375
0
{
376
  /* Some non-zero number */
377
0
  quant->random_state = 0xc2d6038f;
378
379
0
  switch (quant->dither) {
380
0
    case GST_AUDIO_DITHER_TPDF_HF:
381
0
      quant->last_random = g_new0 (gint32, quant->stride);
382
0
      break;
383
0
    case GST_AUDIO_DITHER_RPDF:
384
0
    case GST_AUDIO_DITHER_TPDF:
385
0
      quant->last_random = NULL;
386
0
      break;
387
0
    case GST_AUDIO_DITHER_NONE:
388
0
    default:
389
0
      quant->last_random = NULL;
390
0
      break;
391
0
  }
392
0
  return;
393
0
}
394
395
static void
396
gst_audio_quantize_setup_quantize_func (GstAudioQuantize * quant)
397
0
{
398
0
  gint index;
399
400
0
  if (quant->shift == 0) {
401
0
    quant->quantize = (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (memcpy);
402
0
    return;
403
0
  }
404
405
0
  index = 5 * quant->dither + quant->ns;
406
0
  quant->quantize = quantize_funcs[index];
407
0
}
408
409
static gint
410
count_power (guint v)
411
0
{
412
0
  gint res = 0;
413
0
  while (v > 1) {
414
0
    res++;
415
0
    v >>= 1;
416
0
  }
417
0
  return res;
418
0
}
419
420
/**
421
 * gst_audio_quantize_new: (constructor) (skip):
422
 * @dither: a #GstAudioDitherMethod
423
 * @ns: a #GstAudioNoiseShapingMethod
424
 * @flags: #GstAudioQuantizeFlags
425
 * @format: the #GstAudioFormat of the samples
426
 * @channels: the amount of channels in the samples
427
 * @quantizer: the quantizer to use
428
 *
429
 * Create a new quantizer object with the given parameters.
430
 *
431
 * Output samples will be quantized to a multiple of @quantizer. Better
432
 * performance is achieved when @quantizer is a power of 2.
433
 *
434
 * Dithering and noise-shaping can be performed during quantization with
435
 * the @dither and @ns parameters.
436
 *
437
 * Returns: a new #GstAudioQuantize. Free with gst_audio_quantize_free().
438
 */
439
GstAudioQuantize *
440
gst_audio_quantize_new (GstAudioDitherMethod dither,
441
    GstAudioNoiseShapingMethod ns, GstAudioQuantizeFlags flags,
442
    GstAudioFormat format, guint channels, guint quantizer)
443
0
{
444
0
  GstAudioQuantize *quant;
445
446
0
  g_return_val_if_fail (format == GST_AUDIO_FORMAT_S32, NULL);
447
0
  g_return_val_if_fail (channels > 0, NULL);
448
449
0
  quant = g_new0 (GstAudioQuantize, 1);
450
0
  quant->dither = dither;
451
0
  quant->ns = ns;
452
0
  quant->flags = flags;
453
0
  quant->format = format;
454
0
  if (flags & GST_AUDIO_QUANTIZE_FLAG_NON_INTERLEAVED) {
455
0
    quant->stride = 1;
456
0
    quant->blocks = channels;
457
0
  } else {
458
0
    quant->stride = channels;
459
0
    quant->blocks = 1;
460
0
  }
461
0
  quant->quantizer = quantizer;
462
463
0
  quant->shift = count_power (quantizer);
464
0
  if (quant->shift > 0)
465
0
    quant->bias = (1U << (quant->shift - 1));
466
0
  else
467
0
    quant->bias = 0;
468
0
  quant->mask = (1U << quant->shift) - 1;
469
470
0
  gst_audio_quantize_setup_dither (quant);
471
0
  gst_audio_quantize_setup_noise_shaping (quant);
472
0
  gst_audio_quantize_setup_quantize_func (quant);
473
474
0
  return quant;
475
0
}
476
477
/**
478
 * gst_audio_quantize_free:
479
 * @quant: a #GstAudioQuantize
480
 *
481
 * Free a #GstAudioQuantize.
482
 */
483
void
484
gst_audio_quantize_free (GstAudioQuantize * quant)
485
0
{
486
0
  g_return_if_fail (quant != NULL);
487
488
0
  g_free (quant->error_buf);
489
0
  g_free (quant->coeffs);
490
0
  g_free (quant->last_random);
491
0
  g_free (quant->dither_buf);
492
493
0
  g_free (quant);
494
0
}
495
496
/**
497
 * gst_audio_quantize_reset:
498
 * @quant: a #GstAudioQuantize
499
 *
500
 * Reset @quant to the state is was when created, clearing any
501
 * history it might have.
502
 */
503
void
504
gst_audio_quantize_reset (GstAudioQuantize * quant)
505
0
{
506
0
  g_free (quant->error_buf);
507
0
  quant->error_buf = NULL;
508
0
  quant->error_size = 0;
509
0
}
510
511
/**
512
 * gst_audio_quantize_samples:
513
 * @quant: a #GstAudioQuantize
514
 * @in: (array) (element-type gpointer): input samples
515
 * @out: (array) (element-type gpointer): output samples
516
 * @samples: number of samples
517
 *
518
 * Perform quantization on @samples in @in and write the result to @out.
519
 *
520
 * In case the samples are interleaved, @in and @out must point to an
521
 * array with a single element pointing to a block of interleaved samples.
522
 *
523
 * If non-interleaved samples are used, @in and @out must point to an
524
 * array with pointers to memory blocks, one for each channel.
525
 *
526
 * @in and @out may point to the same memory location, in which case samples will be
527
 * modified in-place.
528
 */
529
void
530
gst_audio_quantize_samples (GstAudioQuantize * quant,
531
    const gpointer in[], gpointer out[], guint samples)
532
0
{
533
0
  guint i;
534
535
0
  g_return_if_fail (quant != NULL);
536
0
  g_return_if_fail (out != NULL || samples == 0);
537
0
  g_return_if_fail (in != NULL || samples == 0);
538
539
0
  for (i = 0; i < quant->blocks; i++)
540
0
    quant->quantize (quant, in[i], out[i], samples);
541
0
}