audio-channel-mixer.c 35.2 KB
Newer Older
1 2
/* GStreamer
 * Copyright (C) 2004 Ronald Bultje <rbultje@ronald.bitfreak.net>
3
 * Copyright (C) 2008 Sebastian Dröge <slomo@circular-chaos.org>
4
 *
5
 * audio-channel-mixer.c: setup of channel conversion matrices
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 27
 */

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

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

30
#include "audio-channel-mixer.h"
31

Wim Taymans's avatar
Wim Taymans committed
32 33 34 35 36 37 38 39 40 41
#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;

42 43
    cat_done = (gsize) _gst_debug_category_new ("audio-channel-mixer", 0,
        "audio-channel-mixer object");
Wim Taymans's avatar
Wim Taymans committed
44 45 46 47 48 49 50 51 52 53 54

    g_once_init_leave (&cat_gonce, cat_done);
  }

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


55
#define PRECISION_INT 10
56

57 58
typedef void (*MixerFunc) (GstAudioChannelMixer * mix, const gpointer src[],
    gpointer dst[], gint samples);
59

60
struct _GstAudioChannelMixer
61 62 63 64 65 66 67
{
  gint in_channels;
  gint out_channels;

  /* channel conversion matrix, m[in_channels][out_channels].
   * If identity matrix, passthrough applies. */
  gfloat **matrix;
68

69 70 71 72
  /* channel conversion matrix with int values, m[in_channels][out_channels].
   * this is matrix * (2^10) as integers */
  gint **matrix_int;

73
  MixerFunc func;
74 75 76
};

/**
77 78
 * gst_audio_channel_mixer_free:
 * @mix: a #GstAudioChannelMixer
79 80 81
 *
 * Free memory allocated by @mix.
 */
82
void
83
gst_audio_channel_mixer_free (GstAudioChannelMixer * mix)
84 85 86 87
{
  gint i;

  /* free */
88 89 90 91
  for (i = 0; i < mix->in_channels; i++)
    g_free (mix->matrix[i]);
  g_free (mix->matrix);
  mix->matrix = NULL;
92

93 94 95 96
  for (i = 0; i < mix->in_channels; i++)
    g_free (mix->matrix_int[i]);
  g_free (mix->matrix_int);
  mix->matrix_int = NULL;
97

98
  g_slice_free (GstAudioChannelMixer, mix);
99 100 101 102 103 104 105 106 107
}

/*
 * Detect and fill in identical channels. E.g.
 * forward the left/right front channels in a
 * 5.1 to 2.0 conversion.
 */

static void
108 109 110
gst_audio_channel_mixer_fill_identical (gfloat ** matrix,
    gint in_channels, GstAudioChannelPosition * in_position, gint out_channels,
    GstAudioChannelPosition * out_position, GstAudioChannelMixerFlags flags)
111 112 113 114 115 116
{
  gint ci, co;

  /* Apart from the compatible channel assignments, we can also have
   * same channel assignments. This is much simpler, we simply copy
   * the value from source to dest! */
117
  for (co = 0; co < out_channels; co++) {
118
    /* find a channel in input with same position */
119 120 121 122 123 124 125
    for (ci = 0; ci < in_channels; ci++) {
      /* If the input was unpositioned, we're simply building
       * an identity matrix */
      if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_UNPOSITIONED_IN) {
        matrix[ci][co] = ci == co ? 1.0 : 0.0;
      } else if (in_position[ci] == out_position[co]) {
        matrix[ci][co] = 1.0;
126 127 128 129 130 131 132 133 134 135 136 137
      }
    }
  }
}

/*
 * Detect and fill in compatible channels. E.g.
 * forward left/right front to mono (or the other
 * way around) when going from 2.0 to 1.0.
 */

static void
138 139 140
gst_audio_channel_mixer_fill_compatible (gfloat ** matrix, gint in_channels,
    GstAudioChannelPosition * in_position, gint out_channels,
    GstAudioChannelPosition * out_position)
