audio-converter.c 42.1 KB
Newer Older
1
2
/* GStreamer
 * 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
8
9
10
11
12
13
14
15
16
17
18
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
19
20
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
21
22
23
24
25
26
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

27
#include <math.h>
28
29
#include <string.h>

30
31
#include "audio-converter.h"
#include "gstaudiopack.h"
32

33
34
/**
 * SECTION:audioconverter
35
 * @title: GstAudioConverter
36
37
38
39
 * @short_description: Generic audio conversion
 *
 * This object is used to convert audio samples from one format to another.
 * The object can perform conversion of:
40
41
42
43
44
45
46
 *
 *  * audio format with optional dithering and noise shaping
 *
 *  * audio samplerate
 *
 *  * audio channels and channel layout
 *
47
48
 */

Wim Taymans's avatar
Wim Taymans committed
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#ifndef GST_DISABLE_GST_DEBUG
#define GST_CAT_DEFAULT ensure_debug_category()
static GstDebugCategory *
ensure_debug_category (void)
{
  static gsize cat_gonce = 0;

  if (g_once_init_enter (&cat_gonce)) {
    gsize cat_done;

    cat_done = (gsize) _gst_debug_category_new ("audio-converter", 0,
        "audio-converter object");

    g_once_init_leave (&cat_gonce, cat_done);
  }

  return (GstDebugCategory *) cat_gonce;
}
#else
#define ensure_debug_category() /* NOOP */
#endif /* GST_DISABLE_GST_DEBUG */

71
typedef struct _AudioChain AudioChain;
72

73
typedef void (*AudioConvertFunc) (gpointer dst, const gpointer src, gint count);
74
75
76
typedef gboolean (*AudioConvertSamplesFunc) (GstAudioConverter * convert,
    GstAudioConverterFlags flags, gpointer in[], gsize in_frames,
    gpointer out[], gsize out_frames);
77
78
typedef void (*AudioConvertEndianFunc) (gpointer dst, const gpointer src,
    gint count);
79

80
/*                           int/int    int/float  float/int float/float
81
 *
82
83
84
85
86
87
 *  unpack                     S32          S32         F64       F64
 *  convert                               S32->F64
 *  channel mix                S32          F64         F64       F64
 *  convert                                           F64->S32
 *  quantize                   S32                      S32
 *  pack                       S32          F64         S32       F64
88
89
90
91
92
 *
 *
 *  interleave
 *  deinterleave
 *  resample
93
 */
94
95
96
97
98
99
100
struct _GstAudioConverter
{
  GstAudioInfo in;
  GstAudioInfo out;

  GstStructure *config;

101
102
103
104
105
  GstAudioConverterFlags flags;
  GstAudioFormat current_format;
  GstAudioLayout current_layout;
  gint current_channels;

106
  gboolean in_writable;
107
  gpointer *in_data;
108
  gsize in_frames;
109
  gpointer *out_data;
110
  gsize out_frames;
111

112
113
  gboolean in_place;            /* the conversion can be done in place; returned by gst_audio_converter_supports_inplace() */

114
  /* unpack */
115
  gboolean in_default;
116
  gboolean unpack_ip;
117

118
  /* convert in */
119
120
  AudioConvertFunc convert_in;

121
  /* channel mix */
122
  gboolean mix_passthrough;
123
  GstAudioChannelMixer *mix;
124

Wim Taymans's avatar
Wim Taymans committed
125
126
127
  /* resample */
  GstAudioResampler *resampler;

128
  /* convert out */
129
130
  AudioConvertFunc convert_out;

131
  /* quant */
132
133
  GstAudioQuantize *quant;

134
  /* pack */
135
  gboolean out_default;
136
  AudioChain *chain_end;        /* NULL for empty chain or points to the last element in the chain */
137

138
139
140
  /* endian swap */
  AudioConvertEndianFunc swap_endian;

141
  AudioConvertSamplesFunc convert;
142
143
};

144
145
146
147
148
149
150
151
152
153
154
155
156
157
static GstAudioConverter *
gst_audio_converter_copy (GstAudioConverter * convert)
{
  GstAudioConverter *res =
      gst_audio_converter_new (convert->flags, &convert->in, &convert->out,
      convert->config);

  return res;
}

G_DEFINE_BOXED_TYPE (GstAudioConverter, gst_audio_converter,
    (GBoxedCopyFunc) gst_audio_converter_copy,
    (GBoxedFreeFunc) gst_audio_converter_free);

158
typedef gboolean (*AudioChainFunc) (AudioChain * chain, gpointer user_data);
Stefan Sauer's avatar
Stefan Sauer committed
159
typedef gpointer *(*AudioChainAllocFunc) (AudioChain * chain, gsize num_samples,
160
161
162
163
164
    gpointer user_data);

struct _AudioChain
{
  AudioChain *prev;
165

166
167
168
169
  AudioChainFunc make_func;
  gpointer make_func_data;
  GDestroyNotify make_func_notify;

170
  const GstAudioFormatInfo *finfo;
171
172
173
174
175
176
177
178
179
180
181
  gint stride;
  gint inc;
  gint blocks;

  gboolean pass_alloc;
  gboolean allow_ip;

  AudioChainAllocFunc alloc_func;
  gpointer alloc_data;

  gpointer *tmp;
182
  gsize allocated_samples;
183
184

  gpointer *samples;
185
  gsize num_samples;
186
187
};

188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
static AudioChain *
audio_chain_new (AudioChain * prev, GstAudioConverter * convert)
{
  AudioChain *chain;

  chain = g_slice_new0 (AudioChain);
  chain->prev = prev;

  if (convert->current_layout == GST_AUDIO_LAYOUT_NON_INTERLEAVED) {
    chain->inc = 1;
    chain->blocks = convert->current_channels;
  } else {
    chain->inc = convert->current_channels;
    chain->blocks = 1;
  }
203
204
  chain->finfo = gst_audio_format_get_info (convert->current_format);
  chain->stride = (chain->finfo->width * chain->inc) / 8;
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228

  return chain;
}

static void
audio_chain_set_make_func (AudioChain * chain,
    AudioChainFunc make_func, gpointer user_data, GDestroyNotify notify)
{
  chain->make_func = make_func;
  chain->make_func_data = user_data;
  chain->make_func_notify = notify;
}

