Coverage Report

Created: 2026-02-14 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gstreamer/subprojects/gst-plugins-base/gst-libs/gst/audio/audio-channel-mixer.c
Line
Count
Source
1
/* GStreamer
2
 * 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
 * This library is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Library General Public
9
 * License as published by the Free Software Foundation; either
10
 * version 2 of the License, or (at your option) any later version.
11
 *
12
 * This library is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Library General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Library General Public
18
 * License along with this library; if not, write to the
19
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20
 * Boston, MA 02110-1301, USA.
21
 */
22
23
#ifdef HAVE_CONFIG_H
24
#include "config.h"
25
#endif
26
27
#include <math.h>
28
#include <string.h>
29
30
#include "audio-channel-mixer.h"
31
32
#ifndef GST_DISABLE_GST_DEBUG
33
#define GST_CAT_DEFAULT ensure_debug_category()
34
static GstDebugCategory *
35
ensure_debug_category (void)
36
0
{
37
0
  static gsize cat_gonce = 0;
38
39
0
  if (g_once_init_enter (&cat_gonce)) {
40
0
    gsize cat_done;
41
42
0
    cat_done = (gsize) _gst_debug_category_new ("audio-channel-mixer", 0,
43
0
        "audio-channel-mixer object");
44
45
0
    g_once_init_leave (&cat_gonce, cat_done);
46
0
  }
47
48
0
  return (GstDebugCategory *) cat_gonce;
49
0
}
50
#else
51
#define ensure_debug_category() /* NOOP */
52
#endif /* GST_DISABLE_GST_DEBUG */
53
54
55
0
#define PRECISION_INT 10
56
0
#define NONZERO_DENSITY_THRESHOLD 0.5
57
58
typedef void (*MixerFunc) (GstAudioChannelMixer * mix, const gpointer src[],
59
    gpointer dst[], gint samples);
60
61
typedef struct _MixOutEntry
62
{
63
  guint index;
64
  guint offset;
65
  guint count;
66
} MixOutEntry;
67
68
typedef struct _MixEntry
69
{
70
  guint index;
71
  gfloat coeff;
72
  gint coeff_int;
73
} MixEntry;
74
75
struct _GstAudioChannelMixer
76
{
77
  gint in_channels;
78
  gint out_channels;
79
80
  const GstAudioFormatInfo *finfo;
81
82
  /* channel conversion matrix, m[in_channels][out_channels].
83
   * If identity matrix, passthrough applies. */
84
  gfloat **matrix;
85
86
  /* channel conversion matrix with int values, m[in_channels][out_channels].
87
   * this is matrix * (2^10) as integers */
88
  gint **matrix_int;
89
90
  MixOutEntry *out_entry;
91
  MixEntry *entry;
92
93
  guint num_valid_out_ch;
94
  gboolean non_interleaved;
95
  gsize clear_unit;
96
97
  MixerFunc func;
98
};
99
100
/**
101
 * gst_audio_channel_mixer_free:
102
 * @mix: a #GstAudioChannelMixer
103
 *
104
 * Free memory allocated by @mix.
105
 */
106
void
107
gst_audio_channel_mixer_free (GstAudioChannelMixer * mix)
108
0
{
109
0
  gint i;
110
111
  /* free */
112
0
  for (i = 0; i < mix->in_channels; i++)
113
0
    g_free (mix->matrix[i]);
114
0
  g_free (mix->matrix);
115
116
0
  if (mix->matrix_int) {
117
0
    for (i = 0; i < mix->in_channels; i++)
118
0
      g_free (mix->matrix_int[i]);
119
0
    g_free (mix->matrix_int);
120
0
  }
121
122
0
  g_free (mix->out_entry);
123
0
  g_free (mix->entry);
124
125
0
  g_free (mix);
126
0
}
127
128
/*
129
 * Detect and fill in identical channels. E.g.
130
 * forward the left/right front channels in a
131
 * 5.1 to 2.0 conversion.
132
 */
133
134
static void
135
gst_audio_channel_mixer_fill_identical (gfloat ** matrix,
136
    gint in_channels, GstAudioChannelPosition * in_position, gint out_channels,
137
    GstAudioChannelPosition * out_position, GstAudioChannelMixerFlags flags)
138
0
{
139
0
  gint ci, co;
140
141
  /* Apart from the compatible channel assignments, we can also have
142
   * same channel assignments. This is much simpler, we simply copy
143
   * the value from source to dest! */
144
0
  for (co = 0; co < out_channels; co++) {
145
    /* find a channel in input with same position */
146
0
    for (ci = 0; ci < in_channels; ci++) {
147
      /* If the input was unpositioned, we're simply building
148
       * an identity matrix */
149
0
      if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_UNPOSITIONED_IN) {
150
0
        matrix[ci][co] = ci == co ? 1.0 : 0.0;
151
0
      } else if (in_position[ci] == out_position[co]) {
152
0
        matrix[ci][co] = 1.0;
153
0
      }
154
0
    }
155
0
  }
156
0
}
157
158
/*
159
 * Detect and fill in compatible channels. E.g.
160
 * forward left/right front to mono (or the other
161
 * way around) when going from 2.0 to 1.0.
162
 */
163
164
static void
165
gst_audio_channel_mixer_fill_compatible (gfloat ** matrix, gint in_channels,
166
    GstAudioChannelPosition * in_position, gint out_channels,
167
    GstAudioChannelPosition * out_position)
