Coverage Report

Created: 2026-05-16 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gstreamer/subprojects/gst-plugins-base/gst-libs/gst/audio/audio-converter.c
Line
Count
Source
1
/* GStreamer
2
 * Copyright (C) 2005 Wim Taymans <wim at fluendo dot com>
3
 *           (C) 2015 Wim Taymans <wim.taymans@gmail.com>
4
 *
5
 * audioconverter.c: Convert audio to different audio formats automatically
6
 *
7
 * This library is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Library General Public
9
 * License as published by the Free Software Foundation; either
10
 * version 2 of the License, or (at your option) any later version.
11
 *
12
 * This library is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Library General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Library General Public
18
 * License along with this library; if not, write to the
19
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20
 * Boston, MA 02110-1301, USA.
21
 */
22
23
#ifdef HAVE_CONFIG_H
24
#include "config.h"
25
#endif
26
27
#include <math.h>
28
#include <string.h>
29
30
#include "audio-converter.h"
31
#include "gstaudiopack.h"
32
33
/**
34
 * SECTION:gstaudioconverter
35
 * @title: GstAudioConverter
36
 * @short_description: Generic audio conversion
37
 *
38
 * This object is used to convert audio samples from one format to another.
39
 * The object can perform conversion of:
40
 *
41
 *  * audio format with optional dithering and noise shaping
42
 *
43
 *  * audio samplerate
44
 *
45
 *  * audio channels and channel layout
46
 *
47
 */
48
49
#ifndef GST_DISABLE_GST_DEBUG
50
#define GST_CAT_DEFAULT ensure_debug_category()
51
static GstDebugCategory *
52
ensure_debug_category (void)
53
0
{
54
0
  static gsize cat_gonce = 0;
55
56
0
  if (g_once_init_enter (&cat_gonce)) {
57
0
    gsize cat_done;
58
59
0
    cat_done = (gsize) _gst_debug_category_new ("audio-converter", 0,
60
0
        "audio-converter object");
61
62
0
    g_once_init_leave (&cat_gonce, cat_done);
63
0
  }
64
65
0
  return (GstDebugCategory *) cat_gonce;
66
0
}
67
#else
68
#define ensure_debug_category() /* NOOP */
69
#endif /* GST_DISABLE_GST_DEBUG */
70
71
typedef struct _AudioChain AudioChain;
72
73
typedef void (*AudioConvertFunc) (gpointer dst, const gpointer src, gint count);
74
typedef gboolean (*AudioConvertSamplesFunc) (GstAudioConverter * convert,
75
    GstAudioConverterFlags flags, gpointer in[], gsize in_frames,
76
    gpointer out[], gsize out_frames);
77
typedef void (*AudioConvertEndianFunc) (gpointer dst, const gpointer src,
78
    gint count);
79
80
/*                           int/int    int/float  float/int float/float
81
 *
82
 *  unpack                     S32          S32         F64       F64
83
 *  convert                               S32->F64
84
 *  channel mix                S32          F64         F64       F64
85
 *  convert                                           F64->S32
86
 *  quantize                   S32                      S32
87
 *  pack                       S32          F64         S32       F64
88
 *
89
 *
90
 *  interleave
91
 *  deinterleave
92
 *  resample
93
 */
94
struct _GstAudioConverter
95
{
96
  GstAudioInfo in;
97
  GstAudioInfo out;
98
99
  GstStructure *config;
100
101
  GstAudioConverterFlags flags;
102
  GstAudioFormat current_format;
103
  GstAudioLayout current_layout;
104
  gint current_channels;
105
106
  gboolean in_writable;
107
  gpointer *in_data;
108
  gsize in_frames;
109
  gpointer *out_data;
110
  gsize out_frames;
111
112
  gboolean in_place;            /* the conversion can be done in place; returned by gst_audio_converter_supports_inplace() */
113
114
  gboolean passthrough;
115
116
  /* unpack */
117
  gboolean in_default;
118
  gboolean unpack_ip;
119
120
  /* convert in */
121
  AudioConvertFunc convert_in;
122
123
  /* channel mix */
124
  gboolean mix_passthrough;
125
  GstAudioChannelMixer *mix;
126
127
  /* resample */
128
  GstAudioResampler *resampler;
129
130
  /* convert out */
131
  AudioConvertFunc convert_out;
132
133
  /* quant */
134
  GstAudioQuantize *quant;
135
136
  /* change layout */
137
  GstAudioFormat chlayout_format;
138
  GstAudioLayout chlayout_target;
139
  gint chlayout_channels;
140
141
  /* pack */
142
  gboolean out_default;
143
  AudioChain *chain_end;        /* NULL for empty chain or points to the last element in the chain */
144
145
  /* endian swap */
146
  AudioConvertEndianFunc swap_endian;
147
148
  AudioConvertSamplesFunc convert;
149
};
150
151
static GstAudioConverter *
152
gst_audio_converter_copy (GstAudioConverter * convert)
153
0
{
154
0
  GstAudioConverter *res =
155
0
      gst_audio_converter_new (convert->flags, &convert->in, &convert->out,
156
0
      convert->config);
157
158
0
  return res;
159
0
}
160
161
0
G_DEFINE_BOXED_TYPE (GstAudioConverter, gst_audio_converter,
162
0
    (GBoxedCopyFunc) gst_audio_converter_copy,
163
0
    (GBoxedFreeFunc) gst_audio_converter_free);
164
0
165
0
typedef gboolean (*AudioChainFunc) (AudioChain * chain, gpointer user_data);
166
0
typedef gpointer *(*AudioChainAllocFunc) (AudioChain * chain, gsize num_samples,
167
0
    gpointer user_data);
168
0
169
0
struct _AudioChain
170
0
{
171
0
  AudioChain *prev;
172
0
173
0
  AudioChainFunc make_func;
174
0
  gpointer make_func_data;
175
0
  GDestroyNotify make_func_notify;
176
0
177
0
  const GstAudioFormatInfo *finfo;
178
0
  gint stride;
179
0
  gint inc;
180
0
  gint blocks;
181
0
182
0
  gboolean pass_alloc;
183
0
  gboolean allow_ip;
184
0
185
0
  AudioChainAllocFunc alloc_func;
186
0
  gpointer alloc_data;
187
0
188
0
  gpointer *tmp;
189
0
  gsize allocated_samples;
190
0
191
0
  gpointer *samples;
192
0
  gsize num_samples;
193
0
};
194
0
195
0
static AudioChain *
196
0
audio_chain_new (AudioChain * prev, GstAudioConverter * convert)
197
0
{
198
0
  AudioChain *chain;
199
200
0
  chain = g_new0 (AudioChain, 1);
201
0
  chain->prev = prev;
202
203
0
  if (convert->current_layout == GST_AUDIO_LAYOUT_NON_INTERLEAVED) {
204
0
    chain->inc = 1;
205
0
    chain->blocks = convert->current_channels;
206
0
  } else {
207
0
    chain->inc = convert->current_channels;
208
0
    chain->blocks = 1;
209
0
  }
210
0
  chain->finfo = gst_audio_format_get_info (convert->current_format);
211
0
  chain->stride = (chain->finfo->width * chain->inc) / 8;
212
213
0
  return chain;
214
0
}
215
216
static void
217
audio_chain_set_make_func (AudioChain * chain,
218
    AudioChainFunc make_func, gpointer user_data, GDestroyNotify notify)
219
0
{
220
0
  chain->make_func = make_func;
221
0
  chain->make_func_data = user_data;
222
0
  chain->make_func_notify = notify;
223
0
}
224
225
static void
226
audio_chain_free (AudioChain * chain)
227
0
{
228
0
  GST_LOG ("free chain %p", chain);
229
0
  if (chain->make_func_notify)
230
0
    chain->make_func_notify (chain->make_func_data);
231
0
  g_free (chain->tmp);
232
0
  g_free (chain);
233
0
}
234
235
static gpointer *
236
audio_chain_alloc_samples (AudioChain * chain, gsize num_samples)
237
0
{
238
0
  return chain->alloc_func (chain, num_samples, chain->alloc_data);
239
0
}
240
241
static void
242
audio_chain_set_samples (AudioChain * chain, gpointer * samples,
243
    gsize num_samples)
244
0
{
245
0
  GST_LOG ("set samples %p %" G_GSIZE_FORMAT, samples, num_samples);
246
247
0
  chain->samples = samples;
248
0
  chain->num_samples = num_samples;
249
0
}
250
251
static gpointer *
252
audio_chain_get_samples (AudioChain * chain, gsize * avail)
253
0
{
254
0
  gpointer *res;
255
256
0
  if (!chain->samples)
257
0
    chain->make_func (chain, chain->make_func_data);
258
259
0
  res = chain->samples;
260
0
  *avail = chain->num_samples;
261
0
  chain->samples = NULL;
262
263
0
  return res;
264
0
}
265
266
static guint
267
get_opt_uint (GstAudioConverter * convert, const gchar * opt, guint def)
268
0
{
269
0
  guint res;
270
0
  if (!gst_structure_get_uint (convert->config, opt, &res))
271
0
    res = def;
272
0
  return res;
273
0
}
274
275
static gint
276
get_opt_enum (GstAudioConverter * convert, const gchar * opt, GType type,
277
    gint def)
