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