audioconvert: Port to the new multichannel caps
[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
30 #include "gstchannelmix.h"
31
32 /*
33  * Channel matrix functions.
34  */
35
36 void
37 gst_channel_mix_unset_matrix (AudioConvertCtx * this)
38 {
39   gint i;
40
41   /* don't access if nothing there */
42   if (!this->matrix)
43     return;
44
45   /* free */
46   for (i = 0; i < this->in.channels; i++)
47     g_free (this->matrix[i]);
48   g_free (this->matrix);
49
50   this->matrix = NULL;
51   g_free (this->tmp);
52   this->tmp = NULL;
53 }
54
55 /*
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.
59  */
60
61 static void
62 gst_channel_mix_fill_identical (AudioConvertCtx * this)
63 {
64   gint ci, co;
65
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;
74       }
75     }
76   }
77 }
78
79 /*
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.
83  */
84
85 static void
86 gst_channel_mix_fill_compatible (AudioConvertCtx * this)
87 {
88   /* Conversions from one-channel to compatible two-channel configs */
89   struct
90   {
91     GstAudioChannelPosition pos1[2];
92     GstAudioChannelPosition pos2[1];
93   } conv[] = {
94     /* front: mono <-> stereo */
95     { {
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 */
100     { {
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}},
104         /* rear: 2 <-> 1 */
105     { {
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}}
110   };
111   gint c;
112
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;
117     gint n;
118
119     for (n = 0; n < this->in.channels; n++) {
120       if (this->in.position[n] == conv[c].pos1[0])
121         pos1_0 = n;
122       else if (this->in.position[n] == conv[c].pos1[1])
123         pos1_1 = n;
124       else if (this->in.position[n] == conv[c].pos2[0])
125         pos1_2 = n;
126     }
127     for (n = 0; n < this->out.channels; n++) {
128       if (this->out.position[n] == conv[c].pos1[0])
129         pos2_0 = n;
130       else if (this->out.position[n] == conv[c].pos1[1])
131         pos2_1 = n;
132       else if (this->out.position[n] == conv[c].pos2[0])
133         pos2_2 = n;
134     }
135
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.
138      */
139
140     /* left -> 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;
147
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;
155
156     /* center -> left */
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;
163
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;
171   }
172 }
173
174 /*
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).
178  *
179  * Unfortunately, limited to static conversions
180  * for now.
181  */
182
183 static void
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)
188 {
189   gint n;
190
191   for (n = 0; n < info->channels; n++) {
192     switch (info->position[n]) {
193       case GST_AUDIO_CHANNEL_POSITION_MONO:
194         f[1] = n;
195         *has_f = TRUE;
196         break;
197       case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT:
198         f[0] = n;
199         *has_f = TRUE;
200         break;
201       case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT:
202         f[2] = n;
203         *has_f = TRUE;
204         break;
205       case GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER:
206         c[1] = n;
207         *has_c = TRUE;
208         break;
209       case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
210         c[0] = n;
211         *has_c = TRUE;
212         break;
213       case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
214         c[2] = n;
215         *has_c = TRUE;
216         break;
217       case GST_AUDIO_CHANNEL_POSITION_REAR_CENTER:
218         r[1] = n;
219         *has_r = TRUE;
220         break;
221       case GST_AUDIO_CHANNEL_POSITION_REAR_LEFT:
222         r[0] = n;
223         *has_r = TRUE;
224         break;
225       case GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT:
226         r[2] = n;
227         *has_r = TRUE;
228         break;
229       case GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT:
230         s[0] = n;
231         *has_s = TRUE;
232         break;
233       case GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT:
234         s[2] = n;
235         *has_s = TRUE;
236         break;
237       case GST_AUDIO_CHANNEL_POSITION_LFE1:
238         *has_b = TRUE;
239         b[1] = n;
240         break;
241       default:
242         break;
243     }
244   }
245 }
246
247 static void
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)
251 {
252
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;
256   }
257
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;
261   }
262
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;
266   }
267
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;
273   }
274
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;
280   }
281
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;
287   }
288
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;
294   }
295 }
296
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))
300
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)
304
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))
308
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))
313
314 static void
315 gst_channel_mix_fill_others (AudioConvertCtx * this)
316 {
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 };
337
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,
341       in_f, &in_has_front,
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);
348
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
354    *
355    * The ratio for the mixing becomes lower as the distance between the
356    * channels gets larger
357    */
358
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
367       && out_has_center) {
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
377       && out_has_rear) {
378     gst_channel_mix_fill_one_other (this->matrix, &this->in, in_c, &this->out,
379         out_r, RATIO_CENTER_REAR);
380   }
381
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
395       && out_has_front) {
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
410       && out_has_rear) {
411     gst_channel_mix_fill_one_other (this->matrix, &this->in, in_f, &this->out,
412         out_r, RATIO_FRONT_REAR);
413   }
414
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
428       && out_has_side) {
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
443       && !out_has_side) {
444     gst_channel_mix_fill_one_other (this->matrix, &this->in, in_s, &this->out,
445         out_c, RATIO_CENTER_SIDE);
446   }
447
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
456       && out_has_rear) {
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
466       && out_has_center) {
467     gst_channel_mix_fill_one_other (this->matrix, &this->in, in_r, &this->out,
468         out_c, RATIO_CENTER_REAR);
469   }
470
471   /* bass <-> any */
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);
476     }
477     if (out_has_front) {
478       gst_channel_mix_fill_one_other (this->matrix,
479           &this->in, in_b, &this->out, out_f, RATIO_FRONT_BASS);
480     }
481     if (out_has_side) {
482       gst_channel_mix_fill_one_other (this->matrix,
483           &this->in, in_b, &this->out, out_s, RATIO_SIDE_BASS);
484     }
485     if (out_has_rear) {
486       gst_channel_mix_fill_one_other (this->matrix,
487           &this->in, in_b, &this->out, out_r, RATIO_REAR_BASS);
488     }
489   } else if (!in_has_bass && out_has_bass) {
490     if (in_has_center) {
491       gst_channel_mix_fill_one_other (this->matrix,
492           &this->in, in_c, &this->out, out_b, RATIO_CENTER_BASS);
493     }
494     if (in_has_front) {
495       gst_channel_mix_fill_one_other (this->matrix,
496           &this->in, in_f, &this->out, out_b, RATIO_FRONT_BASS);
497     }
498     if (in_has_side) {
499       gst_channel_mix_fill_one_other (this->matrix,
500           &this->in, in_s, &this->out, out_b, RATIO_REAR_BASS);
501     }
502     if (in_has_rear) {
503       gst_channel_mix_fill_one_other (this->matrix,
504           &this->in, in_r, &this->out, out_b, RATIO_REAR_BASS);
505     }
506   }
507 }
508
509 /*
510  * Normalize output values.
511  */
512
513 static void
514 gst_channel_mix_fill_normalize (AudioConvertCtx * this)
515 {
516   gfloat sum, top = 0;
517   gint i, j;
518
519   for (j = 0; j < this->out.channels; j++) {
520     /* calculate sum */
521     sum = 0.0;
522     for (i = 0; i < this->in.channels; i++) {
523       sum += fabs (this->matrix[i][j]);
524     }
525     if (sum > top) {
526       top = sum;
527     }
528   }
529
530   /* normalize to this */
531   if (top == 0.0)
532     return;
533
534   for (j = 0; j < this->out.channels; j++) {
535     for (i = 0; i < this->in.channels; i++) {
536       this->matrix[i][j] /= top;
537     }
538   }
539 }
540
541 static gboolean
542 gst_channel_mix_fill_special (AudioConvertCtx * this)
543 {
544   GstAudioInfo *in = &this->in, *out = &this->out;
545
546   /* Special, standard conversions here */
547
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;
557     return TRUE;
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;
566     return TRUE;
567   }
568
569   /* TODO: 5.1 <-> Stereo and other standard conversions */
570
571   return FALSE;
572 }
573
574 /*
575  * Automagically generate conversion matrix.
576  */
577
578 static void
579 gst_channel_mix_fill_matrix (AudioConvertCtx * this)
580 {
581   if (gst_channel_mix_fill_special (this))
582     return;
583
584   gst_channel_mix_fill_identical (this);
585
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);
590   }
591 }
592
593 /* only call after this->out and this->in are filled in */
594 void
595 gst_channel_mix_setup_matrix (AudioConvertCtx * this)
596 {
597   gint i, j;
598
599   /* don't lose memory */
600   gst_channel_mix_unset_matrix (this);
601
602   /* temp storage */
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);
606   } else {
607     this->tmp = (gpointer) g_new (gdouble, this->out.channels);
608   }
609
610   /* allocate */
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.;
616   }
617
618   /* setup the matrix' internal values */
619   gst_channel_mix_fill_matrix (this);
620
621 #ifndef GST_DISABLE_GST_DEBUG
622   /* debug */
623   {
624     GString *s;
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++) {
630       if (i != 0)
631         g_string_append (s, ",");
632       g_string_append (s, " {");
633       for (j = 0; j < this->out.channels; j++) {
634         if (j != 0)
635           g_string_append (s, ",");
636         g_string_append_printf (s, " %f", this->matrix[i][j]);
637       }
638       g_string_append (s, " }");
639     }
640     g_string_append (s, " }");
641     GST_DEBUG ("%s", s->str);
642     g_string_free (s, TRUE);
643   }
644 #endif
645 }
646
647 gboolean
648 gst_channel_mix_passthrough (AudioConvertCtx * this)
649 {
650   gint i;
651   guint64 in_mask, out_mask;
652
653   /* only NxN matrices can be identities */
654   if (this->in.channels != this->out.channels)
655     return FALSE;
656
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];
662   }
663
664   return in_mask == out_mask;
665 }
666
667 /* IMPORTANT: out_data == in_data is possible, make sure to not overwrite data
668  * you might need later on! */
669 void
670 gst_channel_mix_mix_int (AudioConvertCtx * this,
671     gint32 * in_data, gint32 * out_data, gint samples)
672 {
673   gint in, out, n;
674   gint64 res;
675   gboolean backwards;
676   gint inchannels, outchannels;
677   gint32 *tmp = (gint32 *) this->tmp;
678
679   g_return_if_fail (this->matrix != NULL);
680   g_return_if_fail (this->tmp != NULL);
681
682   inchannels = this->in.channels;
683   outchannels = this->out.channels;
684   backwards = outchannels > inchannels;
685
686   /* FIXME: use liboil here? */
687   for (n = (backwards ? samples - 1 : 0); n < samples && n >= 0;
688       backwards ? n-- : n++) {
689     for (out = 0; out < outchannels; out++) {
690       /* convert */
691       res = 0;
692       for (in = 0; in < inchannels; in++) {
693         res += in_data[n * inchannels + in] * this->matrix[in][out];
694       }
695
696       /* clip (shouldn't we use doubles instead as intermediate format?) */
697       if (res < G_MININT32)
698         res = G_MININT32;
699       else if (res > G_MAXINT32)
700         res = G_MAXINT32;
701       tmp[out] = res;
702     }
703     memcpy (&out_data[n * outchannels], this->tmp,
704         sizeof (gint32) * outchannels);
705   }
706 }
707
708 void
709 gst_channel_mix_mix_float (AudioConvertCtx * this,
710     gdouble * in_data, gdouble * out_data, gint samples)
711 {
712   gint in, out, n;
713   gdouble res;
714   gboolean backwards;
715   gint inchannels, outchannels;
716   gdouble *tmp = (gdouble *) this->tmp;
717
718   g_return_if_fail (this->matrix != NULL);
719   g_return_if_fail (this->tmp != NULL);
720
721   inchannels = this->in.channels;
722   outchannels = this->out.channels;
723   backwards = outchannels > inchannels;
724
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++) {
729       /* convert */
730       res = 0.0;
731       for (in = 0; in < inchannels; in++) {
732         res += in_data[n * inchannels + in] * this->matrix[in][out];
733       }
734
735       /* clip (shouldn't we use doubles instead as intermediate format?) */
736       if (res < -1.0)
737         res = -1.0;
738       else if (res > 1.0)
739         res = 1.0;
740       tmp[out] = res;
741     }
742     memcpy (&out_data[n * outchannels], this->tmp,
743         sizeof (gdouble) * outchannels);
744   }
745 }