278
0
{
279
0
  gint res;
280
0
  if (!gst_structure_get_enum (convert->config, opt, type, &res))
281
0
    res = def;
282
0
  return res;
283
0
}
284
285
static const GValue *
286
get_opt_value (GstAudioConverter * convert, const gchar * opt)
287
0
{
288
0
  return gst_structure_get_value (convert->config, opt);
289
0
}
290
291
0
#define DEFAULT_OPT_RESAMPLER_METHOD GST_AUDIO_RESAMPLER_METHOD_BLACKMAN_NUTTALL
292
0
#define DEFAULT_OPT_DITHER_METHOD GST_AUDIO_DITHER_NONE
293
0
#define DEFAULT_OPT_DITHER_THRESHOLD 20
294
0
#define DEFAULT_OPT_NOISE_SHAPING_METHOD GST_AUDIO_NOISE_SHAPING_NONE
295
#define DEFAULT_OPT_QUANTIZATION 1
296
297
0
#define GET_OPT_RESAMPLER_METHOD(c) get_opt_enum(c, \
298
0
    GST_AUDIO_CONVERTER_OPT_RESAMPLER_METHOD, GST_TYPE_AUDIO_RESAMPLER_METHOD, \
299
0
    DEFAULT_OPT_RESAMPLER_METHOD)
300
0
#define GET_OPT_DITHER_METHOD(c) get_opt_enum(c, \
301
0
    GST_AUDIO_CONVERTER_OPT_DITHER_METHOD, GST_TYPE_AUDIO_DITHER_METHOD, \
302
0
    DEFAULT_OPT_DITHER_METHOD)
303
0
#define GET_OPT_DITHER_THRESHOLD(c) get_opt_uint(c, \
304
0
    GST_AUDIO_CONVERTER_OPT_DITHER_THRESHOLD, DEFAULT_OPT_DITHER_THRESHOLD)
305
0
#define GET_OPT_NOISE_SHAPING_METHOD(c) get_opt_enum(c, \
306
0
    GST_AUDIO_CONVERTER_OPT_NOISE_SHAPING_METHOD, GST_TYPE_AUDIO_NOISE_SHAPING_METHOD, \
307
0
    DEFAULT_OPT_NOISE_SHAPING_METHOD)
308
#define GET_OPT_QUANTIZATION(c) get_opt_uint(c, \
309
    GST_AUDIO_CONVERTER_OPT_QUANTIZATION, DEFAULT_OPT_QUANTIZATION)
310
0
#define GET_OPT_MIX_MATRIX(c) get_opt_value(c, \
311
0
    GST_AUDIO_CONVERTER_OPT_MIX_MATRIX)
312
313
static gboolean
314
copy_config (const GstIdStr * fieldname, const GValue * value,
315
    gpointer user_data)
316
0
{
317
0
  GstAudioConverter *convert = user_data;
318
319
0
  gst_structure_id_str_set_value (convert->config, fieldname, value);
320
321
0
  return TRUE;
322
0
}
323
324
/**
325
 * gst_audio_converter_update_config:
326
 * @convert: a #GstAudioConverter
327
 * @in_rate: input rate
328
 * @out_rate: output rate
329
 * @config: (transfer full) (allow-none): a #GstStructure or %NULL
330
 *
331
 * Set @in_rate, @out_rate and @config as extra configuration for @convert.
332
 *
333
 * @in_rate and @out_rate specify the new sample rates of input and output
334
 * formats. A value of 0 leaves the sample rate unchanged.
335
 *
336
 * @config can be %NULL, in which case, the current configuration is not
337
 * changed.
338
 *
339
 * If the parameters in @config can not be set exactly, this function returns
340
 * %FALSE and will try to update as much state as possible. The new state can
341
 * then be retrieved and refined with gst_audio_converter_get_config().
342
 *
343
 * Look at the `GST_AUDIO_CONVERTER_OPT_*` fields to check valid configuration
344
 * option and values.
345
 *
346
 * Returns: %TRUE when the new parameters could be set
347
 */
348
gboolean
349
gst_audio_converter_update_config (GstAudioConverter * convert,
350
    gint in_rate, gint out_rate, GstStructure * config)
351
0
{
352
0
  g_return_val_if_fail (convert != NULL, FALSE);
353
0
  g_return_val_if_fail ((in_rate == 0 && out_rate == 0) ||
354
0
      convert->flags & GST_AUDIO_CONVERTER_FLAG_VARIABLE_RATE, FALSE);
355
356
0
  GST_LOG ("new rate %d -> %d", in_rate, out_rate);
357
358
0
  if (in_rate <= 0)
359
0
    in_rate = convert->in.rate;
360
0
  if (out_rate <= 0)
361
0
    out_rate = convert->out.rate;
362
363
0
  convert->in.rate = in_rate;
364
0
  convert->out.rate = out_rate;
365
366
0
  if (convert->resampler)
367
0
    gst_audio_resampler_update (convert->resampler, in_rate, out_rate, config);
368
369
0
  if (config) {
370
0
    gst_structure_foreach_id_str (config, copy_config, convert);
371
0
    gst_structure_free (config);
372
0
  }
373
374
0
  return TRUE;
375
0
}
376
377
/**
378
 * gst_audio_converter_get_config:
379
 * @convert: a #GstAudioConverter
380
 * @in_rate: (out) (optional): result input rate
381
 * @out_rate: (out) (optional): result output rate
382
 *
383
 * Get the current configuration of @convert.
384
 *
385
 * Returns: (transfer none):
386
 *   a #GstStructure that remains valid for as long as @convert is valid
387
 *   or until gst_audio_converter_update_config() is called.
388
 */
389
const GstStructure *
390
gst_audio_converter_get_config (GstAudioConverter * convert,
391
    gint * in_rate, gint * out_rate)
