Merge branch 'master' into 0.11
[platform/upstream/gstreamer.git] / gst / audioconvert / gstchannelmix.c
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  * gstchannelmix.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., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include <math.h>
28 #include <string.h>
29 #include <gst/audio/multichannel.h>
30
31 #include "gstchannelmix.h"
32
33 /*
34  * Channel matrix functions.
35  */
36
37 void
38 gst_channel_mix_unset_matrix (AudioConvertCtx * this)
39 {
40   gint i;
41
42   /* don't access if nothing there */
43   if (!this->matrix)
44     return;
45
46   /* free */
47   for (i = 0; i < this->in.channels; i++)
48     g_free (this->matrix[i]);
49   g_free (this->matrix);
50
51   this->matrix = NULL;
52   g_free (this->tmp);
53   this->tmp = NULL;
54 }
55
56 /*
57  * Detect and fill in identical channels. E.g.
58  * forward the left/right front channels in a
59  * 5.1 to 2.0 conversion.
60  */
61
62 static void
63 gst_channel_mix_fill_identical (AudioConvertCtx * this)
64 {
65   gint ci, co;
66
67   /* Apart from the compatible channel assignments, we can also have
68    * same channel assignments. This is much simpler, we simply copy
69    * the value from source to dest! */
70   for (co = 0; co < this->out.channels; co++) {
71     /* find a channel in input with same position */
72     for (ci = 0; ci < this->in.channels; ci++) {
73       if (this->in.position[ci] == this->out.position[co]) {
74         this->matrix[ci][co] = 1.0;
75       }
76     }
77   }
78 }
79
80 /*
81  * Detect and fill in compatible channels. E.g.
82  * forward left/right front to mono (or the other
83  * way around) when going from 2.0 to 1.0.
84  */
85
86 static void
87 gst_channel_mix_fill_compatible (AudioConvertCtx * this)
88 {
89   /* Conversions from one-channel to compatible two-channel configs */
90   struct
91   {
92     GstAudioChannelPosition pos1[2];
93     GstAudioChannelPosition pos2[1];
94   } conv[] = {
95     /* front: mono <-> stereo */
96     { {
97     GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
98             GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, {
99     GST_AUDIO_CHANNEL_POSITION_FRONT_MONO}},
100         /* front center: 2 <-> 1 */
101     { {
102     GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
103             GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER}, {
104     GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER}},
105         /* rear: 2 <-> 1 */
106     { {
107     GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
108             GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}, {
109     GST_AUDIO_CHANNEL_POSITION_REAR_CENTER}}, { {
110     GST_AUDIO_CHANNEL_POSITION_INVALID}}
111   };
112   gint c;
113
114   /* conversions from compatible (but not the same) channel schemes */
115   for (c = 0; conv[c].pos1[0] != GST_AUDIO_CHANNEL_POSITION_INVALID; c++) {
116     gint pos1_0 = -1, pos1_1 = -1, pos1_2 = -1;
117     gint pos2_0 = -1, pos2_1 = -1, pos2_2 = -1;
118     gint n;
119
120     for (n = 0; n < this->in.channels; n++) {
121       if (this->in.position[n] == conv[c].pos1[0])
122         pos1_0 = n;
123       else if (this->in.position[n] == conv[c].pos1[1])
124         pos1_1 = n;
125       else if (this->in.position[n] == conv[c].pos2[0])
126         pos1_2 = n;
127     }
128     for (n = 0; n < this->out.channels; n++) {
129       if (this->out.position[n] == conv[c].pos1[0])
130         pos2_0 = n;
131       else if (this->out.position[n] == conv[c].pos1[1])
132         pos2_1 = n;
133       else if (this->out.position[n] == conv[c].pos2[0])
134         pos2_2 = n;
135     }
136
137     /* The general idea here is to fill in channels from the same position
138      * as good as possible. This means mixing left<->center and right<->center.
139      */
140
141     /* left -> center */
142     if (pos1_0 != -1 && pos1_2 == -1 && pos2_0 == -1 && pos2_2 != -1)
143       this->matrix[pos1_0][pos2_2] = 1.0;
144     else if (pos1_0 != -1 && pos1_2 != -1 && pos2_0 == -1 && pos2_2 != -1)
145       this->matrix[pos1_0][pos2_2] = 0.5;
146     else if (pos1_0 != -1 && pos1_2 == -1 && pos2_0 != -1 && pos2_2 != -1)
147       this->matrix[pos1_0][pos2_2] = 1.0;
148
149     /* right -> center */
150     if (pos1_1 != -1 && pos1_2 == -1 && pos2_1 == -1 && pos2_2 != -1)
151       this->matrix[pos1_1][pos2_2] = 1.0;
152     else if (pos1_1 != -1 && pos1_2 != -1 && pos2_1 == -1 && pos2_2 != -1)
153       this->matrix[pos1_1][pos2_2] = 0.5;
154     else if (pos1_1 != -1 && pos1_2 == -1 && pos2_1 != -1 && pos2_2 != -1)
155       this->matrix[pos1_1][pos2_2] = 1.0;
156
157     /* center -> left */
158     if (pos1_2 != -1 && pos1_0 == -1 && pos2_2 == -1 && pos2_0 != -1)
159       this->matrix[pos1_2][pos2_0] = 1.0;
160     else if (pos1_2 != -1 && pos1_0 != -1 && pos2_2 == -1 && pos2_0 != -1)
161       this->matrix[pos1_2][pos2_0] = 0.5;
162     else if (pos1_2 != -1 && pos1_0 == -1 && pos2_2 != -1 && pos2_0 != -1)
163       this->matrix[pos1_2][pos2_0] = 1.0;
164
165     /* center -> right */
166     if (pos1_2 != -1 && pos1_1 == -1 && pos2_2 == -1 && pos2_1 != -1)
167       this->matrix[pos1_2][pos2_1] = 1.0;
168     else if (pos1_2 != -1 && pos1_1 != -1 && pos2_2 == -1 && pos2_1 != -1)
169       this->matrix[pos1_2][pos2_1] = 0.5;
170     else if (pos1_2 != -1 && pos1_1 == -1 && pos2_2 != -1 && pos2_1 != -1)
171       this->matrix[pos1_2][pos2_1] = 1.0;
172   }
173 }
174
175 /*
176  * Detect and fill in channels not handled by the
177  * above two, e.g. center to left/right front in
178  * 5.1 to 2.0 (or the other way around).
179  *
180  * Unfortunately, limited to static conversions
181  * for now.
182  */
183
184 static void
185 gst_channel_mix_detect_pos (GstAudioInfo * info,
186     gint * f, gboolean * has_f,
187     gint * c, gboolean * has_c, gint * r, gboolean * has_r,
188     gint * s, gboolean * has_s, gint * b, gboolean * has_b)
189 {
190   gint n;
191
192   for (n = 0; n < info->channels; n++) {
193     switch (info->position[n]) {
194       case GST_AUDIO_CHANNEL_POSITION_FRONT_MONO:
195         f[1] = n;
196         *has_f = TRUE;
197         break;
198       case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT:
199         f[0] = n;
200         *has_f = TRUE;
201         break;
202       case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT:
203         f[2] = n;
204         *has_f = TRUE;
205         break;
206       case GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER:
207         c[1] = n;
208         *has_c = TRUE;
209         break;
210       case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
211         c[0] = n;
212         *has_c = TRUE;
213         break;
214       case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
215         c[2] = n;
216         *has_c = TRUE;
217         break;
218       case GST_AUDIO_CHANNEL_POSITION_REAR_CENTER:
219         r[1] = n;
220         *has_r = TRUE;
221         break;
222       case GST_AUDIO_CHANNEL_POSITION_REAR_LEFT:
223         r[0] = n;
224         *has_r = TRUE;
225         break;
226       case GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT:
227         r[2] = n;
228         *has_r = TRUE;
229         break;
230       case GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT:
231         s[0] = n;
232         *has_s = TRUE;
233         break;
234       case GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT:
235         s[2] = n;
236         *has_s = TRUE;
237         break;
238       case GST_AUDIO_CHANNEL_POSITION_LFE:
239         *has_b = TRUE;
240         b[1] = n;
241         break;
242       default:
243         break;
244     }
245   }
246 }
247
248 static void
249 gst_channel_mix_fill_one_other (gfloat ** matrix,
250     GstAudioInfo * from_info, gint * from_idx,
251     GstAudioInfo * to_info, gint * to_idx, gfloat ratio)
252 {
253
254   /* src & dst have center => passthrough */
255   if (from_idx[1] != -1 && to_idx[1] != -1) {
256     matrix[from_idx[1]][to_idx[1]] = ratio;
257   }
258
259   /* src & dst have left => passthrough */
260   if (from_idx[0] != -1 && to_idx[0] != -1) {
261     matrix[from_idx[0]][to_idx[0]] = ratio;
262   }
263
264   /* src & dst have right => passthrough */
265   if (from_idx[2] != -1 && to_idx[2] != -1) {
266     matrix[from_idx[2]][to_idx[2]] = ratio;
267   }
268
269   /* src has left & dst has center => put into center */
270   if (from_idx[0] != -1 && to_idx[1] != -1 && from_idx[1] != -1) {
271     matrix[from_idx[0]][to_idx[1]] = 0.5 * ratio;
272   } else if (from_idx[0] != -1 && to_idx[1] != -1 && from_idx[1] == -1) {
273     matrix[from_idx[0]][to_idx[1]] = ratio;
274   }
275
276   /* src has right & dst has center => put into center */
277   if (from_idx[2] != -1 && to_idx[1] != -1 && from_idx[1] != -1) {
278     matrix[from_idx[2]][to_idx[1]] = 0.5 * ratio;
279   } else if (from_idx[2] != -1 && to_idx[1] != -1 && from_idx[1] == -1) {
280     matrix[from_idx[2]][to_idx[1]] = ratio;
281   }
282
283   /* src has center & dst has left => passthrough */
284   if (from_idx[1] != -1 && to_idx[0] != -1 && from_idx[0] != -1) {
285     matrix[from_idx[1]][to_idx[0]] = 0.5 * ratio;
286   } else if (from_idx[1] != -1 && to_idx[0] != -1 && from_idx[0] == -1) {
287     matrix[from_idx[1]][to_idx[0]] = ratio;
288   }
289
290   /* src has center & dst has right => passthrough */
291   if (from_idx[1] != -1 && to_idx[2] != -1 && from_idx[2] != -1) {
292     matrix[from_idx[1]][to_idx[2]] = 0.5 * ratio;
293   } else if (from_idx[1] != -1 && to_idx[2] != -1 && from_idx[2] == -1) {
294     matrix[from_idx[1]][to_idx[2]] = ratio;
295   }
296 }
297
298 #define RATIO_CENTER_FRONT (1.0 / sqrt (2.0))
299 #define RATIO_CENTER_SIDE (1.0 / 2.0)
300 #define RATIO_CENTER_REAR (1.0 / sqrt (8.0))
301
302 #define RATIO_FRONT_CENTER (1.0 / sqrt (2.0))
303 #define RATIO_FRONT_SIDE (1.0 / sqrt (2.0))
304 #define RATIO_FRONT_REAR (1.0 / 2.0)
305
306 #define RATIO_SIDE_CENTER (1.0 / 2.0)
307 #define RATIO_SIDE_FRONT (1.0 / sqrt (2.0))
308 #define RATIO_SIDE_REAR (1.0 / sqrt (2.0))
309
310 #define RATIO_CENTER_BASS (1.0 / sqrt (2.0))
311 #define RATIO_FRONT_BASS (1.0)
312 #define RATIO_SIDE_BASS (1.0 / sqrt (2.0))
313 #define RATIO_REAR_BASS (1.0 / sqrt (2.0))
314
315 static void
316 gst_channel_mix_fill_others (AudioConvertCtx * this)
317 {
318   gboolean in_has_front = FALSE, out_has_front = FALSE,
319       in_has_center = FALSE, out_has_center = FALSE,
320       in_has_rear = FALSE, out_has_rear = FALSE,
321       in_has_side = FALSE, out_has_side = FALSE,
322       in_has_bass = FALSE, out_has_bass = FALSE;
323   /* LEFT, RIGHT, MONO */
324   gint in_f[3] = { -1, -1, -1 };
325   gint out_f[3] = { -1, -1, -1 };
326   /* LOC, ROC, CENTER */
327   gint in_c[3] = { -1, -1, -1 };
328   gint out_c[3] = { -1, -1, -1 };
329   /* RLEFT, RRIGHT, RCENTER */
330   gint in_r[3] = { -1, -1, -1 };
331   gint out_r[3] = { -1, -1, -1 };
332   /* SLEFT, INVALID, SRIGHT */
333   gint in_s[3] = { -1, -1, -1 };
334   gint out_s[3] = { -1, -1, -1 };
335   /* INVALID, LFE, INVALID */
336   gint in_b[3] = { -1, -1, -1 };
337   gint out_b[3] = { -1, -1, -1 };
338
339   /* First see where (if at all) the various channels from/to
340    * which we want to convert are located in our matrix/array. */
341   gst_channel_mix_detect_pos (&this->in,
342       in_f, &in_has_front,
343       in_c, &in_has_center, in_r, &in_has_rear,
344       in_s, &in_has_side, in_b, &in_has_bass);
345   gst_channel_mix_detect_pos (&this->out,
346       out_f, &out_has_front,
347       out_c, &out_has_center, out_r, &out_has_rear,
348       out_s, &out_has_side, out_b, &out_has_bass);
349
350   /* The general idea here is:
351    * - if the source has a channel that the destination doesn't have mix
352    *   it into the nearest available destination channel
353    * - if the destination has a channel that the source doesn't have mix
354    *   the nearest source channel into the destination channel
355    *
356    * The ratio for the mixing becomes lower as the distance between the
357    * channels gets larger
358    */
359
360   /* center <-> front/side/rear */
361   if (!in_has_center && in_has_front && out_has_center) {
362     gst_channel_mix_fill_one_other (this->matrix,
363         &this->in, in_f, &this->out, out_c, RATIO_CENTER_FRONT);
364   } else if (!in_has_center && !in_has_front && in_has_side && out_has_center) {
365     gst_channel_mix_fill_one_other (this->matrix,
366         &this->in, in_s, &this->out, out_c, RATIO_CENTER_SIDE);
367   } else if (!in_has_center && !in_has_front && !in_has_side && in_has_rear
368       && out_has_center) {
369     gst_channel_mix_fill_one_other (this->matrix, &this->in, in_r, &this->out,
370         out_c, RATIO_CENTER_REAR);
371   } else if (in_has_center && !out_has_center && out_has_front) {
372     gst_channel_mix_fill_one_other (this->matrix,
373         &this->in, in_c, &this->out, out_f, RATIO_CENTER_FRONT);
374   } else if (in_has_center && !out_has_center && !out_has_front && out_has_side) {
375     gst_channel_mix_fill_one_other (this->matrix,
376         &this->in, in_c, &this->out, out_s, RATIO_CENTER_SIDE);
377   } else if (in_has_center && !out_has_center && !out_has_front && !out_has_side
378       && out_has_rear) {
379     gst_channel_mix_fill_one_other (this->matrix, &this->in, in_c, &this->out,
380         out_r, RATIO_CENTER_REAR);
381   }
382
383   /* front <-> center/side/rear */
384   if (!in_has_front && in_has_center && !in_has_side && out_has_front) {
385     gst_channel_mix_fill_one_other (this->matrix,
386         &this->in, in_c, &this->out, out_f, RATIO_CENTER_FRONT);
387   } else if (!in_has_front && !in_has_center && in_has_side && out_has_front) {
388     gst_channel_mix_fill_one_other (this->matrix,
389         &this->in, in_s, &this->out, out_f, RATIO_FRONT_SIDE);
390   } else if (!in_has_front && in_has_center && in_has_side && out_has_front) {
391     gst_channel_mix_fill_one_other (this->matrix,
392         &this->in, in_c, &this->out, out_f, 0.5 * RATIO_CENTER_FRONT);
393     gst_channel_mix_fill_one_other (this->matrix,
394         &this->in, in_s, &this->out, out_f, 0.5 * RATIO_FRONT_SIDE);
395   } else if (!in_has_front && !in_has_center && !in_has_side && in_has_rear
396       && out_has_front) {
397     gst_channel_mix_fill_one_other (this->matrix, &this->in, in_r, &this->out,
398         out_f, RATIO_FRONT_REAR);
399   } else if (in_has_front && out_has_center && !out_has_side && !out_has_front) {
400     gst_channel_mix_fill_one_other (this->matrix,
401         &this->in, in_f, &this->out, out_c, RATIO_CENTER_FRONT);
402   } else if (in_has_front && !out_has_center && out_has_side && !out_has_front) {
403     gst_channel_mix_fill_one_other (this->matrix,
404         &this->in, in_f, &this->out, out_s, RATIO_FRONT_SIDE);
405   } else if (in_has_front && out_has_center && out_has_side && !out_has_front) {
406     gst_channel_mix_fill_one_other (this->matrix,
407         &this->in, in_f, &this->out, out_c, 0.5 * RATIO_CENTER_FRONT);
408     gst_channel_mix_fill_one_other (this->matrix,
409         &this->in, in_f, &this->out, out_s, 0.5 * RATIO_FRONT_SIDE);
410   } else if (in_has_front && !out_has_center && !out_has_side && !out_has_front
411       && out_has_rear) {
412     gst_channel_mix_fill_one_other (this->matrix, &this->in, in_f, &this->out,
413         out_r, RATIO_FRONT_REAR);
414   }
415
416   /* side <-> center/front/rear */
417   if (!in_has_side && in_has_front && !in_has_rear && out_has_side) {
418     gst_channel_mix_fill_one_other (this->matrix,
419         &this->in, in_f, &this->out, out_s, RATIO_FRONT_SIDE);
420   } else if (!in_has_side && !in_has_front && in_has_rear && out_has_side) {
421     gst_channel_mix_fill_one_other (this->matrix,
422         &this->in, in_r, &this->out, out_s, RATIO_SIDE_REAR);
423   } else if (!in_has_side && in_has_front && in_has_rear && out_has_side) {
424     gst_channel_mix_fill_one_other (this->matrix,
425         &this->in, in_f, &this->out, out_s, 0.5 * RATIO_FRONT_SIDE);
426     gst_channel_mix_fill_one_other (this->matrix,
427         &this->in, in_r, &this->out, out_s, 0.5 * RATIO_SIDE_REAR);
428   } else if (!in_has_side && !in_has_front && !in_has_rear && in_has_center
429       && out_has_side) {
430     gst_channel_mix_fill_one_other (this->matrix, &this->in, in_c, &this->out,
431         out_s, RATIO_CENTER_SIDE);
432   } else if (in_has_side && out_has_front && !out_has_rear && !out_has_side) {
433     gst_channel_mix_fill_one_other (this->matrix,
434         &this->in, in_s, &this->out, out_f, RATIO_FRONT_SIDE);
435   } else if (in_has_side && !out_has_front && out_has_rear && !out_has_side) {
436     gst_channel_mix_fill_one_other (this->matrix,
437         &this->in, in_s, &this->out, out_r, RATIO_SIDE_REAR);
438   } else if (in_has_side && out_has_front && out_has_rear && !out_has_side) {
439     gst_channel_mix_fill_one_other (this->matrix,
440         &this->in, in_s, &this->out, out_f, 0.5 * RATIO_FRONT_SIDE);
441     gst_channel_mix_fill_one_other (this->matrix,
442         &this->in, in_s, &this->out, out_r, 0.5 * RATIO_SIDE_REAR);
443   } else if (in_has_side && !out_has_front && !out_has_rear && out_has_center
444       && !out_has_side) {
445     gst_channel_mix_fill_one_other (this->matrix, &this->in, in_s, &this->out,
446         out_c, RATIO_CENTER_SIDE);
447   }
448
449   /* rear <-> center/front/side */
450   if (!in_has_rear && in_has_side && out_has_rear) {
451     gst_channel_mix_fill_one_other (this->matrix,
452         &this->in, in_s, &this->out, out_r, RATIO_SIDE_REAR);
453   } else if (!in_has_rear && !in_has_side && in_has_front && out_has_rear) {
454     gst_channel_mix_fill_one_other (this->matrix,
455         &this->in, in_f, &this->out, out_r, RATIO_FRONT_REAR);
456   } else if (!in_has_rear && !in_has_side && !in_has_front && in_has_center
457       && out_has_rear) {
458     gst_channel_mix_fill_one_other (this->matrix, &this->in, in_c, &this->out,
459         out_r, RATIO_CENTER_REAR);
460   } else if (in_has_rear && !out_has_rear && out_has_side) {
461     gst_channel_mix_fill_one_other (this->matrix,
462         &this->in, in_r, &this->out, out_s, RATIO_SIDE_REAR);
463   } else if (in_has_rear && !out_has_rear && !out_has_side && out_has_front) {
464     gst_channel_mix_fill_one_other (this->matrix,
465         &this->in, in_r, &this->out, out_f, RATIO_FRONT_REAR);
466   } else if (in_has_rear && !out_has_rear && !out_has_side && !out_has_front
467       && out_has_center) {
468     gst_channel_mix_fill_one_other (this->matrix, &this->in, in_r, &this->out,
469         out_c, RATIO_CENTER_REAR);
470   }
471
472   /* bass <-> any */
473   if (in_has_bass && !out_has_bass) {
474     if (out_has_center) {
475       gst_channel_mix_fill_one_other (this->matrix,
476           &this->in, in_b, &this->out, out_c, RATIO_CENTER_BASS);
477     }
478     if (out_has_front) {
479       gst_channel_mix_fill_one_other (this->matrix,
480           &this->in, in_b, &this->out, out_f, RATIO_FRONT_BASS);
481     }
482     if (out_has_side) {
483       gst_channel_mix_fill_one_other (this->matrix,
484           &this->in, in_b, &this->out, out_s, RATIO_SIDE_BASS);
485     }
486     if (out_has_rear) {
487       gst_channel_mix_fill_one_other (this->matrix,
488           &this->in, in_b, &this->out, out_r, RATIO_REAR_BASS);
489     }
490   } else if (!in_has_bass && out_has_bass) {
491     if (in_has_center) {
492       gst_channel_mix_fill_one_other (this->matrix,
493           &this->in, in_c, &this->out, out_b, RATIO_CENTER_BASS);
494     }
495     if (in_has_front) {
496       gst_channel_mix_fill_one_other (this->matrix,
497           &this->in, in_f, &this->out, out_b, RATIO_FRONT_BASS);
498     }
499     if (in_has_side) {
500       gst_channel_mix_fill_one_other (this->matrix,
501           &this->in, in_s, &this->out, out_b, RATIO_REAR_BASS);
502     }
503     if (in_has_rear) {
504       gst_channel_mix_fill_one_other (this->matrix,
505           &this->in, in_r, &this->out, out_b, RATIO_REAR_BASS);
506     }
507   }
508 }
509
510 /*
511  * Normalize output values.
512  */
513
514 static void
515 gst_channel_mix_fill_normalize (AudioConvertCtx * this)
516 {
517   gfloat sum, top = 0;
518   gint i, j;
519
520   for (j = 0; j < this->out.channels; j++) {
521     /* calculate sum */
522     sum = 0.0;
523     for (i = 0; i < this->in.channels; i++) {
524       sum += fabs (this->matrix[i][j]);
525     }
526     if (sum > top) {
527       top = sum;
528     }
529   }
530
531   /* normalize to this */
532   if (top == 0.0)
533     return;
534
535   for (j = 0; j < this->out.channels; j++) {
536     for (i = 0; i < this->in.channels; i++) {
537       this->matrix[i][j] /= top;
538     }
539   }
540 }
541
542 static gboolean
543 gst_channel_mix_fill_special (AudioConvertCtx * this)
544 {
545   GstAudioInfo *in = &this->in, *out = &this->out;
546
547   /* Special, standard conversions here */
548
549   /* Mono<->Stereo, just a fast-path */
550   if (in->channels == 2 && out->channels == 1 &&
551       ((in->position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT &&
552               in->position[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT) ||
553           (in->position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT &&
554               in->position[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT)) &&
555       out->position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_MONO) {
556     this->matrix[0][0] = 0.5;
557     this->matrix[1][0] = 0.5;
558     return TRUE;
559   } else if (in->channels == 1 && out->channels == 2 &&
560       ((out->position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT &&
561               out->position[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT) ||
562           (out->position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT &&
563               out->position[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT)) &&
564       in->position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_MONO) {
565     this->matrix[0][0] = 1.0;
566     this->matrix[0][1] = 1.0;
567     return TRUE;
568   }
569
570   /* TODO: 5.1 <-> Stereo and other standard conversions */
571
572   return FALSE;
573 }
574
575 /*
576  * Automagically generate conversion matrix.
577  */
578
579 static void
580 gst_channel_mix_fill_matrix (AudioConvertCtx * this)
581 {
582   if (gst_channel_mix_fill_special (this))
583     return;
584
585   gst_channel_mix_fill_identical (this);
586
587   if (!GST_AUDIO_INFO_IS_UNPOSITIONED (&this->in)) {
588     gst_channel_mix_fill_compatible (this);
589     gst_channel_mix_fill_others (this);
590     gst_channel_mix_fill_normalize (this);
591   }
592 }
593
594 /* only call after this->out and this->in are filled in */
595 void
596 gst_channel_mix_setup_matrix (AudioConvertCtx * this)
597 {
598   gint i, j;
599
600   /* don't lose memory */
601   gst_channel_mix_unset_matrix (this);
602
603   /* temp storage */
604   if (GST_AUDIO_FORMAT_INFO_IS_INT (this->in.finfo) ||
605       GST_AUDIO_FORMAT_INFO_IS_INT (this->out.finfo)) {
606     this->tmp = (gpointer) g_new (gint32, this->out.channels);
607   } else {
608     this->tmp = (gpointer) g_new (gdouble, this->out.channels);
609   }
610
611   /* allocate */
612   this->matrix = g_new0 (gfloat *, this->in.channels);
613   for (i = 0; i < this->in.channels; i++) {
614     this->matrix[i] = g_new (gfloat, this->out.channels);
615     for (j = 0; j < this->out.channels; j++)
616       this->matrix[i][j] = 0.;
617   }
618
619   /* setup the matrix' internal values */
620   gst_channel_mix_fill_matrix (this);
621
622 #ifndef GST_DISABLE_GST_DEBUG
623   /* debug */
624   {
625     GString *s;
626     s = g_string_new ("Matrix for");
627     g_string_append_printf (s, " %d -> %d: ",
628         this->in.channels, this->out.channels);
629     g_string_append (s, "{");
630     for (i = 0; i < this->in.channels; i++) {
631       if (i != 0)
632         g_string_append (s, ",");
633       g_string_append (s, " {");
634       for (j = 0; j < this->out.channels; j++) {
635         if (j != 0)
636           g_string_append (s, ",");
637         g_string_append_printf (s, " %f", this->matrix[i][j]);
638       }
639       g_string_append (s, " }");
640     }
641     g_string_append (s, " }");
642     GST_DEBUG ("%s", s->str);
643     g_string_free (s, TRUE);
644   }
645 #endif
646 }
647
648 gboolean
649 gst_channel_mix_passthrough (AudioConvertCtx * this)
650 {
651   gint i;
652
653   /* only NxN matrices can be identities */
654   if (this->in.channels != this->out.channels)
655     return FALSE;
656
657   /* this assumes a normalized matrix */
658   for (i = 0; i < this->in.channels; i++)
659     if (this->matrix[i][i] != 1.)
660       return FALSE;
661
662   return TRUE;
663 }
664
665 /* IMPORTANT: out_data == in_data is possible, make sure to not overwrite data
666  * you might need later on! */
667 void
668 gst_channel_mix_mix_int (AudioConvertCtx * this,
669     gint32 * in_data, gint32 * out_data, gint samples)
670 {
671   gint in, out, n;
672   gint64 res;
673   gboolean backwards;
674   gint inchannels, outchannels;
675   gint32 *tmp = (gint32 *) this->tmp;
676
677   g_return_if_fail (this->matrix != NULL);
678   g_return_if_fail (this->tmp != NULL);
679
680   inchannels = this->in.channels;
681   outchannels = this->out.channels;
682   backwards = outchannels > inchannels;
683
684   /* FIXME: use liboil here? */
685   for (n = (backwards ? samples - 1 : 0); n < samples && n >= 0;
686       backwards ? n-- : n++) {
687     for (out = 0; out < outchannels; out++) {
688       /* convert */
689       res = 0;
690       for (in = 0; in < inchannels; in++) {
691         res += in_data[n * inchannels + in] * this->matrix[in][out];
692       }
693
694       /* clip (shouldn't we use doubles instead as intermediate format?) */
695       if (res < G_MININT32)
696         res = G_MININT32;
697       else if (res > G_MAXINT32)
698         res = G_MAXINT32;
699       tmp[out] = res;
700     }
701     memcpy (&out_data[n * outchannels], this->tmp,
702         sizeof (gint32) * outchannels);
703   }
704 }
705
706 void
707 gst_channel_mix_mix_float (AudioConvertCtx * this,
708     gdouble * in_data, gdouble * out_data, gint samples)
709 {
710   gint in, out, n;
711   gdouble res;
712   gboolean backwards;
713   gint inchannels, outchannels;
714   gdouble *tmp = (gdouble *) this->tmp;
715
716   g_return_if_fail (this->matrix != NULL);
717   g_return_if_fail (this->tmp != NULL);
718
719   inchannels = this->in.channels;
720   outchannels = this->out.channels;
721   backwards = outchannels > inchannels;
722
723   /* FIXME: use liboil here? */
724   for (n = (backwards ? samples - 1 : 0); n < samples && n >= 0;
725       backwards ? n-- : n++) {
726     for (out = 0; out < outchannels; out++) {
727       /* convert */
728       res = 0.0;
729       for (in = 0; in < inchannels; in++) {
730         res += in_data[n * inchannels + in] * this->matrix[in][out];
731       }
732
733       /* clip (shouldn't we use doubles instead as intermediate format?) */
734       if (res < -1.0)
735         res = -1.0;
736       else if (res > 1.0)
737         res = 1.0;
738       tmp[out] = res;
739     }
740     memcpy (&out_data[n * outchannels], this->tmp,
741         sizeof (gdouble) * outchannels);
742   }
743 }