141 142 143 144 145 146 147 148 149 150 151
{
  /* Conversions from one-channel to compatible two-channel configs */
  struct
  {
    GstAudioChannelPosition pos1[2];
    GstAudioChannelPosition pos2[1];
  } conv[] = {
    /* front: mono <-> stereo */
    { {
    GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
            GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, {
152
    GST_AUDIO_CHANNEL_POSITION_MONO}},
153 154 155 156 157 158 159 160 161 162 163 164 165 166
        /* front center: 2 <-> 1 */
    { {
    GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
            GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER}, {
    GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER}},
        /* rear: 2 <-> 1 */
    { {
    GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
            GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}, {
    GST_AUDIO_CHANNEL_POSITION_REAR_CENTER}}, { {
    GST_AUDIO_CHANNEL_POSITION_INVALID}}
  };
  gint c;

167
  /* conversions from compatible (but not the same) channel schemes */
168
  for (c = 0; conv[c].pos1[0] != GST_AUDIO_CHANNEL_POSITION_INVALID; c++) {
169 170 171
    gint pos1_0 = -1, pos1_1 = -1, pos1_2 = -1;
    gint pos2_0 = -1, pos2_1 = -1, pos2_2 = -1;
    gint n;
172

173 174
    for (n = 0; n < in_channels; n++) {
      if (in_position[n] == conv[c].pos1[0])
175
        pos1_0 = n;
176
      else if (in_position[n] == conv[c].pos1[1])
177
        pos1_1 = n;
178
      else if (in_position[n] == conv[c].pos2[0])
179
        pos1_2 = n;
180
    }
181 182
    for (n = 0; n < out_channels; n++) {
      if (out_position[n] == conv[c].pos1[0])
183
        pos2_0 = n;
184
      else if (out_position[n] == conv[c].pos1[1])
185
        pos2_1 = n;
186
      else if (out_position[n] == conv[c].pos2[0])
187
        pos2_2 = n;
188 189
    }

190 191 192 193 194 195
    /* The general idea here is to fill in channels from the same position
     * as good as possible. This means mixing left<->center and right<->center.
     */

    /* left -> center */
    if (pos1_0 != -1 && pos1_2 == -1 && pos2_0 == -1 && pos2_2 != -1)
196
      matrix[pos1_0][pos2_2] = 1.0;
197
    else if (pos1_0 != -1 && pos1_2 != -1 && pos2_0 == -1 && pos2_2 != -1)
198
      matrix[pos1_0][pos2_2] = 0.5;
199
    else if (pos1_0 != -1 && pos1_2 == -1 && pos2_0 != -1 && pos2_2 != -1)
200
      matrix[pos1_0][pos2_2] = 1.0;
201 202 203

    /* right -> center */
    if (pos1_1 != -1 && pos1_2 == -1 && pos2_1 == -1 && pos2_2 != -1)
204
      matrix[pos1_1][pos2_2] = 1.0;
205
    else if (pos1_1 != -1 && pos1_2 != -1 && pos2_1 == -1 && pos2_2 != -1)
206
      matrix[pos1_1][pos2_2] = 0.5;
207
    else if (pos1_1 != -1 && pos1_2 == -1 && pos2_1 != -1 && pos2_2 != -1)
208
      matrix[pos1_1][pos2_2] = 1.0;
209 210 211

    /* center -> left */
    if (pos1_2 != -1 && pos1_0 == -1 && pos2_2 == -1 && pos2_0 != -1)
212
      matrix[pos1_2][pos2_0] = 1.0;
213
    else if (pos1_2 != -1 && pos1_0 != -1 && pos2_2 == -1 && pos2_0 != -1)
214
      matrix[pos1_2][pos2_0] = 0.5;
215
    else if (pos1_2 != -1 && pos1_0 == -1 && pos2_2 != -1 && pos2_0 != -1)
216
      matrix[pos1_2][pos2_0] = 1.0;
217 218 219

    /* center -> right */
    if (pos1_2 != -1 && pos1_1 == -1 && pos2_2 == -1 && pos2_1 != -1)
220
      matrix[pos1_2][pos2_1] = 1.0;
221
    else if (pos1_2 != -1 && pos1_1 != -1 && pos2_2 == -1 && pos2_1 != -1)
222
      matrix[pos1_2][pos2_1] = 0.5;
223
    else if (pos1_2 != -1 && pos1_1 == -1 && pos2_2 != -1 && pos2_1 != -1)
224
      matrix[pos1_2][pos2_1] = 1.0;
225 226 227 228 229 230 231 232 233 234 235 236 237
  }
}

/*
 * Detect and fill in channels not handled by the
 * above two, e.g. center to left/right front in
 * 5.1 to 2.0 (or the other way around).
 *
 * Unfortunately, limited to static conversions
 * for now.
 */

static void
238
gst_audio_channel_mixer_detect_pos (gint channels,
239 240 241
    GstAudioChannelPosition position[64], gint * f, gboolean * has_f, gint * c,
    gboolean * has_c, gint * r, gboolean * has_r, gint * s, gboolean * has_s,
    gint * b, gboolean * has_b)
242 243 244
{
  gint n;

245 246
  for (n = 0; n < channels; n++) {
    switch (position[n]) {
247
      case GST_AUDIO_CHANNEL_POSITION_MONO:
248 249 250
        f[1] = n;
        *has_f = TRUE;
        break;
251
      case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT:
252 253 254
        f[0] = n;
        *has_f = TRUE;
        break;
255
      case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT:
256
        f[2] = n;
257 258 259
        *has_f = TRUE;
        break;
      case GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER:
260 261 262
        c[1] = n;
        *has_c = TRUE;
        break;
263
      case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
264 265 266
        c[0] = n;
        *has_c = TRUE;
        break;
267
      case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
268
        c[2] = n;
269 270 271
        *has_c = TRUE;
        break;
      case GST_AUDIO_CHANNEL_POSITION_REAR_CENTER:
272 273 274
        r[1] = n;
        *has_r = TRUE;
        break;
275
      case GST_AUDIO_CHANNEL_POSITION_REAR_LEFT:
276 277 278
        r[0] = n;
        *has_r = TRUE;
        break;
279
      case GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT:
280
        r[2] = n;
281 282 283
        *has_r = TRUE;
        break;
      case GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT:
284 285 286
        s[0] = n;
        *has_s = TRUE;
        break;
287
      case GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT:
288
        s[2] = n;
289 290
        *has_s = TRUE;
        break;
291
      case GST_AUDIO_CHANNEL_POSITION_LFE1:
292
        *has_b = TRUE;
293
        b[1] = n;
294 295 296 297 298 299 300 301
        break;
      default:
        break;
    }
  }
}

static void
302
gst_audio_channel_mixer_fill_one_other (gfloat ** matrix,
303
    gint * from_idx, gint * to_idx, gfloat ratio)
304 305
{

306 307 308 309 310 311 312 313 314 315 316 317 318 319
  /* src & dst have center => passthrough */
  if (from_idx[1] != -1 && to_idx[1] != -1) {
    matrix[from_idx[1]][to_idx[1]] = ratio;
  }

  /* src & dst have left => passthrough */
  if (from_idx[0] != -1 && to_idx[0] != -1) {
    matrix[from_idx[0]][to_idx[0]] = ratio;
  }

  /* src & dst have right => passthrough */
  if (from_idx[2] != -1 && to_idx[2] != -1) {
    matrix[from_idx[2]][to_idx[2]] = ratio;
  }
320

321
  /* src has left & dst has center => put into center */
322 323 324
  if (from_idx[0] != -1 && to_idx[1] != -1 && from_idx[1] != -1) {
    matrix[from_idx[0]][to_idx[1]] = 0.5 * ratio;
  } else if (from_idx[0] != -1 && to_idx[1] != -1 && from_idx[1] == -1) {
325 326
    matrix[from_idx[0]][to_idx[1]] = ratio;
  }
327

328
  /* src has right & dst has center => put into center */
329 330 331
  if (from_idx[2] != -1 && to_idx[1] != -1 && from_idx[1] != -1) {
    matrix[from_idx[2]][to_idx[1]] = 0.5 * ratio;
  } else if (from_idx[2] != -1 && to_idx[1] != -1 && from_idx[1] == -1) {
332
    matrix[from_idx[2]][to_idx[1]] = ratio;
333 334
  }

335
  /* src has center & dst has left => passthrough */
336 337 338
  if (from_idx[1] != -1 && to_idx[0] != -1 && from_idx[0] != -1) {
    matrix[from_idx[1]][to_idx[0]] = 0.5 * ratio;
  } else if (from_idx[1] != -1 && to_idx[0] != -1 && from_idx[0] == -1) {
339 340
    matrix[from_idx[1]][to_idx[0]] = ratio;
  }
341

342
  /* src has center & dst has right => passthrough */
343 344 345
  if (from_idx[1] != -1 && to_idx[2] != -1 && from_idx[2] != -1) {
    matrix[from_idx[1]][to_idx[2]] = 0.5 * ratio;
  } else if (from_idx[1] != -1 && to_idx[2] != -1 && from_idx[2] == -1) {
346
    matrix[from_idx[1]][to_idx[2]] = ratio;
347 348 349
  }
}

350 351 352 353
#define RATIO_CENTER_FRONT (1.0 / sqrt (2.0))
#define RATIO_CENTER_SIDE (1.0 / 2.0)
#define RATIO_CENTER_REAR (1.0 / sqrt (8.0))

354
#define RATIO_FRONT_CENTER (1.0 / sqrt (2.0))
355 356 357 358 359 360 361
#define RATIO_FRONT_SIDE (1.0 / sqrt (2.0))
#define RATIO_FRONT_REAR (1.0 / 2.0)

#define RATIO_SIDE_CENTER (1.0 / 2.0)
#define RATIO_SIDE_FRONT (1.0 / sqrt (2.0))
#define RATIO_SIDE_REAR (1.0 / sqrt (2.0))

362
#define RATIO_CENTER_BASS (1.0 / sqrt (2.0))
363
#define RATIO_FRONT_BASS (1.0)
364
#define RATIO_SIDE_BASS (1.0 / sqrt (2.0))
365
#define RATIO_REAR_BASS (1.0 / sqrt (2.0))
366 367

static void
368 369 370
gst_audio_channel_mixer_fill_others (gfloat ** matrix, gint in_channels,
    GstAudioChannelPosition * in_position, gint out_channels,
    GstAudioChannelPosition * out_position)
371 372 373 374 375 376
{
  gboolean in_has_front = FALSE, out_has_front = FALSE,
      in_has_center = FALSE, out_has_center = FALSE,
      in_has_rear = FALSE, out_has_rear = FALSE,
      in_has_side = FALSE, out_has_side = FALSE,
      in_has_bass = FALSE, out_has_bass = FALSE;
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391
  /* LEFT, RIGHT, MONO */
  gint in_f[3] = { -1, -1, -1 };
  gint out_f[3] = { -1, -1, -1 };
  /* LOC, ROC, CENTER */
  gint in_c[3] = { -1, -1, -1 };
  gint out_c[3] = { -1, -1, -1 };
  /* RLEFT, RRIGHT, RCENTER */
  gint in_r[3] = { -1, -1, -1 };
  gint out_r[3] = { -1, -1, -1 };
  /* SLEFT, INVALID, SRIGHT */
  gint in_s[3] = { -1, -1, -1 };
  gint out_s[3] = { -1, -1, -1 };
  /* INVALID, LFE, INVALID */
  gint in_b[3] = { -1, -1, -1 };
  gint out_b[3] = { -1, -1, -1 };
392 393 394

  /* First see where (if at all) the various channels from/to
   * which we want to convert are located in our matrix/array. */
395
  gst_audio_channel_mixer_detect_pos (in_channels, in_position,
396 397 398
      in_f, &in_has_front,
      in_c, &in_has_center, in_r, &in_has_rear,
      in_s, &in_has_side, in_b, &in_has_bass);
399
  gst_audio_channel_mixer_detect_pos (out_channels, out_position,
400 401 402 403
      out_f, &out_has_front,
      out_c, &out_has_center, out_r, &out_has_rear,
      out_s, &out_has_side, out_b, &out_has_bass);

404 405 406 407 408 409 410 411 412 413 414
  /* The general idea here is:
   * - if the source has a channel that the destination doesn't have mix
   *   it into the nearest available destination channel
   * - if the destination has a channel that the source doesn't have mix
   *   the nearest source channel into the destination channel
   *
   * The ratio for the mixing becomes lower as the distance between the
   * channels gets larger
   */

  /* center <-> front/side/rear */
415
  if (!in_has_center && in_has_front && out_has_center) {
416
    gst_audio_channel_mixer_fill_one_other (matrix, in_f, out_c,
417
        RATIO_CENTER_FRONT);
418
  } else if (!in_has_center && !in_has_front && in_has_side && out_has_center) {
419
    gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_c,
420
        RATIO_CENTER_SIDE);
421 422
  } else if (!in_has_center && !in_has_front && !in_has_side && in_has_rear
      && out_has_center) {
423
    gst_audio_channel_mixer_fill_one_other (matrix, in_r, out_c,
424
        RATIO_CENTER_REAR);
425
  } else if (in_has_center && !out_has_center && out_has_front) {
426
    gst_audio_channel_mixer_fill_one_other (matrix, in_c, out_f,
427
        RATIO_CENTER_FRONT);
428
  } else if (in_has_center && !out_has_center && !out_has_front && out_has_side) {
429
    gst_audio_channel_mixer_fill_one_other (matrix, in_c, out_s,
430
        RATIO_CENTER_SIDE);
431 432
  } else if (in_has_center && !out_has_center && !out_has_front && !out_has_side
      && out_has_rear) {
433
    gst_audio_channel_mixer_fill_one_other (matrix, in_c, out_r,
434
        RATIO_CENTER_REAR);
435 436
  }

437 438
  /* front <-> center/side/rear */
  if (!in_has_front && in_has_center && !in_has_side && out_has_front) {
439
    gst_audio_channel_mixer_fill_one_other (matrix, in_c, out_f,
440
        RATIO_CENTER_FRONT);
441
  } else if (!in_has_front && !in_has_center && in_has_side && out_has_front) {
442
    gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_f,
443
        RATIO_FRONT_SIDE);
444
  } else if (!in_has_front && in_has_center && in_has_side && out_has_front) {
445
    gst_audio_channel_mixer_fill_one_other (matrix, in_c, out_f,
446
        0.5 * RATIO_CENTER_FRONT);
447
    gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_f,
448
        0.5 * RATIO_FRONT_SIDE);