392
0
{
393
0
  g_return_val_if_fail (convert != NULL, NULL);
394
395
0
  if (in_rate)
396
0
    *in_rate = convert->in.rate;
397
0
  if (out_rate)
398
0
    *out_rate = convert->out.rate;
399
400
0
  return convert->config;
401
0
}
402
403
static gpointer *
404
get_output_samples (AudioChain * chain, gsize num_samples, gpointer user_data)
405
0
{
406
0
  GstAudioConverter *convert = user_data;
407
408
0
  GST_LOG ("output samples %p %" G_GSIZE_FORMAT, convert->out_data,
409
0
      num_samples);
410
411
0
  return convert->out_data;
412
0
}
413
414
0
#define MEM_ALIGN(m,a) ((gint8 *)((guintptr)((gint8 *)(m) + ((a)-1)) & ~((a)-1)))
415
0
#define ALIGN 16
416
417
static gpointer *
418
get_temp_samples (AudioChain * chain, gsize num_samples, gpointer user_data)
419
0
{
420
0
  if (num_samples > chain->allocated_samples) {
421
0
    gint i;
422
0
    gint8 *s;
423
0
    gsize stride = GST_ROUND_UP_N (num_samples * chain->stride, ALIGN);
424
    /* first part contains the pointers, second part the data, add some extra bytes
425
     * for alignment */
426
0
    gsize needed = (stride + sizeof (gpointer)) * chain->blocks + ALIGN - 1;
427
428
0
    GST_DEBUG ("alloc samples %d %" G_GSIZE_FORMAT " %" G_GSIZE_FORMAT,
429
0
        chain->stride, num_samples, needed);
430
0
    chain->tmp = g_realloc (chain->tmp, needed);
431
0
    chain->allocated_samples = num_samples;
432
433
    /* pointer to the data, make sure it's 16 bytes aligned */
434
0
    s = MEM_ALIGN (&chain->tmp[chain->blocks], ALIGN);
435
436
    /* set up the pointers */
437
0
    for (i = 0; i < chain->blocks; i++)
438
0
      chain->tmp[i] = s + i * stride;
439
0
  }
440
0
  GST_LOG ("temp samples %p %" G_GSIZE_FORMAT, chain->tmp, num_samples);
441
442
0
  return chain->tmp;
443
0
}
444
445
static gboolean
446
do_unpack (AudioChain * chain, gpointer user_data)
447
0
{
448
0
  GstAudioConverter *convert = user_data;
449
0
  gsize num_samples;
450
0
  gpointer *tmp;
451
0
  gboolean in_writable;
452
453
0
  in_writable = convert->in_writable;
454
0
  num_samples = convert->in_frames;
455
456
0
  if (!chain->allow_ip || !in_writable || !convert->in_default) {
457
0
    gint i;
458
459
0
    if (in_writable && chain->allow_ip) {
460
0
      tmp = convert->in_data;
461
0
      GST_LOG ("unpack in-place %p, %" G_GSIZE_FORMAT, tmp, num_samples);
462
0
    } else {
463
0
      tmp = audio_chain_alloc_samples (chain, num_samples);
464
0
      GST_LOG ("unpack to tmp %p, %" G_GSIZE_FORMAT, tmp, num_samples);
465
0
    }
466
467
0
    if (convert->in_data) {
468
0
      for (i = 0; i < chain->blocks; i++) {
469
0
        if (convert->in_default) {
470
0
          GST_LOG ("copy %p, %p, %" G_GSIZE_FORMAT, tmp[i], convert->in_data[i],
471
0
              num_samples);
472
0
          memcpy (tmp[i], convert->in_data[i], num_samples * chain->stride);
473
0
        } else {
474
0
          GST_LOG ("unpack %p, %p, %" G_GSIZE_FORMAT, tmp[i],
475
0
              convert->in_data[i], num_samples);
476
0
          convert->in.finfo->unpack_func (convert->in.finfo,
477
0
              GST_AUDIO_PACK_FLAG_TRUNCATE_RANGE, tmp[i], convert->in_data[i],
478
0
              num_samples * chain->inc);
479
0
        }
480
0
      }
481
0
    } else {
482
0
      for (i = 0; i < chain->blocks; i++) {
483
0
        gst_audio_format_info_fill_silence (chain->finfo, tmp[i],
484
0
            num_samples * chain->inc);
485
0
      }
486
0
    }
487
0
  } else {
488
0
    tmp = convert->in_data;
489
0
    GST_LOG ("get in samples %p", tmp);
490
0
  }
491
0
  audio_chain_set_samples (chain, tmp, num_samples);
492
493
0
  return TRUE;
494
0
}
495
496
static gboolean
497
do_convert_in (AudioChain * chain, gpointer user_data)
498
0
{
499
0
  gsize num_samples;
500
0
  GstAudioConverter *convert = user_data;
501
0
  gpointer *in, *out;
502
0
  gint i;
503
504
0
  in = audio_chain_get_samples (chain->prev, &num_samples);
505
0
  out = (chain->allow_ip ? in : audio_chain_alloc_samples (chain, num_samples));
506
0
  GST_LOG ("convert in %p, %p, %" G_GSIZE_FORMAT, in, out, num_samples);
507
508
0
  for (i = 0; i < chain->blocks; i++)
509
0
    convert->convert_in (out[i], in[i], num_samples * chain->inc);
510
511
0
  audio_chain_set_samples (chain, out, num_samples);
512
513
0
  return TRUE;
514
0
}
515
516
static gboolean
517
do_mix (AudioChain * chain, gpointer user_data)
518
0
{
519
0
  gsize num_samples;
520
0
  GstAudioConverter *convert = user_data;
521
0
  gpointer *in, *out;
522
523
0
  in = audio_chain_get_samples (chain->prev, &num_samples);
524
0
  out = (chain->allow_ip ? in : audio_chain_alloc_samples (chain, num_samples));
525
0
  GST_LOG ("mix %p, %p, %" G_GSIZE_FORMAT, in, out, num_samples);
526
527
0
  gst_audio_channel_mixer_samples (convert->mix, in, out, num_samples);
528
529
0
  audio_chain_set_samples (chain, out, num_samples);
530
531
0
  return TRUE;
532
0
}
533
534
static gboolean
535
do_resample (AudioChain * chain, gpointer user_data)
536
0
{
537
0
  GstAudioConverter *convert = user_data;
538
0
  gpointer *in, *out;
539
0
  gsize in_frames, out_frames;
540
541
0
  in = audio_chain_get_samples (chain->prev, &in_frames);
542
0
  out_frames = convert->out_frames;
543
0
  out = (chain->allow_ip ? in : audio_chain_alloc_samples (chain, out_frames));
544
545
0
  GST_LOG ("resample %p %p,%" G_GSIZE_FORMAT " %" G_GSIZE_FORMAT, in,
546
0
      out, in_frames, out_frames);
547
548
0
  gst_audio_resampler_resample (convert->resampler, in, in_frames, out,
549
0
      out_frames);
550
551
0
  audio_chain_set_samples (chain, out, out_frames);
552
553
0
  return TRUE;
554
0
}
555
556
static gboolean
557
do_convert_out (AudioChain * chain, gpointer user_data)
558
0
{
559
0
  GstAudioConverter *convert = user_data;
560
0
  gsize num_samples;
561
0
  gpointer *in, *out;
562
0
  gint i;
563
564
0
  in = audio_chain_get_samples (chain->prev, &num_samples);
565
0
  out = (chain->allow_ip ? in : audio_chain_alloc_samples (chain, num_samples));
566
0
  GST_LOG ("convert out %p, %p %" G_GSIZE_FORMAT, in, out, num_samples);
567
568
0
  for (i = 0; i < chain->blocks; i++)
569
0
    convert->convert_out (out[i], in[i], num_samples * chain->inc);
570
571
0
  audio_chain_set_samples (chain, out, num_samples);
572
573
0
  return TRUE;
574
0
}
575
576
static gboolean
577
do_quantize (AudioChain * chain, gpointer user_data)
578
0
{
579
0
  GstAudioConverter *convert = user_data;
580
0
  gsize num_samples;
581
0
  gpointer *in, *out;
582
583
0
  in = audio_chain_get_samples (chain->prev, &num_samples);
584
0
  out = (chain->allow_ip ? in : audio_chain_alloc_samples (chain, num_samples));
585
0
  GST_LOG ("quantize %p, %p %" G_GSIZE_FORMAT, in, out, num_samples);
586
587
0
  if (in && out)
588
0
    gst_audio_quantize_samples (convert->quant, in, out, num_samples);
589
590
0
  audio_chain_set_samples (chain, out, num_samples);
591
592
0
  return TRUE;
593
0
}
594
595
#define MAKE_INTERLEAVE_FUNC(type) \
596
static inline void \
597
interleave_##type (const type * in[], type * out[], \
598
0
    gsize num_samples, gint channels) \
599
0
{ \
600
0
  gsize s; \
601
0
  gint c; \
602
0
  for (s = 0; s < num_samples; s++) { \
603
0
    for (c = 0; c < channels; c++) { \
604
0
      out[0][s * channels + c] = in[c][s]; \
605
0
    } \
606
0
  } \
607
0
}
Unexecuted instantiation: audio-converter.c:interleave_gint16
Unexecuted instantiation: audio-converter.c:interleave_gint32
Unexecuted instantiation: audio-converter.c:interleave_gfloat
Unexecuted instantiation: audio-converter.c:interleave_gdouble
608
609
#define MAKE_DEINTERLEAVE_FUNC(type) \
610
static inline void \
611
deinterleave_##type (const type * in[], type * out[], \
612
0
    gsize num_samples, gint channels) \