168
0
{
169
  /* Conversions from one-channel to compatible two-channel configs */
170
0
  struct
171
0
  {
172
0
    GstAudioChannelPosition pos1[2];
173
0
    GstAudioChannelPosition pos2[1];
174
0
  } conv[] = {
175
    /* front: mono <-> stereo */
176
0
    {{
177
0
                GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
178
0
            GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, {
179
0
            GST_AUDIO_CHANNEL_POSITION_MONO}},
180
    /* front center: 2 <-> 1 */
181
0
    {{
182
0
                GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
183
0
            GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER}, {
184
0
            GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER}},
185
    /* rear: 2 <-> 1 */
186
0
    {{
187
0
                GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
188
0
            GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}, {
189
0
            GST_AUDIO_CHANNEL_POSITION_REAR_CENTER}}, {{
190
0
            GST_AUDIO_CHANNEL_POSITION_INVALID}}
191
0
  };
192
0
  gint c;
193
194
  /* conversions from compatible (but not the same) channel schemes */
195
0
  for (c = 0; conv[c].pos1[0] != GST_AUDIO_CHANNEL_POSITION_INVALID; c++) {
196
0
    gint pos1_0 = -1, pos1_1 = -1, pos1_2 = -1;
197
0
    gint pos2_0 = -1, pos2_1 = -1, pos2_2 = -1;
198
0
    gint n;
199
200
0
    for (n = 0; n < in_channels; n++) {
201
0
      if (in_position[n] == conv[c].pos1[0])
202
0
        pos1_0 = n;
203
0
      else if (in_position[n] == conv[c].pos1[1])
204
0
        pos1_1 = n;
205
0
      else if (in_position[n] == conv[c].pos2[0])
206
0
        pos1_2 = n;
207
0
    }
208
0
    for (n = 0; n < out_channels; n++) {
209
0
      if (out_position[n] == conv[c].pos1[0])
210
0
        pos2_0 = n;
211
0
      else if (out_position[n] == conv[c].pos1[1])
212
0
        pos2_1 = n;
213
0
      else if (out_position[n] == conv[c].pos2[0])
214
0
        pos2_2 = n;
215
0
    }
216
217
    /* The general idea here is to fill in channels from the same position
218
     * as good as possible. This means mixing left<->center and right<->center.
219
     */
220
221
    /* left -> center */
222
0
    if (pos1_0 != -1 && pos1_2 == -1 && pos2_0 == -1 && pos2_2 != -1)
223
0
      matrix[pos1_0][pos2_2] = 1.0;
224
0
    else if (pos1_0 != -1 && pos1_2 != -1 && pos2_0 == -1 && pos2_2 != -1)
225
0
      matrix[pos1_0][pos2_2] = 0.5;
226
0
    else if (pos1_0 != -1 && pos1_2 == -1 && pos2_0 != -1 && pos2_2 != -1)
227
0
      matrix[pos1_0][pos2_2] = 1.0;
228
229
    /* right -> center */
230
0
    if (pos1_1 != -1 && pos1_2 == -1 && pos2_1 == -1 && pos2_2 != -1)
231
0
      matrix[pos1_1][pos2_2] = 1.0;
232
0
    else if (pos1_1 != -1 && pos1_2 != -1 && pos2_1 == -1 && pos2_2 != -1)
233
0
      matrix[pos1_1][pos2_2] = 0.5;
234
0
    else if (pos1_1 != -1 && pos1_2 == -1 && pos2_1 != -1 && pos2_2 != -1)
235
0
      matrix[pos1_1][pos2_2] = 1.0;
236
237
    /* center -> left */
238
0
    if (pos1_2 != -1 && pos1_0 == -1 && pos2_2 == -1 && pos2_0 != -1)
239
0
      matrix[pos1_2][pos2_0] = 1.0;
240
0
    else if (pos1_2 != -1 && pos1_0 != -1 && pos2_2 == -1 && pos2_0 != -1)
241
0
      matrix[pos1_2][pos2_0] = 0.5;
242
0
    else if (pos1_2 != -1 && pos1_0 == -1 && pos2_2 != -1 && pos2_0 != -1)
243
0
      matrix[pos1_2][pos2_0] = 1.0;
244
245
    /* center -> right */
246
0
    if (pos1_2 != -1 && pos1_1 == -1 && pos2_2 == -1 && pos2_1 != -1)
247
0
      matrix[pos1_2][pos2_1] = 1.0;
248
0
    else if (pos1_2 != -1 && pos1_1 != -1 && pos2_2 == -1 && pos2_1 != -1)
249
0
      matrix[pos1_2][pos2_1] = 0.5;
250
0
    else if (pos1_2 != -1 && pos1_1 == -1 && pos2_2 != -1 && pos2_1 != -1)
251
0
      matrix[pos1_2][pos2_1] = 1.0;
252
0
  }
253
0
}
254
255
/*
256
 * Detect and fill in channels not handled by the
257
 * above two, e.g. center to left/right front in
258
 * 5.1 to 2.0 (or the other way around).
259
 *
260
 * Unfortunately, limited to static conversions
261
 * for now.
262
 */
263
264
static void
265
gst_audio_channel_mixer_detect_pos (gint channels,
266
    GstAudioChannelPosition position[64], gint * f, gboolean * has_f, gint * c,
267
    gboolean * has_c, gint * r, gboolean * has_r, gint * s, gboolean * has_s,
268
    gint * b, gboolean * has_b)
269
0
{
270
0
  gint n;
271
272
0
  for (n = 0; n < channels; n++) {
273
0
    switch (position[n]) {
274
0
      case GST_AUDIO_CHANNEL_POSITION_MONO:
275
0
        f[1] = n;
276
0
        *has_f = TRUE;
277
0
        break;
278
0
      case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT:
279
0
        f[0] = n;
280
0
        *has_f = TRUE;
281
0
        break;
282
0
      case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT:
283
0
        f[2] = n;
284
0
        *has_f = TRUE;
285
0
        break;
286
0
      case GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER:
287
0
        c[1] = n;
288
0
        *has_c = TRUE;
289
0
        break;
290
0
      case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
291
0
        c[0] = n;
292
0
        *has_c = TRUE;
293
0
        break;
294
0
      case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
295
0
        c[2] = n;
296
0
        *has_c = TRUE;
297
0
        break;
298
0
      case GST_AUDIO_CHANNEL_POSITION_REAR_CENTER:
299
0
        r[1] = n;
300
0
        *has_r = TRUE;
301
0
        break;
302
0
      case GST_AUDIO_CHANNEL_POSITION_REAR_LEFT:
303
0
        r[0] = n;
304
0
        *has_r = TRUE;
305
0
        break;
306
0
      case GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT:
307
0
        r[2] = n;
308
0
        *has_r = TRUE;
309
0
        break;
310
0
      case GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT:
311
0
        s[0] = n;
312
0
        *has_s = TRUE;
313
0
        break;
314
0
      case GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT:
315
0
        s[2] = n;
316
0
        *has_s = TRUE;
317
0
        break;
318
0
      case GST_AUDIO_CHANNEL_POSITION_LFE1:
319
0
        *has_b = TRUE;
320
0
        b[1] = n;
321
0
        break;
322
0
      default:
323
0
        break;
324
0
    }
325
0
  }
326
0
}
327
328
static void
329
gst_audio_channel_mixer_fill_one_other (gfloat ** matrix,
330
    gint * from_idx, gint * to_idx, gfloat ratio)
331
0
{
332
333
  /* src & dst have center => passthrough */
334
0
  if (from_idx[1] != -1 && to_idx[1] != -1) {
335
0
    matrix[from_idx[1]][to_idx[1]] = ratio;
336
0
  }
337
338
  /* src & dst have left => passthrough */
339
0
  if (from_idx[0] != -1 && to_idx[0] != -1) {
340
0
    matrix[from_idx[0]][to_idx[0]] = ratio;
341
0
  }
342
343
  /* src & dst have right => passthrough */
344
0
  if (from_idx[2] != -1 && to_idx[2] != -1) {
345
0
    matrix[from_idx[2]][to_idx[2]] = ratio;
346
0
  }
347
348
  /* src has left & dst has center => put into center */
349
0
  if (from_idx[0] != -1 && to_idx[1] != -1 && from_idx[1] != -1) {
350
0
    matrix[from_idx[0]][to_idx[1]] = 0.5 * ratio;
351
0
  } else if (from_idx[0] != -1 && to_idx[1] != -1 && from_idx[1] == -1) {
352
0
    matrix[from_idx[0]][to_idx[1]] = ratio;
353
0
  }
354
355
  /* src has right & dst has center => put into center */
356
0
  if (from_idx[2] != -1 && to_idx[1] != -1 && from_idx[1] != -1) {
357
0
    matrix[from_idx[2]][to_idx[1]] = 0.5 * ratio;
358
0
  } else if (from_idx[2] != -1 && to_idx[1] != -1 && from_idx[1] == -1) {
359
0
    matrix[from_idx[2]][to_idx[1]] = ratio;
360
0
  }
361
362
  /* src has center & dst has left => passthrough */
363
0
  if (from_idx[1] != -1 && to_idx[0] != -1 && from_idx[0] != -1) {
364
0
    matrix[from_idx[1]][to_idx[0]] = 0.5 * ratio;
365
0
  } else if (from_idx[1] != -1 && to_idx[0] != -1 && from_idx[0] == -1) {
366
0
    matrix[from_idx[1]][to_idx[0]] = ratio;
367
0
  }
368
369
  /* src has center & dst has right => passthrough */
370
0
  if (from_idx[1] != -1 && to_idx[2] != -1 && from_idx[2] != -1) {
371
0
    matrix[from_idx[1]][to_idx[2]] = 0.5 * ratio;
372
0
  } else if (from_idx[1] != -1 && to_idx[2] != -1 && from_idx[2] == -1) {
373
0
    matrix[from_idx[1]][to_idx[2]] = ratio;
374
0
  }
375
0
}
376
377
0
#define RATIO_CENTER_FRONT (1.0 / sqrt (2.0))
378
0
#define RATIO_CENTER_SIDE (1.0 / 2.0)
379
0
#define RATIO_CENTER_REAR (1.0 / sqrt (8.0))
380
381
#define RATIO_FRONT_CENTER (1.0 / sqrt (2.0))
382
0
#define RATIO_FRONT_SIDE (1.0 / sqrt (2.0))
383
0
#define RATIO_FRONT_REAR (1.0 / 2.0)
384
385
#define RATIO_SIDE_CENTER (1.0 / 2.0)
386
#define RATIO_SIDE_FRONT (1.0 / sqrt (2.0))
387
0
#define RATIO_SIDE_REAR (1.0 / sqrt (2.0))
388
389
0
#define RATIO_CENTER_BASS (1.0 / sqrt (2.0))
390
0
#define RATIO_FRONT_BASS (1.0)
391
0
#define RATIO_SIDE_BASS (1.0 / sqrt (2.0))
392
0
#define RATIO_REAR_BASS (1.0 / sqrt (2.0))
393
394
static void
395
gst_audio_channel_mixer_fill_others (gfloat ** matrix, gint in_channels,
396
    GstAudioChannelPosition * in_position, gint out_channels,
397
    GstAudioChannelPosition * out_position)
398
0
{
399
0
  gboolean in_has_front = FALSE, out_has_front = FALSE,
400
0
      in_has_center = FALSE, out_has_center = FALSE,
401
0
      in_has_rear = FALSE, out_has_rear = FALSE,
402
0
      in_has_side = FALSE, out_has_side = FALSE,
403
0
      in_has_bass = FALSE, out_has_bass = FALSE;
404
  /* LEFT, RIGHT, MONO */
405
0
  gint in_f[3] = { -1, -1, -1 };
406
0
  gint out_f[3] = { -1, -1, -1 };
407
  /* LOC, ROC, CENTER */
408
0
  gint in_c[3] = { -1, -1, -1 };
409
0
  gint out_c[3] = { -1, -1, -1 };
410
  /* RLEFT, RRIGHT, RCENTER */
411
0
  gint in_r[3] = { -1, -1, -1 };
412
0
  gint out_r[3] = { -1, -1, -1 };
413
  /* SLEFT, INVALID, SRIGHT */
414
0
  gint in_s[3] = { -1, -1, -1 };
415
0
  gint out_s[3] = { -1, -1, -1 };
416
  /* INVALID, LFE, INVALID */
417
0
  gint in_b[3] = { -1, -1, -1 };
418
0
  gint out_b[3] = { -1, -1, -1 };
419
420
  /* First see where (if at all) the various channels from/to
421
   * which we want to convert are located in our matrix/array. */
422
0
  gst_audio_channel_mixer_detect_pos (in_channels, in_position,
423
0
      in_f, &in_has_front,
424
0
      in_c, &in_has_center, in_r, &in_has_rear,
425
0
      in_s, &in_has_side, in_b, &in_has_bass);
426
0
  gst_audio_channel_mixer_detect_pos (out_channels, out_position,
427
0
      out_f, &out_has_front,
428
0
      out_c, &out_has_center, out_r, &out_has_rear,
429
0
      out_s, &out_has_side, out_b, &out_has_bass);
430
431
  /* The general idea here is:
432
   * - if the source has a channel that the destination doesn't have mix
433
   *   it into the nearest available destination channel
434
   * - if the destination has a channel that the source doesn't have mix
435
   *   the nearest source channel into the destination channel
436
   *
437
   * The ratio for the mixing becomes lower as the distance between the
438
   * channels gets larger
439
   */
440
441
  /* center <-> front/side/rear */
442
0
  if (!in_has_center && in_has_front && out_has_center) {
443
0
    gst_audio_channel_mixer_fill_one_other (matrix, in_f, out_c,
444
0
        RATIO_CENTER_FRONT);
445
0
  } else if (!in_has_center && !in_has_front && in_has_side && out_has_center) {
446
0
    gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_c,
447
0
        RATIO_CENTER_SIDE);
448
0
  } else if (!in_has_center && !in_has_front && !in_has_side && in_has_rear
449
0
      && out_has_center) {
450
0
    gst_audio_channel_mixer_fill_one_other (matrix, in_r, out_c,
451
0
        RATIO_CENTER_REAR);
452
0
  } else if (in_has_center && !out_has_center && out_has_front) {
453
0
    gst_audio_channel_mixer_fill_one_other (matrix, in_c, out_f,
454
0
        RATIO_CENTER_FRONT);
455
0
  } else if (in_has_center && !out_has_center && !out_has_front && out_has_side) {
456
0
    gst_audio_channel_mixer_fill_one_other (matrix, in_c, out_s,
457
0
        RATIO_CENTER_SIDE);
458
0
  } else if (in_has_center && !out_has_center && !out_has_front && !out_has_side
459
0
      && out_has_rear) {
460
0
    gst_audio_channel_mixer_fill_one_other (matrix, in_c, out_r,
461
0
        RATIO_CENTER_REAR);
462
0
  }
463
464
  /* front <-> center/side/rear */
465
0
  if (!in_has_front && in_has_center && !in_has_side && out_has_front) {
466
0
    gst_audio_channel_mixer_fill_one_other (matrix, in_c, out_f,
467
0
        RATIO_CENTER_FRONT);
468
0
  } else if (!in_has_front && !in_has_center && in_has_side && out_has_front) {
469
0
    gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_f,
470
0
        RATIO_FRONT_SIDE);
471
0
  } else if (!in_has_front && in_has_center && in_has_side && out_has_front) {
472
0
    gst_audio_channel_mixer_fill_one_other (matrix, in_c, out_f,
473
0
        0.5 * RATIO_CENTER_FRONT);
474
0
    gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_f,
475
0
        0.5 * RATIO_FRONT_SIDE);
