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  * audio-channel-mixer.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., 51 Franklin St, Fifth Floor,
20  * Boston, MA 02110-1301, 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 "audio-channel-mixer.h"
31 
32 #ifndef GST_DISABLE_GST_DEBUG
33 #define GST_CAT_DEFAULT ensure_debug_category()
34 static GstDebugCategory *
ensure_debug_category(void)35 ensure_debug_category (void)
36 {
37   static gsize cat_gonce = 0;
38 
39   if (g_once_init_enter (&cat_gonce)) {
40     gsize cat_done;
41 
42     cat_done = (gsize) _gst_debug_category_new ("audio-channel-mixer", 0,
43         "audio-channel-mixer object");
44 
45     g_once_init_leave (&cat_gonce, cat_done);
46   }
47 
48   return (GstDebugCategory *) cat_gonce;
49 }
50 #else
51 #define ensure_debug_category() /* NOOP */
52 #endif /* GST_DISABLE_GST_DEBUG */
53 
54 
55 #define PRECISION_INT 10
56 
57 typedef void (*MixerFunc) (GstAudioChannelMixer * mix, const gpointer src[],
58     gpointer dst[], gint samples);
59 
60 struct _GstAudioChannelMixer
61 {
62   gint in_channels;
63   gint out_channels;
64 
65   /* channel conversion matrix, m[in_channels][out_channels].
66    * If identity matrix, passthrough applies. */
67   gfloat **matrix;
68 
69   /* channel conversion matrix with int values, m[in_channels][out_channels].
70    * this is matrix * (2^10) as integers */
71   gint **matrix_int;
72 
73   MixerFunc func;
74 };
75 
76 /**
77  * gst_audio_channel_mixer_free:
78  * @mix: a #GstAudioChannelMixer
79  *
80  * Free memory allocated by @mix.
81  */
82 void
gst_audio_channel_mixer_free(GstAudioChannelMixer * mix)83 gst_audio_channel_mixer_free (GstAudioChannelMixer * mix)
84 {
85   gint i;
86 
87   /* free */
88   for (i = 0; i < mix->in_channels; i++)
89     g_free (mix->matrix[i]);
90   g_free (mix->matrix);
91   mix->matrix = NULL;
92 
93   for (i = 0; i < mix->in_channels; i++)
94     g_free (mix->matrix_int[i]);
95   g_free (mix->matrix_int);
96   mix->matrix_int = NULL;
97 
98   g_slice_free (GstAudioChannelMixer, mix);
99 }
100 
101 /*
102  * Detect and fill in identical channels. E.g.
103  * forward the left/right front channels in a
104  * 5.1 to 2.0 conversion.
105  */
106 
107 static void
gst_audio_channel_mixer_fill_identical(gfloat ** matrix,gint in_channels,GstAudioChannelPosition * in_position,gint out_channels,GstAudioChannelPosition * out_position,GstAudioChannelMixerFlags flags)108 gst_audio_channel_mixer_fill_identical (gfloat ** matrix,
109     gint in_channels, GstAudioChannelPosition * in_position, gint out_channels,
110     GstAudioChannelPosition * out_position, GstAudioChannelMixerFlags flags)
111 {
112   gint ci, co;
113 
114   /* Apart from the compatible channel assignments, we can also have
115    * same channel assignments. This is much simpler, we simply copy
116    * the value from source to dest! */
117   for (co = 0; co < out_channels; co++) {
118     /* find a channel in input with same position */
119     for (ci = 0; ci < in_channels; ci++) {
120       /* If the input was unpositioned, we're simply building
121        * an identity matrix */
122       if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_UNPOSITIONED_IN) {
123         matrix[ci][co] = ci == co ? 1.0 : 0.0;
124       } else if (in_position[ci] == out_position[co]) {
125         matrix[ci][co] = 1.0;
126       }
127     }
128   }
129 }
130 
131 /*
132  * Detect and fill in compatible channels. E.g.
133  * forward left/right front to mono (or the other
134  * way around) when going from 2.0 to 1.0.
135  */
136 
137 static void
gst_audio_channel_mixer_fill_compatible(gfloat ** matrix,gint in_channels,GstAudioChannelPosition * in_position,gint out_channels,GstAudioChannelPosition * out_position)138 gst_audio_channel_mixer_fill_compatible (gfloat ** matrix, gint in_channels,
139     GstAudioChannelPosition * in_position, gint out_channels,
140     GstAudioChannelPosition * out_position)
141 {
142   /* Conversions from one-channel to compatible two-channel configs */
143   struct
144   {
145     GstAudioChannelPosition pos1[2];
146     GstAudioChannelPosition pos2[1];
147   } conv[] = {
148     /* front: mono <-> stereo */
149     { {
150     GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
151             GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, {
152     GST_AUDIO_CHANNEL_POSITION_MONO}},
153         /* front center: 2 <-> 1 */
154     { {
155     GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
156             GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER}, {
157     GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER}},
158         /* rear: 2 <-> 1 */
159     { {
160     GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
161             GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}, {
162     GST_AUDIO_CHANNEL_POSITION_REAR_CENTER}}, { {
163     GST_AUDIO_CHANNEL_POSITION_INVALID}}
164   };
165   gint c;
166 
167   /* conversions from compatible (but not the same) channel schemes */
168   for (c = 0; conv[c].pos1[0] != GST_AUDIO_CHANNEL_POSITION_INVALID; c++) {
169     gint pos1_0 = -1, pos1_1 = -1, pos1_2 = -1;
170     gint pos2_0 = -1, pos2_1 = -1, pos2_2 = -1;
171     gint n;
172 
173     for (n = 0; n < in_channels; n++) {
174       if (in_position[n] == conv[c].pos1[0])
175         pos1_0 = n;
176       else if (in_position[n] == conv[c].pos1[1])
177         pos1_1 = n;
178       else if (in_position[n] == conv[c].pos2[0])
179         pos1_2 = n;
180     }
181     for (n = 0; n < out_channels; n++) {
182       if (out_position[n] == conv[c].pos1[0])
183         pos2_0 = n;
184       else if (out_position[n] == conv[c].pos1[1])
185         pos2_1 = n;
186       else if (out_position[n] == conv[c].pos2[0])
187         pos2_2 = n;
188     }
189 
190     /* The general idea here is to fill in channels from the same position
191      * as good as possible. This means mixing left<->center and right<->center.
192      */
193 
194     /* left -> center */
195     if (pos1_0 != -1 && pos1_2 == -1 && pos2_0 == -1 && pos2_2 != -1)
196       matrix[pos1_0][pos2_2] = 1.0;
197     else if (pos1_0 != -1 && pos1_2 != -1 && pos2_0 == -1 && pos2_2 != -1)
198       matrix[pos1_0][pos2_2] = 0.5;
199     else if (pos1_0 != -1 && pos1_2 == -1 && pos2_0 != -1 && pos2_2 != -1)
200       matrix[pos1_0][pos2_2] = 1.0;
201 
202     /* right -> center */
203     if (pos1_1 != -1 && pos1_2 == -1 && pos2_1 == -1 && pos2_2 != -1)
204       matrix[pos1_1][pos2_2] = 1.0;
205     else if (pos1_1 != -1 && pos1_2 != -1 && pos2_1 == -1 && pos2_2 != -1)
206       matrix[pos1_1][pos2_2] = 0.5;
207     else if (pos1_1 != -1 && pos1_2 == -1 && pos2_1 != -1 && pos2_2 != -1)
208       matrix[pos1_1][pos2_2] = 1.0;
209 
210     /* center -> left */
211     if (pos1_2 != -1 && pos1_0 == -1 && pos2_2 == -1 && pos2_0 != -1)
212       matrix[pos1_2][pos2_0] = 1.0;
213     else if (pos1_2 != -1 && pos1_0 != -1 && pos2_2 == -1 && pos2_0 != -1)
214       matrix[pos1_2][pos2_0] = 0.5;
215     else if (pos1_2 != -1 && pos1_0 == -1 && pos2_2 != -1 && pos2_0 != -1)
216       matrix[pos1_2][pos2_0] = 1.0;
217 
218     /* center -> right */
219     if (pos1_2 != -1 && pos1_1 == -1 && pos2_2 == -1 && pos2_1 != -1)
220       matrix[pos1_2][pos2_1] = 1.0;
221     else if (pos1_2 != -1 && pos1_1 != -1 && pos2_2 == -1 && pos2_1 != -1)
222       matrix[pos1_2][pos2_1] = 0.5;
223     else if (pos1_2 != -1 && pos1_1 == -1 && pos2_2 != -1 && pos2_1 != -1)
224       matrix[pos1_2][pos2_1] = 1.0;
225   }
226 }
227 
228 /*
229  * Detect and fill in channels not handled by the
230  * above two, e.g. center to left/right front in
231  * 5.1 to 2.0 (or the other way around).
232  *
233  * Unfortunately, limited to static conversions
234  * for now.
235  */
236 
237 static void
gst_audio_channel_mixer_detect_pos(gint channels,GstAudioChannelPosition position[64],gint * f,gboolean * has_f,gint * c,gboolean * has_c,gint * r,gboolean * has_r,gint * s,gboolean * has_s,gint * b,gboolean * has_b)238 gst_audio_channel_mixer_detect_pos (gint channels,
239     GstAudioChannelPosition position[64], gint * f, gboolean * has_f, gint * c,
240     gboolean * has_c, gint * r, gboolean * has_r, gint * s, gboolean * has_s,
241     gint * b, gboolean * has_b)
242 {
243   gint n;
244 
245   for (n = 0; n < channels; n++) {
246     switch (position[n]) {
247       case GST_AUDIO_CHANNEL_POSITION_MONO:
248         f[1] = n;
249         *has_f = TRUE;
250         break;
251       case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT:
252         f[0] = n;
253         *has_f = TRUE;
254         break;
255       case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT:
256         f[2] = n;
257         *has_f = TRUE;
258         break;
259       case GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER:
260         c[1] = n;
261         *has_c = TRUE;
262         break;
263       case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
264         c[0] = n;
265         *has_c = TRUE;
266         break;
267       case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
268         c[2] = n;
269         *has_c = TRUE;
270         break;
271       case GST_AUDIO_CHANNEL_POSITION_REAR_CENTER:
272         r[1] = n;
273         *has_r = TRUE;
274         break;
275       case GST_AUDIO_CHANNEL_POSITION_REAR_LEFT:
276         r[0] = n;
277         *has_r = TRUE;
278         break;
279       case GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT:
280         r[2] = n;
281         *has_r = TRUE;
282         break;
283       case GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT:
284         s[0] = n;
285         *has_s = TRUE;
286         break;
287       case GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT:
288         s[2] = n;
289         *has_s = TRUE;
290         break;
291       case GST_AUDIO_CHANNEL_POSITION_LFE1:
292         *has_b = TRUE;
293         b[1] = n;
294         break;
295       default:
296         break;
297     }
298   }
299 }
300 
301 static void
gst_audio_channel_mixer_fill_one_other(gfloat ** matrix,gint * from_idx,gint * to_idx,gfloat ratio)302 gst_audio_channel_mixer_fill_one_other (gfloat ** matrix,
303     gint * from_idx, gint * to_idx, gfloat ratio)
304 {
305 
306   /* src & dst have center => passthrough */
307   if (from_idx[1] != -1 && to_idx[1] != -1) {
308     matrix[from_idx[1]][to_idx[1]] = ratio;
309   }
310 
311   /* src & dst have left => passthrough */
312   if (from_idx[0] != -1 && to_idx[0] != -1) {
313     matrix[from_idx[0]][to_idx[0]] = ratio;
314   }
315 
316   /* src & dst have right => passthrough */
317   if (from_idx[2] != -1 && to_idx[2] != -1) {
318     matrix[from_idx[2]][to_idx[2]] = ratio;
319   }
320 
321   /* src has left & dst has center => put into center */
322   if (from_idx[0] != -1 && to_idx[1] != -1 && from_idx[1] != -1) {
323     matrix[from_idx[0]][to_idx[1]] = 0.5 * ratio;
324   } else if (from_idx[0] != -1 && to_idx[1] != -1 && from_idx[1] == -1) {
325     matrix[from_idx[0]][to_idx[1]] = ratio;
326   }
327 
328   /* src has right & dst has center => put into center */
329   if (from_idx[2] != -1 && to_idx[1] != -1 && from_idx[1] != -1) {
330     matrix[from_idx[2]][to_idx[1]] = 0.5 * ratio;
331   } else if (from_idx[2] != -1 && to_idx[1] != -1 && from_idx[1] == -1) {
332     matrix[from_idx[2]][to_idx[1]] = ratio;
333   }
334 
335   /* src has center & dst has left => passthrough */
336   if (from_idx[1] != -1 && to_idx[0] != -1 && from_idx[0] != -1) {
337     matrix[from_idx[1]][to_idx[0]] = 0.5 * ratio;
338   } else if (from_idx[1] != -1 && to_idx[0] != -1 && from_idx[0] == -1) {
339     matrix[from_idx[1]][to_idx[0]] = ratio;
340   }
341 
342   /* src has center & dst has right => passthrough */
343   if (from_idx[1] != -1 && to_idx[2] != -1 && from_idx[2] != -1) {
344     matrix[from_idx[1]][to_idx[2]] = 0.5 * ratio;
345   } else if (from_idx[1] != -1 && to_idx[2] != -1 && from_idx[2] == -1) {
346     matrix[from_idx[1]][to_idx[2]] = ratio;
347   }
348 }
349 
350 #define RATIO_CENTER_FRONT (1.0 / sqrt (2.0))
351 #define RATIO_CENTER_SIDE (1.0 / 2.0)
352 #define RATIO_CENTER_REAR (1.0 / sqrt (8.0))
353 
354 #define RATIO_FRONT_CENTER (1.0 / sqrt (2.0))
355 #define RATIO_FRONT_SIDE (1.0 / sqrt (2.0))
356 #define RATIO_FRONT_REAR (1.0 / 2.0)
357 
358 #define RATIO_SIDE_CENTER (1.0 / 2.0)
359 #define RATIO_SIDE_FRONT (1.0 / sqrt (2.0))
360 #define RATIO_SIDE_REAR (1.0 / sqrt (2.0))
361 
362 #define RATIO_CENTER_BASS (1.0 / sqrt (2.0))
363 #define RATIO_FRONT_BASS (1.0)
364 #define RATIO_SIDE_BASS (1.0 / sqrt (2.0))
365 #define RATIO_REAR_BASS (1.0 / sqrt (2.0))
366 
367 static void
gst_audio_channel_mixer_fill_others(gfloat ** matrix,gint in_channels,GstAudioChannelPosition * in_position,gint out_channels,GstAudioChannelPosition * out_position)368 gst_audio_channel_mixer_fill_others (gfloat ** matrix, gint in_channels,
369     GstAudioChannelPosition * in_position, gint out_channels,
370     GstAudioChannelPosition * out_position)
371 {
372   gboolean in_has_front = FALSE, out_has_front = FALSE,
373       in_has_center = FALSE, out_has_center = FALSE,
374       in_has_rear = FALSE, out_has_rear = FALSE,
375       in_has_side = FALSE, out_has_side = FALSE,
376       in_has_bass = FALSE, out_has_bass = FALSE;
377   /* LEFT, RIGHT, MONO */
378   gint in_f[3] = { -1, -1, -1 };
379   gint out_f[3] = { -1, -1, -1 };
380   /* LOC, ROC, CENTER */
381   gint in_c[3] = { -1, -1, -1 };
382   gint out_c[3] = { -1, -1, -1 };
383   /* RLEFT, RRIGHT, RCENTER */
384   gint in_r[3] = { -1, -1, -1 };
385   gint out_r[3] = { -1, -1, -1 };
386   /* SLEFT, INVALID, SRIGHT */
387   gint in_s[3] = { -1, -1, -1 };
388   gint out_s[3] = { -1, -1, -1 };
389   /* INVALID, LFE, INVALID */
390   gint in_b[3] = { -1, -1, -1 };
391   gint out_b[3] = { -1, -1, -1 };
392 
393   /* First see where (if at all) the various channels from/to
394    * which we want to convert are located in our matrix/array. */
395   gst_audio_channel_mixer_detect_pos (in_channels, in_position,
396       in_f, &in_has_front,
397       in_c, &in_has_center, in_r, &in_has_rear,
398       in_s, &in_has_side, in_b, &in_has_bass);
399   gst_audio_channel_mixer_detect_pos (out_channels, out_position,
400       out_f, &out_has_front,
401       out_c, &out_has_center, out_r, &out_has_rear,
402       out_s, &out_has_side, out_b, &out_has_bass);
403 
404   /* The general idea here is:
405    * - if the source has a channel that the destination doesn't have mix
406    *   it into the nearest available destination channel
407    * - if the destination has a channel that the source doesn't have mix
408    *   the nearest source channel into the destination channel
409    *
410    * The ratio for the mixing becomes lower as the distance between the
411    * channels gets larger
412    */
413 
414   /* center <-> front/side/rear */
415   if (!in_has_center && in_has_front && out_has_center) {
416     gst_audio_channel_mixer_fill_one_other (matrix, in_f, out_c,
417         RATIO_CENTER_FRONT);
418   } else if (!in_has_center && !in_has_front && in_has_side && out_has_center) {
419     gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_c,
420         RATIO_CENTER_SIDE);
421   } else if (!in_has_center && !in_has_front && !in_has_side && in_has_rear
422       && out_has_center) {
423     gst_audio_channel_mixer_fill_one_other (matrix, in_r, out_c,
424         RATIO_CENTER_REAR);
425   } else if (in_has_center && !out_has_center && out_has_front) {
426     gst_audio_channel_mixer_fill_one_other (matrix, in_c, out_f,
427         RATIO_CENTER_FRONT);
428   } else if (in_has_center && !out_has_center && !out_has_front && out_has_side) {
429     gst_audio_channel_mixer_fill_one_other (matrix, in_c, out_s,
430         RATIO_CENTER_SIDE);
431   } else if (in_has_center && !out_has_center && !out_has_front && !out_has_side
432       && out_has_rear) {
433     gst_audio_channel_mixer_fill_one_other (matrix, in_c, out_r,
434         RATIO_CENTER_REAR);
435   }
436 
437   /* front <-> center/side/rear */
438   if (!in_has_front && in_has_center && !in_has_side && out_has_front) {
439     gst_audio_channel_mixer_fill_one_other (matrix, in_c, out_f,
440         RATIO_CENTER_FRONT);
441   } else if (!in_has_front && !in_has_center && in_has_side && out_has_front) {
442     gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_f,
443         RATIO_FRONT_SIDE);
444   } else if (!in_has_front && in_has_center && in_has_side && out_has_front) {
445     gst_audio_channel_mixer_fill_one_other (matrix, in_c, out_f,
446         0.5 * RATIO_CENTER_FRONT);
447     gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_f,
448         0.5 * RATIO_FRONT_SIDE);
449   } else if (!in_has_front && !in_has_center && !in_has_side && in_has_rear
450       && out_has_front) {
451     gst_audio_channel_mixer_fill_one_other (matrix, in_r, out_f,
452         RATIO_FRONT_REAR);
453   } else if (in_has_front && out_has_center && !out_has_side && !out_has_front) {
454     gst_audio_channel_mixer_fill_one_other (matrix,
455         in_f, out_c, RATIO_CENTER_FRONT);
456   } else if (in_has_front && !out_has_center && out_has_side && !out_has_front) {
457     gst_audio_channel_mixer_fill_one_other (matrix, in_f, out_s,
458         RATIO_FRONT_SIDE);
459   } else if (in_has_front && out_has_center && out_has_side && !out_has_front) {
460     gst_audio_channel_mixer_fill_one_other (matrix, in_f, out_c,
461         0.5 * RATIO_CENTER_FRONT);
462     gst_audio_channel_mixer_fill_one_other (matrix, in_f, out_s,
463         0.5 * RATIO_FRONT_SIDE);
464   } else if (in_has_front && !out_has_center && !out_has_side && !out_has_front
465       && out_has_rear) {
466     gst_audio_channel_mixer_fill_one_other (matrix, in_f, out_r,
467         RATIO_FRONT_REAR);
468   }
469 
470   /* side <-> center/front/rear */
471   if (!in_has_side && in_has_front && !in_has_rear && out_has_side) {
472     gst_audio_channel_mixer_fill_one_other (matrix, in_f, out_s,
473         RATIO_FRONT_SIDE);
474   } else if (!in_has_side && !in_has_front && in_has_rear && out_has_side) {
475     gst_audio_channel_mixer_fill_one_other (matrix, in_r, out_s,
476         RATIO_SIDE_REAR);
477   } else if (!in_has_side && in_has_front && in_has_rear && out_has_side) {
478     gst_audio_channel_mixer_fill_one_other (matrix, in_f, out_s,
479         0.5 * RATIO_FRONT_SIDE);
480     gst_audio_channel_mixer_fill_one_other (matrix, in_r, out_s,
481         0.5 * RATIO_SIDE_REAR);
482   } else if (!in_has_side && !in_has_front && !in_has_rear && in_has_center
483       && out_has_side) {
484     gst_audio_channel_mixer_fill_one_other (matrix, in_c, out_s,
485         RATIO_CENTER_SIDE);
486   } else if (in_has_side && out_has_front && !out_has_rear && !out_has_side) {
487     gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_f,
488         RATIO_FRONT_SIDE);
489   } else if (in_has_side && !out_has_front && out_has_rear && !out_has_side) {
490     gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_r,
491         RATIO_SIDE_REAR);
492   } else if (in_has_side && out_has_front && out_has_rear && !out_has_side) {
493     gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_f,
494         0.5 * RATIO_FRONT_SIDE);
495     gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_r,
496         0.5 * RATIO_SIDE_REAR);
497   } else if (in_has_side && !out_has_front && !out_has_rear && out_has_center
498       && !out_has_side) {
499     gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_c,
500         RATIO_CENTER_SIDE);
501   }
502 
503   /* rear <-> center/front/side */
504   if (!in_has_rear && in_has_side && out_has_rear) {
505     gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_r,
506         RATIO_SIDE_REAR);
507   } else if (!in_has_rear && !in_has_side && in_has_front && out_has_rear) {
508     gst_audio_channel_mixer_fill_one_other (matrix, in_f, out_r,
509         RATIO_FRONT_REAR);
510   } else if (!in_has_rear && !in_has_side && !in_has_front && in_has_center
511       && out_has_rear) {
512     gst_audio_channel_mixer_fill_one_other (matrix, in_c, out_r,
513         RATIO_CENTER_REAR);
514   } else if (in_has_rear && !out_has_rear && out_has_side) {
515     gst_audio_channel_mixer_fill_one_other (matrix, in_r, out_s,
516         RATIO_SIDE_REAR);
517   } else if (in_has_rear && !out_has_rear && !out_has_side && out_has_front) {
518     gst_audio_channel_mixer_fill_one_other (matrix, in_r, out_f,
519         RATIO_FRONT_REAR);
520   } else if (in_has_rear && !out_has_rear && !out_has_side && !out_has_front
521       && out_has_center) {
522     gst_audio_channel_mixer_fill_one_other (matrix, in_r, out_c,
523         RATIO_CENTER_REAR);
524   }
525 
526   /* bass <-> any */
527   if (in_has_bass && !out_has_bass) {
528     if (out_has_center) {
529       gst_audio_channel_mixer_fill_one_other (matrix, in_b, out_c,
530           RATIO_CENTER_BASS);
531     }
532     if (out_has_front) {
533       gst_audio_channel_mixer_fill_one_other (matrix, in_b, out_f,
534           RATIO_FRONT_BASS);
535     }
536     if (out_has_side) {
537       gst_audio_channel_mixer_fill_one_other (matrix, in_b, out_s,
538           RATIO_SIDE_BASS);
539     }
540     if (out_has_rear) {
541       gst_audio_channel_mixer_fill_one_other (matrix, in_b, out_r,
542           RATIO_REAR_BASS);
543     }
544   } else if (!in_has_bass && out_has_bass) {
545     if (in_has_center) {
546       gst_audio_channel_mixer_fill_one_other (matrix, in_c, out_b,
547           RATIO_CENTER_BASS);
548     }
549     if (in_has_front) {
550       gst_audio_channel_mixer_fill_one_other (matrix, in_f, out_b,
551           RATIO_FRONT_BASS);
552     }
553     if (in_has_side) {
554       gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_b,
555           RATIO_REAR_BASS);
556     }
557     if (in_has_rear) {
558       gst_audio_channel_mixer_fill_one_other (matrix, in_r, out_b,
559           RATIO_REAR_BASS);
560     }
561   }
562 }
563 
564 /*
565  * Normalize output values.
566  */
567 
568 static void
gst_audio_channel_mixer_fill_normalize(gfloat ** matrix,gint in_channels,gint out_channels)569 gst_audio_channel_mixer_fill_normalize (gfloat ** matrix, gint in_channels,
570     gint out_channels)
571 {
572   gfloat sum, top = 0;
573   gint i, j;
574 
575   for (j = 0; j < out_channels; j++) {
576     /* calculate sum */
577     sum = 0.0;
578     for (i = 0; i < in_channels; i++) {
579       sum += fabs (matrix[i][j]);
580     }
581     if (sum > top) {
582       top = sum;
583     }
584   }
585 
586   /* normalize to mix */
587   if (top == 0.0)
588     return;
589 
590   for (j = 0; j < out_channels; j++) {
591     for (i = 0; i < in_channels; i++) {
592       matrix[i][j] /= top;
593     }
594   }
595 }
596 
597 static gboolean
gst_audio_channel_mixer_fill_special(gfloat ** matrix,gint in_channels,GstAudioChannelPosition * in_position,gint out_channels,GstAudioChannelPosition * out_position)598 gst_audio_channel_mixer_fill_special (gfloat ** matrix, gint in_channels,
599     GstAudioChannelPosition * in_position, gint out_channels,
600     GstAudioChannelPosition * out_position)
601 {
602   /* Special, standard conversions here */
603 
604   /* Mono<->Stereo, just a fast-path */
605   if (in_channels == 2 && out_channels == 1 &&
606       ((in_position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT &&
607               in_position[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT) ||
608           (in_position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT &&
609               in_position[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT)) &&
610       out_position[0] == GST_AUDIO_CHANNEL_POSITION_MONO) {
611     matrix[0][0] = 0.5;
612     matrix[1][0] = 0.5;
613     return TRUE;
614   } else if (in_channels == 1 && out_channels == 2 &&
615       ((out_position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT &&
616               out_position[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT) ||
617           (out_position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT &&
618               out_position[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT)) &&
619       in_position[0] == GST_AUDIO_CHANNEL_POSITION_MONO) {
620     matrix[0][0] = 1.0;
621     matrix[0][1] = 1.0;
622     return TRUE;
623   }
624 
625   /* TODO: 5.1 <-> Stereo and other standard conversions */
626 
627   return FALSE;
628 }
629 
630 /*
631  * Automagically generate conversion matrix.
632  */
633 
634 static void
gst_audio_channel_mixer_fill_matrix(gfloat ** matrix,GstAudioChannelMixerFlags flags,gint in_channels,GstAudioChannelPosition * in_position,gint out_channels,GstAudioChannelPosition * out_position)635 gst_audio_channel_mixer_fill_matrix (gfloat ** matrix,
636     GstAudioChannelMixerFlags flags, gint in_channels,
637     GstAudioChannelPosition * in_position, gint out_channels,
638     GstAudioChannelPosition * out_position)
639 {
640   if (gst_audio_channel_mixer_fill_special (matrix, in_channels, in_position,
641           out_channels, out_position))
642     return;
643 
644   gst_audio_channel_mixer_fill_identical (matrix, in_channels, in_position,
645       out_channels, out_position, flags);
646 
647   if (!(flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_UNPOSITIONED_IN)) {
648     gst_audio_channel_mixer_fill_compatible (matrix, in_channels, in_position,
649         out_channels, out_position);
650     gst_audio_channel_mixer_fill_others (matrix, in_channels, in_position,
651         out_channels, out_position);
652     gst_audio_channel_mixer_fill_normalize (matrix, in_channels, out_channels);
653   }
654 }
655 
656 /* only call mix after mix->matrix is fully set up and normalized */
657 static void
gst_audio_channel_mixer_setup_matrix_int(GstAudioChannelMixer * mix)658 gst_audio_channel_mixer_setup_matrix_int (GstAudioChannelMixer * mix)
659 {
660   gint i, j;
661   gfloat tmp;
662   gfloat factor = (1 << PRECISION_INT);
663 
664   mix->matrix_int = g_new0 (gint *, mix->in_channels);
665 
666   for (i = 0; i < mix->in_channels; i++) {
667     mix->matrix_int[i] = g_new (gint, mix->out_channels);
668 
669     for (j = 0; j < mix->out_channels; j++) {
670       tmp = mix->matrix[i][j] * factor;
671       mix->matrix_int[i][j] = (gint) tmp;
672     }
673   }
674 }
675 
676 static gfloat **
gst_audio_channel_mixer_setup_matrix(GstAudioChannelMixerFlags flags,gint in_channels,GstAudioChannelPosition * in_position,gint out_channels,GstAudioChannelPosition * out_position)677 gst_audio_channel_mixer_setup_matrix (GstAudioChannelMixerFlags flags,
678     gint in_channels, GstAudioChannelPosition * in_position,
679     gint out_channels, GstAudioChannelPosition * out_position)
680 {
681   gint i, j;
682   gfloat **matrix = g_new0 (gfloat *, in_channels);
683 
684   for (i = 0; i < in_channels; i++) {
685     matrix[i] = g_new (gfloat, out_channels);
686     for (j = 0; j < out_channels; j++)
687       matrix[i][j] = 0.;
688   }
689 
690   /* setup the matrix' internal values */
691   gst_audio_channel_mixer_fill_matrix (matrix, flags, in_channels, in_position,
692       out_channels, out_position);
693 
694   return matrix;
695 }
696 
697 #define DEFINE_GET_DATA_FUNCS(type) \
698 static inline type \
699 _get_in_data_interleaved_##type (const type * in_data[], \
700     gint sample, gint channel, gint total_channels) \
701 { \
702   return in_data[0][sample * total_channels + channel]; \
703 } \
704 \
705 static inline type * \
706 _get_out_data_interleaved_##type (type * out_data[], \
707     gint sample, gint channel, gint total_channels) \
708 { \
709   return &out_data[0][sample * total_channels + channel]; \
710 } \
711 \
712 static inline type \
713 _get_in_data_planar_##type (const type * in_data[], \
714     gint sample, gint channel, gint total_channels) \
715 { \
716   (void) total_channels; \
717   return in_data[channel][sample]; \
718 } \
719 \
720 static inline type * \
721 _get_out_data_planar_##type (type * out_data[], \
722     gint sample, gint channel, gint total_channels) \
723 { \
724   (void) total_channels; \
725   return &out_data[channel][sample]; \
726 }
727 
728 #define DEFINE_INTEGER_MIX_FUNC(bits, resbits, inlayout, outlayout) \
729 static void \
730 gst_audio_channel_mixer_mix_int##bits##_##inlayout##_##outlayout ( \
731     GstAudioChannelMixer * mix, const gint##bits * in_data[], \
732     gint##bits * out_data[], gint samples) \
733 { \
734   gint in, out, n; \
735   gint##resbits res; \
736   gint inchannels, outchannels; \
737   \
738   inchannels = mix->in_channels; \
739   outchannels = mix->out_channels; \
740   \
741   for (n = 0; n < samples; n++) { \
742     for (out = 0; out < outchannels; out++) { \
743       /* convert */ \
744       res = 0; \
745       for (in = 0; in < inchannels; in++) \
746         res += \
747           _get_in_data_##inlayout##_gint##bits (in_data, n, in, inchannels) * \
748           (gint##resbits) mix->matrix_int[in][out]; \
749       \
750       /* remove factor from int matrix */ \
751       res = (res + (1 << (PRECISION_INT - 1))) >> PRECISION_INT; \
752       *_get_out_data_##outlayout##_gint##bits (out_data, n, out, outchannels) = \
753           CLAMP (res, G_MININT##bits, G_MAXINT##bits); \
754     } \
755   } \
756 }
757 
758 #define DEFINE_FLOAT_MIX_FUNC(type, inlayout, outlayout) \
759 static void \
760 gst_audio_channel_mixer_mix_##type##_##inlayout##_##outlayout ( \
761     GstAudioChannelMixer * mix, const g##type * in_data[], \
762     g##type * out_data[], gint samples) \
763 { \
764   gint in, out, n; \
765   g##type res; \
766   gint inchannels, outchannels; \
767   \
768   inchannels = mix->in_channels; \
769   outchannels = mix->out_channels; \
770   \
771   for (n = 0; n < samples; n++) { \
772     for (out = 0; out < outchannels; out++) { \
773       /* convert */ \
774       res = 0.0; \
775       for (in = 0; in < inchannels; in++) \
776         res += \
777           _get_in_data_##inlayout##_g##type (in_data, n, in, inchannels) * \
778           mix->matrix[in][out]; \
779       \
780       *_get_out_data_##outlayout##_g##type (out_data, n, out, outchannels) = res; \
781     } \
782   } \
783 }
784 
785 DEFINE_GET_DATA_FUNCS (gint16);
786 DEFINE_INTEGER_MIX_FUNC (16, 32, interleaved, interleaved);
787 DEFINE_INTEGER_MIX_FUNC (16, 32, interleaved, planar);
788 DEFINE_INTEGER_MIX_FUNC (16, 32, planar, interleaved);
789 DEFINE_INTEGER_MIX_FUNC (16, 32, planar, planar);
790 
791 DEFINE_GET_DATA_FUNCS (gint32);
792 DEFINE_INTEGER_MIX_FUNC (32, 64, interleaved, interleaved);
793 DEFINE_INTEGER_MIX_FUNC (32, 64, interleaved, planar);
794 DEFINE_INTEGER_MIX_FUNC (32, 64, planar, interleaved);
795 DEFINE_INTEGER_MIX_FUNC (32, 64, planar, planar);
796 
797 DEFINE_GET_DATA_FUNCS (gfloat);
798 DEFINE_FLOAT_MIX_FUNC (float, interleaved, interleaved);
799 DEFINE_FLOAT_MIX_FUNC (float, interleaved, planar);
800 DEFINE_FLOAT_MIX_FUNC (float, planar, interleaved);
801 DEFINE_FLOAT_MIX_FUNC (float, planar, planar);
802 
803 DEFINE_GET_DATA_FUNCS (gdouble);
804 DEFINE_FLOAT_MIX_FUNC (double, interleaved, interleaved);
805 DEFINE_FLOAT_MIX_FUNC (double, interleaved, planar);
806 DEFINE_FLOAT_MIX_FUNC (double, planar, interleaved);
807 DEFINE_FLOAT_MIX_FUNC (double, planar, planar);
808 
809 /**
810  * gst_audio_channel_mixer_new_with_matrix: (skip):
811  * @flags: #GstAudioChannelMixerFlags
812  * @in_channels: number of input channels
813  * @out_channels: number of output channels
814  * @matrix: (transfer full) (nullable): channel conversion matrix, m[@in_channels][@out_channels].
815  *   If identity matrix, passthrough applies. If %NULL, a (potentially truncated)
816  *   identity matrix is generated.
817  *
818  * Create a new channel mixer object for the given parameters.
819  *
820  * Returns: a new #GstAudioChannelMixer object, or %NULL if @format isn't supported,
821  *   @matrix is invalid, or @matrix is %NULL and @in_channels != @out_channels.
822  *   Free with gst_audio_channel_mixer_free() after usage.
823  *
824  * Since: 1.14
825  */
826 GstAudioChannelMixer *
gst_audio_channel_mixer_new_with_matrix(GstAudioChannelMixerFlags flags,GstAudioFormat format,gint in_channels,gint out_channels,gfloat ** matrix)827 gst_audio_channel_mixer_new_with_matrix (GstAudioChannelMixerFlags flags,
828     GstAudioFormat format,
829     gint in_channels, gint out_channels, gfloat ** matrix)
830 {
831   GstAudioChannelMixer *mix;
832 
833   g_return_val_if_fail (format == GST_AUDIO_FORMAT_S16
834       || format == GST_AUDIO_FORMAT_S32
835       || format == GST_AUDIO_FORMAT_F32
836       || format == GST_AUDIO_FORMAT_F64, NULL);
837   g_return_val_if_fail (in_channels > 0 && in_channels < 64, NULL);
838   g_return_val_if_fail (out_channels > 0 && out_channels < 64, NULL);
839 
840   mix = g_slice_new0 (GstAudioChannelMixer);
841   mix->in_channels = in_channels;
842   mix->out_channels = out_channels;
843 
844   if (!matrix) {
845     /* Generate (potentially truncated) identity matrix */
846     gint i, j;
847 
848     mix->matrix = g_new0 (gfloat *, in_channels);
849 
850     for (i = 0; i < in_channels; i++) {
851       mix->matrix[i] = g_new (gfloat, out_channels);
852       for (j = 0; j < out_channels; j++) {
853         mix->matrix[i][j] = i == j ? 1.0 : 0.0;
854       }
855     }
856   } else {
857     mix->matrix = matrix;
858   }
859 
860   gst_audio_channel_mixer_setup_matrix_int (mix);
861 
862 #ifndef GST_DISABLE_GST_DEBUG
863   /* debug */
864   {
865     GString *s;
866     gint i, j;
867 
868     s = g_string_new ("Matrix for");
869     g_string_append_printf (s, " %d -> %d: ",
870         mix->in_channels, mix->out_channels);
871     g_string_append (s, "{");
872     for (i = 0; i < mix->in_channels; i++) {
873       if (i != 0)
874         g_string_append (s, ",");
875       g_string_append (s, " {");
876       for (j = 0; j < mix->out_channels; j++) {
877         if (j != 0)
878           g_string_append (s, ",");
879         g_string_append_printf (s, " %f", mix->matrix[i][j]);
880       }
881       g_string_append (s, " }");
882     }
883     g_string_append (s, " }");
884     GST_DEBUG ("%s", s->str);
885     g_string_free (s, TRUE);
886   }
887 #endif
888 
889   switch (format) {
890     case GST_AUDIO_FORMAT_S16:
891       if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_IN) {
892         if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT) {
893           mix->func = (MixerFunc)
894               gst_audio_channel_mixer_mix_int16_planar_planar;
895         } else {
896           mix->func = (MixerFunc)
897               gst_audio_channel_mixer_mix_int16_planar_interleaved;
898         }
899       } else {
900         if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT) {
901           mix->func = (MixerFunc)
902               gst_audio_channel_mixer_mix_int16_interleaved_planar;
903         } else {
904           mix->func = (MixerFunc)
905               gst_audio_channel_mixer_mix_int16_interleaved_interleaved;
906         }
907       }
908       break;
909     case GST_AUDIO_FORMAT_S32:
910       if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_IN) {
911         if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT) {
912           mix->func = (MixerFunc)
913               gst_audio_channel_mixer_mix_int32_planar_planar;
914         } else {
915           mix->func = (MixerFunc)
916               gst_audio_channel_mixer_mix_int32_planar_interleaved;
917         }
918       } else {
919         if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT) {
920           mix->func = (MixerFunc)
921               gst_audio_channel_mixer_mix_int32_interleaved_planar;
922         } else {
923           mix->func = (MixerFunc)
924               gst_audio_channel_mixer_mix_int32_interleaved_interleaved;
925         }
926       }
927       break;
928     case GST_AUDIO_FORMAT_F32:
929       if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_IN) {
930         if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT) {
931           mix->func = (MixerFunc)
932               gst_audio_channel_mixer_mix_float_planar_planar;
933         } else {
934           mix->func = (MixerFunc)
935               gst_audio_channel_mixer_mix_float_planar_interleaved;
936         }
937       } else {
938         if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT) {
939           mix->func = (MixerFunc)
940               gst_audio_channel_mixer_mix_float_interleaved_planar;
941         } else {
942           mix->func = (MixerFunc)
943               gst_audio_channel_mixer_mix_float_interleaved_interleaved;
944         }
945       }
946       break;
947     case GST_AUDIO_FORMAT_F64:
948       if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_IN) {
949         if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT) {
950           mix->func = (MixerFunc)
951               gst_audio_channel_mixer_mix_double_planar_planar;
952         } else {
953           mix->func = (MixerFunc)
954               gst_audio_channel_mixer_mix_double_planar_interleaved;
955         }
956       } else {
957         if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT) {
958           mix->func = (MixerFunc)
959               gst_audio_channel_mixer_mix_double_interleaved_planar;
960         } else {
961           mix->func = (MixerFunc)
962               gst_audio_channel_mixer_mix_double_interleaved_interleaved;
963         }
964       }
965       break;
966     default:
967       g_assert_not_reached ();
968       break;
969   }
970   return mix;
971 }
972 
973 /**
974  * gst_audio_channel_mixer_new: (skip):
975  * @flags: #GstAudioChannelMixerFlags
976  * @in_channels: number of input channels
977  * @in_position: positions of input channels
978  * @out_channels: number of output channels
979  * @out_position: positions of output channels
980  *
981  * Create a new channel mixer object for the given parameters.
982  *
983  * Returns: a new #GstAudioChannelMixer object, or %NULL if @format isn't supported.
984  *   Free with gst_audio_channel_mixer_free() after usage.
985  */
986 GstAudioChannelMixer *
gst_audio_channel_mixer_new(GstAudioChannelMixerFlags flags,GstAudioFormat format,gint in_channels,GstAudioChannelPosition * in_position,gint out_channels,GstAudioChannelPosition * out_position)987 gst_audio_channel_mixer_new (GstAudioChannelMixerFlags flags,
988     GstAudioFormat format,
989     gint in_channels,
990     GstAudioChannelPosition * in_position,
991     gint out_channels, GstAudioChannelPosition * out_position)
992 {
993   gfloat **matrix;
994 
995   g_return_val_if_fail (format == GST_AUDIO_FORMAT_S16
996       || format == GST_AUDIO_FORMAT_S32
997       || format == GST_AUDIO_FORMAT_F32
998       || format == GST_AUDIO_FORMAT_F64, NULL);
999   g_return_val_if_fail (in_channels > 0 && in_channels < 64, NULL);
1000   g_return_val_if_fail (out_channels > 0 && out_channels < 64, NULL);
1001 
1002   matrix =
1003       gst_audio_channel_mixer_setup_matrix (flags, in_channels, in_position,
1004       out_channels, out_position);
1005   return gst_audio_channel_mixer_new_with_matrix (flags, format, in_channels,
1006       out_channels, matrix);
1007 }
1008 
1009 /**
1010  * gst_audio_channel_mixer_is_passthrough:
1011  * @mix: a #GstAudioChannelMixer
1012  *
1013  * Check if @mix is in passthrough.
1014  *
1015  * Only N x N mix identity matrices are considered passthrough,
1016  * this is determined by comparing the contents of the matrix
1017  * with 0.0 and 1.0.
1018  *
1019  * As this is floating point comparisons, if the values have been
1020  * generated, they should be rounded up or down by explicit
1021  * assignment of 0.0 or 1.0 to values within a user-defined
1022  * epsilon, this code doesn't make assumptions as to what may
1023  * constitute an appropriate epsilon.
1024  *
1025  * Returns: %TRUE is @mix is passthrough.
1026  */
1027 gboolean
gst_audio_channel_mixer_is_passthrough(GstAudioChannelMixer * mix)1028 gst_audio_channel_mixer_is_passthrough (GstAudioChannelMixer * mix)
1029 {
1030   gint i, j;
1031   gboolean res;
1032 
1033   /* only NxN matrices can be identities */
1034   if (mix->in_channels != mix->out_channels)
1035     return FALSE;
1036 
1037   res = TRUE;
1038 
1039   for (i = 0; i < mix->in_channels; i++) {
1040     for (j = 0; j < mix->out_channels; j++) {
1041       if ((i == j && mix->matrix[i][j] != 1.0f) ||
1042           (i != j && mix->matrix[i][j] != 0.0f)) {
1043         res = FALSE;
1044         break;
1045       }
1046     }
1047   }
1048 
1049   return res;
1050 }
1051 
1052 /**
1053  * gst_audio_channel_mixer_samples:
1054  * @mix: a #GstAudioChannelMixer
1055  * @in: input samples
1056  * @out: output samples
1057  * @samples: number of samples
1058  *
1059  * In case the samples are interleaved, @in and @out must point to an
1060  * array with a single element pointing to a block of interleaved samples.
1061  *
1062  * If non-interleaved samples are used, @in and @out must point to an
1063  * array with pointers to memory blocks, one for each channel.
1064  *
1065  * Perform channel mixing on @in_data and write the result to @out_data.
1066  * @in_data and @out_data need to be in @format and @layout.
1067  */
1068 void
gst_audio_channel_mixer_samples(GstAudioChannelMixer * mix,const gpointer in[],gpointer out[],gint samples)1069 gst_audio_channel_mixer_samples (GstAudioChannelMixer * mix,
1070     const gpointer in[], gpointer out[], gint samples)
1071 {
1072   g_return_if_fail (mix != NULL);
1073   g_return_if_fail (mix->matrix != NULL);
1074 
1075   mix->func (mix, in, out, samples);
1076 }
1077