449 450
  } else if (!in_has_front && !in_has_center && !in_has_side && in_has_rear
      && out_has_front) {
451
    gst_audio_channel_mixer_fill_one_other (matrix, in_r, out_f,
452
        RATIO_FRONT_REAR);
453
  } else if (in_has_front && out_has_center && !out_has_side && !out_has_front) {
454
    gst_audio_channel_mixer_fill_one_other (matrix,
455
        in_f, out_c, RATIO_CENTER_FRONT);
456
  } else if (in_has_front && !out_has_center && out_has_side && !out_has_front) {
457
    gst_audio_channel_mixer_fill_one_other (matrix, in_f, out_s,
458
        RATIO_FRONT_SIDE);
459
  } else if (in_has_front && out_has_center && out_has_side && !out_has_front) {
460
    gst_audio_channel_mixer_fill_one_other (matrix, in_f, out_c,
461
        0.5 * RATIO_CENTER_FRONT);
462
    gst_audio_channel_mixer_fill_one_other (matrix, in_f, out_s,
463
        0.5 * RATIO_FRONT_SIDE);
464 465
  } else if (in_has_front && !out_has_center && !out_has_side && !out_has_front
      && out_has_rear) {
466
    gst_audio_channel_mixer_fill_one_other (matrix, in_f, out_r,
467
        RATIO_FRONT_REAR);
468 469 470 471
  }

  /* side <-> center/front/rear */
  if (!in_has_side && in_has_front && !in_has_rear && out_has_side) {
472
    gst_audio_channel_mixer_fill_one_other (matrix, in_f, out_s,
473
        RATIO_FRONT_SIDE);
474
  } else if (!in_has_side && !in_has_front && in_has_rear && out_has_side) {
475
    gst_audio_channel_mixer_fill_one_other (matrix, in_r, out_s,
476
        RATIO_SIDE_REAR);
477
  } else if (!in_has_side && in_has_front && in_has_rear && out_has_side) {
478
    gst_audio_channel_mixer_fill_one_other (matrix, in_f, out_s,
479
        0.5 * RATIO_FRONT_SIDE);
480
    gst_audio_channel_mixer_fill_one_other (matrix, in_r, out_s,
481
        0.5 * RATIO_SIDE_REAR);
482 483
  } else if (!in_has_side && !in_has_front && !in_has_rear && in_has_center
      && out_has_side) {
484
    gst_audio_channel_mixer_fill_one_other (matrix, in_c, out_s,
485
        RATIO_CENTER_SIDE);
486
  } else if (in_has_side && out_has_front && !out_has_rear && !out_has_side) {
487
    gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_f,
488
        RATIO_FRONT_SIDE);