613
0
{ \
614
0
  gsize s; \
615
0
  gint c; \
616
0
  for (s = 0; s < num_samples; s++) { \
617
0
    for (c = 0; c < channels; c++) { \
618
0
      out[c][s] = in[0][s * channels + c]; \
619
0
    } \
620
0
  } \
621
0
}
Unexecuted instantiation: audio-converter.c:deinterleave_gint16
Unexecuted instantiation: audio-converter.c:deinterleave_gint32
Unexecuted instantiation: audio-converter.c:deinterleave_gfloat
Unexecuted instantiation: audio-converter.c:deinterleave_gdouble
622
623
MAKE_INTERLEAVE_FUNC (gint16);
624
MAKE_INTERLEAVE_FUNC (gint32);
625
MAKE_INTERLEAVE_FUNC (gfloat);
626
MAKE_INTERLEAVE_FUNC (gdouble);
627
MAKE_DEINTERLEAVE_FUNC (gint16);
628
MAKE_DEINTERLEAVE_FUNC (gint32);
629
MAKE_DEINTERLEAVE_FUNC (gfloat);
630
MAKE_DEINTERLEAVE_FUNC (gdouble);
631
632
static gboolean
633
do_change_layout (AudioChain * chain, gpointer user_data)
634
0
{
635
0
  GstAudioConverter *convert = user_data;
636
0
  GstAudioFormat format = convert->chlayout_format;
637
0
  GstAudioLayout out_layout = convert->chlayout_target;
638
0
  gint channels = convert->chlayout_channels;
639
0
  gsize num_samples;
640
0
  gpointer *in, *out;
641
642
0
  in = audio_chain_get_samples (chain->prev, &num_samples);
643
0
  out = (chain->allow_ip ? in : audio_chain_alloc_samples (chain, num_samples));
644
645
0
  if (out_layout == GST_AUDIO_LAYOUT_INTERLEAVED) {
646
    /* interleave */
647
0
    GST_LOG ("interleaving %p, %p %" G_GSIZE_FORMAT, in, out, num_samples);
648
0
    switch (format) {
649
0
      case GST_AUDIO_FORMAT_S16:
650
0
        interleave_gint16 ((const gint16 **) in, (gint16 **) out,
651
0
            num_samples, channels);
652
0
        break;
653
0
      case GST_AUDIO_FORMAT_S32:
654
0
        interleave_gint32 ((const gint32 **) in, (gint32 **) out,
655
0
            num_samples, channels);
656
0
        break;
657
0
      case GST_AUDIO_FORMAT_F32:
658
0
        interleave_gfloat ((const gfloat **) in, (gfloat **) out,
659
0
            num_samples, channels);
660
0
        break;
661
0
      case GST_AUDIO_FORMAT_F64:
662
0
        interleave_gdouble ((const gdouble **) in, (gdouble **) out,
663
0
            num_samples, channels);
664
0
        break;
665
0
      default:
666
0
        g_assert_not_reached ();
667
0
        break;
668
0
    }
669
0
  } else {
670
    /* deinterleave */
671
0
    GST_LOG ("deinterleaving %p, %p %" G_GSIZE_FORMAT, in, out, num_samples);
672
0
    switch (format) {
673
0
      case GST_AUDIO_FORMAT_S16:
674
0
        deinterleave_gint16 ((const gint16 **) in, (gint16 **) out,
675
0
            num_samples, channels);
676
0
        break;
677
0
      case GST_AUDIO_FORMAT_S32:
678
0
        deinterleave_gint32 ((const gint32 **) in, (gint32 **) out,
679
0
            num_samples, channels);
680
0
        break;
681
0
      case GST_AUDIO_FORMAT_F32:
682
0
        deinterleave_gfloat ((const gfloat **) in, (gfloat **) out,
683
0
            num_samples, channels);
684
0
        break;
685
0
      case GST_AUDIO_FORMAT_F64:
686
0
        deinterleave_gdouble ((const gdouble **) in, (gdouble **) out,
687
0
            num_samples, channels);
688
0
        break;
689
0
      default:
690
0
        g_assert_not_reached ();
691
0
        break;
692
0
    }
693
0
  }
694
695
0
  audio_chain_set_samples (chain, out, num_samples);
696
0
  return TRUE;
697
0
}
698
699
static gboolean
700
is_intermediate_format (GstAudioFormat format)
701
0
{
702
0
  return (format == GST_AUDIO_FORMAT_S16 ||
703
0
      format == GST_AUDIO_FORMAT_S32 ||
704
0
      format == GST_AUDIO_FORMAT_F32 || format == GST_AUDIO_FORMAT_F64);
705
0
}
706
707
static AudioChain *
708
chain_unpack (GstAudioConverter * convert)
709
0
{
710
0
  AudioChain *prev;
711
0
  GstAudioInfo *in = &convert->in;
712
0
  GstAudioInfo *out = &convert->out;
713
0
  gboolean same_format;
714
715
0
  same_format = in->finfo->format == out->finfo->format;
716
717
  /* do not unpack if we have the same input format as the output format
718
   * and it is a possible intermediate format */
719
0
  if (same_format && is_intermediate_format (in->finfo->format)) {
720
0
    convert->current_format = in->finfo->format;
721
0
  } else {
722
0
    convert->current_format = in->finfo->unpack_format;
723
0
  }
724
0
  convert->current_layout = in->layout;
725
0
  convert->current_channels = in->channels;
726
727
0
  convert->in_default = convert->current_format == in->finfo->format;
728
729
0
  GST_INFO ("unpack format %s to %s",
730
0
      gst_audio_format_to_string (in->finfo->format),
731
0
      gst_audio_format_to_string (convert->current_format));
732
733
0
  prev = audio_chain_new (NULL, convert);
734
0
  prev->allow_ip = prev->finfo->width <= in->finfo->width;
735
0
  prev->pass_alloc = FALSE;
736
0
  audio_chain_set_make_func (prev, do_unpack, convert, NULL);
737
738
0
  return prev;
739
0
}
740
741
static AudioChain *
742
chain_convert_in (GstAudioConverter * convert, AudioChain * prev)
743
0
{
744
0
  gboolean in_int, out_int;
745
0
  GstAudioInfo *in = &convert->in;
746
0
  GstAudioInfo *out = &convert->out;
747
748
0
  in_int = GST_AUDIO_FORMAT_INFO_IS_INTEGER (in->finfo);
749
0
  out_int = GST_AUDIO_FORMAT_INFO_IS_INTEGER (out->finfo);
750
751
0
  if (in_int && !out_int) {
752
0
    GST_INFO ("convert S32 to F64");
753
0
    convert->convert_in = (AudioConvertFunc) audio_orc_s32_to_double;
754
0
    convert->current_format = GST_AUDIO_FORMAT_F64;
755
756
0
    prev = audio_chain_new (prev, convert);
757
0
    prev->allow_ip = FALSE;
758
0
    prev->pass_alloc = FALSE;
759
0
    audio_chain_set_make_func (prev, do_convert_in, convert, NULL);
760
0
  }
761
0
  return prev;
762
0
}
763
764
static gboolean
765
check_mix_matrix (guint in_channels, guint out_channels, const GValue * value)
766
0
{
767
0
  guint i, j;
768
769
  /* audio-channel-mixer will generate an identity matrix */
770
0
  if (gst_value_array_get_size (value) == 0)
771
0
    return TRUE;
772
773
0
  if (gst_value_array_get_size (value) != out_channels) {
774
0
    GST_ERROR ("Invalid mix matrix size, should be %d", out_channels);
775
0
    goto fail;
776
0
  }
777
778
0
  for (j = 0; j < out_channels; j++) {
779
0
    const GValue *row = gst_value_array_get_value (value, j);
780
781
0
    if (gst_value_array_get_size (row) != in_channels) {
782
0
      GST_ERROR ("Invalid mix matrix row size, should be %d", in_channels);
783
0
      goto fail;
784
0
    }
785
786
0
    for (i = 0; i < in_channels; i++) {
787
0
      const GValue *itm;
788
789
0
      itm = gst_value_array_get_value (row, i);
790
0
      if (!G_VALUE_HOLDS_FLOAT (itm) &&
791
0
          !G_VALUE_HOLDS_DOUBLE (itm) &&
792
0
          !G_VALUE_HOLDS_INT (itm) &&
793
0
          !G_VALUE_HOLDS_INT64 (itm) &&
794
0
          !G_VALUE_HOLDS_UINT (itm) && !G_VALUE_HOLDS_UINT64 (itm)) {
795
0
        GST_ERROR
796
0
            ("Invalid mix matrix element type, should be float or double or integer");
797
0
        goto fail;
798
0
      }
799
0
    }
800
0
  }
801
802
0
  return TRUE;
803
804
0
fail:
805
0
  return FALSE;
806
0
}
807
808
static gfloat **
809
mix_matrix_from_g_value (guint in_channels, guint out_channels,
810
    const GValue * value)