static void
audio_chain_free (AudioChain * chain)
{
  GST_LOG ("free chain %p", chain);
  if (chain->make_func_notify)
    chain->make_func_notify (chain->make_func_data);
  g_free (chain->tmp);
  g_slice_free (AudioChain, chain);
}

static gpointer *
Wim Taymans's avatar
Wim Taymans committed
229
audio_chain_alloc_samples (AudioChain * chain, gsize num_samples)
230
{
Wim Taymans's avatar
Wim Taymans committed
231
  return chain->alloc_func (chain, num_samples, chain->alloc_data);
232
233
}

234
235
236
237
static void
audio_chain_set_samples (AudioChain * chain, gpointer * samples,
    gsize num_samples)
{
Wim Taymans's avatar
Wim Taymans committed
238
  GST_LOG ("set samples %p %" G_GSIZE_FORMAT, samples, num_samples);
239
240
241
242
243

  chain->samples = samples;
  chain->num_samples = num_samples;
}

244
static gpointer *
245
audio_chain_get_samples (AudioChain * chain, gsize * avail)
246
247
248
249
{
  gpointer *res;

  while (!chain->samples)
250
    chain->make_func (chain, chain->make_func_data);
251
252

  res = chain->samples;
253
  *avail = chain->num_samples;
254
255
256
257
258
  chain->samples = NULL;

  return res;
}

259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
/*
static guint
get_opt_uint (GstAudioConverter * convert, const gchar * opt, guint def)
{
  guint res;
  if (!gst_structure_get_uint (convert->config, opt, &res))
    res = def;
  return res;
}
*/

static gint
get_opt_enum (GstAudioConverter * convert, const gchar * opt, GType type,
    gint def)
{
  gint res;
  if (!gst_structure_get_enum (convert->config, opt, type, &res))
    res = def;
  return res;
}

280
281
282
283
284
285
static const GValue *
get_opt_value (GstAudioConverter * convert, const gchar * opt)
{
  return gst_structure_get_value (convert->config, opt);
}

Wim Taymans's avatar
Wim Taymans committed
286
#define DEFAULT_OPT_RESAMPLER_METHOD GST_AUDIO_RESAMPLER_METHOD_BLACKMAN_NUTTALL
287
288
289
290
#define DEFAULT_OPT_DITHER_METHOD GST_AUDIO_DITHER_NONE
#define DEFAULT_OPT_NOISE_SHAPING_METHOD GST_AUDIO_NOISE_SHAPING_NONE
#define DEFAULT_OPT_QUANTIZATION 1

Wim Taymans's avatar
Wim Taymans committed
291
292
293
#define GET_OPT_RESAMPLER_METHOD(c) get_opt_enum(c, \
    GST_AUDIO_CONVERTER_OPT_RESAMPLER_METHOD, GST_TYPE_AUDIO_RESAMPLER_METHOD, \
    DEFAULT_OPT_RESAMPLER_METHOD)
294
295
296
297
298
299
300
301
#define GET_OPT_DITHER_METHOD(c) get_opt_enum(c, \
    GST_AUDIO_CONVERTER_OPT_DITHER_METHOD, GST_TYPE_AUDIO_DITHER_METHOD, \
    DEFAULT_OPT_DITHER_METHOD)
#define GET_OPT_NOISE_SHAPING_METHOD(c) get_opt_enum(c, \
    GST_AUDIO_CONVERTER_OPT_NOISE_SHAPING_METHOD, GST_TYPE_AUDIO_NOISE_SHAPING_METHOD, \
    DEFAULT_OPT_NOISE_SHAPING_METHOD)
#define GET_OPT_QUANTIZATION(c) get_opt_uint(c, \
    GST_AUDIO_CONVERTER_OPT_QUANTIZATION, DEFAULT_OPT_QUANTIZATION)
302
303
#define GET_OPT_MIX_MATRIX(c) get_opt_value(c, \
    GST_AUDIO_CONVERTER_OPT_MIX_MATRIX)
304
305
306
307
308
309
310
311
312
313
314
315

static gboolean
copy_config (GQuark field_id, const GValue * value, gpointer user_data)
{
  GstAudioConverter *convert = user_data;

  gst_structure_id_set_value (convert->config, field_id, value);

  return TRUE;
}

/**
316
 * gst_audio_converter_update_config:
317
 * @convert: a #GstAudioConverter
318
319
 * @in_rate: input rate
 * @out_rate: output rate
320
 * @config: (transfer full) (allow-none): a #GstStructure or %NULL
321
 *
322
 * Set @in_rate, @out_rate and @config as extra configuration for @convert.
323
 *
Wim Taymans's avatar
Wim Taymans committed
324
 * @in_rate and @out_rate specify the new sample rates of input and output
325
 * formats. A value of 0 leaves the sample rate unchanged.
326
 *
327
328
329
 * @config can be %NULL, in which case, the current configuration is not
 * changed.
 *
330
331
332
333
334
335
336
 * If the parameters in @config can not be set exactly, this function returns
 * %FALSE and will try to update as much state as possible. The new state can
 * then be retrieved and refined with gst_audio_converter_get_config().
 *
 * Look at the #GST_AUDIO_CONVERTER_OPT_* fields to check valid configuration
 * option and values.
 *
337
 * Returns: %TRUE when the new parameters could be set
338
 */
339
gboolean
340
341
gst_audio_converter_update_config (GstAudioConverter * convert,
    gint in_rate, gint out_rate, GstStructure * config)
342
343
{
  g_return_val_if_fail (convert != NULL, FALSE);
344
345
346
347
348
349
350
351
352
353
354
355
  g_return_val_if_fail ((in_rate == 0 && out_rate == 0) ||
      convert->flags & GST_AUDIO_CONVERTER_FLAG_VARIABLE_RATE, FALSE);

  GST_LOG ("new rate %d -> %d", in_rate, out_rate);

  if (in_rate <= 0)
    in_rate = convert->in.rate;
  if (out_rate <= 0)
    out_rate = convert->out.rate;

  convert->in.rate = in_rate;
  convert->out.rate = out_rate;
356

357
358
359
  if (convert->resampler)
    gst_audio_resampler_update (convert->resampler, in_rate, out_rate, config);

360
361
362
363
  if (config) {
    gst_structure_foreach (config, copy_config, convert);
    gst_structure_free (config);
  }
364
365
366
367
368
369
370

  return TRUE;
}