489
  } else if (in_has_side && !out_has_front && out_has_rear && !out_has_side) {
490
    gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_r,
491
        RATIO_SIDE_REAR);
492
  } else if (in_has_side && out_has_front && out_has_rear && !out_has_side) {
493
    gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_f,
494
        0.5 * RATIO_FRONT_SIDE);
495
    gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_r,
496
        0.5 * RATIO_SIDE_REAR);
497 498
  } else if (in_has_side && !out_has_front && !out_has_rear && out_has_center
      && !out_has_side) {
499
    gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_c,
500
        RATIO_CENTER_SIDE);
501 502
  }

503 504
  /* rear <-> center/front/side */
  if (!in_has_rear && in_has_side && out_has_rear) {
505
    gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_r,
506
        RATIO_SIDE_REAR);
507
  } else if (!in_has_rear && !in_has_side && in_has_front && out_has_rear) {
508
    gst_audio_channel_mixer_fill_one_other (matrix, in_f, out_r,
509
        RATIO_FRONT_REAR);
510 511
  } else if (!in_has_rear && !in_has_side && !in_has_front && in_has_center
      && out_has_rear) {
512
    gst_audio_channel_mixer_fill_one_other (matrix, in_c, out_r,
513
        RATIO_CENTER_REAR);
514
  } else if (in_has_rear && !out_has_rear && out_has_side) {
515
    gst_audio_channel_mixer_fill_one_other (matrix, in_r, out_s,
516
        RATIO_SIDE_REAR);
517
  } else if (in_has_rear && !out_has_rear && !out_has_side && out_has_front) {
518
    gst_audio_channel_mixer_fill_one_other (matrix, in_r, out_f,
519
        RATIO_FRONT_REAR);
520 521
  } else if (in_has_rear && !out_has_rear && !out_has_side && !out_has_front
      && out_has_center) {
522
    gst_audio_channel_mixer_fill_one_other (matrix, in_r, out_c,
523
        RATIO_CENTER_REAR);
524 525 526
  }

  /* bass <-> any */