476
0
  } else if (!in_has_front && !in_has_center && !in_has_side && in_has_rear
477
0
      && out_has_front) {
478
0
    gst_audio_channel_mixer_fill_one_other (matrix, in_r, out_f,
479
0
        RATIO_FRONT_REAR);
480
0
  } else if (in_has_front && out_has_center && !out_has_side && !out_has_front) {
481
0
    gst_audio_channel_mixer_fill_one_other (matrix,
482
0
        in_f, out_c, RATIO_CENTER_FRONT);
483
0
  } else if (in_has_front && !out_has_center && out_has_side && !out_has_front) {
484
0
    gst_audio_channel_mixer_fill_one_other (matrix, in_f, out_s,
485
0
        RATIO_FRONT_SIDE);
486
0
  } else if (in_has_front && out_has_center && out_has_side && !out_has_front) {
487
0
    gst_audio_channel_mixer_fill_one_other (matrix, in_f, out_c,
488
0
        0.5 * RATIO_CENTER_FRONT);
489
0
    gst_audio_channel_mixer_fill_one_other (matrix, in_f, out_s,
490
0
        0.5 * RATIO_FRONT_SIDE);
491
0
  } else if (in_has_front && !out_has_center && !out_has_side && !out_has_front
492
0
      && out_has_rear) {
493
0
    gst_audio_channel_mixer_fill_one_other (matrix, in_f, out_r,
494
0
        RATIO_FRONT_REAR);
495
0
  }
496
497
  /* side <-> center/front/rear */
498
0
  if (!in_has_side && in_has_front && !in_has_rear && out_has_side) {
499
0
    gst_audio_channel_mixer_fill_one_other (matrix, in_f, out_s,
500
0
        RATIO_FRONT_SIDE);
501
0
  } else if (!in_has_side && !in_has_front && in_has_rear && out_has_side) {
502
0
    gst_audio_channel_mixer_fill_one_other (matrix, in_r, out_s,
503
0
        RATIO_SIDE_REAR);
504
0
  } else if (!in_has_side && in_has_front && in_has_rear && out_has_side) {
505
0
    gst_audio_channel_mixer_fill_one_other (matrix, in_f, out_s,
506
0
        0.5 * RATIO_FRONT_SIDE);
507
0
    gst_audio_channel_mixer_fill_one_other (matrix, in_r, out_s,
508
0
        0.5 * RATIO_SIDE_REAR);
509
0
  } else if (!in_has_side && !in_has_front && !in_has_rear && in_has_center
510
0
      && out_has_side) {
511
0
    gst_audio_channel_mixer_fill_one_other (matrix, in_c, out_s,
512
0
        RATIO_CENTER_SIDE);
513
0
  } else if (in_has_side && out_has_front && !out_has_rear && !out_has_side) {
514
0
    gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_f,
515
0
        RATIO_FRONT_SIDE);
516
0
  } else if (in_has_side && !out_has_front && out_has_rear && !out_has_side) {
517
0
    gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_r,
518
0
        RATIO_SIDE_REAR);
519
0
  } else if (in_has_side && out_has_front && out_has_rear && !out_has_side) {
520
0
    gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_f,
521
0
        0.5 * RATIO_FRONT_SIDE);
522
0
    gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_r,
523
0
        0.5 * RATIO_SIDE_REAR);
524
0
  } else if (in_has_side && !out_has_front && !out_has_rear && out_has_center
525
0
      && !out_has_side) {
526
0
    gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_c,
527
0
        RATIO_CENTER_SIDE);
528
0
  }
529
530
  /* rear <-> center/front/side */
531
0
  if (!in_has_rear && in_has_side && out_has_rear) {
532
0
    gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_r,
533
0
        RATIO_SIDE_REAR);
534
0
  } else if (!in_has_rear && !in_has_side && in_has_front && out_has_rear) {
535
0
    gst_audio_channel_mixer_fill_one_other (matrix, in_f, out_r,
536
0
        RATIO_FRONT_REAR);
537
0
  } else if (!in_has_rear && !in_has_side && !in_has_front && in_has_center
538
0
      && out_has_rear) {
539
0
    gst_audio_channel_mixer_fill_one_other (matrix, in_c, out_r,
540
0
        RATIO_CENTER_REAR);
541
0
  } else if (in_has_rear && !out_has_rear && out_has_side) {
542
0
    gst_audio_channel_mixer_fill_one_other (matrix, in_r, out_s,
543
0
        RATIO_SIDE_REAR);
544
0
  } else if (in_has_rear && !out_has_rear && !out_has_side && out_has_front) {
545
0
    gst_audio_channel_mixer_fill_one_other (matrix, in_r, out_f,
546
0
        RATIO_FRONT_REAR);
547
0
  } else if (in_has_rear && !out_has_rear && !out_has_side && !out_has_front
548
0
      && out_has_center) {
549
0
    gst_audio_channel_mixer_fill_one_other (matrix, in_r, out_c,
550
0
        RATIO_CENTER_REAR);
551
0
  }
552
553
  /* bass <-> any */