/**
 * gst_audio_converter_get_config:
 * @convert: a #GstAudioConverter
371
372
 * @in_rate: result input rate
 * @out_rate: result output rate
373
374
375
376
 *
 * Get the current configuration of @convert.
 *
 * Returns: a #GstStructure that remains valid for as long as @convert is valid
377
 *   or until gst_audio_converter_update_config() is called.
378
379
 */
const GstStructure *
380
381
gst_audio_converter_get_config (GstAudioConverter * convert,
    gint * in_rate, gint * out_rate)
382
383
384
{
  g_return_val_if_fail (convert != NULL, NULL);

385
386
387
388
389
  if (in_rate)
    *in_rate = convert->in.rate;
  if (out_rate)
    *out_rate = convert->out.rate;

390
391
392
  return convert->config;
}

393
394
395
396
397
398
399
400
401
402
403
static gpointer *
get_output_samples (AudioChain * chain, gsize num_samples, gpointer user_data)
{
  GstAudioConverter *convert = user_data;

  GST_LOG ("output samples %p %" G_GSIZE_FORMAT, convert->out_data,
      num_samples);

  return convert->out_data;
}

404
405
406
#define MEM_ALIGN(m,a) ((gint8 *)((guintptr)((gint8 *)(m) + ((a)-1)) & ~((a)-1)))
#define ALIGN 16

407
408
409
static gpointer *
get_temp_samples (AudioChain * chain, gsize num_samples, gpointer user_data)
{
410
  if (num_samples > chain->allocated_samples) {
411
    gint i;
412
413
414
415
    gint8 *s;
    gsize stride = GST_ROUND_UP_N (num_samples * chain->stride, ALIGN);
    /* first part contains the pointers, second part the data, add some extra bytes
     * for alignement */
Wim Taymans's avatar
Wim Taymans committed
416
    gsize needed = (stride + sizeof (gpointer)) * chain->blocks + ALIGN - 1;
417

418
419
    GST_DEBUG ("alloc samples %d %" G_GSIZE_FORMAT " %" G_GSIZE_FORMAT,
        chain->stride, num_samples, needed);
420
    chain->tmp = g_realloc (chain->tmp, needed);
421
    chain->allocated_samples = num_samples;
422

423
424
    /* pointer to the data, make sure it's 16 bytes aligned */
    s = MEM_ALIGN (&chain->tmp[chain->blocks], ALIGN);
425
426
427

    /* set up the pointers */
    for (i = 0; i < chain->blocks; i++)
428
      chain->tmp[i] = s + i * stride;
429
430
431
432
433
434
  }
  GST_LOG ("temp samples %p %" G_GSIZE_FORMAT, chain->tmp, num_samples);

  return chain->tmp;
}

435
static gboolean
436
do_unpack (AudioChain * chain, gpointer user_data)
437
{
438
  GstAudioConverter *convert = user_data;
439
  gsize num_samples;
440
  gpointer *tmp;
441
  gboolean in_writable;
442

443
  in_writable = convert->in_writable;
444
  num_samples = convert->in_frames;
445

446
  if (!chain->allow_ip || !in_writable || !convert->in_default) {
447
    gint i;
448

449
    if (in_writable && chain->allow_ip) {
450
      tmp = convert->in_data;
451
452
      GST_LOG ("unpack in-place %p, %" G_GSIZE_FORMAT, tmp, num_samples);
    } else {
Stefan Sauer's avatar
Stefan Sauer committed
453
      tmp = audio_chain_alloc_samples (chain, num_samples);
454
455
      GST_LOG ("unpack to tmp %p, %" G_GSIZE_FORMAT, tmp, num_samples);
    }
456

457
458
    if (convert->in_data) {
      for (i = 0; i < chain->blocks; i++) {
459
460
461
462
463
464
465
466
467
468
469
        if (convert->in_default) {
          GST_LOG ("copy %p, %p, %" G_GSIZE_FORMAT, tmp[i], convert->in_data[i],
              num_samples);
          memcpy (tmp[i], convert->in_data[i], num_samples * chain->stride);
        } else {
          GST_LOG ("unpack %p, %p, %" G_GSIZE_FORMAT, tmp[i],
              convert->in_data[i], num_samples);
          convert->in.finfo->unpack_func (convert->in.finfo,
              GST_AUDIO_PACK_FLAG_TRUNCATE_RANGE, tmp[i], convert->in_data[i],
              num_samples * chain->inc);
        }
470
471
472
473
474
475
      }
    } else {
      for (i = 0; i < chain->blocks; i++) {
        gst_audio_format_fill_silence (chain->finfo, tmp[i],
            num_samples * chain->inc);
      }
476
477
478
479
480
    }
  } else {
    tmp = convert->in_data;
    GST_LOG ("get in samples %p", tmp);
  }
481
  audio_chain_set_samples (chain, tmp, num_samples);
482

483
484
  return TRUE;
}
485

486
static gboolean
487
do_convert_in (AudioChain * chain, gpointer user_data)
488
{
489
  gsize num_samples;
Wim Taymans's avatar
Wim Taymans committed
490
  GstAudioConverter *convert = user_data;
491
492
  gpointer *in, *out;
  gint i;
493

494
  in = audio_chain_get_samples (chain->prev, &num_samples);
Stefan Sauer's avatar
Stefan Sauer committed
495
496
  out = (chain->allow_ip ? in : audio_chain_alloc_samples (chain, num_samples));
  GST_LOG ("convert in %p, %p, %" G_GSIZE_FORMAT, in, out, num_samples);
497

498
  for (i = 0; i < chain->blocks; i++)
Stefan Sauer's avatar
Stefan Sauer committed
499
    convert->convert_in (out[i], in[i], num_samples * chain->inc);
500

501
  audio_chain_set_samples (chain, out, num_samples);
Wim Taymans's avatar
Wim Taymans committed
502

503
504
  return TRUE;
}
505