811
0
{
812
0
  guint i, j;
813
0
  gfloat **matrix = g_new (gfloat *, in_channels);
814
815
0
  for (i = 0; i < in_channels; i++)
816
0
    matrix[i] = g_new (gfloat, out_channels);
817
818
0
  for (j = 0; j < out_channels; j++) {
819
0
    const GValue *row = gst_value_array_get_value (value, j);
820
821
0
    for (i = 0; i < in_channels; i++) {
822
0
      const GValue *itm;
823
0
      gfloat coefficient;
824
825
0
      itm = gst_value_array_get_value (row, i);
826
0
      if (G_VALUE_HOLDS_FLOAT (itm))
827
0
        coefficient = g_value_get_float (itm);
828
0
      else if (G_VALUE_HOLDS_DOUBLE (itm))
829
0
        coefficient = g_value_get_double (itm);
830
0
      else if (G_VALUE_HOLDS_INT (itm))
831
0
        coefficient = g_value_get_int (itm);
832
0
      else if (G_VALUE_HOLDS_INT64 (itm))
833
0
        coefficient = g_value_get_int64 (itm);
834
0
      else if (G_VALUE_HOLDS_UINT (itm))
835
0
        coefficient = g_value_get_uint (itm);
836
0
      else if (G_VALUE_HOLDS_UINT64 (itm))
837
0
        coefficient = g_value_get_uint64 (itm);
838
0
      else
839
0
        g_assert_not_reached ();
840
841
0
      matrix[i][j] = coefficient;
842
0
    }
843
0
  }
844
845
0
  return matrix;
846
0
}
847
848
static AudioChain *
849
chain_mix (GstAudioConverter * convert, AudioChain * prev)
850
0
{
851
0
  GstAudioInfo *in = &convert->in;
852
0
  GstAudioInfo *out = &convert->out;
853
0
  GstAudioFormat format = convert->current_format;
854
0
  const GValue *opt_matrix = GET_OPT_MIX_MATRIX (convert);
855
0
  GstAudioChannelMixerFlags flags = 0;
856
857
0
  convert->current_channels = out->channels;
858
859
  /* keep the input layout */
860
0
  if (convert->current_layout == GST_AUDIO_LAYOUT_NON_INTERLEAVED) {
861
0
    flags |= GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_IN;
862
0
    flags |= GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT;
863
0
  }
864
865
0
  if (opt_matrix) {
866
0
    gfloat **matrix = NULL;
867
868
0
    if (gst_value_array_get_size (opt_matrix))
869
0
      matrix =
870
0
          mix_matrix_from_g_value (in->channels, out->channels, opt_matrix);
871
872
0
    convert->mix =
873
0
        gst_audio_channel_mixer_new_with_matrix (flags, format, in->channels,
874
0
        out->channels, matrix);
875
0
  } else {
876
0
    flags |=
877
0
        GST_AUDIO_INFO_IS_UNPOSITIONED (in) ?
878
0
        GST_AUDIO_CHANNEL_MIXER_FLAGS_UNPOSITIONED_IN : 0;
879
0
    flags |=
880
0
        GST_AUDIO_INFO_IS_UNPOSITIONED (out) ?
881
0
        GST_AUDIO_CHANNEL_MIXER_FLAGS_UNPOSITIONED_OUT : 0;
882
883
0
    convert->mix =
884
0
        gst_audio_channel_mixer_new (flags, format, in->channels, in->position,
885
0
        out->channels, out->position);
886
0
  }
887
888
0
  convert->mix_passthrough =
889
0
      gst_audio_channel_mixer_is_passthrough (convert->mix);
890
0
  GST_INFO ("mix format %s, passthrough %d, in_channels %d, out_channels %d",
891
0
      gst_audio_format_to_string (format), convert->mix_passthrough,
892
0
      in->channels, out->channels);
893
894
0
  if (!convert->mix_passthrough) {
895
0
    prev = audio_chain_new (prev, convert);
896
0
    prev->allow_ip = FALSE;
897
0
    prev->pass_alloc = FALSE;
898
0
    audio_chain_set_make_func (prev, do_mix, convert, NULL);
899
0
  }
900
0
  return prev;
901
0
}
902
903
static AudioChain *
904
chain_resample (GstAudioConverter * convert, AudioChain * prev)
905
0
{
906
0
  GstAudioInfo *in = &convert->in;
907
0
  GstAudioInfo *out = &convert->out;
908
0
  GstAudioResamplerMethod method;
909
0
  GstAudioResamplerFlags flags;
910
0
  GstAudioFormat format = convert->current_format;
911
0
  gint channels = convert->current_channels;
912
0
  gboolean variable_rate;
913
914
0
  variable_rate = convert->flags & GST_AUDIO_CONVERTER_FLAG_VARIABLE_RATE;
915
916
0
  if (in->rate != out->rate || variable_rate) {
917
0
    method = GET_OPT_RESAMPLER_METHOD (convert);
918
919
0
    flags = 0;
920
0
    if (convert->current_layout == GST_AUDIO_LAYOUT_NON_INTERLEAVED) {
921
0
      flags |= GST_AUDIO_RESAMPLER_FLAG_NON_INTERLEAVED_IN;
922
0
    }
923
    /* if the resampler is activated, it is optimal to change layout here */
924
0
    if (out->layout == GST_AUDIO_LAYOUT_NON_INTERLEAVED) {
925
0
      flags |= GST_AUDIO_RESAMPLER_FLAG_NON_INTERLEAVED_OUT;
926
0
    }
927
0
    convert->current_layout = out->layout;
928
929
0
    if (variable_rate)
930
0
      flags |= GST_AUDIO_RESAMPLER_FLAG_VARIABLE_RATE;
931
932
0
    convert->resampler =
933
0
        gst_audio_resampler_new (method, flags, format, channels, in->rate,
934
0
        out->rate, convert->config);
935
936
0
    prev = audio_chain_new (prev, convert);
937
0
    prev->allow_ip = FALSE;
938
0
    prev->pass_alloc = FALSE;
939
0
    audio_chain_set_make_func (prev, do_resample, convert, NULL);
940
0
  }
941
0
  return prev;
942
0
}
943
944
static AudioChain *
945
chain_convert_out (GstAudioConverter * convert, AudioChain * prev)
946
0
{
947
0
  gboolean in_int, out_int;
948
0
  GstAudioInfo *in = &convert->in;
949
0
  GstAudioInfo *out = &convert->out;
950
951
0
  in_int = GST_AUDIO_FORMAT_INFO_IS_INTEGER (in->finfo);
952
0
  out_int = GST_AUDIO_FORMAT_INFO_IS_INTEGER (out->finfo);
953
954
0
  if (!in_int && out_int) {
955
0
    convert->convert_out = (AudioConvertFunc) audio_orc_double_to_s32;
956
0
    convert->current_format = GST_AUDIO_FORMAT_S32;
957
958
0
    GST_INFO ("convert F64 to S32");
959
0
    prev = audio_chain_new (prev, convert);
960
0
    prev->allow_ip = TRUE;
961
0
    prev->pass_alloc = FALSE;
962
0
    audio_chain_set_make_func (prev, do_convert_out, convert, NULL);
963
0
  }
964
0
  return prev;
965
0
}
966
967
static AudioChain *
968
chain_quantize (GstAudioConverter * convert, AudioChain * prev)
969
0
{
970
0
  const GstAudioFormatInfo *cur_finfo;
971
0
  GstAudioInfo *out = &convert->out;
972
0
  gint in_depth, out_depth;
973
0
  gboolean in_int, out_int;
974
0
  GstAudioDitherMethod dither;
975
0
  guint dither_threshold;
976
0
  GstAudioNoiseShapingMethod ns;
977
978
0
  dither = GET_OPT_DITHER_METHOD (convert);
979
0
  dither_threshold = GET_OPT_DITHER_THRESHOLD (convert);
980
0
  ns = GET_OPT_NOISE_SHAPING_METHOD (convert);
981
982
0
  cur_finfo = gst_audio_format_get_info (convert->current_format);
983
984
0
  in_depth = GST_AUDIO_FORMAT_INFO_DEPTH (cur_finfo);
985
0
  out_depth = GST_AUDIO_FORMAT_INFO_DEPTH (out->finfo);
986
0
  GST_INFO ("depth in %d, out %d", in_depth, out_depth);
987
988
0
  in_int = GST_AUDIO_FORMAT_INFO_IS_INTEGER (cur_finfo);
989
0
  out_int = GST_AUDIO_FORMAT_INFO_IS_INTEGER (out->finfo);
990
991
  /* Don't dither or apply noise shaping if target depth is bigger than 20 bits
992
   * as DA converters only can do a SNR up to 20 bits in reality.
993
   * Also don't dither or apply noise shaping if target depth is larger than
994
   * source depth. */
995
0
  if (out_depth > dither_threshold || (in_int && out_depth >= in_depth)) {
996
0
    dither = GST_AUDIO_DITHER_NONE;
997
0
    ns = GST_AUDIO_NOISE_SHAPING_NONE;
998
0
    GST_INFO ("using no dither and noise shaping");
999
0
  } else {
1000
0
    GST_INFO ("using dither %d and noise shaping %d", dither, ns);
1001
    /* Use simple error feedback when output sample rate is smaller than
1002
     * 32000 as the other methods might move the noise to audible ranges */
1003
0
    if (ns > GST_AUDIO_NOISE_SHAPING_ERROR_FEEDBACK && out->rate < 32000)
1004
0
      ns = GST_AUDIO_NOISE_SHAPING_ERROR_FEEDBACK;
1005
0
  }
1006
  /* we still want to run the quantization step when reducing bits to get
1007
   * the rounding correct */
1008
0
  if (out_int && out_depth < 32
1009
0
      && convert->current_format == GST_AUDIO_FORMAT_S32) {
1010
0
    GST_INFO ("quantize to %d bits, dither %d, ns %d", out_depth, dither, ns);
1011
0
    convert->quant =
1012
0
        gst_audio_quantize_new (dither, ns, 0, convert->current_format,
1013
0
        out->channels, 1U << (32 - out_depth));
1014
1015
0
    prev = audio_chain_new (prev, convert);
1016
0
    prev->allow_ip = TRUE;
1017
0
    prev->pass_alloc = TRUE;
1018
0
    audio_chain_set_make_func (prev, do_quantize, convert, NULL);
1019
0
  }
1020
0
  return prev;
1021
0
}
1022
1023
static AudioChain *
1024
chain_change_layout (GstAudioConverter * convert, AudioChain * prev)
1025
0
{
1026
0
  GstAudioInfo *out = &convert->out;
1027
1028
0
  if (convert->current_layout != out->layout) {
1029
0
    convert->current_layout = out->layout;
1030
1031
    /* if there is only 1 channel, layouts are identical */
1032
0
    if (convert->current_channels > 1) {
1033
0
      convert->chlayout_target = convert->current_layout;
1034
0
      convert->chlayout_format = convert->current_format;
1035
0
      convert->chlayout_channels = convert->current_channels;
1036
1037
0
      prev = audio_chain_new (prev, convert);
1038
0
      prev->allow_ip = FALSE;
1039
0
      prev->pass_alloc = FALSE;
1040
0
      audio_chain_set_make_func (prev, do_change_layout, convert, NULL);
1041
0
    }
1042
0
  }
1043
0
  return prev;
1044
0
}
1045
1046
static AudioChain *
1047
chain_pack (GstAudioConverter * convert, AudioChain * prev)
1048
0
{
1049
0
  GstAudioInfo *out = &convert->out;
1050
0
  GstAudioFormat format = convert->current_format;
1051
1052
0
  convert->current_format = out->finfo->format;
1053
1054
0
  convert->out_default = format == out->finfo->format;
1055
0
  GST_INFO ("pack format %s to %s", gst_audio_format_to_string (format),
1056
0
      gst_audio_format_to_string (out->finfo->format));
1057
1058
0
  return prev;
1059
0
}
1060
1061
static void
1062
setup_allocators (GstAudioConverter * convert)
1063
0
{
1064
0
  AudioChain *chain;
1065
0
  AudioChainAllocFunc alloc_func;
1066
0
  gboolean allow_ip;
1067
1068
  /* start with using dest if we can directly write into it */
1069
0
  if (convert->out_default) {
1070
0
    alloc_func = get_output_samples;
1071
0
    allow_ip = FALSE;
1072
0
  } else {
1073
0
    alloc_func = get_temp_samples;
1074
0
    allow_ip = TRUE;
1075
0
  }
1076
  /* now walk backwards, we try to write into the dest samples directly
1077
   * and keep track if the source needs to be writable */
1078
0
  for (chain = convert->chain_end; chain; chain = chain->prev) {
1079
0
    chain->alloc_func = alloc_func;
1080
0
    chain->alloc_data = convert;
1081
0
    chain->allow_ip = allow_ip && chain->allow_ip;
1082
0
    GST_LOG ("chain %p: %d %d", chain, allow_ip, chain->allow_ip);
1083
1084
0
    if (!chain->pass_alloc) {
1085
      /* can't pass allocator, make new temp line allocator */
1086
0
      alloc_func = get_temp_samples;
1087
0
      allow_ip = TRUE;
1088
0
    }
1089
0
  }
1090
0
}
1091
1092
static gboolean
1093
converter_passthrough (GstAudioConverter * convert,
1094
    GstAudioConverterFlags flags, gpointer in[], gsize in_frames,
1095
    gpointer out[], gsize out_frames)