554
0
  if (in_has_bass && !out_has_bass) {
555
0
    if (out_has_center) {
556
0
      gst_audio_channel_mixer_fill_one_other (matrix, in_b, out_c,
557
0
          RATIO_CENTER_BASS);
558
0
    }
559
0
    if (out_has_front) {
560
0
      gst_audio_channel_mixer_fill_one_other (matrix, in_b, out_f,
561
0
          RATIO_FRONT_BASS);
562
0
    }
563
0
    if (out_has_side) {
564
0
      gst_audio_channel_mixer_fill_one_other (matrix, in_b, out_s,
565
0
          RATIO_SIDE_BASS);
566
0
    }
567
0
    if (out_has_rear) {
568
0
      gst_audio_channel_mixer_fill_one_other (matrix, in_b, out_r,
569
0
          RATIO_REAR_BASS);
570
0
    }
571
0
  } else if (!in_has_bass && out_has_bass) {
572
0
    if (in_has_center) {
573
0
      gst_audio_channel_mixer_fill_one_other (matrix, in_c, out_b,
574
0
          RATIO_CENTER_BASS);
575
0
    }
576
0
    if (in_has_front) {
577
0
      gst_audio_channel_mixer_fill_one_other (matrix, in_f, out_b,
578
0
          RATIO_FRONT_BASS);
579
0
    }
580
0
    if (in_has_side) {
581
0
      gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_b,
582
0
          RATIO_REAR_BASS);
583
0
    }
584
0
    if (in_has_rear) {
585
0
      gst_audio_channel_mixer_fill_one_other (matrix, in_r, out_b,
586
0
          RATIO_REAR_BASS);
587
0
    }
588
0
  }
589
0
}
590
591
/*
592
 * Normalize output values.
593
 */
594
595
static void
596
gst_audio_channel_mixer_fill_normalize (gfloat ** matrix, gint in_channels,
597
    gint out_channels)
598
0
{
599
0
  gfloat sum, top = 0;
600
0
  gint i, j;
601
602
0
  for (j = 0; j < out_channels; j++) {
603
    /* calculate sum */
604
0
    sum = 0.0;
605
0
    for (i = 0; i < in_channels; i++) {
606
0
      sum += fabs (matrix[i][j]);
607
0
    }
608
0
    if (sum > top) {
609
0
      top = sum;
610
0
    }
611
0
  }
612
613
  /* normalize to mix */
614
0
  if (top == 0.0)
615
0
    return;
616
617
0
  for (j = 0; j < out_channels; j++) {
618
0
    for (i = 0; i < in_channels; i++) {
619
0
      matrix[i][j] /= top;
620
0
    }
621
0
  }
622
0
}
623
624
static gboolean
625
gst_audio_channel_mixer_fill_special (gfloat ** matrix, gint in_channels,
626
    GstAudioChannelPosition * in_position, gint out_channels,
627
    GstAudioChannelPosition * out_position)
628
0
{
629
  /* Special, standard conversions here */
630
631
  /* Mono<->Stereo, just a fast-path */
632
0
  if (in_channels == 2 && out_channels == 1 &&
633
0
      ((in_position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT &&
634
0
              in_position[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT) ||
635
0
          (in_position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT &&
636
0
              in_position[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT)) &&
637
0
      out_position[0] == GST_AUDIO_CHANNEL_POSITION_MONO) {
638
0
    matrix[0][0] = 0.5;
639
0
    matrix[1][0] = 0.5;
640
0
    return TRUE;
641
0
  } else if (in_channels == 1 && out_channels == 2 &&
642
0
      ((out_position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT &&
643
0
              out_position[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT) ||
644
0
          (out_position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT &&
645
0
              out_position[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT)) &&
646
0
      in_position[0] == GST_AUDIO_CHANNEL_POSITION_MONO) {
647
0
    matrix[0][0] = 1.0;
648
0
    matrix[0][1] = 1.0;
649
0
    return TRUE;
650
0
  }
651
652
  /* TODO: 5.1 <-> Stereo and other standard conversions */
653
654
0
  return FALSE;
655
0
}
656
657
/*
658
 * Automagically generate conversion matrix.
659
 */
660
661
typedef enum
662
{
663
  GST_AUDIO_CHANNEL_MIXER_VIRTUAL_INPUT_NONE = 0,
664
  GST_AUDIO_CHANNEL_MIXER_VIRTUAL_INPUT_MONO,
665
  GST_AUDIO_CHANNEL_MIXER_VIRTUAL_INPUT_STEREO
666
} GstAudioChannelMixerVirtualInput;
667
668
/* Detects specific input channels configurations introduced in the
669
 * audioconvert element (since version 1.26) with the
670
 * `GstAudioConvertInputChannelsReorder` configurations.
671
 *
672
 * If all input channels are positioned to GST_AUDIO_CHANNEL_POSITION_MONO,
673
 * the automatic mixing matrix should be configured like if there was only one
674
 * virtual input mono channel. This virtual mono channel is the mix of all the
675
 * real mono channels.
676
 *
677
 * If all input channels with an even index are positioned to
678
 * GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT and all input channels with an odd
679
 * index are positioned to GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, then the
680
 * automatic mixing matrix should be configured like if there were only one
681
 * virtual input left channel and one virtual input right channel. This virtual
682
 * left or right channel is the mix of all the real left or right channels.
683
 */
684
static gboolean
685
gst_audio_channel_mixer_detect_virtual_input_channels (gint channels,
686
    GstAudioChannelPosition * position,
687
    GstAudioChannelMixerVirtualInput * virtual_input)
688
0
{
689
0
  g_return_val_if_fail (position != NULL, FALSE);
690
0
  g_return_val_if_fail (virtual_input != NULL, FALSE);
691
692
0
  *virtual_input = GST_AUDIO_CHANNEL_MIXER_VIRTUAL_INPUT_NONE;
693
694
0
  if (channels < 2)
695
0
    return FALSE;
696
697
0
  static const GstAudioChannelPosition alternate_positions[2] =
698
0
      { GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
699
0
    GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT
700
0
  };
701
702
0
  gboolean is_mono = TRUE;
703
0
  gboolean is_alternate = TRUE;
704
0
  for (gint i = 0; i < channels; ++i) {
705
0
    if (position[i] != GST_AUDIO_CHANNEL_POSITION_MONO)
706
0
      is_mono = FALSE;
707
708
0
    if (position[i] != alternate_positions[i % 2])
709
0
      is_alternate = FALSE;
710
711
0
    if (!is_mono && !is_alternate)
712
0
      return FALSE;
713
0
  }
714
715
0
  if (is_mono) {
716
0
    g_assert (!is_alternate);
717
0
    *virtual_input = GST_AUDIO_CHANNEL_MIXER_VIRTUAL_INPUT_MONO;
718
0
    return TRUE;
719
0
  }
720
721
0
  if (is_alternate && (channels > 2)) {
722
0
    g_assert (!is_mono);
723
0
    *virtual_input = GST_AUDIO_CHANNEL_MIXER_VIRTUAL_INPUT_STEREO;
724
0
    return TRUE;
725
0
  }
726
727
0
  return FALSE;
728
0
}
729
730
static void
731
gst_audio_channel_mixer_fill_matrix (gfloat ** matrix,
732
    GstAudioChannelMixerFlags flags, gint in_channels,
733
    GstAudioChannelPosition * in_position, gint out_channels,
734
    GstAudioChannelPosition * out_position)
735
0
{
736
0
  if (gst_audio_channel_mixer_fill_special (matrix, in_channels, in_position,
737
0
          out_channels, out_position))
738
0
    return;
739
740
  /* If all input channels are positioned to mono, the mix matrix should be
741
   * configured like if there was only one virtual input mono channel. This
742
   * virtual mono channel is the mix of all the real input mono channels.
743
   *
744
   * If all input channels are positioned to left and right alternately, the mix
745
   * matrix should be configured like if there were only two virtual input
746
   * channels: one left and one right. This virtual left or right channel is the
747
   * mix of all the real input left or right channels.
748
   */
749
0
  gint in_size = in_channels;
750
0
  GstAudioChannelMixerVirtualInput virtual_input =
751
0
      GST_AUDIO_CHANNEL_MIXER_VIRTUAL_INPUT_NONE;
752
0
  if (gst_audio_channel_mixer_detect_virtual_input_channels (in_size,
753
0
          in_position, &virtual_input)) {
754
0
    switch (virtual_input) {
755
0
      case GST_AUDIO_CHANNEL_MIXER_VIRTUAL_INPUT_MONO:
756
0
        in_size = 1;
757
0
        break;
758
0
      case GST_AUDIO_CHANNEL_MIXER_VIRTUAL_INPUT_STEREO:
759
0
        in_size = 2;
760
0
        break;
761
0
      default:
762
0
        break;
763
0
    }
764
0
  }
765
766
0
  gst_audio_channel_mixer_fill_identical (matrix, in_size, in_position,
767
0
      out_channels, out_position, flags);
768
769
0
  if (!(flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_UNPOSITIONED_IN)) {
770
0
    gst_audio_channel_mixer_fill_compatible (matrix, in_size, in_position,
771
0
        out_channels, out_position);
772
0
    gst_audio_channel_mixer_fill_others (matrix, in_size, in_position,
773
0
        out_channels, out_position);
774
0
    gst_audio_channel_mixer_fill_normalize (matrix, in_size, out_channels);
775
0
  }
776
777
0
  switch (virtual_input) {
778
0
    case GST_AUDIO_CHANNEL_MIXER_VIRTUAL_INPUT_MONO:{
779
0
      for (gint out = 0; out < out_channels; ++out)
780
0
        matrix[0][out] /= in_channels;
781
782
0
      for (gint in = 1; in < in_channels; ++in)
783
0
        memcpy (matrix[in], matrix[0], out_channels * sizeof (gfloat));
784
785
0
      break;
786
0
    }
787
788
0
    case GST_AUDIO_CHANNEL_MIXER_VIRTUAL_INPUT_STEREO:{
789
0
      gint right_channels = in_channels >> 1;
790
0
      gint left_channels = right_channels + (in_channels % 2);
791
792
0
      for (gint out = 0; out < out_channels; ++out) {
793
0
        matrix[0][out] /= left_channels;
794
0
        matrix[1][out] /= right_channels;
795
0
      }
796
797
0
      for (gint in = 2; in < in_channels; ++in)
798
0
        memcpy (matrix[in], matrix[in % 2], out_channels * sizeof (gfloat));
799
800
0
      break;
801
0
    }
802
803
0
    default:
804
0
      break;
805
0
  }
806
0
}
807
808
/* only call mix after mix->matrix is fully set up and normalized */
809
static void
810
gst_audio_channel_mixer_setup_matrix_int (GstAudioChannelMixer * mix)
811
0
{
812
0
  gint i, j;
813
0
  gfloat tmp;
814
0
  gfloat factor = (1 << PRECISION_INT);
815
816
0
  mix->matrix_int = g_new0 (gint *, mix->in_channels);
817
818
0
  for (i = 0; i < mix->in_channels; i++) {
819
0
    mix->matrix_int[i] = g_new (gint, mix->out_channels);
820
821
0
    for (j = 0; j < mix->out_channels; j++) {
822
0
      tmp = mix->matrix[i][j] * factor;
823
0
      mix->matrix_int[i][j] = (gint) tmp;
824
0
    }
825
0
  }
826
0
}
827
828
static gfloat **
829
gst_audio_channel_mixer_setup_matrix (GstAudioChannelMixerFlags flags,
830
    gint in_channels, GstAudioChannelPosition * in_position,
831
    gint out_channels, GstAudioChannelPosition * out_position)
832
0
{
833
0
  gint i, j;
834
0
  gfloat **matrix = g_new0 (gfloat *, in_channels);
835
836
0
  for (i = 0; i < in_channels; i++) {
837
0
    matrix[i] = g_new (gfloat, out_channels);
838
0
    for (j = 0; j < out_channels; j++)
839
0
      matrix[i][j] = 0.;
840
0
  }
841
842
  /* setup the matrix' internal values */
843
0
  gst_audio_channel_mixer_fill_matrix (matrix, flags, in_channels, in_position,
844
0
      out_channels, out_position);
845
846
0
  return matrix;
847
0
}
848
849
#define DEFINE_GET_DATA_FUNCS(type) \
850
static inline type \
851
_get_in_data_interleaved_##type (const type * in_data[], \
852
0
    gint sample, gint channel, gint total_channels) \
853
0
{ \
854
0
  return in_data[0][sample * total_channels + channel]; \
855
0
} \
Unexecuted instantiation: audio-channel-mixer.c:_get_in_data_interleaved_gint16
Unexecuted instantiation: audio-channel-mixer.c:_get_in_data_interleaved_gint32
Unexecuted instantiation: audio-channel-mixer.c:_get_in_data_interleaved_gfloat
Unexecuted instantiation: audio-channel-mixer.c:_get_in_data_interleaved_gdouble
856
\
857
static inline type * \
858
_get_out_data_interleaved_##type (type * out_data[], \
859
0
    gint sample, gint channel, gint total_channels) \
860
0
{ \
861
0
  return &out_data[0][sample * total_channels + channel]; \
862
0
} \
Unexecuted instantiation: audio-channel-mixer.c:_get_out_data_interleaved_gint16
Unexecuted instantiation: audio-channel-mixer.c:_get_out_data_interleaved_gint32
Unexecuted instantiation: audio-channel-mixer.c:_get_out_data_interleaved_gfloat
Unexecuted instantiation: audio-channel-mixer.c:_get_out_data_interleaved_gdouble
863
\
864
static inline type \
865
_get_in_data_planar_##type (const type * in_data[], \
866
0
    gint sample, gint channel, gint total_channels) \
867
0
{ \
868
0
  (void) total_channels; \
869
0
  return in_data[channel][sample]; \
870
0
} \
Unexecuted instantiation: audio-channel-mixer.c:_get_in_data_planar_gint16
Unexecuted instantiation: audio-channel-mixer.c:_get_in_data_planar_gint32
Unexecuted instantiation: audio-channel-mixer.c:_get_in_data_planar_gfloat
Unexecuted instantiation: audio-channel-mixer.c:_get_in_data_planar_gdouble
871
\
872
static inline type * \
873
_get_out_data_planar_##type (type * out_data[], \
874
0
    gint sample, gint channel, gint total_channels) \