506
static gboolean
507
do_mix (AudioChain * chain, gpointer user_data)
508
{
509
  gsize num_samples;
Wim Taymans's avatar
Wim Taymans committed
510
  GstAudioConverter *convert = user_data;
511
  gpointer *in, *out;
512

513
  in = audio_chain_get_samples (chain->prev, &num_samples);
Stefan Sauer's avatar
Stefan Sauer committed
514
515
  out = (chain->allow_ip ? in : audio_chain_alloc_samples (chain, num_samples));
  GST_LOG ("mix %p, %p, %" G_GSIZE_FORMAT, in, out, num_samples);
516

517
  gst_audio_channel_mixer_samples (convert->mix, in, out, num_samples);
518

519
  audio_chain_set_samples (chain, out, num_samples);
520
521
522
523

  return TRUE;
}

Wim Taymans's avatar
Wim Taymans committed
524
525
526
527
528
static gboolean
do_resample (AudioChain * chain, gpointer user_data)
{
  GstAudioConverter *convert = user_data;
  gpointer *in, *out;
Wim Taymans's avatar
Wim Taymans committed
529
  gsize in_frames, out_frames;
Wim Taymans's avatar
Wim Taymans committed
530
531

  in = audio_chain_get_samples (chain->prev, &in_frames);
532
  out_frames = convert->out_frames;
Wim Taymans's avatar
Wim Taymans committed
533
  out = (chain->allow_ip ? in : audio_chain_alloc_samples (chain, out_frames));
Wim Taymans's avatar
Wim Taymans committed
534

Wim Taymans's avatar
Wim Taymans committed
535
536
  GST_LOG ("resample %p %p,%" G_GSIZE_FORMAT " %" G_GSIZE_FORMAT, in,
      out, in_frames, out_frames);
Wim Taymans's avatar
Wim Taymans committed
537
538

  gst_audio_resampler_resample (convert->resampler, in, in_frames, out,
Wim Taymans's avatar
Wim Taymans committed
539
      out_frames);
Wim Taymans's avatar
Wim Taymans committed
540

Wim Taymans's avatar
Wim Taymans committed
541
  audio_chain_set_samples (chain, out, out_frames);
Wim Taymans's avatar
Wim Taymans committed
542
543
544
545

  return TRUE;
}

546
static gboolean
547
do_convert_out (AudioChain * chain, gpointer user_data)
548
549
{
  GstAudioConverter *convert = user_data;
550
  gsize num_samples;
551
552
553
  gpointer *in, *out;
  gint i;

554
  in = audio_chain_get_samples (chain->prev, &num_samples);
Stefan Sauer's avatar
Stefan Sauer committed
555
  out = (chain->allow_ip ? in : audio_chain_alloc_samples (chain, num_samples));
Wim Taymans's avatar
Wim Taymans committed
556
  GST_LOG ("convert out %p, %p %" G_GSIZE_FORMAT, in, out, num_samples);
557
558

  for (i = 0; i < chain->blocks; i++)
Stefan Sauer's avatar
Stefan Sauer committed
559
    convert->convert_out (out[i], in[i], num_samples * chain->inc);
560

561
  audio_chain_set_samples (chain, out, num_samples);
562
563
564
565
566

  return TRUE;
}

static gboolean
567
do_quantize (AudioChain * chain, gpointer user_data)
568
569
{
  GstAudioConverter *convert = user_data;
570
  gsize num_samples;
571
572
  gpointer *in, *out;

573
  in = audio_chain_get_samples (chain->prev, &num_samples);
Stefan Sauer's avatar
Stefan Sauer committed
574
  out = (chain->allow_ip ? in : audio_chain_alloc_samples (chain, num_samples));
Wim Taymans's avatar
Wim Taymans committed
575
  GST_LOG ("quantize %p, %p %" G_GSIZE_FORMAT, in, out, num_samples);
576

Stefan Sauer's avatar
Stefan Sauer committed
577
  gst_audio_quantize_samples (convert->quant, in, out, num_samples);
578

579
  audio_chain_set_samples (chain, out, num_samples);
580
581
582
583

  return TRUE;
}

584
585
586
587
588
589
590
591
static gboolean
is_intermediate_format (GstAudioFormat format)
{
  return (format == GST_AUDIO_FORMAT_S16 ||
      format == GST_AUDIO_FORMAT_S32 ||
      format == GST_AUDIO_FORMAT_F32 || format == GST_AUDIO_FORMAT_F64);
}

592
593
594
595
596
static AudioChain *
chain_unpack (GstAudioConverter * convert)
{
  AudioChain *prev;
  GstAudioInfo *in = &convert->in;
597
598
  GstAudioInfo *out = &convert->out;
  gboolean same_format;
599

600
601
602
603
604
605
606
607
608
  same_format = in->finfo->format == out->finfo->format;

  /* do not unpack if we have the same input format as the output format
   * and it is a possible intermediate format */
  if (same_format && is_intermediate_format (in->finfo->format)) {
    convert->current_format = in->finfo->format;
  } else {
    convert->current_format = in->finfo->unpack_format;
  }
609
610
  convert->current_layout = in->layout;
  convert->current_channels = in->channels;
611
612

  convert->in_default = convert->current_format == in->finfo->format;
613

614
615
  GST_INFO ("unpack format %s to %s",
      gst_audio_format_to_string (in->finfo->format),
616
617
      gst_audio_format_to_string (convert->current_format));

618
  prev = audio_chain_new (NULL, convert);
619
  prev->allow_ip = prev->finfo->width <= in->finfo->width;
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
  prev->pass_alloc = FALSE;
  audio_chain_set_make_func (prev, do_unpack, convert, NULL);

  return prev;
}

static AudioChain *
chain_convert_in (GstAudioConverter * convert, AudioChain * prev)
{
  gboolean in_int, out_int;
  GstAudioInfo *in = &convert->in;
  GstAudioInfo *out = &convert->out;

  in_int = GST_AUDIO_FORMAT_INFO_IS_INTEGER (in->finfo);
  out_int = GST_AUDIO_FORMAT_INFO_IS_INTEGER (out->finfo);
635

636
637
  if (in_int && !out_int) {
    GST_INFO ("convert S32 to F64");
638
    convert->convert_in = (AudioConvertFunc) audio_orc_s32_to_double;
639
640
    convert->current_format = GST_AUDIO_FORMAT_F64;

641
    prev = audio_chain_new (prev, convert);
642
643
644
    prev->allow_ip = FALSE;
    prev->pass_alloc = FALSE;
    audio_chain_set_make_func (prev, do_convert_in, convert, NULL);
645
  }
646
647
648
  return prev;
}