1096
0
{
1097
0
  gint i;
1098
0
  AudioChain *chain;
1099
0
  gsize samples;
1100
1101
  /* in-place passthrough -> do nothing */
1102
0
  if (in == out) {
1103
0
    g_assert (convert->in_place);
1104
0
    return TRUE;
1105
0
  }
1106
1107
0
  chain = convert->chain_end;
1108
1109
0
  samples = in_frames * chain->inc;
1110
1111
0
  GST_LOG ("passthrough: %" G_GSIZE_FORMAT " / %" G_GSIZE_FORMAT " samples",
1112
0
      in_frames, samples);
1113
1114
0
  if (in) {
1115
0
    gsize bytes;
1116
1117
0
    bytes = samples * (convert->in.bpf / convert->in.channels);
1118
1119
0
    for (i = 0; i < chain->blocks; i++) {
1120
0
      if (out[i] == in[i]) {
1121
0
        g_assert (convert->in_place);
1122
0
        continue;
1123
0
      }
1124
1125
0
      memcpy (out[i], in[i], bytes);
1126
0
    }
1127
0
  } else {
1128
0
    for (i = 0; i < chain->blocks; i++)
1129
0
      gst_audio_format_info_fill_silence (convert->in.finfo, out[i], samples);
1130
0
  }
1131
0
  return TRUE;
1132
0
}
1133
1134
/* perform LE<->BE conversion on a block of @count 16-bit samples
1135
 * dst may equal src for in-place conversion
1136
 */
1137
static void
1138
converter_swap_endian_16 (gpointer dst, const gpointer src, gint count)
1139
0
{
1140
0
  guint16 *out = dst;
1141
0
  const guint16 *in = src;
1142
0
  gint i;
1143
1144
0
  for (i = 0; i < count; i++)
1145
0
    out[i] = GUINT16_SWAP_LE_BE (in[i]);
1146
0
}
1147
1148
/* perform LE<->BE conversion on a block of @count 24-bit samples
1149
 * dst may equal src for in-place conversion
1150
 *
1151
 * naive algorithm, which performs better with -O3 and worse with -O2
1152
 * than the commented out optimized algorithm below
1153
 */
1154
static void
1155
converter_swap_endian_24 (gpointer dst, const gpointer src, gint count)
1156
0
{
1157
0
  guint8 *out = dst;
1158
0
  const guint8 *in = src;
1159
0
  gint i;
1160
1161
0
  count *= 3;
1162
1163
0
  for (i = 0; i < count; i += 3) {
1164
0
    guint8 x = in[i + 0];
1165
0
    out[i + 0] = in[i + 2];
1166
0
    out[i + 1] = in[i + 1];
1167
0
    out[i + 2] = x;
1168
0
  }
1169
0
}
1170
1171
/* the below code performs better with -O2 but worse with -O3 */
1172
#if 0
1173
/* perform LE<->BE conversion on a block of @count 24-bit samples
1174
 * dst may equal src for in-place conversion
1175
 *
1176
 * assumes that dst and src are 32-bit aligned
1177
 */
1178
static void
1179
converter_swap_endian_24 (gpointer dst, const gpointer src, gint count)
1180
{
1181
  guint32 *out = dst;
1182
  const guint32 *in = src;
1183
  guint8 *out8;
1184
  const guint8 *in8;
1185
  gint i;
1186
1187
  /* first convert 24-bit samples in multiples of 4 reading 3x 32-bits in one cycle
1188
   *
1189
   * input:               A1 B1 C1 A2 , B2 C2 A3 B3 , C3 A4 B4 C4
1190
   * 32-bit endian swap:  A2 C1 B1 A1 , B3 A3 C2 B2 , C4 B4 A4 C3
1191
   *                      <--  x  -->   <--  y  --> , <--  z  -->
1192
   *
1193
   * desired output:      C1 B1 A1 C2 , B2 A2 C3 B3 , A3 C4 B4 A4
1194
   */
1195
  for (i = 0; i < count / 4; i++, in += 3, out += 3) {
1196
    guint32 x, y, z;
1197
1198
    x = GUINT32_SWAP_LE_BE (in[0]);
1199
    y = GUINT32_SWAP_LE_BE (in[1]);
1200
    z = GUINT32_SWAP_LE_BE (in[2]);
1201
1202
#if G_BYTE_ORDER == G_BIG_ENDIAN
1203
    out[0] = (x << 8) + ((y >> 8) & 0xff);
1204
    out[1] = (in[1] & 0xff0000ff) + ((x >> 8) & 0xff0000) + ((z << 8) & 0xff00);
1205
    out[2] = (z >> 8) + ((y << 8) & 0xff000000);
1206
#else
1207
    out[0] = (x >> 8) + ((y << 8) & 0xff000000);
1208
    out[1] = (in[1] & 0xff0000ff) + ((x << 8) & 0xff00) + ((z >> 8) & 0xff0000);
1209
    out[2] = (z << 8) + ((y >> 8) & 0xff);
1210
#endif
1211
  }
1212
1213
  /* convert the remainder less efficiently */
1214
  for (out8 = (guint8 *) out, in8 = (const guint8 *) in, i = 0; i < (count & 3);
1215
      i++) {
1216
    guint8 x = in8[i + 0];
1217
    out8[i + 0] = in8[i + 2];
1218
    out8[i + 1] = in8[i + 1];
1219
    out8[i + 2] = x;
1220
  }
1221
}
1222
#endif
1223
1224
/* perform LE<->BE conversion on a block of @count 32-bit samples
1225
 * dst may equal src for in-place conversion
1226
 */
1227
static void
1228
converter_swap_endian_32 (gpointer dst, const gpointer src, gint count)
1229
0
{
1230
0
  guint32 *out = dst;
1231
0
  const guint32 *in = src;
1232
0
  gint i;
1233
1234
0
  for (i = 0; i < count; i++)
1235
0
    out[i] = GUINT32_SWAP_LE_BE (in[i]);
1236
0
}
1237
1238
/* perform LE<->BE conversion on a block of @count 64-bit samples
1239
 * dst may equal src for in-place conversion
1240
 */
1241
static void
1242
converter_swap_endian_64 (gpointer dst, const gpointer src, gint count)
1243
0
{
1244
0
  guint64 *out = dst;
1245
0
  const guint64 *in = src;
1246
0
  gint i;
1247
1248
0
  for (i = 0; i < count; i++)
1249
0
    out[i] = GUINT64_SWAP_LE_BE (in[i]);
1250
0
}
1251
1252
/* the worker function to perform endian-conversion only
1253
 * assuming finfo and foutinfo have the same depth
1254
 */
1255
static gboolean
1256
converter_endian (GstAudioConverter * convert,
1257
    GstAudioConverterFlags flags, gpointer in[], gsize in_frames,
1258
    gpointer out[], gsize out_frames)