527 528
  if (in_has_bass && !out_has_bass) {
    if (out_has_center) {
529
      gst_audio_channel_mixer_fill_one_other (matrix, in_b, out_c,
530
          RATIO_CENTER_BASS);
531
    }
532
    if (out_has_front) {
533
      gst_audio_channel_mixer_fill_one_other (matrix, in_b, out_f,
534
          RATIO_FRONT_BASS);
535
    }
536
    if (out_has_side) {
537
      gst_audio_channel_mixer_fill_one_other (matrix, in_b, out_s,
538
          RATIO_SIDE_BASS);
539
    }
540
    if (out_has_rear) {
541
      gst_audio_channel_mixer_fill_one_other (matrix, in_b, out_r,
542
          RATIO_REAR_BASS);
543
    }
544
  } else if (!in_has_bass && out_has_bass) {
545
    if (in_has_center) {
546
      gst_audio_channel_mixer_fill_one_other (matrix, in_c, out_b,
547
          RATIO_CENTER_BASS);
548
    }
549
    if (in_has_front) {
550
      gst_audio_channel_mixer_fill_one_other (matrix, in_f, out_b,
551
          RATIO_FRONT_BASS);
552
    }
553
    if (in_has_side) {
554
      gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_b,
555
          RATIO_REAR_BASS);
556 557
    }
    if (in_has_rear) {
558
      gst_audio_channel_mixer_fill_one_other (matrix, in_r, out_b,
559
          RATIO_REAR_BASS);
560 561
    }
  }
562 563 564 565 566 567 568
}

/*
 * Normalize output values.
 */

static void
569 570
gst_audio_channel_mixer_fill_normalize (gfloat ** matrix, gint in_channels,
    gint out_channels)
571 572 573 574
{
  gfloat sum, top = 0;
  gint i, j;

575
  for (j = 0; j < out_channels; j++) {
576 577
    /* calculate sum */
    sum = 0.0;
578 579
    for (i = 0; i < in_channels; i++) {
      sum += fabs (matrix[i][j]);
580 581 582 583 584 585
    }
    if (sum > top) {
      top = sum;
    }
  }

586
  /* normalize to mix */
587 588 589
  if (top == 0.0)
    return;

590 591 592
  for (j = 0; j < out_channels; j++) {
    for (i = 0; i < in_channels; i++) {
      matrix[i][j] /= top;
593 594 595 596
    }
  }
}

597
static gboolean
598 599 600
gst_audio_channel_mixer_fill_special (gfloat ** matrix, gint in_channels,
    GstAudioChannelPosition * in_position, gint out_channels,
    GstAudioChannelPosition * out_position)