875
0
{ \
876
0
  (void) total_channels; \
877
0
  return &out_data[channel][sample]; \
878
0
}
Unexecuted instantiation: audio-channel-mixer.c:_get_out_data_planar_gint16
Unexecuted instantiation: audio-channel-mixer.c:_get_out_data_planar_gint32
Unexecuted instantiation: audio-channel-mixer.c:_get_out_data_planar_gfloat
Unexecuted instantiation: audio-channel-mixer.c:_get_out_data_planar_gdouble
879
880
#define DEFINE_INTEGER_SPARSE_MIX_FUNC(bits, resbits, inlayout, outlayout) \
881
static void \
882
gst_audio_channel_mixer_sparse_mix_int##bits##_##inlayout##_##outlayout ( \
883
    GstAudioChannelMixer * mix, const gint##bits * in_data[], \
884
0
    gint##bits * out_data[], gint samples) \
885
0
{ \
886
0
  gint in, out, n; \
887
0
  gint##resbits res; \
888
0
  gint inchannels, outchannels; \
889
0
  guint offset, count; \
890
0
  const MixOutEntry *out_entry; \
891
0
  const MixEntry *entry; \
892
0
  \
893
0
  inchannels = mix->in_channels; \
894
0
  outchannels = mix->out_channels; \
895
0
  \
896
0
  n = mix->non_interleaved ? mix->out_channels : 1; \
897
0
  for (out = 0; out < n; out++) { \
898
0
    gst_audio_format_info_fill_silence (mix->finfo, \
899
0
      out_data[out], samples * mix->clear_unit); \
900
0
  } \
901
0
  \
902
0
  for (n = 0; n < samples; n++) { \
903
0
    for (out = 0; out < mix->num_valid_out_ch; out++) { \
904
0
      /* convert */ \
905
0
      out_entry = &mix->out_entry[out]; \
906
0
      offset = out_entry->offset; \
907
0
      count = out_entry->count; \
908
0
      res = 0; \
909
0
      for (in = 0; in < count; in++) { \
910
0
        entry = &mix->entry[offset + in]; \
911
0
        res += \
912
0
          _get_in_data_##inlayout##_gint##bits (in_data, n, entry->index, inchannels) * \
913
0
          (gint##resbits) entry->coeff_int; \
914
0
      } \
915
0
      /* remove factor from int matrix */ \
916
0
      res = (res + (1 << (PRECISION_INT - 1))) >> PRECISION_INT; \
917
0
      *_get_out_data_##outlayout##_gint##bits (out_data, n, out_entry->index, outchannels) = \
918
0
          CLAMP (res, G_MININT##bits, G_MAXINT##bits); \
919
0
    } \
920
0
  } \
921
0
}
Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_sparse_mix_int16_planar_planar
Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_sparse_mix_int16_planar_interleaved
Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_sparse_mix_int16_interleaved_planar
Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_sparse_mix_int16_interleaved_interleaved
Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_sparse_mix_int32_planar_planar
Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_sparse_mix_int32_planar_interleaved
Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_sparse_mix_int32_interleaved_planar
Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_sparse_mix_int32_interleaved_interleaved
922
923
#define DEFINE_FLOAT_SPARSE_MIX_FUNC(type, inlayout, outlayout) \
924
static void \
925
gst_audio_channel_mixer_sparse_mix_##type##_##inlayout##_##outlayout ( \
926
    GstAudioChannelMixer * mix, const g##type * in_data[], \
927
0
    g##type * out_data[], gint samples) \
928
0
{ \
929
0
  gint in, out, n; \
930
0
  g##type res; \
931
0
  gint inchannels, outchannels; \
932
0
  guint offset, count; \
933
0
  const MixOutEntry *out_entry; \
934
0
  const MixEntry *entry; \
935
0
  \
936
0
  inchannels = mix->in_channels; \
937
0
  outchannels = mix->out_channels; \
938
0
  \
939
0
  n = mix->non_interleaved ? mix->out_channels : 1; \
940
0
  for (out = 0; out < n; out++) { \
941
0
    gst_audio_format_info_fill_silence (mix->finfo, \
942
0
      out_data[out], samples * mix->clear_unit); \
943
0
  } \
944
0
  \
945
0
  for (n = 0; n < samples; n++) { \
946
0
    for (out = 0; out < mix->num_valid_out_ch; out++) { \
947
0
      /* convert */ \
948
0
      out_entry = &mix->out_entry[out]; \
949
0
      offset = out_entry->offset; \
950
0
      count = out_entry->count; \
951
0
      res = 0.0; \
952
0
      for (in = 0; in < count; in++) { \
953
0
        entry = &mix->entry[offset + in]; \
954
0
        res += \
955
0
          _get_in_data_##inlayout##_g##type (in_data, n, entry->index, inchannels) * \
956
0
          entry->coeff; \
957
0
      } \
958
0
      *_get_out_data_##outlayout##_g##type (out_data, n, out_entry->index, outchannels) = res; \
959
0
    } \
960
0
  } \
961
0
}
Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_sparse_mix_float_planar_planar
Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_sparse_mix_float_planar_interleaved
Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_sparse_mix_float_interleaved_planar
Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_sparse_mix_float_interleaved_interleaved
Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_sparse_mix_double_planar_planar
Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_sparse_mix_double_planar_interleaved
Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_sparse_mix_double_interleaved_planar
Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_sparse_mix_double_interleaved_interleaved
962
963
#define DEFINE_INTEGER_MIX_FUNC(bits, resbits, inlayout, outlayout) \
964
static void \
965
gst_audio_channel_mixer_mix_int##bits##_##inlayout##_##outlayout ( \
966
    GstAudioChannelMixer * mix, const gint##bits * in_data[], \
967
0
    gint##bits * out_data[], gint samples) \
968
0
{ \
969
0
  gint in, out, n; \
970
0
  gint##resbits res; \
971
0
  gint inchannels, outchannels; \
972
0
  \
973
0
  inchannels = mix->in_channels; \
974
0
  outchannels = mix->out_channels; \
975
0
  \
976
0
  for (n = 0; n < samples; n++) { \
977
0
    for (out = 0; out < outchannels; out++) { \
978
0
      /* convert */ \
979
0
      res = 0; \
980
0
      for (in = 0; in < inchannels; in++) \
981
0
        res += \
982
0
          _get_in_data_##inlayout##_gint##bits (in_data, n, in, inchannels) * \
983
0
          (gint##resbits) mix->matrix_int[in][out]; \
984
0
      \
985
0
      /* remove factor from int matrix */ \
986
0
      res = (res + (1 << (PRECISION_INT - 1))) >> PRECISION_INT; \
987
0
      *_get_out_data_##outlayout##_gint##bits (out_data, n, out, outchannels) = \
988
0
          CLAMP (res, G_MININT##bits, G_MAXINT##bits); \
989
0
    } \
990
0
  } \
991
0
}
Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_mix_int16_planar_planar
Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_mix_int16_planar_interleaved
Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_mix_int16_interleaved_planar
Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_mix_int16_interleaved_interleaved
Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_mix_int32_planar_planar
Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_mix_int32_planar_interleaved
Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_mix_int32_interleaved_planar
Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_mix_int32_interleaved_interleaved
992
993
#define DEFINE_FLOAT_MIX_FUNC(type, inlayout, outlayout) \
994
static void \
995
gst_audio_channel_mixer_mix_##type##_##inlayout##_##outlayout ( \
996
    GstAudioChannelMixer * mix, const g##type * in_data[], \
997
0
    g##type * out_data[], gint samples) \
998
0
{ \
999
0
  gint in, out, n; \
1000
0
  g##type res; \
1001
0
  gint inchannels, outchannels; \
1002
0
  \
1003
0
  inchannels = mix->in_channels; \
1004
0
  outchannels = mix->out_channels; \
1005
0
  \
1006
0
  for (n = 0; n < samples; n++) { \
1007
0
    for (out = 0; out < outchannels; out++) { \
1008
0
      /* convert */ \
1009
0
      res = 0.0; \
1010
0
      for (in = 0; in < inchannels; in++) \
1011
0
        res += \
1012
0
          _get_in_data_##inlayout##_g##type (in_data, n, in, inchannels) * \
1013
0
          mix->matrix[in][out]; \
1014
0
      \
1015
0
      *_get_out_data_##outlayout##_g##type (out_data, n, out, outchannels) = res; \
1016
0
    } \
1017
0
  } \
1018
0
}
Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_mix_float_planar_planar
Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_mix_float_planar_interleaved
Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_mix_float_interleaved_planar
Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_mix_float_interleaved_interleaved
Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_mix_double_planar_planar
Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_mix_double_planar_interleaved
Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_mix_double_interleaved_planar
Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_mix_double_interleaved_interleaved
1019
1020
DEFINE_GET_DATA_FUNCS (gint16);
1021
DEFINE_INTEGER_MIX_FUNC (16, 32, interleaved, interleaved);
1022
DEFINE_INTEGER_MIX_FUNC (16, 32, interleaved, planar);
1023
DEFINE_INTEGER_MIX_FUNC (16, 32, planar, interleaved);
1024
DEFINE_INTEGER_MIX_FUNC (16, 32, planar, planar);
1025
1026
DEFINE_INTEGER_SPARSE_MIX_FUNC (16, 32, interleaved, interleaved);
1027
DEFINE_INTEGER_SPARSE_MIX_FUNC (16, 32, interleaved, planar);
1028
DEFINE_INTEGER_SPARSE_MIX_FUNC (16, 32, planar, interleaved);
1029
DEFINE_INTEGER_SPARSE_MIX_FUNC (16, 32, planar, planar);
1030
1031
DEFINE_GET_DATA_FUNCS (gint32);
1032
DEFINE_INTEGER_MIX_FUNC (32, 64, interleaved, interleaved);
1033
DEFINE_INTEGER_MIX_FUNC (32, 64, interleaved, planar);
1034
DEFINE_INTEGER_MIX_FUNC (32, 64, planar, interleaved);
1035
DEFINE_INTEGER_MIX_FUNC (32, 64, planar, planar);
1036
1037
DEFINE_INTEGER_SPARSE_MIX_FUNC (32, 64, interleaved, interleaved);
1038
DEFINE_INTEGER_SPARSE_MIX_FUNC (32, 64, interleaved, planar);
1039
DEFINE_INTEGER_SPARSE_MIX_FUNC (32, 64, planar, interleaved);
1040
DEFINE_INTEGER_SPARSE_MIX_FUNC (32, 64, planar, planar);
1041
1042
DEFINE_GET_DATA_FUNCS (gfloat);
1043
DEFINE_FLOAT_MIX_FUNC (float, interleaved, interleaved);
1044
DEFINE_FLOAT_MIX_FUNC (float, interleaved, planar);
1045
DEFINE_FLOAT_MIX_FUNC (float, planar, interleaved);
1046
DEFINE_FLOAT_MIX_FUNC (float, planar, planar);
1047
1048
DEFINE_FLOAT_SPARSE_MIX_FUNC (float, interleaved, interleaved);
1049
DEFINE_FLOAT_SPARSE_MIX_FUNC (float, interleaved, planar);
1050
DEFINE_FLOAT_SPARSE_MIX_FUNC (float, planar, interleaved);
1051
DEFINE_FLOAT_SPARSE_MIX_FUNC (float, planar, planar);
1052
1053
DEFINE_GET_DATA_FUNCS (gdouble);
1054
DEFINE_FLOAT_MIX_FUNC (double, interleaved, interleaved);
1055
DEFINE_FLOAT_MIX_FUNC (double, interleaved, planar);
1056
DEFINE_FLOAT_MIX_FUNC (double, planar, interleaved);
1057
DEFINE_FLOAT_MIX_FUNC (double, planar, planar);
1058
1059
DEFINE_FLOAT_SPARSE_MIX_FUNC (double, interleaved, interleaved);
1060
DEFINE_FLOAT_SPARSE_MIX_FUNC (double, interleaved, planar);
1061
DEFINE_FLOAT_SPARSE_MIX_FUNC (double, planar, interleaved);
1062
DEFINE_FLOAT_SPARSE_MIX_FUNC (double, planar, planar);
1063
1064
static gboolean
1065
gst_audio_channel_mixer_build_sparse_matrix (GstAudioChannelMixer * mix)
1066
0
{
1067
0
  const gfloat eps = 1e-6f;
1068
0
  gint out, in;
1069
0
  gint offset = 0;
1070
0
  gint total_pairs = 0;
1071
0
  gdouble density;
1072
1073
0
  for (out = 0; out < mix->out_channels; out++) {
1074
0
    for (in = 0; in < mix->in_channels; in++) {
1075
0
      if (fabsf (mix->matrix[in][out]) > eps)
1076
0
        total_pairs++;
1077
0
    }
1078
0
  }
1079
1080
0
  density = ((double) total_pairs) / (mix->out_channels * mix->in_channels);
1081
1082
0
  GST_DEBUG ("nonzero coeff ratio: %.2lf (%d / %d)", density, total_pairs,
1083
0
      mix->out_channels * mix->in_channels);
1084
1085
  /* Sparse matrix mixing involves extra lookup and memset overhead.
1086
   * Use sparse optimization only when a sufficient number of zero coefficients
1087
   * is detected */
1088
0
  if (NONZERO_DENSITY_THRESHOLD <= density)
1089
0
    return FALSE;
1090
1091
0
  mix->out_entry = g_new0 (MixOutEntry, mix->out_channels);
1092
0
  mix->entry = g_new0 (MixEntry, total_pairs);
1093
1094
0
  for (out = 0; out < mix->out_channels; out++) {
1095
0
    guint count = 0;
1096
0
    for (in = 0; in < mix->in_channels; in++) {
1097
0
      gfloat coeff = mix->matrix[in][out];
1098
0
      if (fabsf (coeff) > eps) {
1099
0
        mix->entry[offset].index = in;
1100
0
        if (GST_AUDIO_FORMAT_INFO_IS_INTEGER (mix->finfo)) {
1101
0
          mix->entry[offset].coeff_int =
1102
0
              (gint) (((gfloat) (1 << PRECISION_INT)) * coeff);
1103
0
        } else {
1104
0
          mix->entry[offset].coeff = coeff;
1105
0
        }
1106
0
        offset++;
1107
0
        count++;
1108
0
      }
1109
0
    }
1110
1111
0
    if (count > 0) {
1112
0
      MixOutEntry *out_entry = &mix->out_entry[mix->num_valid_out_ch];
1113
0
      out_entry->index = out;
1114
0
      out_entry->offset = offset - count;
1115
0
      out_entry->count = count;
1116
0
      mix->num_valid_out_ch++;
1117
0
    }
1118
0
  }
1119
1120
0
  GST_DEBUG ("Use sparse matrix");
1121
1122
0
  return TRUE;
1123
0
}
1124
1125
/**
1126
 * gst_audio_channel_mixer_new_with_matrix: (constructor) (skip):
1127
 * @flags: #GstAudioChannelMixerFlags
1128
 * @in_channels: number of input channels
1129
 * @out_channels: number of output channels
1130
 * @matrix: (array) (element-type float*) (transfer full) (nullable): channel conversion matrix, m[@in_channels][@out_channels].
1131
 *   If identity matrix, passthrough applies. If %NULL, a (potentially truncated)
1132
 *   identity matrix is generated.
1133
 *
1134
 * Create a new channel mixer object for the given parameters.
1135
 *
1136
 * Returns: (transfer full): a new #GstAudioChannelMixer object.
1137
 *   Free with gst_audio_channel_mixer_free() after usage.
1138
 *
1139
 * Since: 1.14
1140
 */
1141
GstAudioChannelMixer *
1142
gst_audio_channel_mixer_new_with_matrix (GstAudioChannelMixerFlags flags,
1143
    GstAudioFormat format,
1144
    gint in_channels, gint out_channels, gfloat ** matrix)
1145
0
{
1146
0
  GstAudioChannelMixer *mix;
1147
0
  gboolean use_sparse;
1148
1149
0
  g_return_val_if_fail (format == GST_AUDIO_FORMAT_S16
1150
0
      || format == GST_AUDIO_FORMAT_S32
1151
0
      || format == GST_AUDIO_FORMAT_F32
1152
0
      || format == GST_AUDIO_FORMAT_F64, NULL);
1153
0
  g_return_val_if_fail (in_channels > 0, NULL);
1154
0
  g_return_val_if_fail (out_channels > 0, NULL);
1155
1156
0
  mix = g_new0 (GstAudioChannelMixer, 1);
1157
0
  mix->in_channels = in_channels;
1158
0
  mix->out_channels = out_channels;
1159
1160
0
  if (!matrix) {
1161
    /* Generate (potentially truncated) identity matrix */
1162
0
    gint i, j;
1163
1164
0
    mix->matrix = g_new0 (gfloat *, in_channels);
1165
1166
0
    for (i = 0; i < in_channels; i++) {
1167
0
      mix->matrix[i] = g_new (gfloat, out_channels);
1168
0
      for (j = 0; j < out_channels; j++) {
1169
0
        mix->matrix[i][j] = i == j ? 1.0 : 0.0;
1170
0
      }
1171
0
    }
1172
0
  } else {
1173
0
    mix->matrix = matrix;
1174
0
  }
1175
1176
0
  mix->finfo = gst_audio_format_get_info (format);
1177
1178
0
  use_sparse = gst_audio_channel_mixer_build_sparse_matrix (mix);
1179
0
  if (use_sparse) {
1180
0
    if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT) {
1181
0
      mix->non_interleaved = TRUE;
1182
0
      mix->clear_unit = mix->finfo->width / 8;
1183
0
    } else {
1184
0
      mix->clear_unit = mix->finfo->width * out_channels / 8;
1185
0
    }
1186
0
  } else if (GST_AUDIO_FORMAT_INFO_IS_INTEGER (mix->finfo)) {
1187
0
    gst_audio_channel_mixer_setup_matrix_int (mix);
1188
0
  }
1189
1190
0
#ifndef GST_DISABLE_GST_DEBUG
1191
  /* debug */
1192
0
  {
1193
0
    GString *s;
1194
0
    gint i, j;
1195
1196
0
    s = g_string_new ("Matrix for");
1197
0
    g_string_append_printf (s, " %d -> %d: ",
1198
0
        mix->in_channels, mix->out_channels);
1199
0
    g_string_append (s, "{");
1200
0
    for (i = 0; i < mix->in_channels; i++) {
1201
0
      if (i != 0)
1202
0
        g_string_append (s, ",");
1203
0
      g_string_append (s, " {");
1204
0
      for (j = 0; j < mix->out_channels; j++) {
1205
0
        if (j != 0)
1206
0
          g_string_append (s, ",");
1207
0
        g_string_append_printf (s, " %f", mix->matrix[i][j]);
1208
0
      }
1209
0
      g_string_append (s, " }");
1210
0
    }
1211
0
    g_string_append (s, " }");
1212
0
    GST_DEBUG ("%s", s->str);
1213
0
    g_string_free (s, TRUE);
1214
0
  }
1215
0
#endif
1216
1217
0
  switch (format) {
1218
0
    case GST_AUDIO_FORMAT_S16:
1219
0
      if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_IN) {
1220
0
        if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT) {
1221
0
          mix->func = (MixerFunc)
1222
0
              gst_audio_channel_mixer_mix_int16_planar_planar;
1223
0
          if (use_sparse) {
1224
0
            mix->func = (MixerFunc)
1225
0
                gst_audio_channel_mixer_sparse_mix_int16_planar_planar;
1226
0
          }
1227
0
        } else {
1228
0
          mix->func = (MixerFunc)
1229
0
              gst_audio_channel_mixer_mix_int16_planar_interleaved;
1230
0
          if (use_sparse) {
1231
0
            mix->func = (MixerFunc)
1232
0
                gst_audio_channel_mixer_sparse_mix_int16_planar_interleaved;
1233
0
          }
1234
0
        }
1235
0
      } else {
1236
0
        if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT) {
1237
0
          mix->func = (MixerFunc)
1238
0
              gst_audio_channel_mixer_mix_int16_interleaved_planar;
1239
0
          if (use_sparse) {
1240
0
            mix->func = (MixerFunc)
1241
0
                gst_audio_channel_mixer_sparse_mix_int16_interleaved_planar;
1242
0
          }
1243
0
        } else {
1244
0
          mix->func = (MixerFunc)
1245
0
              gst_audio_channel_mixer_mix_int16_interleaved_interleaved;
1246
0
          if (use_sparse) {
1247
0
            mix->func = (MixerFunc)
1248
0
                gst_audio_channel_mixer_sparse_mix_int16_interleaved_interleaved;
1249
0
          }
1250
0
        }
1251
0
      }
1252
0
      break;
1253
0
    case GST_AUDIO_FORMAT_S32:
1254
0
      if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_IN) {
1255
0
        if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT) {
1256
0
          mix->func = (MixerFunc)
1257
0
              gst_audio_channel_mixer_mix_int32_planar_planar;
1258
0
          if (use_sparse) {
1259
0
            mix->func = (MixerFunc)
1260
0
                gst_audio_channel_mixer_sparse_mix_int32_planar_planar;
1261
0
          }
1262
0
        } else {
1263
0
          mix->func = (MixerFunc)
1264
0
              gst_audio_channel_mixer_mix_int32_planar_interleaved;
1265
0
          if (use_sparse) {
1266
0
            mix->func = (MixerFunc)
1267
0
                gst_audio_channel_mixer_sparse_mix_int32_planar_interleaved;
1268
0
          }
1269
0
        }
1270
0
      } else {
1271
0
        if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT) {
1272
0
          mix->func = (MixerFunc)
1273
0
              gst_audio_channel_mixer_mix_int32_interleaved_planar;
1274
0
          if (use_sparse) {
1275
0
            mix->func = (MixerFunc)
1276
0
                gst_audio_channel_mixer_sparse_mix_int32_interleaved_planar;
1277
0
          }
1278
0
        } else {
1279
0
          mix->func = (MixerFunc)
1280
0
              gst_audio_channel_mixer_mix_int32_interleaved_interleaved;
1281
0
          if (use_sparse) {
1282
0
            mix->func = (MixerFunc)
1283
0
                gst_audio_channel_mixer_sparse_mix_int32_interleaved_interleaved;
1284
0
          }
1285
0
        }