649
650
651
652
653
static gboolean
check_mix_matrix (guint in_channels, guint out_channels, const GValue * value)
{
  guint i, j;

654
655
656
657
  /* audio-channel-mixer will generate an identity matrix */
  if (gst_value_array_get_size (value) == 0)
    return TRUE;

658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
  if (gst_value_array_get_size (value) != out_channels) {
    GST_ERROR ("Invalid mix matrix size, should be %d", out_channels);
    goto fail;
  }

  for (j = 0; j < out_channels; j++) {
    const GValue *row = gst_value_array_get_value (value, j);

    if (gst_value_array_get_size (row) != in_channels) {
      GST_ERROR ("Invalid mix matrix row size, should be %d", in_channels);
      goto fail;
    }

    for (i = 0; i < in_channels; i++) {
      const GValue *itm;

      itm = gst_value_array_get_value (row, i);
      if (!G_VALUE_HOLDS_FLOAT (itm)) {
        GST_ERROR ("Invalid mix matrix element type, should be float");
        goto fail;
      }
    }
  }

  return TRUE;

fail:
  return FALSE;
}

static gfloat **
mix_matrix_from_g_value (guint in_channels, guint out_channels,
    const GValue * value)
{
  guint i, j;
  gfloat **matrix = g_new (gfloat *, in_channels);

  for (i = 0; i < in_channels; i++)
    matrix[i] = g_new (gfloat, out_channels);

  for (j = 0; j < out_channels; j++) {
    const GValue *row = gst_value_array_get_value (value, j);

    for (i = 0; i < in_channels; i++) {
      const GValue *itm;
      gfloat coefficient;

      itm = gst_value_array_get_value (row, i);
      coefficient = g_value_get_float (itm);
      matrix[i][j] = coefficient;
    }
  }

  return matrix;
}

714
715
716
717
718
719
static AudioChain *
chain_mix (GstAudioConverter * convert, AudioChain * prev)
{
  GstAudioInfo *in = &convert->in;
  GstAudioInfo *out = &convert->out;
  GstAudioFormat format = convert->current_format;
720
  const GValue *opt_matrix = GET_OPT_MIX_MATRIX (convert);
721
722

  convert->current_channels = out->channels;
723

724
  if (opt_matrix) {
725
726
727
728
729
    gfloat **matrix = NULL;

    if (gst_value_array_get_size (opt_matrix))
      matrix =
          mix_matrix_from_g_value (in->channels, out->channels, opt_matrix);
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748

    convert->mix =
        gst_audio_channel_mixer_new_with_matrix (0, format, in->channels,
        out->channels, matrix);
  } else {
    GstAudioChannelMixerFlags flags;

    flags =
        GST_AUDIO_INFO_IS_UNPOSITIONED (in) ?
        GST_AUDIO_CHANNEL_MIXER_FLAGS_UNPOSITIONED_IN : 0;
    flags |=
        GST_AUDIO_INFO_IS_UNPOSITIONED (out) ?
        GST_AUDIO_CHANNEL_MIXER_FLAGS_UNPOSITIONED_OUT : 0;

    convert->mix =
        gst_audio_channel_mixer_new (flags, format, in->channels, in->position,
        out->channels, out->position);
  }

749
  convert->mix_passthrough =
750
      gst_audio_channel_mixer_is_passthrough (convert->mix);
751
  GST_INFO ("mix format %s, passthrough %d, in_channels %d, out_channels %d",
752
      gst_audio_format_to_string (format), convert->mix_passthrough,
753
754
      in->channels, out->channels);

755
  if (!convert->mix_passthrough) {
756
    prev = audio_chain_new (prev, convert);
757
758
    prev->allow_ip = FALSE;
    prev->pass_alloc = FALSE;
759
760
761
762
763
    audio_chain_set_make_func (prev, do_mix, convert, NULL);
  }
  return prev;
}

Wim Taymans's avatar
Wim Taymans committed
764
765
766
767
768
769
770
771
772
static AudioChain *
chain_resample (GstAudioConverter * convert, AudioChain * prev)
{
  GstAudioInfo *in = &convert->in;
  GstAudioInfo *out = &convert->out;
  GstAudioResamplerMethod method;
  GstAudioResamplerFlags flags;
  GstAudioFormat format = convert->current_format;
  gint channels = convert->current_channels;
773
  gboolean variable_rate;
Wim Taymans's avatar
Wim Taymans committed
774

775
776
777
  variable_rate = convert->flags & GST_AUDIO_CONVERTER_FLAG_VARIABLE_RATE;

  if (in->rate != out->rate || variable_rate) {
Wim Taymans's avatar
Wim Taymans committed
778
779
780
    method = GET_OPT_RESAMPLER_METHOD (convert);

    flags = 0;
781
782
783
784
    if (convert->current_layout == GST_AUDIO_LAYOUT_NON_INTERLEAVED) {
      flags |= GST_AUDIO_RESAMPLER_FLAG_NON_INTERLEAVED_IN;
      flags |= GST_AUDIO_RESAMPLER_FLAG_NON_INTERLEAVED_OUT;
    }
785
786
    if (variable_rate)
      flags |= GST_AUDIO_RESAMPLER_FLAG_VARIABLE_RATE;
Wim Taymans's avatar
Wim Taymans committed
787
788
789
790
791

    convert->resampler =
        gst_audio_resampler_new (method, flags, format, channels, in->rate,
        out->rate, convert->config);

792
    prev = audio_chain_new (prev, convert);
Wim Taymans's avatar
Wim Taymans committed
793
794
795
796
797
798
799
    prev->allow_ip = FALSE;
    prev->pass_alloc = FALSE;
    audio_chain_set_make_func (prev, do_resample, convert, NULL);
  }
  return prev;
}

