gst/audioconvert/gstchannelmix.c: Normalize using absolute values.
[platform/upstream/gstreamer.git] / gst / audioconvert / gstchannelmix.c
1 /* GStreamer
2  * Copyright (C) 2004 Ronald Bultje <rbultje@ronald.bitfreak.net>
3  *
4  * gstchannelmix.c: setup of channel conversion matrices
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include <math.h>
27 #include <gst/audio/multichannel.h>
28
29 #include "gstchannelmix.h"
30
31 /*
32  * Channel matrix functions.
33  */
34
35 void
36 gst_audio_convert_unset_matrix (GstAudioConvert * this)
37 {
38   gint i;
39
40   /* don't access if nothing there */
41   if (!this->matrix)
42     return;
43
44   /* free */
45   for (i = 0; i < this->sinkcaps.channels; i++)
46     g_free (this->matrix[i]);
47   g_free (this->matrix);
48
49   this->matrix = NULL;
50 }
51
52 /*
53  * Detect and fill in identical channels. E.g.
54  * forward the left/right front channels in a
55  * 5.1 to 2.0 conversion.
56  */
57
58 static void
59 gst_audio_convert_fill_identical (GstAudioConvert * this)
60 {
61   gint ci, co;
62
63   /* Apart from the compatible channel assignments, we can also have
64    * same channel assignments. This is much simpler, we simply copy
65    * the value from source to dest! */
66   for (co = 0; co < this->srccaps.channels; co++) {
67     /* find a channel in input with same position */
68     for (ci = 0; ci < this->sinkcaps.channels; ci++) {
69       if (this->sinkcaps.pos[ci] == this->srccaps.pos[co]) {
70         this->matrix[ci][co] = 1.0;
71       }
72     }
73   }
74 }
75
76 /*
77  * Detect and fill in compatible channels. E.g.
78  * forward left/right front to mono (or the other
79  * way around) when going from 2.0 to 1.0.
80  */
81
82 static void
83 gst_audio_convert_fill_compatible (GstAudioConvert * this)
84 {
85   /* Conversions from one-channel to compatible two-channel configs */
86   struct
87   {
88     GstAudioChannelPosition pos1[2];
89     GstAudioChannelPosition pos2[1];
90   } conv[] = {
91     /* front: mono <-> stereo */
92     { {
93     GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
94             GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, {
95     GST_AUDIO_CHANNEL_POSITION_FRONT_MONO}},
96         /* front center: 2 <-> 1 */
97     { {
98     GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
99             GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER}, {
100     GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER}},
101         /* rear: 2 <-> 1 */
102     { {
103     GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
104             GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}, {
105     GST_AUDIO_CHANNEL_POSITION_REAR_CENTER}}, { {
106     GST_AUDIO_CHANNEL_POSITION_INVALID}}
107   };
108   gint c;
109
110   /* conversions from compatible (but not the same) channel schemes. This
111    * goes two ways: if the sink has both pos1[0,1] and src has pos2[0] or
112    * if the src has both pos1[0,1] and sink has pos2[0], then we do the
113    * conversion. We hereby assume that the existance of pos1[0,1] and
114    * pos2[0] are mututally exclusive. There are no checks for that,
115    * unfortunately. This shouldn't lead to issues (like crashes or so),
116    * though. */
117   for (c = 0; conv[c].pos1[0] != GST_AUDIO_CHANNEL_POSITION_INVALID; c++) {
118     gint pos1_0 = -1, pos1_1 = -1, pos2_0 = -1, n;
119
120     /* Try to go from the given 2 channels to the given 1 channel */
121     for (n = 0; n < this->sinkcaps.channels; n++) {
122       if (this->sinkcaps.pos[n] == conv[c].pos1[0])
123         pos1_0 = n;
124       else if (this->sinkcaps.pos[n] == conv[c].pos1[1])
125         pos1_1 = n;
126     }
127     for (n = 0; n < this->srccaps.channels; n++) {
128       if (this->srccaps.pos[n] == conv[c].pos2[0])
129         pos2_0 = n;
130     }
131
132     if (pos1_0 != -1 && pos1_1 != -1 && pos2_0 != -1) {
133       this->matrix[pos1_0][pos2_0] = -1.0;
134       this->matrix[pos1_1][pos2_0] = 1.0;
135     }
136
137     /* Try to go from the given 1 channel to the given 2 channels */
138     pos1_0 = -1;
139     pos1_1 = -1;
140     pos2_0 = -1;
141
142     for (n = 0; n < this->srccaps.channels; n++) {
143       if (this->srccaps.pos[n] == conv[c].pos1[0])
144         pos1_0 = n;
145       else if (this->srccaps.pos[n] == conv[c].pos1[1])
146         pos1_1 = n;
147     }
148     for (n = 0; n < this->sinkcaps.channels; n++) {
149       if (this->sinkcaps.pos[n] == conv[c].pos2[0])
150         pos2_0 = n;
151     }
152
153     if (pos1_0 != -1 && pos1_1 != -1 && pos2_0 != -1) {
154       this->matrix[pos2_0][pos1_0] = -1.0;
155       this->matrix[pos2_0][pos1_1] = 1.0;
156     }
157   }
158 }
159
160 /*
161  * Detect and fill in channels not handled by the
162  * above two, e.g. center to left/right front in
163  * 5.1 to 2.0 (or the other way around).
164  *
165  * Unfortunately, limited to static conversions
166  * for now.
167  */
168
169 static void
170 gst_audio_convert_detect_pos (GstAudioConvertCaps * caps,
171     gint * f, gboolean * has_f,
172     gint * c, gboolean * has_c, gint * r, gboolean * has_r,
173     gint * s, gboolean * has_s, gint * b, gboolean * has_b)
174 {
175   gint n;
176
177   for (n = 0; n < caps->channels; n++) {
178     switch (caps->pos[n]) {
179       case GST_AUDIO_CHANNEL_POSITION_FRONT_MONO:
180       case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT:
181       case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT:
182         *has_f = TRUE;
183         if (f[0] == -1)
184           f[0] = n;
185         else
186           f[1] = n;
187         break;
188       case GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER:
189       case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
190       case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
191         *has_c = TRUE;
192         if (c[0] == -1)
193           c[0] = n;
194         else
195           c[1] = n;
196         break;
197       case GST_AUDIO_CHANNEL_POSITION_REAR_CENTER:
198       case GST_AUDIO_CHANNEL_POSITION_REAR_LEFT:
199       case GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT:
200         *has_r = TRUE;
201         if (r[0] == -1)
202           r[0] = n;
203         else
204           r[1] = n;
205         break;
206       case GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT:
207       case GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT:
208         *has_s = TRUE;
209         if (s[0] == -1)
210           s[0] = n;
211         else
212           s[1] = n;
213         break;
214       case GST_AUDIO_CHANNEL_POSITION_LFE:
215         *has_b = TRUE;
216         b[0] = n;
217         break;
218       default:
219         break;
220     }
221   }
222 }
223
224 static void
225 gst_audio_convert_fill_one_other (gfloat ** matrix,
226     GstAudioConvertCaps * from_caps, gint * from_idx,
227     GstAudioChannelPosition from_pos_l,
228     GstAudioChannelPosition from_pos_r,
229     GstAudioChannelPosition from_pos_c,
230     GstAudioConvertCaps * to_caps, gint * to_idx,
231     GstAudioChannelPosition to_pos_l,
232     GstAudioChannelPosition to_pos_r,
233     GstAudioChannelPosition to_pos_c, gfloat ratio)
234 {
235   gfloat in_r, out_r[2];
236
237   /*
238    * The idea is that we add up from the input (which means that if we
239    * have stereo input, we divide their sum by two) and put that in
240    * the matrix for their output ratio (given in $ratio).
241    * For left channels, we need to invert the signal sign (* -1).
242    */
243
244   if (from_caps->pos[from_idx[0]] == from_pos_c)
245     in_r = 1.0;
246   else
247     in_r = 0.5;
248
249   if (to_caps->pos[to_idx[0]] == to_pos_l)
250     out_r[0] = in_r * -ratio;
251   else
252     out_r[0] = in_r * ratio;
253
254   if (to_idx[1] != -1) {
255     if (to_caps->pos[to_idx[1]] == to_pos_l)
256       out_r[1] = in_r * -ratio;
257     else
258       out_r[1] = in_r * ratio;
259   }
260
261   matrix[from_idx[0]][to_idx[0]] = out_r[0];
262   if (to_idx[1] != -1)
263     matrix[from_idx[0]][to_idx[1]] = out_r[1];
264   if (from_idx[1] != -1) {
265     matrix[from_idx[1]][to_idx[0]] = out_r[0];
266     if (to_idx[1] != -1)
267       matrix[from_idx[1]][to_idx[1]] = out_r[1];
268   }
269 }
270
271 #define RATIO_FRONT_CENTER (1.0 / sqrt (2.0))
272 #define RATIO_FRONT_REAR (1.0 / sqrt (2.0))
273 #define RATIO_FRONT_BASS (1.0)
274 #define RATIO_REAR_BASS (1.0 / sqrt (2.0))
275 #define RATIO_CENTER_BASS (1.0 / sqrt (2.0))
276
277 static void
278 gst_audio_convert_fill_others (GstAudioConvert * this)
279 {
280   gboolean in_has_front = FALSE, out_has_front = FALSE,
281       in_has_center = FALSE, out_has_center = FALSE,
282       in_has_rear = FALSE, out_has_rear = FALSE,
283       in_has_side = FALSE, out_has_side = FALSE,
284       in_has_bass = FALSE, out_has_bass = FALSE;
285   gint in_f[2] = { -1, -1 }, out_f[2] = {
286   -1, -1}, in_c[2] = {
287   -1, -1}, out_c[2] = {
288   -1, -1}, in_r[2] = {
289   -1, -1}, out_r[2] = {
290   -1, -1}, in_s[2] = {
291   -1, -1}, out_s[2] = {
292   -1, -1}, in_b[2] = {
293   -1, -1}, out_b[2] = {
294   -1, -1};
295
296   /* First see where (if at all) the various channels from/to
297    * which we want to convert are located in our matrix/array. */
298   gst_audio_convert_detect_pos (&this->sinkcaps,
299       in_f, &in_has_front,
300       in_c, &in_has_center, in_r, &in_has_rear,
301       in_s, &in_has_side, in_b, &in_has_bass);
302   gst_audio_convert_detect_pos (&this->srccaps,
303       out_f, &out_has_front,
304       out_c, &out_has_center, out_r, &out_has_rear,
305       out_s, &out_has_side, out_b, &out_has_bass);
306
307   /* center/front */
308   if (!in_has_center && in_has_front && out_has_center) {
309     gst_audio_convert_fill_one_other (this->matrix,
310         &this->sinkcaps, in_f,
311         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
312         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
313         GST_AUDIO_CHANNEL_POSITION_FRONT_MONO,
314         &this->srccaps, out_c,
315         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
316         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
317         GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, RATIO_FRONT_CENTER);
318   } else if (in_has_center && !out_has_center && out_has_front) {
319     gst_audio_convert_fill_one_other (this->matrix,
320         &this->sinkcaps, in_c,
321         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
322         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
323         GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
324         &this->srccaps, out_f,
325         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
326         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
327         GST_AUDIO_CHANNEL_POSITION_FRONT_MONO, RATIO_FRONT_CENTER);
328   }
329
330   /* rear/front */
331   if (!in_has_rear && in_has_front && out_has_rear) {
332     gst_audio_convert_fill_one_other (this->matrix,
333         &this->sinkcaps, in_f,
334         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
335         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
336         GST_AUDIO_CHANNEL_POSITION_FRONT_MONO,
337         &this->srccaps, out_r,
338         GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
339         GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
340         GST_AUDIO_CHANNEL_POSITION_REAR_CENTER, RATIO_FRONT_REAR);
341   } else if (in_has_center && !out_has_center && out_has_front) {
342     gst_audio_convert_fill_one_other (this->matrix,
343         &this->sinkcaps, in_r,
344         GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
345         GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
346         GST_AUDIO_CHANNEL_POSITION_REAR_CENTER,
347         &this->srccaps, out_f,
348         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
349         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
350         GST_AUDIO_CHANNEL_POSITION_FRONT_MONO, RATIO_FRONT_REAR);
351   }
352
353   /* bass/any */
354   if (in_has_bass && !out_has_bass) {
355     if (out_has_front) {
356       gst_audio_convert_fill_one_other (this->matrix,
357           &this->sinkcaps, in_b,
358           GST_AUDIO_CHANNEL_POSITION_INVALID,
359           GST_AUDIO_CHANNEL_POSITION_INVALID,
360           GST_AUDIO_CHANNEL_POSITION_LFE,
361           &this->srccaps, out_f,
362           GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
363           GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
364           GST_AUDIO_CHANNEL_POSITION_FRONT_MONO, RATIO_FRONT_BASS);
365     }
366     if (out_has_center) {
367       gst_audio_convert_fill_one_other (this->matrix,
368           &this->sinkcaps, in_b,
369           GST_AUDIO_CHANNEL_POSITION_INVALID,
370           GST_AUDIO_CHANNEL_POSITION_INVALID,
371           GST_AUDIO_CHANNEL_POSITION_LFE,
372           &this->srccaps, out_c,
373           GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
374           GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
375           GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, RATIO_CENTER_BASS);
376     }
377     if (out_has_rear) {
378       gst_audio_convert_fill_one_other (this->matrix,
379           &this->sinkcaps, in_b,
380           GST_AUDIO_CHANNEL_POSITION_INVALID,
381           GST_AUDIO_CHANNEL_POSITION_INVALID,
382           GST_AUDIO_CHANNEL_POSITION_LFE,
383           &this->srccaps, out_r,
384           GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
385           GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
386           GST_AUDIO_CHANNEL_POSITION_REAR_CENTER, RATIO_REAR_BASS);
387     }
388   } else if (!in_has_bass && out_has_bass) {
389     if (in_has_front) {
390       gst_audio_convert_fill_one_other (this->matrix,
391           &this->sinkcaps, in_f,
392           GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
393           GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
394           GST_AUDIO_CHANNEL_POSITION_FRONT_MONO,
395           &this->srccaps, out_b,
396           GST_AUDIO_CHANNEL_POSITION_INVALID,
397           GST_AUDIO_CHANNEL_POSITION_INVALID,
398           GST_AUDIO_CHANNEL_POSITION_LFE, RATIO_FRONT_BASS);
399     }
400     if (in_has_center) {
401       gst_audio_convert_fill_one_other (this->matrix,
402           &this->sinkcaps, in_c,
403           GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
404           GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
405           GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
406           &this->srccaps, out_b,
407           GST_AUDIO_CHANNEL_POSITION_INVALID,
408           GST_AUDIO_CHANNEL_POSITION_INVALID,
409           GST_AUDIO_CHANNEL_POSITION_LFE, RATIO_CENTER_BASS);
410     }
411     if (in_has_rear) {
412       gst_audio_convert_fill_one_other (this->matrix,
413           &this->sinkcaps, in_r,
414           GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
415           GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
416           GST_AUDIO_CHANNEL_POSITION_REAR_CENTER,
417           &this->srccaps, out_b,
418           GST_AUDIO_CHANNEL_POSITION_INVALID,
419           GST_AUDIO_CHANNEL_POSITION_INVALID,
420           GST_AUDIO_CHANNEL_POSITION_LFE, RATIO_REAR_BASS);
421     }
422   }
423
424   /* FIXME: side */
425 }
426
427 /*
428  * Normalize output values.
429  */
430
431 static void
432 gst_audio_convert_fill_normalize (GstAudioConvert * this)
433 {
434   gfloat sum, top = 0;
435   gint i, j;
436
437   for (j = 0; j < this->srccaps.channels; j++) {
438     /* calculate sum */
439     sum = 0.0;
440     for (i = 0; i < this->sinkcaps.channels; i++) {
441       sum += fabs (this->matrix[i][j]);
442     }
443     if (sum > top) {
444       top = sum;
445     }
446   }
447
448   /* normalize to this */
449   for (j = 0; j < this->srccaps.channels; j++) {
450     for (i = 0; i < this->sinkcaps.channels; i++) {
451       this->matrix[i][j] /= top;
452     }
453   }
454 }
455
456 /*
457  * Automagically generate conversion matrix.
458  */
459
460 static void
461 gst_audio_convert_fill_matrix (GstAudioConvert * this)
462 {
463   gst_audio_convert_fill_identical (this);
464   gst_audio_convert_fill_compatible (this);
465   gst_audio_convert_fill_others (this);
466   gst_audio_convert_fill_normalize (this);
467 }
468
469 void
470 gst_audio_convert_setup_matrix (GstAudioConvert * this)
471 {
472   gint i, j;
473   GString *s;
474
475   /* don't lose memory */
476   gst_audio_convert_unset_matrix (this);
477
478   /* allocate */
479   this->matrix = g_new0 (gfloat *, this->sinkcaps.channels);
480   for (i = 0; i < this->sinkcaps.channels; i++) {
481     this->matrix[i] = g_new (gfloat, this->srccaps.channels);
482     for (j = 0; j < this->srccaps.channels; j++)
483       this->matrix[i][j] = 0.;
484   }
485
486   /* setup the matrix' internal values */
487   gst_audio_convert_fill_matrix (this);
488
489   /* debug */
490   s = g_string_new ("Matrix for");
491   g_string_append_printf (s, " %d -> %d: ",
492       this->sinkcaps.channels, this->srccaps.channels);
493   g_string_append (s, "{");
494   for (i = 0; i < this->sinkcaps.channels; i++) {
495     if (i != 0)
496       g_string_append (s, ",");
497     g_string_append (s, " {");
498     for (j = 0; j < this->srccaps.channels; j++) {
499       if (j != 0)
500         g_string_append (s, ",");
501       g_string_append_printf (s, " %f", this->matrix[i][j]);
502     }
503     g_string_append (s, " }");
504   }
505   g_string_append (s, " }");
506   GST_DEBUG (s->str);
507   g_string_free (s, TRUE);
508 }
509
510 gboolean
511 gst_audio_convert_passthrough (GstAudioConvert * this)
512 {
513   gint i;
514
515   /* only NxN matrices can be identities */
516   if (this->sinkcaps.channels != this->srccaps.channels)
517     return FALSE;
518
519   /* this assumes a normalized matrix */
520   for (i = 0; i < this->sinkcaps.channels; i++)
521     if (this->matrix[i][i] != 1.)
522       return FALSE;
523
524   return TRUE;
525 }
526
527 /* IMPORTANT: out_data == in_data is possible, make sure to not overwrite data
528  * you might need later on! */
529 void
530 gst_audio_convert_mix (GstAudioConvert * this,
531     gint32 * in_data, gint32 * out_data, gint samples)
532 {
533   gint in, out, n;
534   gint64 res;
535   gboolean backwards = this->srccaps.channels > this->sinkcaps.channels;
536
537   /* FIXME: use liboil here? */
538   for (n = (backwards ? samples - 1 : 0); n < samples && n >= 0;
539       backwards ? n-- : n++) {
540     for (out = 0; out < this->srccaps.channels; out++) {
541       /* convert */
542       res = 0;
543       for (in = 0; in < this->sinkcaps.channels; in++) {
544         res += in_data[n * this->sinkcaps.channels + in] *
545             this->matrix[in][out];
546       }
547
548       /* clip (shouldn't we use doubles instead as intermediate format?) */
549       if (res < G_MININT32)
550         res = G_MININT32;
551       else if (res > G_MAXINT32)
552         res = G_MAXINT32;
553
554       /* store */
555       out_data[n * this->srccaps.channels + out] = res;
556     }
557   }
558 }