1286
0
      }
1287
0
      break;
1288
0
    case GST_AUDIO_FORMAT_F32:
1289
0
      if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_IN) {
1290
0
        if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT) {
1291
0
          mix->func = (MixerFunc)
1292
0
              gst_audio_channel_mixer_mix_float_planar_planar;
1293
0
          if (use_sparse) {
1294
0
            mix->func = (MixerFunc)
1295
0
                gst_audio_channel_mixer_sparse_mix_float_planar_planar;
1296
0
          }
1297
0
        } else {
1298
0
          mix->func = (MixerFunc)
1299
0
              gst_audio_channel_mixer_mix_float_planar_interleaved;
1300
0
          if (use_sparse) {
1301
0
            mix->func = (MixerFunc)
1302
0
                gst_audio_channel_mixer_sparse_mix_float_planar_interleaved;
1303
0
          }
1304
0
        }
1305
0
      } else {
1306
0
        if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT) {
1307
0
          mix->func = (MixerFunc)
1308
0
              gst_audio_channel_mixer_mix_float_interleaved_planar;
1309
0
          if (use_sparse) {
1310
0
            mix->func = (MixerFunc)
1311
0
                gst_audio_channel_mixer_sparse_mix_float_interleaved_planar;
1312
0
          }
1313
0
        } else {
1314
0
          mix->func = (MixerFunc)
1315
0
              gst_audio_channel_mixer_mix_float_interleaved_interleaved;
1316
0
          if (use_sparse) {
1317
0
            mix->func = (MixerFunc)
1318
0
                gst_audio_channel_mixer_sparse_mix_float_interleaved_interleaved;
1319
0
          }
1320
0
        }
