audio-converter.c 40.9 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