800
801
802
803
804
805
806
807
808
809
static AudioChain *
chain_convert_out (GstAudioConverter * convert, AudioChain * prev)
{
  gboolean in_int, out_int;
  GstAudioInfo *in = &convert->in;
  GstAudioInfo *out = &convert->out;

  in_int = GST_AUDIO_FORMAT_INFO_IS_INTEGER (in->finfo);
  out_int = GST_AUDIO_FORMAT_INFO_IS_INTEGER (out->finfo);

810
  if (!in_int && out_int) {
811
    convert->convert_out = (AudioConvertFunc) audio_orc_double_to_s32;
812
813
814
    convert->current_format = GST_AUDIO_FORMAT_S32;

    GST_INFO ("convert F64 to S32");
815
    prev = audio_chain_new (prev, convert);
816
817
818
    prev->allow_ip = TRUE;
    prev->pass_alloc = FALSE;
    audio_chain_set_make_func (prev, do_convert_out, convert, NULL);
819
  }
820
821
822
823
824
825
  return prev;
}

static AudioChain *
chain_quantize (GstAudioConverter * convert, AudioChain * prev)
{
826
  const GstAudioFormatInfo *cur_finfo;
827
828
829
830
831
832
833
834
835
  GstAudioInfo *out = &convert->out;
  gint in_depth, out_depth;
  gboolean in_int, out_int;
  GstAudioDitherMethod dither;
  GstAudioNoiseShapingMethod ns;

  dither = GET_OPT_DITHER_METHOD (convert);
  ns = GET_OPT_NOISE_SHAPING_METHOD (convert);

836
837
838
  cur_finfo = gst_audio_format_get_info (convert->current_format);

  in_depth = GST_AUDIO_FORMAT_INFO_DEPTH (cur_finfo);
839
840
841
  out_depth = GST_AUDIO_FORMAT_INFO_DEPTH (out->finfo);
  GST_INFO ("depth in %d, out %d", in_depth, out_depth);

842
  in_int = GST_AUDIO_FORMAT_INFO_IS_INTEGER (cur_finfo);
843
844
  out_int = GST_AUDIO_FORMAT_INFO_IS_INTEGER (out->finfo);

845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
  /* Don't dither or apply noise shaping if target depth is bigger than 20 bits
   * as DA converters only can do a SNR up to 20 bits in reality.
   * Also don't dither or apply noise shaping if target depth is larger than
   * source depth. */
  if (out_depth > 20 || (in_int && out_depth >= in_depth)) {
    dither = GST_AUDIO_DITHER_NONE;
    ns = GST_AUDIO_NOISE_SHAPING_NONE;
    GST_INFO ("using no dither and noise shaping");
  } else {
    GST_INFO ("using dither %d and noise shaping %d", dither, ns);
    /* Use simple error feedback when output sample rate is smaller than
     * 32000 as the other methods might move the noise to audible ranges */
    if (ns > GST_AUDIO_NOISE_SHAPING_ERROR_FEEDBACK && out->rate < 32000)
      ns = GST_AUDIO_NOISE_SHAPING_ERROR_FEEDBACK;
  }
  /* we still want to run the quantization step when reducing bits to get
   * the rounding correct */
862
863
  if (out_int && out_depth < 32
      && convert->current_format == GST_AUDIO_FORMAT_S32) {
864
    GST_INFO ("quantize to %d bits, dither %d, ns %d", out_depth, dither, ns);
865
866
    convert->quant =
        gst_audio_quantize_new (dither, ns, 0, convert->current_format,
867
        out->channels, 1U << (32 - out_depth));
868

869
    prev = audio_chain_new (prev, convert);
870
871
872
    prev->allow_ip = TRUE;
    prev->pass_alloc = TRUE;
    audio_chain_set_make_func (prev, do_quantize, convert, NULL);
873
  }
874
875
876
877
878
879
880
881
882
883
884
  return prev;
}

static AudioChain *
chain_pack (GstAudioConverter * convert, AudioChain * prev)
{
  GstAudioInfo *out = &convert->out;
  GstAudioFormat format = convert->current_format;

  convert->current_format = out->finfo->format;

885
  convert->out_default = format == out->finfo->format;
886
887
888
  GST_INFO ("pack format %s to %s", gst_audio_format_to_string (format),
      gst_audio_format_to_string (out->finfo->format));

889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
  return prev;
}

static void
setup_allocators (GstAudioConverter * convert)
{
  AudioChain *chain;
  AudioChainAllocFunc alloc_func;
  gboolean allow_ip;

  /* start with using dest if we can directly write into it */
  if (convert->out_default) {
    alloc_func = get_output_samples;
    allow_ip = FALSE;
  } else {
    alloc_func = get_temp_samples;
    allow_ip = TRUE;
  }
  /* now walk backwards, we try to write into the dest samples directly
   * and keep track if the source needs to be writable */
909
  for (chain = convert->chain_end; chain; chain = chain->prev) {
910
911
912
    chain->alloc_func = alloc_func;
    chain->alloc_data = convert;
    chain->allow_ip = allow_ip && chain->allow_ip;
913
    GST_LOG ("chain %p: %d %d", chain, allow_ip, chain->allow_ip);
914
915
916
917
918
919
920
921
922

    if (!chain->pass_alloc) {
      /* can't pass allocator, make new temp line allocator */
      alloc_func = get_temp_samples;
      allow_ip = TRUE;
    }
  }
}

923
924
925
926
927
928
929
static gboolean
converter_passthrough (GstAudioConverter * convert,
    GstAudioConverterFlags flags, gpointer in[], gsize in_frames,
    gpointer out[], gsize out_frames)
{
  gint i;
  AudioChain *chain;
930
  gsize samples;
931

932
933
934
935
936
937
  /* in-place passthrough -> do nothing */
  if (in == out) {
    g_assert (convert->in_place);
    return TRUE;
  }

938
  chain = convert->chain_end;
939

940
  samples = in_frames * chain->inc;
941

942
943
  GST_LOG ("passthrough: %" G_GSIZE_FORMAT " / %" G_GSIZE_FORMAT " samples",
      in_frames, samples);
944

945
946
947
948
949
  if (in) {
    gsize bytes;

    bytes = samples * (convert->in.bpf / convert->in.channels);

950
951
952
953
954
955
    for (i = 0; i < chain->blocks; i++) {
      if (out[i] == in[i]) {
        g_assert (convert->in_place);
        continue;
      }

956
      memcpy (out[i], in[i], bytes);
957
    }
958
959
960
961
  } else {
    for (i = 0; i < chain->blocks; i++)
      gst_audio_format_fill_silence (convert->in.finfo, out[i], samples);
  }
962
963
964
  return TRUE;
}