1321
0
      }
1322
0
      break;
1323
0
    case GST_AUDIO_FORMAT_F64:
1324
0
      if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_IN) {
1325
0
        if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT) {
1326
0
          mix->func = (MixerFunc)
1327
0
              gst_audio_channel_mixer_mix_double_planar_planar;
1328
0
          if (use_sparse) {
1329
0
            mix->func = (MixerFunc)
1330
0
                gst_audio_channel_mixer_sparse_mix_double_planar_planar;
1331
0
          }
1332
0
        } else {
1333
0
          mix->func = (MixerFunc)
1334
0
              gst_audio_channel_mixer_mix_double_planar_interleaved;
1335
0
          if (use_sparse) {
1336
0
            mix->func = (MixerFunc)
1337
0
                gst_audio_channel_mixer_sparse_mix_double_planar_interleaved;
1338
0
          }
1339
0
        }
1340
0
      } else {
1341
0
        if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT) {
1342
0
          mix->func = (MixerFunc)
1343
0
              gst_audio_channel_mixer_mix_double_interleaved_planar;
1344
0
          if (use_sparse) {
1345
0
            mix->func = (MixerFunc)
1346
0
                gst_audio_channel_mixer_sparse_mix_double_interleaved_planar;
1347
0
          }
1348
0
        } else {
1349
0
          mix->func = (MixerFunc)
1350
0
              gst_audio_channel_mixer_mix_double_interleaved_interleaved;
1351
0
          if (use_sparse) {
1352
0
            mix->func = (MixerFunc)
1353
0
                gst_audio_channel_mixer_sparse_mix_double_interleaved_interleaved;
1354
0
          }
1355
0
        }