601 602 603 604
{
  /* Special, standard conversions here */

  /* Mono<->Stereo, just a fast-path */
605 606 607 608 609 610 611 612
  if (in_channels == 2 && out_channels == 1 &&
      ((in_position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT &&
              in_position[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT) ||
          (in_position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT &&
              in_position[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT)) &&
      out_position[0] == GST_AUDIO_CHANNEL_POSITION_MONO) {
    matrix[0][0] = 0.5;
    matrix[1][0] = 0.5;
613
    return TRUE;
614 615 616 617 618 619 620 621
  } else if (in_channels == 1 && out_channels == 2 &&
      ((out_position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT &&
              out_position[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT) ||
          (out_position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT &&
              out_position[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT)) &&
      in_position[0] == GST_AUDIO_CHANNEL_POSITION_MONO) {
    matrix[0][0] = 1.0;
    matrix[0][1] = 1.0;
622 623 624 625 626 627 628 629
    return TRUE;
  }

  /* TODO: 5.1 <-> Stereo and other standard conversions */

  return FALSE;
}

630 631 632 633 634
/*
 * Automagically generate conversion matrix.
 */

static void
635 636 637 638
gst_audio_channel_mixer_fill_matrix (gfloat ** matrix,
    GstAudioChannelMixerFlags flags, gint in_channels,
    GstAudioChannelPosition * in_position, gint out_channels,
    GstAudioChannelPosition * out_position)
639
{
640 641
  if (gst_audio_channel_mixer_fill_special (matrix, in_channels, in_position,
          out_channels, out_position))
642 643
    return;

644 645
  gst_audio_channel_mixer_fill_identical (matrix, in_channels, in_position,
      out_channels, out_position, flags);
646

647 648 649 650 651 652
  if (!(flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_UNPOSITIONED_IN)) {
    gst_audio_channel_mixer_fill_compatible (matrix, in_channels, in_position,
        out_channels, out_position);
    gst_audio_channel_mixer_fill_others (matrix, in_channels, in_position,
        out_channels, out_position);
    gst_audio_channel_mixer_fill_normalize (matrix, in_channels, out_channels);
653
  }
654 655
}

656
/* only call mix after mix->matrix is fully set up and normalized */
657
static void
658
gst_audio_channel_mixer_setup_matrix_int (GstAudioChannelMixer * mix)
659 660 661
{
  gint i, j;
  gfloat tmp;
662
  gfloat factor = (1 << PRECISION_INT);
663

664
  mix->matrix_int = g_new0 (gint *, mix->in_channels);
665

666 667
  for (i = 0; i < mix->in_channels; i++) {
    mix->matrix_int[i] = g_new (gint, mix->out_channels);
668

669 670 671
    for (j = 0; j < mix->out_channels; j++) {
      tmp = mix->matrix[i][j] * factor;
      mix->matrix_int[i][j] = (gint) tmp;
672 673 674 675
    }
  }
}

676 677 678 679
static gfloat **
gst_audio_channel_mixer_setup_matrix (GstAudioChannelMixerFlags flags,
    gint in_channels, GstAudioChannelPosition * in_position,
    gint out_channels, GstAudioChannelPosition * out_position)
680 681
{
  gint i, j;
682
  gfloat **matrix = g_new0 (gfloat *, in_channels);
683

684 685 686 687
  for (i = 0; i < in_channels; i++) {
    matrix[i] = g_new (gfloat, out_channels);
    for (j = 0; j < out_channels; j++)
      matrix[i][j] = 0.;
688 689 690
  }

  /* setup the matrix' internal values */
691 692
  gst_audio_channel_mixer_fill_matrix (matrix, flags, in_channels, in_position,
      out_channels, out_position);
693

694
  return matrix;
695 696
}

697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725
#define DEFINE_GET_DATA_FUNCS(type) \
static inline type \
_get_in_data_interleaved_##type (const type * in_data[], \
    gint sample, gint channel, gint total_channels) \
{ \
  return in_data[0][sample * total_channels + channel]; \
} \
\
static inline type * \
_get_out_data_interleaved_##type (type * out_data[], \
    gint sample, gint channel, gint total_channels) \
{ \
  return &out_data[0][sample * total_channels + channel]; \
} \
\
static inline type \
_get_in_data_planar_##type (const type * in_data[], \
    gint sample, gint channel, gint total_channels) \
{ \
  (void) total_channels; \
  return in_data[channel][sample]; \
} \
\
static inline type * \
_get_out_data_planar_##type (type * out_data[], \
    gint sample, gint channel, gint total_channels) \
{ \
  (void) total_channels; \
  return &out_data[channel][sample]; \
726 727
}

