2 * Copyright (C) 2004 Ronald Bultje <rbultje@ronald.bitfreak.net>
3 * Copyright (C) 2008 Sebastian Dröge <slomo@circular-chaos.org>
5 * gstchannelmix.c: setup of channel conversion matrices
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.
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.
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.
30 #include "gstchannelmix.h"
33 * Channel matrix functions.
37 gst_channel_mix_unset_matrix (AudioConvertCtx * this)
41 /* don't access if nothing there */
46 for (i = 0; i < this->in.channels; i++)
47 g_free (this->matrix[i]);
48 g_free (this->matrix);
56 * Detect and fill in identical channels. E.g.
57 * forward the left/right front channels in a
58 * 5.1 to 2.0 conversion.
62 gst_channel_mix_fill_identical (AudioConvertCtx * this)
66 /* Apart from the compatible channel assignments, we can also have
67 * same channel assignments. This is much simpler, we simply copy
68 * the value from source to dest! */
69 for (co = 0; co < this->out.channels; co++) {
70 /* find a channel in input with same position */
71 for (ci = 0; ci < this->in.channels; ci++) {
72 if (this->in.position[ci] == this->out.position[co]) {
73 this->matrix[ci][co] = 1.0;
80 * Detect and fill in compatible channels. E.g.
81 * forward left/right front to mono (or the other
82 * way around) when going from 2.0 to 1.0.
86 gst_channel_mix_fill_compatible (AudioConvertCtx * this)
88 /* Conversions from one-channel to compatible two-channel configs */
91 GstAudioChannelPosition pos1[2];
92 GstAudioChannelPosition pos2[1];
94 /* front: mono <-> stereo */
96 GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
97 GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, {
98 GST_AUDIO_CHANNEL_POSITION_MONO}},
99 /* front center: 2 <-> 1 */
101 GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
102 GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER}, {
103 GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER}},
106 GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
107 GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}, {
108 GST_AUDIO_CHANNEL_POSITION_REAR_CENTER}}, { {
109 GST_AUDIO_CHANNEL_POSITION_INVALID}}
113 /* conversions from compatible (but not the same) channel schemes */
114 for (c = 0; conv[c].pos1[0] != GST_AUDIO_CHANNEL_POSITION_INVALID; c++) {
115 gint pos1_0 = -1, pos1_1 = -1, pos1_2 = -1;
116 gint pos2_0 = -1, pos2_1 = -1, pos2_2 = -1;
119 for (n = 0; n < this->in.channels; n++) {
120 if (this->in.position[n] == conv[c].pos1[0])
122 else if (this->in.position[n] == conv[c].pos1[1])
124 else if (this->in.position[n] == conv[c].pos2[0])
127 for (n = 0; n < this->out.channels; n++) {
128 if (this->out.position[n] == conv[c].pos1[0])
130 else if (this->out.position[n] == conv[c].pos1[1])
132 else if (this->out.position[n] == conv[c].pos2[0])
136 /* The general idea here is to fill in channels from the same position
137 * as good as possible. This means mixing left<->center and right<->center.
141 if (pos1_0 != -1 && pos1_2 == -1 && pos2_0 == -1 && pos2_2 != -1)
142 this->matrix[pos1_0][pos2_2] = 1.0;
143 else if (pos1_0 != -1 && pos1_2 != -1 && pos2_0 == -1 && pos2_2 != -1)
144 this->matrix[pos1_0][pos2_2] = 0.5;
145 else if (pos1_0 != -1 && pos1_2 == -1 && pos2_0 != -1 && pos2_2 != -1)
146 this->matrix[pos1_0][pos2_2] = 1.0;
148 /* right -> center */
149 if (pos1_1 != -1 && pos1_2 == -1 && pos2_1 == -1 && pos2_2 != -1)
150 this->matrix[pos1_1][pos2_2] = 1.0;
151 else if (pos1_1 != -1 && pos1_2 != -1 && pos2_1 == -1 && pos2_2 != -1)
152 this->matrix[pos1_1][pos2_2] = 0.5;
153 else if (pos1_1 != -1 && pos1_2 == -1 && pos2_1 != -1 && pos2_2 != -1)
154 this->matrix[pos1_1][pos2_2] = 1.0;
157 if (pos1_2 != -1 && pos1_0 == -1 && pos2_2 == -1 && pos2_0 != -1)
158 this->matrix[pos1_2][pos2_0] = 1.0;
159 else if (pos1_2 != -1 && pos1_0 != -1 && pos2_2 == -1 && pos2_0 != -1)
160 this->matrix[pos1_2][pos2_0] = 0.5;
161 else if (pos1_2 != -1 && pos1_0 == -1 && pos2_2 != -1 && pos2_0 != -1)
162 this->matrix[pos1_2][pos2_0] = 1.0;
164 /* center -> right */
165 if (pos1_2 != -1 && pos1_1 == -1 && pos2_2 == -1 && pos2_1 != -1)
166 this->matrix[pos1_2][pos2_1] = 1.0;
167 else if (pos1_2 != -1 && pos1_1 != -1 && pos2_2 == -1 && pos2_1 != -1)
168 this->matrix[pos1_2][pos2_1] = 0.5;
169 else if (pos1_2 != -1 && pos1_1 == -1 && pos2_2 != -1 && pos2_1 != -1)
170 this->matrix[pos1_2][pos2_1] = 1.0;
175 * Detect and fill in channels not handled by the
176 * above two, e.g. center to left/right front in
177 * 5.1 to 2.0 (or the other way around).
179 * Unfortunately, limited to static conversions
184 gst_channel_mix_detect_pos (GstAudioInfo * info,
185 gint * f, gboolean * has_f,
186 gint * c, gboolean * has_c, gint * r, gboolean * has_r,
187 gint * s, gboolean * has_s, gint * b, gboolean * has_b)
191 for (n = 0; n < info->channels; n++) {
192 switch (info->position[n]) {
193 case GST_AUDIO_CHANNEL_POSITION_MONO:
197 case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT:
201 case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT:
205 case GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER:
209 case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
213 case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
217 case GST_AUDIO_CHANNEL_POSITION_REAR_CENTER:
221 case GST_AUDIO_CHANNEL_POSITION_REAR_LEFT:
225 case GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT:
229 case GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT:
233 case GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT:
237 case GST_AUDIO_CHANNEL_POSITION_LFE1:
248 gst_channel_mix_fill_one_other (gfloat ** matrix,
249 GstAudioInfo * from_info, gint * from_idx,
250 GstAudioInfo * to_info, gint * to_idx, gfloat ratio)
253 /* src & dst have center => passthrough */
254 if (from_idx[1] != -1 && to_idx[1] != -1) {
255 matrix[from_idx[1]][to_idx[1]] = ratio;
258 /* src & dst have left => passthrough */
259 if (from_idx[0] != -1 && to_idx[0] != -1) {
260 matrix[from_idx[0]][to_idx[0]] = ratio;
263 /* src & dst have right => passthrough */
264 if (from_idx[2] != -1 && to_idx[2] != -1) {
265 matrix[from_idx[2]][to_idx[2]] = ratio;
268 /* src has left & dst has center => put into center */
269 if (from_idx[0] != -1 && to_idx[1] != -1 && from_idx[1] != -1) {
270 matrix[from_idx[0]][to_idx[1]] = 0.5 * ratio;
271 } else if (from_idx[0] != -1 && to_idx[1] != -1 && from_idx[1] == -1) {
272 matrix[from_idx[0]][to_idx[1]] = ratio;
275 /* src has right & dst has center => put into center */
276 if (from_idx[2] != -1 && to_idx[1] != -1 && from_idx[1] != -1) {
277 matrix[from_idx[2]][to_idx[1]] = 0.5 * ratio;
278 } else if (from_idx[2] != -1 && to_idx[1] != -1 && from_idx[1] == -1) {
279 matrix[from_idx[2]][to_idx[1]] = ratio;
282 /* src has center & dst has left => passthrough */
283 if (from_idx[1] != -1 && to_idx[0] != -1 && from_idx[0] != -1) {
284 matrix[from_idx[1]][to_idx[0]] = 0.5 * ratio;
285 } else if (from_idx[1] != -1 && to_idx[0] != -1 && from_idx[0] == -1) {
286 matrix[from_idx[1]][to_idx[0]] = ratio;
289 /* src has center & dst has right => passthrough */
290 if (from_idx[1] != -1 && to_idx[2] != -1 && from_idx[2] != -1) {
291 matrix[from_idx[1]][to_idx[2]] = 0.5 * ratio;
292 } else if (from_idx[1] != -1 && to_idx[2] != -1 && from_idx[2] == -1) {
293 matrix[from_idx[1]][to_idx[2]] = ratio;
297 #define RATIO_CENTER_FRONT (1.0 / sqrt (2.0))
298 #define RATIO_CENTER_SIDE (1.0 / 2.0)
299 #define RATIO_CENTER_REAR (1.0 / sqrt (8.0))
301 #define RATIO_FRONT_CENTER (1.0 / sqrt (2.0))
302 #define RATIO_FRONT_SIDE (1.0 / sqrt (2.0))
303 #define RATIO_FRONT_REAR (1.0 / 2.0)
305 #define RATIO_SIDE_CENTER (1.0 / 2.0)
306 #define RATIO_SIDE_FRONT (1.0 / sqrt (2.0))
307 #define RATIO_SIDE_REAR (1.0 / sqrt (2.0))
309 #define RATIO_CENTER_BASS (1.0 / sqrt (2.0))
310 #define RATIO_FRONT_BASS (1.0)
311 #define RATIO_SIDE_BASS (1.0 / sqrt (2.0))
312 #define RATIO_REAR_BASS (1.0 / sqrt (2.0))
315 gst_channel_mix_fill_others (AudioConvertCtx * this)
317 gboolean in_has_front = FALSE, out_has_front = FALSE,
318 in_has_center = FALSE, out_has_center = FALSE,
319 in_has_rear = FALSE, out_has_rear = FALSE,
320 in_has_side = FALSE, out_has_side = FALSE,
321 in_has_bass = FALSE, out_has_bass = FALSE;
322 /* LEFT, RIGHT, MONO */
323 gint in_f[3] = { -1, -1, -1 };
324 gint out_f[3] = { -1, -1, -1 };
325 /* LOC, ROC, CENTER */
326 gint in_c[3] = { -1, -1, -1 };
327 gint out_c[3] = { -1, -1, -1 };
328 /* RLEFT, RRIGHT, RCENTER */
329 gint in_r[3] = { -1, -1, -1 };
330 gint out_r[3] = { -1, -1, -1 };
331 /* SLEFT, INVALID, SRIGHT */
332 gint in_s[3] = { -1, -1, -1 };
333 gint out_s[3] = { -1, -1, -1 };
334 /* INVALID, LFE, INVALID */
335 gint in_b[3] = { -1, -1, -1 };
336 gint out_b[3] = { -1, -1, -1 };
338 /* First see where (if at all) the various channels from/to
339 * which we want to convert are located in our matrix/array. */
340 gst_channel_mix_detect_pos (&this->in,
342 in_c, &in_has_center, in_r, &in_has_rear,
343 in_s, &in_has_side, in_b, &in_has_bass);
344 gst_channel_mix_detect_pos (&this->out,
345 out_f, &out_has_front,
346 out_c, &out_has_center, out_r, &out_has_rear,
347 out_s, &out_has_side, out_b, &out_has_bass);
349 /* The general idea here is:
350 * - if the source has a channel that the destination doesn't have mix
351 * it into the nearest available destination channel
352 * - if the destination has a channel that the source doesn't have mix
353 * the nearest source channel into the destination channel
355 * The ratio for the mixing becomes lower as the distance between the
356 * channels gets larger
359 /* center <-> front/side/rear */
360 if (!in_has_center && in_has_front && out_has_center) {
361 gst_channel_mix_fill_one_other (this->matrix,
362 &this->in, in_f, &this->out, out_c, RATIO_CENTER_FRONT);
363 } else if (!in_has_center && !in_has_front && in_has_side && out_has_center) {
364 gst_channel_mix_fill_one_other (this->matrix,
365 &this->in, in_s, &this->out, out_c, RATIO_CENTER_SIDE);
366 } else if (!in_has_center && !in_has_front && !in_has_side && in_has_rear
368 gst_channel_mix_fill_one_other (this->matrix, &this->in, in_r, &this->out,
369 out_c, RATIO_CENTER_REAR);
370 } else if (in_has_center && !out_has_center && out_has_front) {
371 gst_channel_mix_fill_one_other (this->matrix,
372 &this->in, in_c, &this->out, out_f, RATIO_CENTER_FRONT);
373 } else if (in_has_center && !out_has_center && !out_has_front && out_has_side) {
374 gst_channel_mix_fill_one_other (this->matrix,
375 &this->in, in_c, &this->out, out_s, RATIO_CENTER_SIDE);
376 } else if (in_has_center && !out_has_center && !out_has_front && !out_has_side
378 gst_channel_mix_fill_one_other (this->matrix, &this->in, in_c, &this->out,
379 out_r, RATIO_CENTER_REAR);
382 /* front <-> center/side/rear */
383 if (!in_has_front && in_has_center && !in_has_side && out_has_front) {
384 gst_channel_mix_fill_one_other (this->matrix,
385 &this->in, in_c, &this->out, out_f, RATIO_CENTER_FRONT);
386 } else if (!in_has_front && !in_has_center && in_has_side && out_has_front) {
387 gst_channel_mix_fill_one_other (this->matrix,
388 &this->in, in_s, &this->out, out_f, RATIO_FRONT_SIDE);
389 } else if (!in_has_front && in_has_center && in_has_side && out_has_front) {
390 gst_channel_mix_fill_one_other (this->matrix,
391 &this->in, in_c, &this->out, out_f, 0.5 * RATIO_CENTER_FRONT);
392 gst_channel_mix_fill_one_other (this->matrix,
393 &this->in, in_s, &this->out, out_f, 0.5 * RATIO_FRONT_SIDE);
394 } else if (!in_has_front && !in_has_center && !in_has_side && in_has_rear
396 gst_channel_mix_fill_one_other (this->matrix, &this->in, in_r, &this->out,
397 out_f, RATIO_FRONT_REAR);
398 } else if (in_has_front && out_has_center && !out_has_side && !out_has_front) {
399 gst_channel_mix_fill_one_other (this->matrix,
400 &this->in, in_f, &this->out, out_c, RATIO_CENTER_FRONT);
401 } else if (in_has_front && !out_has_center && out_has_side && !out_has_front) {
402 gst_channel_mix_fill_one_other (this->matrix,
403 &this->in, in_f, &this->out, out_s, RATIO_FRONT_SIDE);
404 } else if (in_has_front && out_has_center && out_has_side && !out_has_front) {
405 gst_channel_mix_fill_one_other (this->matrix,
406 &this->in, in_f, &this->out, out_c, 0.5 * RATIO_CENTER_FRONT);
407 gst_channel_mix_fill_one_other (this->matrix,
408 &this->in, in_f, &this->out, out_s, 0.5 * RATIO_FRONT_SIDE);
409 } else if (in_has_front && !out_has_center && !out_has_side && !out_has_front
411 gst_channel_mix_fill_one_other (this->matrix, &this->in, in_f, &this->out,
412 out_r, RATIO_FRONT_REAR);
415 /* side <-> center/front/rear */
416 if (!in_has_side && in_has_front && !in_has_rear && out_has_side) {
417 gst_channel_mix_fill_one_other (this->matrix,
418 &this->in, in_f, &this->out, out_s, RATIO_FRONT_SIDE);
419 } else if (!in_has_side && !in_has_front && in_has_rear && out_has_side) {
420 gst_channel_mix_fill_one_other (this->matrix,
421 &this->in, in_r, &this->out, out_s, RATIO_SIDE_REAR);
422 } else if (!in_has_side && in_has_front && in_has_rear && out_has_side) {
423 gst_channel_mix_fill_one_other (this->matrix,
424 &this->in, in_f, &this->out, out_s, 0.5 * RATIO_FRONT_SIDE);
425 gst_channel_mix_fill_one_other (this->matrix,
426 &this->in, in_r, &this->out, out_s, 0.5 * RATIO_SIDE_REAR);
427 } else if (!in_has_side && !in_has_front && !in_has_rear && in_has_center
429 gst_channel_mix_fill_one_other (this->matrix, &this->in, in_c, &this->out,
430 out_s, RATIO_CENTER_SIDE);
431 } else if (in_has_side && out_has_front && !out_has_rear && !out_has_side) {
432 gst_channel_mix_fill_one_other (this->matrix,
433 &this->in, in_s, &this->out, out_f, RATIO_FRONT_SIDE);
434 } else if (in_has_side && !out_has_front && out_has_rear && !out_has_side) {
435 gst_channel_mix_fill_one_other (this->matrix,
436 &this->in, in_s, &this->out, out_r, RATIO_SIDE_REAR);
437 } else if (in_has_side && out_has_front && out_has_rear && !out_has_side) {
438 gst_channel_mix_fill_one_other (this->matrix,
439 &this->in, in_s, &this->out, out_f, 0.5 * RATIO_FRONT_SIDE);
440 gst_channel_mix_fill_one_other (this->matrix,
441 &this->in, in_s, &this->out, out_r, 0.5 * RATIO_SIDE_REAR);
442 } else if (in_has_side && !out_has_front && !out_has_rear && out_has_center
444 gst_channel_mix_fill_one_other (this->matrix, &this->in, in_s, &this->out,
445 out_c, RATIO_CENTER_SIDE);
448 /* rear <-> center/front/side */
449 if (!in_has_rear && in_has_side && out_has_rear) {
450 gst_channel_mix_fill_one_other (this->matrix,
451 &this->in, in_s, &this->out, out_r, RATIO_SIDE_REAR);
452 } else if (!in_has_rear && !in_has_side && in_has_front && out_has_rear) {
453 gst_channel_mix_fill_one_other (this->matrix,
454 &this->in, in_f, &this->out, out_r, RATIO_FRONT_REAR);
455 } else if (!in_has_rear && !in_has_side && !in_has_front && in_has_center
457 gst_channel_mix_fill_one_other (this->matrix, &this->in, in_c, &this->out,
458 out_r, RATIO_CENTER_REAR);
459 } else if (in_has_rear && !out_has_rear && out_has_side) {
460 gst_channel_mix_fill_one_other (this->matrix,
461 &this->in, in_r, &this->out, out_s, RATIO_SIDE_REAR);
462 } else if (in_has_rear && !out_has_rear && !out_has_side && out_has_front) {
463 gst_channel_mix_fill_one_other (this->matrix,
464 &this->in, in_r, &this->out, out_f, RATIO_FRONT_REAR);
465 } else if (in_has_rear && !out_has_rear && !out_has_side && !out_has_front
467 gst_channel_mix_fill_one_other (this->matrix, &this->in, in_r, &this->out,
468 out_c, RATIO_CENTER_REAR);
472 if (in_has_bass && !out_has_bass) {
473 if (out_has_center) {
474 gst_channel_mix_fill_one_other (this->matrix,
475 &this->in, in_b, &this->out, out_c, RATIO_CENTER_BASS);
478 gst_channel_mix_fill_one_other (this->matrix,
479 &this->in, in_b, &this->out, out_f, RATIO_FRONT_BASS);
482 gst_channel_mix_fill_one_other (this->matrix,
483 &this->in, in_b, &this->out, out_s, RATIO_SIDE_BASS);
486 gst_channel_mix_fill_one_other (this->matrix,
487 &this->in, in_b, &this->out, out_r, RATIO_REAR_BASS);
489 } else if (!in_has_bass && out_has_bass) {
491 gst_channel_mix_fill_one_other (this->matrix,
492 &this->in, in_c, &this->out, out_b, RATIO_CENTER_BASS);
495 gst_channel_mix_fill_one_other (this->matrix,
496 &this->in, in_f, &this->out, out_b, RATIO_FRONT_BASS);
499 gst_channel_mix_fill_one_other (this->matrix,
500 &this->in, in_s, &this->out, out_b, RATIO_REAR_BASS);
503 gst_channel_mix_fill_one_other (this->matrix,
504 &this->in, in_r, &this->out, out_b, RATIO_REAR_BASS);
510 * Normalize output values.
514 gst_channel_mix_fill_normalize (AudioConvertCtx * this)
519 for (j = 0; j < this->out.channels; j++) {
522 for (i = 0; i < this->in.channels; i++) {
523 sum += fabs (this->matrix[i][j]);
530 /* normalize to this */
534 for (j = 0; j < this->out.channels; j++) {
535 for (i = 0; i < this->in.channels; i++) {
536 this->matrix[i][j] /= top;
542 gst_channel_mix_fill_special (AudioConvertCtx * this)
544 GstAudioInfo *in = &this->in, *out = &this->out;
546 /* Special, standard conversions here */
548 /* Mono<->Stereo, just a fast-path */
549 if (in->channels == 2 && out->channels == 1 &&
550 ((in->position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT &&
551 in->position[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT) ||
552 (in->position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT &&
553 in->position[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT)) &&
554 out->position[0] == GST_AUDIO_CHANNEL_POSITION_MONO) {
555 this->matrix[0][0] = 0.5;
556 this->matrix[1][0] = 0.5;
558 } else if (in->channels == 1 && out->channels == 2 &&
559 ((out->position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT &&
560 out->position[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT) ||
561 (out->position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT &&
562 out->position[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT)) &&
563 in->position[0] == GST_AUDIO_CHANNEL_POSITION_MONO) {
564 this->matrix[0][0] = 1.0;
565 this->matrix[0][1] = 1.0;
569 /* TODO: 5.1 <-> Stereo and other standard conversions */
575 * Automagically generate conversion matrix.
579 gst_channel_mix_fill_matrix (AudioConvertCtx * this)
581 if (gst_channel_mix_fill_special (this))
584 gst_channel_mix_fill_identical (this);
586 if (!GST_AUDIO_INFO_IS_UNPOSITIONED (&this->in)) {
587 gst_channel_mix_fill_compatible (this);
588 gst_channel_mix_fill_others (this);
589 gst_channel_mix_fill_normalize (this);
593 /* only call after this->out and this->in are filled in */
595 gst_channel_mix_setup_matrix (AudioConvertCtx * this)
599 /* don't lose memory */
600 gst_channel_mix_unset_matrix (this);
603 if (GST_AUDIO_FORMAT_INFO_IS_INTEGER (this->in.finfo) ||
604 GST_AUDIO_FORMAT_INFO_IS_INTEGER (this->out.finfo)) {
605 this->tmp = (gpointer) g_new (gint32, this->out.channels);
607 this->tmp = (gpointer) g_new (gdouble, this->out.channels);
611 this->matrix = g_new0 (gfloat *, this->in.channels);
612 for (i = 0; i < this->in.channels; i++) {
613 this->matrix[i] = g_new (gfloat, this->out.channels);
614 for (j = 0; j < this->out.channels; j++)
615 this->matrix[i][j] = 0.;
618 /* setup the matrix' internal values */
619 gst_channel_mix_fill_matrix (this);
621 #ifndef GST_DISABLE_GST_DEBUG
625 s = g_string_new ("Matrix for");
626 g_string_append_printf (s, " %d -> %d: ",
627 this->in.channels, this->out.channels);
628 g_string_append (s, "{");
629 for (i = 0; i < this->in.channels; i++) {
631 g_string_append (s, ",");
632 g_string_append (s, " {");
633 for (j = 0; j < this->out.channels; j++) {
635 g_string_append (s, ",");
636 g_string_append_printf (s, " %f", this->matrix[i][j]);
638 g_string_append (s, " }");
640 g_string_append (s, " }");
641 GST_DEBUG ("%s", s->str);
642 g_string_free (s, TRUE);
648 gst_channel_mix_passthrough (AudioConvertCtx * this)
651 guint64 in_mask, out_mask;
653 /* only NxN matrices can be identities */
654 if (this->in.channels != this->out.channels)
657 /* passthrough if both channel masks are the same */
658 in_mask = out_mask = 0;
659 for (i = 0; i < this->in.channels; i++) {
660 in_mask |= this->in.position[i];
661 out_mask |= this->out.position[i];
664 return in_mask == out_mask;
667 /* IMPORTANT: out_data == in_data is possible, make sure to not overwrite data
668 * you might need later on! */
670 gst_channel_mix_mix_int (AudioConvertCtx * this,
671 gint32 * in_data, gint32 * out_data, gint samples)
676 gint inchannels, outchannels;
677 gint32 *tmp = (gint32 *) this->tmp;
679 g_return_if_fail (this->matrix != NULL);
680 g_return_if_fail (this->tmp != NULL);
682 inchannels = this->in.channels;
683 outchannels = this->out.channels;
684 backwards = outchannels > inchannels;
686 /* FIXME: use orc here? */
687 for (n = (backwards ? samples - 1 : 0); n < samples && n >= 0;
688 backwards ? n-- : n++) {
689 for (out = 0; out < outchannels; out++) {
692 for (in = 0; in < inchannels; in++) {
693 res += in_data[n * inchannels + in] * this->matrix[in][out];
696 /* clip (shouldn't we use doubles instead as intermediate format?) */
697 if (res < G_MININT32)
699 else if (res > G_MAXINT32)
703 memcpy (&out_data[n * outchannels], this->tmp,
704 sizeof (gint32) * outchannels);
709 gst_channel_mix_mix_float (AudioConvertCtx * this,
710 gdouble * in_data, gdouble * out_data, gint samples)
715 gint inchannels, outchannels;
716 gdouble *tmp = (gdouble *) this->tmp;
718 g_return_if_fail (this->matrix != NULL);
719 g_return_if_fail (this->tmp != NULL);
721 inchannels = this->in.channels;
722 outchannels = this->out.channels;
723 backwards = outchannels > inchannels;
725 /* FIXME: use liboil here? */
726 for (n = (backwards ? samples - 1 : 0); n < samples && n >= 0;
727 backwards ? n-- : n++) {
728 for (out = 0; out < outchannels; out++) {
731 for (in = 0; in < inchannels; in++) {
732 res += in_data[n * inchannels + in] * this->matrix[in][out];
735 /* clip (shouldn't we use doubles instead as intermediate format?) */
742 memcpy (&out_data[n * outchannels], this->tmp,
743 sizeof (gdouble) * outchannels);