1356
0
      }
1357
0
      break;
1358
0
    default:
1359
0
      g_assert_not_reached ();
1360
0
      break;
1361
0
  }
1362
0
  return mix;
1363
0
}
1364
1365
/**
1366
 * gst_audio_channel_mixer_new: (constructor) (skip):
1367
 * @flags: #GstAudioChannelMixerFlags
1368
 * @in_channels: number of input channels
1369
 * @in_position: (array length=in_channels): positions of input channels
1370
 * @out_channels: number of output channels
1371
 * @out_position: (array length=out_channels): positions of output channels
1372
 *
1373
 * Create a new channel mixer object for the given parameters.
1374
 *
1375
 * Returns: (transfer full): a new #GstAudioChannelMixer object.
1376
 *   Free with gst_audio_channel_mixer_free() after usage.
1377
 */
1378
GstAudioChannelMixer *
1379
gst_audio_channel_mixer_new (GstAudioChannelMixerFlags flags,
1380
    GstAudioFormat format,
1381
    gint in_channels,
1382
    GstAudioChannelPosition * in_position,
1383
    gint out_channels, GstAudioChannelPosition * out_position)
1384
0
{
1385
0
  gfloat **matrix;
1386
1387
0
  g_return_val_if_fail (format == GST_AUDIO_FORMAT_S16
1388
0
      || format == GST_AUDIO_FORMAT_S32
1389
0
      || format == GST_AUDIO_FORMAT_F32
1390
0
      || format == GST_AUDIO_FORMAT_F64, NULL);
1391
1392
0
  matrix =
1393
0
      gst_audio_channel_mixer_setup_matrix (flags, in_channels, in_position,
1394
0
      out_channels, out_position);
1395
0
  return gst_audio_channel_mixer_new_with_matrix (flags, format, in_channels,
1396
0
      out_channels, matrix);
1397
0
}
1398
1399
/**
1400
 * gst_audio_channel_mixer_is_passthrough:
1401
 * @mix: a #GstAudioChannelMixer
1402
 *
1403
 * Check if @mix is in passthrough.
1404
 *
1405
 * Only N x N mix identity matrices are considered passthrough,
1406
 * this is determined by comparing the contents of the matrix
1407
 * with 0.0 and 1.0.
1408
 *
1409
 * As this is floating point comparisons, if the values have been
1410
 * generated, they should be rounded up or down by explicit
1411
 * assignment of 0.0 or 1.0 to values within a user-defined
1412
 * epsilon, this code doesn't make assumptions as to what may
1413
 * constitute an appropriate epsilon.
1414
 *
1415
 * Returns: %TRUE is @mix is passthrough.
1416
 */
1417
gboolean
1418
gst_audio_channel_mixer_is_passthrough (GstAudioChannelMixer * mix)
1419
0
{
1420
0
  gint i, j;
1421
0
  gboolean res;
1422
1423
  /* only NxN matrices can be identities */
1424
0
  if (mix->in_channels != mix->out_channels)
1425
0
    return FALSE;
1426
1427
0
  res = TRUE;
1428
1429
0
  for (i = 0; i < mix->in_channels; i++) {
1430
0
    for (j = 0; j < mix->out_channels; j++) {
1431
0
      if ((i == j && mix->matrix[i][j] != 1.0f) ||
1432
0
          (i != j && mix->matrix[i][j] != 0.0f)) {
1433
0
        res = FALSE;
1434
0
        break;
1435
0
      }
1436
0
    }
1437
0
  }
1438
1439
0
  return res;
1440
0
}
1441
1442
/**
1443
 * gst_audio_channel_mixer_samples:
1444
 * @mix: a #GstAudioChannelMixer
1445
 * @in: (array) (element-type gpointer): input samples
1446
 * @out: (array) (element-type gpointer): output samples
1447
 * @samples: number of samples
1448
 *
1449
 * In case the samples are interleaved, @in and @out must point to an
1450
 * array with a single element pointing to a block of interleaved samples.
1451
 *
1452
 * If non-interleaved samples are used, @in and @out must point to an
1453
 * array with pointers to memory blocks, one for each channel.
1454
 *
1455
 * Perform channel mixing on @in_data and write the result to @out_data.
1456
 * @in_data and @out_data need to be in @format and @layout.
1457
 */
1458
void
1459
gst_audio_channel_mixer_samples (GstAudioChannelMixer * mix,
1460
    const gpointer in[], gpointer out[], gint samples)
1461
0
{
1462
0
  g_return_if_fail (mix != NULL);
1463
0
  g_return_if_fail (mix->matrix != NULL);
1464
1465
0
  mix->func (mix, in, out, samples);
1466
0
}