1259
0
{
1260
0
  gint i;
1261
0
  AudioChain *chain;
1262
0
  gsize samples;
1263
1264
0
  chain = convert->chain_end;
1265
0
  samples = in_frames * chain->inc;
1266
1267
0
  GST_LOG ("convert endian: %" G_GSIZE_FORMAT " / %" G_GSIZE_FORMAT " samples",
1268
0
      in_frames, samples);
1269
1270
0
  if (in) {
1271
0
    for (i = 0; i < chain->blocks; i++)
1272
0
      convert->swap_endian (out[i], in[i], samples);
1273
0
  } else {
1274
0
    for (i = 0; i < chain->blocks; i++)
1275
0
      gst_audio_format_info_fill_silence (convert->in.finfo, out[i], samples);
1276
0
  }
1277
0
  return TRUE;
1278
0
}
1279
1280
static gboolean
1281
converter_generic (GstAudioConverter * convert,
1282
    GstAudioConverterFlags flags, gpointer in[], gsize in_frames,
1283
    gpointer out[], gsize out_frames)
1284
0
{
1285
0
  AudioChain *chain;
1286
0
  gpointer *tmp;
1287
0
  gint i;
1288
0
  gsize produced;
1289
1290
0
  chain = convert->chain_end;
1291
1292
0
  convert->in_writable = flags & GST_AUDIO_CONVERTER_FLAG_IN_WRITABLE;
1293
0
  convert->in_data = in;
1294
0
  convert->in_frames = in_frames;
1295
0
  convert->out_data = out;
1296
0
  convert->out_frames = out_frames;
1297
1298
  /* get frames to pack */
1299
0
  tmp = audio_chain_get_samples (chain, &produced);
1300
1301
0
  if (!convert->out_default && tmp && out) {
1302
0
    GST_LOG ("pack %p, %p %" G_GSIZE_FORMAT, tmp, out, produced);
1303
    /* and pack if needed */
1304
0
    for (i = 0; i < chain->blocks; i++)
1305
0
      convert->out.finfo->pack_func (convert->out.finfo, 0, tmp[i], out[i],
1306
0
          produced * chain->inc);
1307
0
  }
1308
0
  return TRUE;
1309
0
}
1310
1311
static gboolean
1312
converter_resample (GstAudioConverter * convert,
1313
    GstAudioConverterFlags flags, gpointer in[], gsize in_frames,
1314
    gpointer out[], gsize out_frames)
1315
0
{
1316
0
  gst_audio_resampler_resample (convert->resampler, in, in_frames, out,
1317
0
      out_frames);
1318
1319
0
  return TRUE;
1320
0
}
1321
1322
#define GST_AUDIO_FORMAT_IS_ENDIAN_CONVERSION(info1, info2) \
1323
0
    ( \
1324
0
      !(((info1)->flags ^ (info2)->flags) & (~GST_AUDIO_FORMAT_FLAG_UNPACK)) && \
1325
0
      (info1)->endianness != (info2)->endianness && \
1326
0
      (info1)->width == (info2)->width && \
1327
0
      (info1)->depth == (info2)->depth \
1328
0
    )
1329
1330
/**
1331
 * gst_audio_converter_new:
1332
 * @flags: extra #GstAudioConverterFlags
1333
 * @in_info: a source #GstAudioInfo
1334
 * @out_info: a destination #GstAudioInfo
1335
 * @config: (transfer full) (nullable): a #GstStructure with configuration options
1336
 *
1337
 * Create a new #GstAudioConverter that is able to convert between @in and @out
1338
 * audio formats.
1339
 *
1340
 * @config contains extra configuration options, see `GST_AUDIO_CONVERTER_OPT_*`
1341
 * parameters for details about the options and values.
1342
 *
1343
 * Returns: (nullable): a #GstAudioConverter or %NULL if conversion is not possible.
1344
 */
1345
GstAudioConverter *
1346
gst_audio_converter_new (GstAudioConverterFlags flags, GstAudioInfo * in_info,
1347
    GstAudioInfo * out_info, GstStructure * config)
1348
0
{
1349
0
  GstAudioConverter *convert;
1350
0
  AudioChain *prev;
1351
0
  const GValue *opt_matrix = NULL;
1352
1353
0
  g_return_val_if_fail (in_info != NULL, FALSE);
1354
0
  g_return_val_if_fail (out_info != NULL, FALSE);
1355
1356
0
  if (config)
1357
0
    opt_matrix =
1358
0
        gst_structure_get_value (config, GST_AUDIO_CONVERTER_OPT_MIX_MATRIX);
1359
1360
0
  if (opt_matrix
1361
0
      && !check_mix_matrix (in_info->channels, out_info->channels, opt_matrix))
1362
0
    goto invalid_mix_matrix;
1363
1364
0
  if ((GST_AUDIO_INFO_CHANNELS (in_info) != GST_AUDIO_INFO_CHANNELS (out_info))
1365
0
      && (GST_AUDIO_INFO_IS_UNPOSITIONED (in_info)
1366
0
          || GST_AUDIO_INFO_IS_UNPOSITIONED (out_info))
1367
0
      && !opt_matrix)
1368
0
    goto unpositioned;
1369
1370
0
  convert = g_new0 (GstAudioConverter, 1);
1371
1372
0
  convert->flags = flags;
1373
0
  convert->in = *in_info;
1374
0
  convert->out = *out_info;
1375
1376
  /* default config */
1377
0
  convert->config = gst_structure_new_static_str_empty ("GstAudioConverter");
1378
0
  if (config)
1379
0
    gst_audio_converter_update_config (convert, 0, 0, config);
1380
1381
0
  GST_INFO ("unitsizes: %d -> %d", in_info->bpf, out_info->bpf);
1382
1383
  /* step 1, unpack */
1384
0
  prev = chain_unpack (convert);
1385
  /* step 2, optional convert from S32 to F64 for channel mix */
1386
0
  prev = chain_convert_in (convert, prev);
1387
  /* step 3, channel mix */
1388
0
  prev = chain_mix (convert, prev);
1389
  /* step 4, resample */
1390
0
  prev = chain_resample (convert, prev);
1391
  /* step 5, optional convert for quantize */
1392
0
  prev = chain_convert_out (convert, prev);
1393
  /* step 6, optional quantize */
1394
0
  prev = chain_quantize (convert, prev);
1395
  /* step 7, change layout */
1396
0
  prev = chain_change_layout (convert, prev);
1397
  /* step 8, pack */
1398
0
  convert->chain_end = chain_pack (convert, prev);
1399
1400
0
  convert->convert = converter_generic;
1401
0
  convert->in_place = FALSE;
1402
0
  convert->passthrough = FALSE;
1403
1404
  /* optimize */
1405
0
  if (convert->mix_passthrough) {
1406
0
    if (out_info->finfo->format == in_info->finfo->format) {
1407
0
      if (convert->resampler == NULL) {
1408
0
        if (out_info->layout == in_info->layout) {
1409
0
          GST_INFO ("same formats, same layout, no resampler and "
1410
0
              "passthrough mixing -> passthrough");
1411
0
          convert->convert = converter_passthrough;
1412
0
          convert->in_place = TRUE;
1413
0
          convert->passthrough = TRUE;
1414
0
        }
1415
0
      } else {
1416
0
        if (is_intermediate_format (in_info->finfo->format)) {
1417
0
          GST_INFO ("same formats, and passthrough mixing -> only resampling");
1418
0
          convert->convert = converter_resample;
1419
0
        }
1420
0
      }
1421
0
    } else if (GST_AUDIO_FORMAT_IS_ENDIAN_CONVERSION (out_info->finfo,
1422
0
            in_info->finfo)) {
1423
0
      if (convert->resampler == NULL && out_info->layout == in_info->layout) {
1424
0
        GST_INFO ("no resampler, passthrough mixing -> only endian conversion");
1425
0
        convert->convert = converter_endian;
1426
0
        convert->in_place = TRUE;
1427
1428
0
        switch (GST_AUDIO_INFO_WIDTH (in_info)) {
1429
0
          case 16:
1430
0
            GST_DEBUG ("initializing 16-bit endian conversion");
1431
0
            convert->swap_endian = converter_swap_endian_16;
1432
0
            break;
1433
0
          case 24:
1434
0
            GST_DEBUG ("initializing 24-bit endian conversion");
1435
0
            convert->swap_endian = converter_swap_endian_24;
1436
0
            break;
1437
0
          case 32:
1438
0
            GST_DEBUG ("initializing 32-bit endian conversion");
1439
0
            convert->swap_endian = converter_swap_endian_32;
1440
0
            break;
1441
0
          case 64:
1442
0
            GST_DEBUG ("initializing 64-bit endian conversion");
1443
0
            convert->swap_endian = converter_swap_endian_64;
1444
0
            break;
1445
0
          default:
1446
0
            GST_ERROR ("unsupported sample width for endian conversion");
1447
0
            g_assert_not_reached ();
1448
0
        }
1449
0
      }
1450
0
    }
1451
0
  }
1452
1453
0
  setup_allocators (convert);
1454
1455
0
  return convert;
1456
1457
  /* ERRORS */
1458
0
unpositioned:
1459
0
  {
1460
0
    GST_WARNING ("unpositioned channels");
1461
0
    g_clear_pointer (&config, gst_structure_free);
1462
0
    return NULL;
1463
0
  }
1464
1465
0
invalid_mix_matrix:
1466
0
  {
1467
0
    GST_WARNING ("Invalid mix matrix");
1468
0
    g_clear_pointer (&config, gst_structure_free);
1469
0
    return NULL;
1470
0
  }
1471
0
}
1472
1473
/**
1474
 * gst_audio_converter_free:
1475
 * @convert: a #GstAudioConverter
1476
 *
1477
 * Free a previously allocated @convert instance.
1478
 */