965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
/* perform LE<->BE conversion on a block of @count 16-bit samples
 * dst may equal src for in-place conversion
 */
static void
converter_swap_endian_16 (gpointer dst, const gpointer src, gint count)
{
  guint16 *out = dst;
  const guint16 *in = src;
  gint i;

  for (i = 0; i < count; i++)
    out[i] = GUINT16_SWAP_LE_BE (in[i]);
}

/* perform LE<->BE conversion on a block of @count 24-bit samples
 * dst may equal src for in-place conversion
 *
 * naive algorithm, which performs better with -O3 and worse with -O2
 * than the commented out optimized algorithm below
 */
static void
converter_swap_endian_24 (gpointer dst, const gpointer src, gint count)
{
  guint8 *out = dst;
  const guint8 *in = src;
  gint i;

  count *= 3;

  for (i = 0; i < count; i += 3) {
    guint8 x = in[i + 0];
    out[i + 0] = in[i + 2];
    out[i + 1] = in[i + 1];
    out[i + 2] = x;
  }
}

/* the below code performs better with -O2 but worse with -O3 */
#if 0
/* perform LE<->BE conversion on a block of @count 24-bit samples
 * dst may equal src for in-place conversion
 *
 * assumes that dst and src are 32-bit aligned
 */
static void
converter_swap_endian_24 (gpointer dst, const gpointer src, gint count)
{
  guint32 *out = dst;
  const guint32 *in = src;
  guint8 *out8;
  const guint8 *in8;
  gint i;

  /* first convert 24-bit samples in multiples of 4 reading 3x 32-bits in one cycle
   *
   * input:               A1 B1 C1 A2 , B2 C2 A3 B3 , C3 A4 B4 C4
   * 32-bit endian swap:  A2 C1 B1 A1 , B3 A3 C2 B2 , C4 B4 A4 C3
   *                      <--  x  -->   <--  y  --> , <--  z  -->
   *
   * desired output:      C1 B1 A1 C2 , B2 A2 C3 B3 , A3 C4 B4 A4
   */
  for (i = 0; i < count / 4; i++, in += 3, out += 3) {
    guint32 x, y, z;

    x = GUINT32_SWAP_LE_BE (in[0]);
    y = GUINT32_SWAP_LE_BE (in[1]);
    z = GUINT32_SWAP_LE_BE (in[2]);

#if G_BYTE_ORDER == G_BIG_ENDIAN
    out[0] = (x << 8) + ((y >> 8) & 0xff);
    out[1] = (in[1] & 0xff0000ff) + ((x >> 8) & 0xff0000) + ((z << 8) & 0xff00);
    out[2] = (z >> 8) + ((y << 8) & 0xff000000);
#else
    out[0] = (x >> 8) + ((y << 8) & 0xff000000);
    out[1] = (in[1] & 0xff0000ff) + ((x << 8) & 0xff00) + ((z >> 8) & 0xff0000);
    out[2] = (z << 8) + ((y >> 8) & 0xff);
#endif
  }

  /* convert the remainder less efficiently */
  for (out8 = (guint8 *) out, in8 = (const guint8 *) in, i = 0; i < (count & 3);
      i++) {
    guint8 x = in8[i + 0];
    out8[i + 0] = in8[i + 2];
    out8[i + 1] = in8[i + 1];
    out8[i + 2] = x;
  }
}
#endif

/* perform LE<->BE conversion on a block of @count 32-bit samples
 * dst may equal src for in-place conversion
 */
static void
converter_swap_endian_32 (gpointer dst, const gpointer src, gint count)
{
  guint32 *out = dst;
  const guint32 *in = src;
  gint i;

  for (i = 0; i < count; i++)
    out[i] = GUINT32_SWAP_LE_BE (in[i]);
}

/* perform LE<->BE conversion on a block of @count 64-bit samples
 * dst may equal src for in-place conversion
 */
static void
converter_swap_endian_64 (gpointer dst, const gpointer src, gint count)
{
  guint64 *out = dst;
  const guint64 *in = src;
  gint i;

  for (i = 0; i < count; i++)
    out[i] = GUINT64_SWAP_LE_BE (in[i]);
}

/* the worker function to perform endian-conversion only
 * assuming finfo and foutinfo have the same depth
 */
static gboolean
converter_endian (GstAudioConverter * convert,
    GstAudioConverterFlags flags, gpointer in[], gsize in_frames,
    gpointer out[], gsize out_frames)
{
  gint i;
  AudioChain *chain;
  gsize samples;

  chain = convert->chain_end;
  samples = in_frames * chain->inc;

  GST_LOG ("convert endian: %" G_GSIZE_FORMAT " / %" G_GSIZE_FORMAT " samples",
      in_frames, samples);

  if (in) {
    for (i = 0; i < chain->blocks; i++)
      convert->swap_endian (out[i], in[i], samples);
  } else {
    for (i = 0; i < chain->blocks; i++)
      gst_audio_format_fill_silence (convert->in.finfo, out[i], samples);
  }
  return TRUE;
}

1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
static gboolean
converter_generic (GstAudioConverter * convert,
    GstAudioConverterFlags flags, gpointer in[], gsize in_frames,
    gpointer out[], gsize out_frames)
{
  AudioChain *chain;
  gpointer *tmp;
  gint i;
  gsize produced;

1121
  chain = convert->chain_end;
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141

  convert->in_writable = flags & GST_AUDIO_CONVERTER_FLAG_IN_WRITABLE;
  convert->in_data = in;
  convert->in_frames = in_frames;
  convert->out_data = out;
  convert->out_frames = out_frames;

  /* get frames to pack */
  tmp = audio_chain_get_samples (chain, &produced);

  if (!convert->out_default) {
    GST_LOG ("pack %p, %p %" G_GSIZE_FORMAT, tmp, out, produced);
    /* and pack if needed */
    for (i = 0; i < chain->blocks; i++)
      convert->out.finfo->pack_func (convert->out.finfo, 0, tmp[i], out[i],
          produced * chain->inc);
  }
  return TRUE;
}

