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