728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755
#define DEFINE_INTEGER_MIX_FUNC(bits, resbits, inlayout, outlayout) \
static void \
gst_audio_channel_mixer_mix_int##bits##_##inlayout##_##outlayout ( \
    GstAudioChannelMixer * mix, const gint##bits * in_data[], \
    gint##bits * out_data[], gint samples) \
{ \
  gint in, out, n; \
  gint##resbits res; \
  gint inchannels, outchannels; \
  \
  inchannels = mix->in_channels; \
  outchannels = mix->out_channels; \
  \
  for (n = 0; n < samples; n++) { \
    for (out = 0; out < outchannels; out++) { \
      /* convert */ \
      res = 0; \
      for (in = 0; in < inchannels; in++) \
        res += \
          _get_in_data_##inlayout##_gint##bits (in_data, n, in, inchannels) * \
          (gint##resbits) mix->matrix_int[in][out]; \
      \
      /* remove factor from int matrix */ \
      res = (res + (1 << (PRECISION_INT - 1))) >> PRECISION_INT; \
      *_get_out_data_##outlayout##_gint##bits (out_data, n, out, outchannels) = \
          CLAMP (res, G_MININT##bits, G_MAXINT##bits); \
    } \
  } \
756 757
}

758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782
#define DEFINE_FLOAT_MIX_FUNC(type, inlayout, outlayout) \
static void \
gst_audio_channel_mixer_mix_##type##_##inlayout##_##outlayout ( \
    GstAudioChannelMixer * mix, const g##type * in_data[], \
    g##type * out_data[], gint samples) \
{ \
  gint in, out, n; \
  g##type res; \
  gint inchannels, outchannels; \
  \
  inchannels = mix->in_channels; \
  outchannels = mix->out_channels; \
  \
  for (n = 0; n < samples; n++) { \
    for (out = 0; out < outchannels; out++) { \
      /* convert */ \
      res = 0.0; \
      for (in = 0; in < inchannels; in++) \
        res += \
          _get_in_data_##inlayout##_g##type (in_data, n, in, inchannels) * \
          mix->matrix[in][out]; \
      \
      *_get_out_data_##outlayout##_g##type (out_data, n, out, outchannels) = res; \
    } \
  } \
783
}
784

785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807
DEFINE_GET_DATA_FUNCS (gint16);
DEFINE_INTEGER_MIX_FUNC (16, 32, interleaved, interleaved);
DEFINE_INTEGER_MIX_FUNC (16, 32, interleaved, planar);
DEFINE_INTEGER_MIX_FUNC (16, 32, planar, interleaved);
DEFINE_INTEGER_MIX_FUNC (16, 32, planar, planar);

DEFINE_GET_DATA_FUNCS (gint32);
DEFINE_INTEGER_MIX_FUNC (32, 64, interleaved, interleaved);
DEFINE_INTEGER_MIX_FUNC (32, 64, interleaved, planar);
DEFINE_INTEGER_MIX_FUNC (32, 64, planar, interleaved);
DEFINE_INTEGER_MIX_FUNC (32, 64, planar, planar);

DEFINE_GET_DATA_FUNCS (gfloat);
DEFINE_FLOAT_MIX_FUNC (float, interleaved, interleaved);
DEFINE_FLOAT_MIX_FUNC (float, interleaved, planar);
DEFINE_FLOAT_MIX_FUNC (float, planar, interleaved);
DEFINE_FLOAT_MIX_FUNC (float, planar, planar);

DEFINE_GET_DATA_FUNCS (gdouble);
DEFINE_FLOAT_MIX_FUNC (double, interleaved, interleaved);
DEFINE_FLOAT_MIX_FUNC (double, interleaved, planar);
DEFINE_FLOAT_MIX_FUNC (double, planar, interleaved);
DEFINE_FLOAT_MIX_FUNC (double, planar, planar);
808

809
/**
810
 * gst_audio_channel_mixer_new_with_matrix: (skip):
811 812 813
 * @flags: #GstAudioChannelMixerFlags
 * @in_channels: number of input channels
 * @out_channels: number of output channels
814
 * @matrix: (transfer full) (nullable): channel conversion matrix, m[@in_channels][@out_channels].
815 816
 *   If identity matrix, passthrough applies. If %NULL, a (potentially truncated)
 *   identity matrix is generated.
817
 *
818
 * Create a new channel mixer object for the given parameters.
819
 *
820 821 822 823 824
 * Returns: a new #GstAudioChannelMixer object, or %NULL if @format isn't supported,
 *   @matrix is invalid, or @matrix is %NULL and @in_channels != @out_channels.
 *   Free with gst_audio_channel_mixer_free() after usage.
 *
 * Since: 1.14
825
 */
826
GstAudioChannelMixer *
827
gst_audio_channel_mixer_new_with_matrix (GstAudioChannelMixerFlags flags,
828
    GstAudioFormat format,
829
    gint in_channels, gint out_channels, gfloat ** matrix)
830
{
831
  GstAudioChannelMixer *mix;
832

833 834 835
  g_return_val_if_fail (format == GST_AUDIO_FORMAT_S16
      || format == GST_AUDIO_FORMAT_S32
      || format == GST_AUDIO_FORMAT_F32
836
      || format == GST_AUDIO_FORMAT_F64, NULL);
837 838
  g_return_val_if_fail (in_channels > 0 && in_channels < 64, NULL);
  g_return_val_if_fail (out_channels > 0 && out_channels < 64, NULL);
839

840
  mix = g_slice_new0 (GstAudioChannelMixer);
841 842 843
  mix->in_channels = in_channels;
  mix->out_channels = out_channels;

844
  if (!matrix) {
845
    /* Generate (potentially truncated) identity matrix */
846 847 848 849 850 851 852 853 854 855 856 857 858 859 860
    gint i, j;

    mix->matrix = g_new0 (gfloat *, in_channels);

    for (i = 0; i < in_channels; i++) {
      mix->matrix[i] = g_new (gfloat, out_channels);
      for (j = 0; j < out_channels; j++) {
        mix->matrix[i][j] = i == j ? 1.0 : 0.0;
      }
    }
  } else {
    mix->matrix = matrix;
  }

  gst_audio_channel_mixer_setup_matrix_int (mix);
861

862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887
#ifndef GST_DISABLE_GST_DEBUG
  /* debug */
  {
    GString *s;
    gint i, j;

    s = g_string_new ("Matrix for");
    g_string_append_printf (s, " %d -> %d: ",
        mix->in_channels, mix->out_channels);
    g_string_append (s, "{");
    for (i = 0; i < mix->in_channels; i++) {
      if (i != 0)
        g_string_append (s, ",");
      g_string_append (s, " {");
      for (j = 0; j < mix->out_channels; j++) {
        if (j != 0)
          g_string_append (s, ",");
        g_string_append_printf (s, " %f", mix->matrix[i][j]);
      }
      g_string_append (s, " }");
    }
    g_string_append (s, " }");
    GST_DEBUG ("%s", s->str);
    g_string_free (s, TRUE);
  }
#endif
888

889
  switch (format) {
890
    case GST_AUDIO_FORMAT_S16:
891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907
      if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_IN) {
        if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT) {
          mix->func = (MixerFunc)
              gst_audio_channel_mixer_mix_int16_planar_planar;
        } else {
          mix->func = (MixerFunc)
              gst_audio_channel_mixer_mix_int16_planar_interleaved;
        }
      } else {
        if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT) {
          mix->func = (MixerFunc)
              gst_audio_channel_mixer_mix_int16_interleaved_planar;
        } else {
          mix->func = (MixerFunc)
              gst_audio_channel_mixer_mix_int16_interleaved_interleaved;
        }
      }
908
      break;
909
    case GST_AUDIO_FORMAT_S32:
910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926
      if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_IN) {
        if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT) {
          mix->func = (MixerFunc)
              gst_audio_channel_mixer_mix_int32_planar_planar;
        } else {
          mix->func = (MixerFunc)
              gst_audio_channel_mixer_mix_int32_planar_interleaved;
        }
      } else {
        if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT) {
          mix->func = (MixerFunc)
              gst_audio_channel_mixer_mix_int32_interleaved_planar;
        } else {
          mix->func = (MixerFunc)
              gst_audio_channel_mixer_mix_int32_interleaved_interleaved;
        }
      }