1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
static gboolean
converter_resample (GstAudioConverter * convert,
    GstAudioConverterFlags flags, gpointer in[], gsize in_frames,
    gpointer out[], gsize out_frames)
{
  gst_audio_resampler_resample (convert->resampler, in, in_frames, out,
      out_frames);

  return TRUE;
}

1153
1154
1155
1156
1157
1158
1159
1160
#define GST_AUDIO_FORMAT_IS_ENDIAN_CONVERSION(info1, info2) \
		( \
			!(((info1)->flags ^ (info2)->flags) & (~GST_AUDIO_FORMAT_FLAG_UNPACK)) && \
			(info1)->endianness != (info2)->endianness && \
			(info1)->width == (info2)->width && \
			(info1)->depth == (info2)->depth \
		)

1161
/**
1162
 * gst_audio_converter_new:
Wim Taymans's avatar
Wim Taymans committed
1163
 * @flags: extra #GstAudioConverterFlags
1164
1165
 * @in_info: a source #GstAudioInfo
 * @out_info: a destination #GstAudioInfo
1166
 * @config: (transfer full) (nullable): a #GstStructure with configuration options
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
 *
 * Create a new #GstAudioConverter that is able to convert between @in and @out
 * audio formats.
 *
 * @config contains extra configuration options, see #GST_VIDEO_CONVERTER_OPT_*
 * parameters for details about the options and values.
 *
 * Returns: a #GstAudioConverter or %NULL if conversion is not possible.
 */
GstAudioConverter *
1177
1178
gst_audio_converter_new (GstAudioConverterFlags flags, GstAudioInfo * in_info,
    GstAudioInfo * out_info, GstStructure * config)
1179
1180
1181
{
  GstAudioConverter *convert;
  AudioChain *prev;
1182
  const GValue *opt_matrix = NULL;
1183

1184
1185
1186
1187
  g_return_val_if_fail (in_info != NULL, FALSE);
  g_return_val_if_fail (out_info != NULL, FALSE);
  g_return_val_if_fail (in_info->layout == GST_AUDIO_LAYOUT_INTERLEAVED, FALSE);
  g_return_val_if_fail (in_info->layout == out_info->layout, FALSE);
1188

1189
1190
1191
1192
1193
1194
1195
1196
  if (config)
    opt_matrix =
        gst_structure_get_value (config, GST_AUDIO_CONVERTER_OPT_MIX_MATRIX);

  if (opt_matrix
      && !check_mix_matrix (in_info->channels, out_info->channels, opt_matrix))
    goto invalid_mix_matrix;

1197
1198
  if ((GST_AUDIO_INFO_CHANNELS (in_info) != GST_AUDIO_INFO_CHANNELS (out_info))
      && (GST_AUDIO_INFO_IS_UNPOSITIONED (in_info)
1199
1200
          || GST_AUDIO_INFO_IS_UNPOSITIONED (out_info))
      && !opt_matrix)
1201
1202
1203
1204
    goto unpositioned;

  convert = g_slice_new0 (GstAudioConverter);

1205
  convert->flags = flags;
1206
1207
  convert->in = *in_info;
  convert->out = *out_info;
1208
1209
1210
1211

  /* default config */
  convert->config = gst_structure_new_empty ("GstAudioConverter");
  if (config)
1212
    gst_audio_converter_update_config (convert, 0, 0, config);
1213

1214
  GST_INFO ("unitsizes: %d -> %d", in_info->bpf, out_info->bpf);
1215
1216
1217
1218
1219
1220
1221

  /* step 1, unpack */
  prev = chain_unpack (convert);
  /* step 2, optional convert from S32 to F64 for channel mix */
  prev = chain_convert_in (convert, prev);
  /* step 3, channel mix */
  prev = chain_mix (convert, prev);
Wim Taymans's avatar
Wim Taymans committed
1222
1223
1224
  /* step 4, resample */
  prev = chain_resample (convert, prev);
  /* step 5, optional convert for quantize */
1225
  prev = chain_convert_out (convert, prev);
Wim Taymans's avatar
Wim Taymans committed
1226
  /* step 6, optional quantize */
1227
  prev = chain_quantize (convert, prev);
Wim Taymans's avatar
Wim Taymans committed
1228
  /* step 7, pack */
1229
  convert->chain_end = chain_pack (convert, prev);
1230

1231
  convert->convert = converter_generic;
1232
  convert->in_place = FALSE;
1233

1234
  /* optimize */
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
  if (convert->mix_passthrough) {
    if (out_info->finfo->format == in_info->finfo->format) {
      if (convert->resampler == NULL) {
        GST_INFO
            ("same formats, no resampler and passthrough mixing -> passthrough");
        convert->convert = converter_passthrough;
        convert->in_place = TRUE;
      } else {
        if (is_intermediate_format (in_info->finfo->format)) {
          GST_INFO ("same formats, and passthrough mixing -> only resampling");
          convert->convert = converter_resample;
        }
      }
    } else if (GST_AUDIO_FORMAT_IS_ENDIAN_CONVERSION (out_info->finfo,
            in_info->finfo)) {
      if (convert->resampler == NULL) {
        GST_INFO ("no resampler, passthrough mixing -> only endian conversion");
        convert->convert = converter_endian;
        convert->in_place = TRUE;

        switch (GST_AUDIO_INFO_BPS (in_info)) {
          case 2:
            GST_DEBUG ("initializing 16-bit endian conversion");
            convert->swap_endian = converter_swap_endian_16;
            break;
          case 3:
            GST_DEBUG ("initializing 24-bit endian conversion");
            convert->swap_endian = converter_swap_endian_24;
            break;
          case 4:
            GST_DEBUG ("initializing 32-bit endian conversion");
            convert->swap_endian = converter_swap_endian_32;
            break;
          case 8:
            GST_DEBUG ("initializing 64-bit endian conversion");
            convert->swap_endian = converter_swap_endian_64;
            break;
          default:
            GST_ERROR ("unsupported sample width for endian conversion");
            g_assert_not_reached ();
        }
1276
      }
1277
    }
1278
  }
1279

1280
1281
  setup_allocators (convert);

1282
  return convert;
1283
1284