1479
void
1480
gst_audio_converter_free (GstAudioConverter * convert)
1481
0
{
1482
0
  AudioChain *chain;
1483
1484
0
  g_return_if_fail (convert != NULL);
1485
1486
  /* walk the chain backwards and free all elements */
1487
0
  for (chain = convert->chain_end; chain;) {
1488
0
    AudioChain *prev = chain->prev;
1489
0
    audio_chain_free (chain);
1490
0
    chain = prev;
1491
0
  }
1492
1493
0
  if (convert->quant)
1494
0
    gst_audio_quantize_free (convert->quant);
1495
0
  if (convert->mix)
1496
0
    gst_audio_channel_mixer_free (convert->mix);
1497
0
  if (convert->resampler)
1498
0
    gst_audio_resampler_free (convert->resampler);
1499
0
  gst_audio_info_init (&convert->in);
1500
0
  gst_audio_info_init (&convert->out);
1501
1502
0
  gst_structure_free (convert->config);
1503
1504
0
  g_free (convert);
1505
0
}
1506
1507
/**
1508
 * gst_audio_converter_get_out_frames:
1509
 * @convert: a #GstAudioConverter
1510
 * @in_frames: number of input frames
1511
 *
1512
 * Calculate how many output frames can be produced when @in_frames input
1513
 * frames are given to @convert.
1514
 *
1515
 * Returns: the number of output frames
1516
 */
1517
gsize
1518
gst_audio_converter_get_out_frames (GstAudioConverter * convert,
1519
    gsize in_frames)
1520
0
{
1521
0
  if (convert->resampler)
1522
0
    return gst_audio_resampler_get_out_frames (convert->resampler, in_frames);
1523
0
  else
1524
0
    return in_frames;
1525
0
}
1526
1527
/**
1528
 * gst_audio_converter_get_in_frames:
1529
 * @convert: a #GstAudioConverter
1530
 * @out_frames: number of output frames
1531
 *
1532
 * Calculate how many input frames are currently needed by @convert to produce
1533
 * @out_frames of output frames.
1534
 *
1535
 * Returns: the number of input frames
1536
 */
1537
gsize
1538
gst_audio_converter_get_in_frames (GstAudioConverter * convert,
1539
    gsize out_frames)
1540
0
{
1541
0
  if (convert->resampler)
1542
0
    return gst_audio_resampler_get_in_frames (convert->resampler, out_frames);
1543
0
  else
1544
0
    return out_frames;
1545
0
}
1546
1547
/**
1548
 * gst_audio_converter_get_max_latency:
1549
 * @convert: a #GstAudioConverter
1550
 *
1551
 * Get the maximum number of input frames that the converter would
1552
 * need before producing output.
1553
 *
1554
 * Returns: the latency of @convert as expressed in the number of
1555
 * frames.
1556
 */
1557
gsize
1558
gst_audio_converter_get_max_latency (GstAudioConverter * convert)
1559
0
{
1560
0
  if (convert->resampler)
1561
0
    return gst_audio_resampler_get_max_latency (convert->resampler);
1562
0
  else
1563
0
    return 0;
1564
0
}
1565
1566
/**
1567
 * gst_audio_converter_reset:
1568
 * @convert: a #GstAudioConverter
1569
 *
1570
 * Reset @convert to the state it was when it was first created, clearing
1571
 * any history it might currently have.
1572
 */
1573
void
1574
gst_audio_converter_reset (GstAudioConverter * convert)
1575
0
{
1576
0
  if (convert->resampler)
1577
0
    gst_audio_resampler_reset (convert->resampler);
1578
0
  if (convert->quant)
1579
0
    gst_audio_quantize_reset (convert->quant);
1580
0
}
1581
1582
/**
1583
 * gst_audio_converter_samples:
1584
 * @convert: a #GstAudioConverter
1585
 * @flags: extra #GstAudioConverterFlags
1586
 * @in: (array) (element-type gpointer): input frames
1587
 * @in_frames: number of input frames
1588
 * @out: (array) (element-type gpointer): output frames
1589
 * @out_frames: number of output frames
1590
 *
1591
 * Perform the conversion with @in_frames in @in to @out_frames in @out
1592
 * using @convert.
1593
 *
1594
 * In case the samples are interleaved, @in and @out must point to an
1595
 * array with a single element pointing to a block of interleaved samples.
1596
 *
1597
 * If non-interleaved samples are used, @in and @out must point to an
1598
 * array with pointers to memory blocks, one for each channel.
1599
 *
1600
 * @in may be %NULL, in which case @in_frames of silence samples are processed
1601
 * by the converter.
1602
 *
1603
 * This function always produces @out_frames of output and consumes @in_frames of
1604
 * input. Use gst_audio_converter_get_out_frames() and
1605
 * gst_audio_converter_get_in_frames() to make sure @in_frames and @out_frames
1606
 * are matching and @in and @out point to enough memory.
1607
 *
1608
 * Returns: %TRUE is the conversion could be performed.
1609
 */
1610
gboolean
1611
gst_audio_converter_samples (GstAudioConverter * convert,
1612
    GstAudioConverterFlags flags, gpointer in[], gsize in_frames,
1613
    gpointer out[], gsize out_frames)
1614
0
{
1615
0
  g_return_val_if_fail (convert != NULL, FALSE);
1616
0
  g_return_val_if_fail (out != NULL, FALSE);
1617
1618
0
  if (in_frames == 0) {
1619
0
    GST_LOG ("skipping empty buffer");
1620
0
    return TRUE;
1621
0
  }
1622
0
  return convert->convert (convert, flags, in, in_frames, out, out_frames);
1623
0
}
1624
1625
/**
1626
 * gst_audio_converter_convert:
1627
 * @convert: a #GstAudioConverter
1628
 * @flags: extra #GstAudioConverterFlags
1629
 * @in: (array length=in_size) (element-type guint8): input data
1630
 * @in_size: size of @in
1631
 * @out: (out) (array length=out_size) (element-type guint8): a pointer where
1632
 *  the output data will be written
1633
 * @out_size: (out): a pointer where the size of @out will be written
1634
 *
1635
 * Convenience wrapper around gst_audio_converter_samples(), which will
1636
 * perform allocation of the output buffer based on the result from
1637
 * gst_audio_converter_get_out_frames().
1638
 *
1639
 * Returns: %TRUE is the conversion could be performed.
1640
 *
1641
 * Since: 1.14
1642
 */
1643
gboolean
1644
gst_audio_converter_convert (GstAudioConverter * convert,
1645
    GstAudioConverterFlags flags, gpointer in, gsize in_size,
1646
    gpointer * out, gsize * out_size)
1647
0
{
1648
0
  gsize in_frames;
1649
0
  gsize out_frames;
1650
1651
0
  g_return_val_if_fail (convert != NULL, FALSE);
1652
0
  g_return_val_if_fail (flags ^ GST_AUDIO_CONVERTER_FLAG_IN_WRITABLE, FALSE);
1653
1654
0
  in_frames = in_size / convert->in.bpf;
1655
0
  out_frames = gst_audio_converter_get_out_frames (convert, in_frames);
1656
1657
0
  *out_size = out_frames * convert->out.bpf;
1658
0
  *out = g_malloc0 (*out_size);
1659
1660
0
  return gst_audio_converter_samples (convert, flags, &in, in_frames, out,
1661
0
      out_frames);
1662
0
}
1663
1664
/**
1665
 * gst_audio_converter_supports_inplace:
1666
 * @convert: a #GstAudioConverter
1667
 *
1668
 * Returns whether the audio converter can perform the conversion in-place.
1669
 * The return value would be typically input to gst_base_transform_set_in_place()
1670
 *
1671
 * Returns: %TRUE when the conversion can be done in place.
1672
 *
1673
 * Since: 1.12
1674
 */
1675
gboolean
1676
gst_audio_converter_supports_inplace (GstAudioConverter * convert)
1677
0
{
1678
0
  return convert->in_place;
1679
0
}
1680
1681
/**
1682
 * gst_audio_converter_is_passthrough:
1683
 *
1684
 * Returns whether the audio converter will operate in passthrough mode.
1685
 * The return value would be typically input to gst_base_transform_set_passthrough()
1686
 *
1687
 * Returns: %TRUE when no conversion will actually occur.
1688
 *
1689
 * Since: 1.16
1690
 */
1691
gboolean
1692
gst_audio_converter_is_passthrough (GstAudioConverter * convert)
1693
0
{
1694
0
  return convert->passthrough;
1695
0
}