927 928
      break;
    case GST_AUDIO_FORMAT_F32:
929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945
      if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_IN) {
        if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT) {
          mix->func = (MixerFunc)
              gst_audio_channel_mixer_mix_float_planar_planar;
        } else {
          mix->func = (MixerFunc)
              gst_audio_channel_mixer_mix_float_planar_interleaved;
        }
      } else {
        if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT) {
          mix->func = (MixerFunc)
              gst_audio_channel_mixer_mix_float_interleaved_planar;
        } else {
          mix->func = (MixerFunc)
              gst_audio_channel_mixer_mix_float_interleaved_interleaved;
        }
      }
946 947
      break;
    case GST_AUDIO_FORMAT_F64:
948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964
      if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_IN) {
        if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT) {
          mix->func = (MixerFunc)
              gst_audio_channel_mixer_mix_double_planar_planar;
        } else {
          mix->func = (MixerFunc)
              gst_audio_channel_mixer_mix_double_planar_interleaved;
        }
      } else {
        if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT) {
          mix->func = (MixerFunc)
              gst_audio_channel_mixer_mix_double_interleaved_planar;
        } else {
          mix->func = (MixerFunc)
              gst_audio_channel_mixer_mix_double_interleaved_interleaved;
        }
      }
965 966 967 968 969 970 971 972
      break;
    default:
      g_assert_not_reached ();
      break;
  }
  return mix;
}

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
/**
 * gst_audio_channel_mixer_new: (skip):
 * @flags: #GstAudioChannelMixerFlags
 * @in_channels: number of input channels
 * @in_position: positions of input channels
 * @out_channels: number of output channels
 * @out_position: positions of output channels
 *
 * Create a new channel mixer object for the given parameters.
 *
 * Returns: a new #GstAudioChannelMixer object, or %NULL if @format isn't supported.
 *   Free with gst_audio_channel_mixer_free() after usage.
 */
GstAudioChannelMixer *
gst_audio_channel_mixer_new (GstAudioChannelMixerFlags flags,
    GstAudioFormat format,
    gint in_channels,
    GstAudioChannelPosition * in_position,
    gint out_channels, GstAudioChannelPosition * out_position)
{
  gfloat **matrix;

  g_return_val_if_fail (format == GST_AUDIO_FORMAT_S16
      || format == GST_AUDIO_FORMAT_S32
      || format == GST_AUDIO_FORMAT_F32
      || format == GST_AUDIO_FORMAT_F64, NULL);
  g_return_val_if_fail (in_channels > 0 && in_channels < 64, NULL);
  g_return_val_if_fail (out_channels > 0 && out_channels < 64, NULL);

  matrix =
      gst_audio_channel_mixer_setup_matrix (flags, in_channels, in_position,
      out_channels, out_position);
  return gst_audio_channel_mixer_new_with_matrix (flags, format, in_channels,
      out_channels, matrix);
}

1009
/**
1010 1011
 * gst_audio_channel_mixer_is_passthrough:
 * @mix: a #GstAudioChannelMixer
1012 1013 1014
 *
 * Check if @mix is in passthrough.
 *
1015 1016 1017 1018 1019 1020 1021 1022 1023 1024
 * Only N x N mix identity matrices are considered passthrough,
 * this is determined by comparing the contents of the matrix
 * with 0.0 and 1.0.
 *
 * As this is floating point comparisons, if the values have been
 * generated, they should be rounded up or down by explicit
 * assignment of 0.0 or 1.0 to values within a user-defined
 * epsilon, this code doesn't make assumptions as to what may
 * constitute an appropriate epsilon.
 *
1025 1026 1027
 * Returns: %TRUE is @mix is passthrough.
 */
gboolean
1028
gst_audio_channel_mixer_is_passthrough (GstAudioChannelMixer * mix)
1029
{
1030 1031
  gint i, j;
  gboolean res;
1032 1033 1034 1035 1036

  /* only NxN matrices can be identities */
  if (mix->in_channels != mix->out_channels)
    return FALSE;

1037
  res = TRUE;
1038 1039

  for (i = 0; i < mix->in_channels; i++) {
1040 1041 1042 1043 1044 1045 1046
    for (j = 0; j < mix->out_channels; j++) {
      if ((i == j && mix->matrix[i][j] != 1.0f) ||
          (i != j && mix->matrix[i][j] != 0.0f)) {
        res = FALSE;
        break;
      }
    }
1047
  }
1048 1049

  return res;
1050 1051
}

1052
/**
1053 1054
 * gst_audio_channel_mixer_samples:
 * @mix: a #GstAudioChannelMixer
1055 1056
 * @in: input samples
 * @out: output samples
1057
 * @samples: number of samples
1058
 *
1059 1060 1061 1062 1063 1064
 * In case the samples are interleaved, @in and @out must point to an
 * array with a single element pointing to a block of interleaved samples.
 *
 * If non-interleaved samples are used, @in and @out must point to an
 * array with pointers to memory blocks, one for each channel.
 *
1065 1066
 * Perform channel mixing on @in_data and write the result to @out_data.
 * @in_data and @out_data need to be in @format and @layout.
1067 1068
 */
void
1069
gst_audio_channel_mixer_samples (GstAudioChannelMixer * mix,
1070
    const gpointer in[], gpointer out[], gint samples)
1071 1072 1073 1074
{
  g_return_if_fail (mix != NULL);
  g_return_if_fail (mix->matrix != NULL);

1075
  mix->func (mix, in, out, samples);
1076
}