/src/gstreamer/subprojects/gst-plugins-base/gst-libs/gst/audio/audio-channel-mixer.c
Line | Count | Source |
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 * |
35 | | ensure_debug_category (void) |
36 | 0 | { |
37 | 0 | static gsize cat_gonce = 0; |
38 | |
|
39 | 0 | if (g_once_init_enter (&cat_gonce)) { |
40 | 0 | gsize cat_done; |
41 | |
|
42 | 0 | cat_done = (gsize) _gst_debug_category_new ("audio-channel-mixer", 0, |
43 | 0 | "audio-channel-mixer object"); |
44 | |
|
45 | 0 | g_once_init_leave (&cat_gonce, cat_done); |
46 | 0 | } |
47 | |
|
48 | 0 | return (GstDebugCategory *) cat_gonce; |
49 | 0 | } |
50 | | #else |
51 | | #define ensure_debug_category() /* NOOP */ |
52 | | #endif /* GST_DISABLE_GST_DEBUG */ |
53 | | |
54 | | |
55 | 0 | #define PRECISION_INT 10 |
56 | 0 | #define NONZERO_DENSITY_THRESHOLD 0.5 |
57 | | |
58 | | typedef void (*MixerFunc) (GstAudioChannelMixer * mix, const gpointer src[], |
59 | | gpointer dst[], gint samples); |
60 | | |
61 | | typedef struct _MixOutEntry |
62 | | { |
63 | | guint index; |
64 | | guint offset; |
65 | | guint count; |
66 | | } MixOutEntry; |
67 | | |
68 | | typedef struct _MixEntry |
69 | | { |
70 | | guint index; |
71 | | gfloat coeff; |
72 | | gint coeff_int; |
73 | | } MixEntry; |
74 | | |
75 | | struct _GstAudioChannelMixer |
76 | | { |
77 | | gint in_channels; |
78 | | gint out_channels; |
79 | | |
80 | | const GstAudioFormatInfo *finfo; |
81 | | |
82 | | /* channel conversion matrix, m[in_channels][out_channels]. |
83 | | * If identity matrix, passthrough applies. */ |
84 | | gfloat **matrix; |
85 | | |
86 | | /* channel conversion matrix with int values, m[in_channels][out_channels]. |
87 | | * this is matrix * (2^10) as integers */ |
88 | | gint **matrix_int; |
89 | | |
90 | | MixOutEntry *out_entry; |
91 | | MixEntry *entry; |
92 | | |
93 | | guint num_valid_out_ch; |
94 | | gboolean non_interleaved; |
95 | | gsize clear_unit; |
96 | | |
97 | | MixerFunc func; |
98 | | }; |
99 | | |
100 | | /** |
101 | | * gst_audio_channel_mixer_free: |
102 | | * @mix: a #GstAudioChannelMixer |
103 | | * |
104 | | * Free memory allocated by @mix. |
105 | | */ |
106 | | void |
107 | | gst_audio_channel_mixer_free (GstAudioChannelMixer * mix) |
108 | 0 | { |
109 | 0 | gint i; |
110 | | |
111 | | /* free */ |
112 | 0 | for (i = 0; i < mix->in_channels; i++) |
113 | 0 | g_free (mix->matrix[i]); |
114 | 0 | g_free (mix->matrix); |
115 | |
|
116 | 0 | if (mix->matrix_int) { |
117 | 0 | for (i = 0; i < mix->in_channels; i++) |
118 | 0 | g_free (mix->matrix_int[i]); |
119 | 0 | g_free (mix->matrix_int); |
120 | 0 | } |
121 | |
|
122 | 0 | g_free (mix->out_entry); |
123 | 0 | g_free (mix->entry); |
124 | |
|
125 | 0 | g_free (mix); |
126 | 0 | } |
127 | | |
128 | | /* |
129 | | * Detect and fill in identical channels. E.g. |
130 | | * forward the left/right front channels in a |
131 | | * 5.1 to 2.0 conversion. |
132 | | */ |
133 | | |
134 | | static void |
135 | | gst_audio_channel_mixer_fill_identical (gfloat ** matrix, |
136 | | gint in_channels, GstAudioChannelPosition * in_position, gint out_channels, |
137 | | GstAudioChannelPosition * out_position, GstAudioChannelMixerFlags flags) |
138 | 0 | { |
139 | 0 | gint ci, co; |
140 | | |
141 | | /* Apart from the compatible channel assignments, we can also have |
142 | | * same channel assignments. This is much simpler, we simply copy |
143 | | * the value from source to dest! */ |
144 | 0 | for (co = 0; co < out_channels; co++) { |
145 | | /* find a channel in input with same position */ |
146 | 0 | for (ci = 0; ci < in_channels; ci++) { |
147 | | /* If the input was unpositioned, we're simply building |
148 | | * an identity matrix */ |
149 | 0 | if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_UNPOSITIONED_IN) { |
150 | 0 | matrix[ci][co] = ci == co ? 1.0 : 0.0; |
151 | 0 | } else if (in_position[ci] == out_position[co]) { |
152 | 0 | matrix[ci][co] = 1.0; |
153 | 0 | } |
154 | 0 | } |
155 | 0 | } |
156 | 0 | } |
157 | | |
158 | | /* |
159 | | * Detect and fill in compatible channels. E.g. |
160 | | * forward left/right front to mono (or the other |
161 | | * way around) when going from 2.0 to 1.0. |
162 | | */ |
163 | | |
164 | | static void |
165 | | gst_audio_channel_mixer_fill_compatible (gfloat ** matrix, gint in_channels, |
166 | | GstAudioChannelPosition * in_position, gint out_channels, |
167 | | GstAudioChannelPosition * out_position) |
168 | 0 | { |
169 | | /* Conversions from one-channel to compatible two-channel configs */ |
170 | 0 | struct |
171 | 0 | { |
172 | 0 | GstAudioChannelPosition pos1[2]; |
173 | 0 | GstAudioChannelPosition pos2[1]; |
174 | 0 | } conv[] = { |
175 | | /* front: mono <-> stereo */ |
176 | 0 | {{ |
177 | 0 | GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, |
178 | 0 | GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, { |
179 | 0 | GST_AUDIO_CHANNEL_POSITION_MONO}}, |
180 | | /* front center: 2 <-> 1 */ |
181 | 0 | {{ |
182 | 0 | GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, |
183 | 0 | GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER}, { |
184 | 0 | GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER}}, |
185 | | /* rear: 2 <-> 1 */ |
186 | 0 | {{ |
187 | 0 | GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, |
188 | 0 | GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}, { |
189 | 0 | GST_AUDIO_CHANNEL_POSITION_REAR_CENTER}}, {{ |
190 | 0 | GST_AUDIO_CHANNEL_POSITION_INVALID}} |
191 | 0 | }; |
192 | 0 | gint c; |
193 | | |
194 | | /* conversions from compatible (but not the same) channel schemes */ |
195 | 0 | for (c = 0; conv[c].pos1[0] != GST_AUDIO_CHANNEL_POSITION_INVALID; c++) { |
196 | 0 | gint pos1_0 = -1, pos1_1 = -1, pos1_2 = -1; |
197 | 0 | gint pos2_0 = -1, pos2_1 = -1, pos2_2 = -1; |
198 | 0 | gint n; |
199 | |
|
200 | 0 | for (n = 0; n < in_channels; n++) { |
201 | 0 | if (in_position[n] == conv[c].pos1[0]) |
202 | 0 | pos1_0 = n; |
203 | 0 | else if (in_position[n] == conv[c].pos1[1]) |
204 | 0 | pos1_1 = n; |
205 | 0 | else if (in_position[n] == conv[c].pos2[0]) |
206 | 0 | pos1_2 = n; |
207 | 0 | } |
208 | 0 | for (n = 0; n < out_channels; n++) { |
209 | 0 | if (out_position[n] == conv[c].pos1[0]) |
210 | 0 | pos2_0 = n; |
211 | 0 | else if (out_position[n] == conv[c].pos1[1]) |
212 | 0 | pos2_1 = n; |
213 | 0 | else if (out_position[n] == conv[c].pos2[0]) |
214 | 0 | pos2_2 = n; |
215 | 0 | } |
216 | | |
217 | | /* The general idea here is to fill in channels from the same position |
218 | | * as good as possible. This means mixing left<->center and right<->center. |
219 | | */ |
220 | | |
221 | | /* left -> center */ |
222 | 0 | if (pos1_0 != -1 && pos1_2 == -1 && pos2_0 == -1 && pos2_2 != -1) |
223 | 0 | matrix[pos1_0][pos2_2] = 1.0; |
224 | 0 | else if (pos1_0 != -1 && pos1_2 != -1 && pos2_0 == -1 && pos2_2 != -1) |
225 | 0 | matrix[pos1_0][pos2_2] = 0.5; |
226 | 0 | else if (pos1_0 != -1 && pos1_2 == -1 && pos2_0 != -1 && pos2_2 != -1) |
227 | 0 | matrix[pos1_0][pos2_2] = 1.0; |
228 | | |
229 | | /* right -> center */ |
230 | 0 | if (pos1_1 != -1 && pos1_2 == -1 && pos2_1 == -1 && pos2_2 != -1) |
231 | 0 | matrix[pos1_1][pos2_2] = 1.0; |
232 | 0 | else if (pos1_1 != -1 && pos1_2 != -1 && pos2_1 == -1 && pos2_2 != -1) |
233 | 0 | matrix[pos1_1][pos2_2] = 0.5; |
234 | 0 | else if (pos1_1 != -1 && pos1_2 == -1 && pos2_1 != -1 && pos2_2 != -1) |
235 | 0 | matrix[pos1_1][pos2_2] = 1.0; |
236 | | |
237 | | /* center -> left */ |
238 | 0 | if (pos1_2 != -1 && pos1_0 == -1 && pos2_2 == -1 && pos2_0 != -1) |
239 | 0 | matrix[pos1_2][pos2_0] = 1.0; |
240 | 0 | else if (pos1_2 != -1 && pos1_0 != -1 && pos2_2 == -1 && pos2_0 != -1) |
241 | 0 | matrix[pos1_2][pos2_0] = 0.5; |
242 | 0 | else if (pos1_2 != -1 && pos1_0 == -1 && pos2_2 != -1 && pos2_0 != -1) |
243 | 0 | matrix[pos1_2][pos2_0] = 1.0; |
244 | | |
245 | | /* center -> right */ |
246 | 0 | if (pos1_2 != -1 && pos1_1 == -1 && pos2_2 == -1 && pos2_1 != -1) |
247 | 0 | matrix[pos1_2][pos2_1] = 1.0; |
248 | 0 | else if (pos1_2 != -1 && pos1_1 != -1 && pos2_2 == -1 && pos2_1 != -1) |
249 | 0 | matrix[pos1_2][pos2_1] = 0.5; |
250 | 0 | else if (pos1_2 != -1 && pos1_1 == -1 && pos2_2 != -1 && pos2_1 != -1) |
251 | 0 | matrix[pos1_2][pos2_1] = 1.0; |
252 | 0 | } |
253 | 0 | } |
254 | | |
255 | | /* |
256 | | * Detect and fill in channels not handled by the |
257 | | * above two, e.g. center to left/right front in |
258 | | * 5.1 to 2.0 (or the other way around). |
259 | | * |
260 | | * Unfortunately, limited to static conversions |
261 | | * for now. |
262 | | */ |
263 | | |
264 | | static void |
265 | | gst_audio_channel_mixer_detect_pos (gint channels, |
266 | | GstAudioChannelPosition position[64], gint * f, gboolean * has_f, gint * c, |
267 | | gboolean * has_c, gint * r, gboolean * has_r, gint * s, gboolean * has_s, |
268 | | gint * b, gboolean * has_b) |
269 | 0 | { |
270 | 0 | gint n; |
271 | |
|
272 | 0 | for (n = 0; n < channels; n++) { |
273 | 0 | switch (position[n]) { |
274 | 0 | case GST_AUDIO_CHANNEL_POSITION_MONO: |
275 | 0 | f[1] = n; |
276 | 0 | *has_f = TRUE; |
277 | 0 | break; |
278 | 0 | case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT: |
279 | 0 | f[0] = n; |
280 | 0 | *has_f = TRUE; |
281 | 0 | break; |
282 | 0 | case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT: |
283 | 0 | f[2] = n; |
284 | 0 | *has_f = TRUE; |
285 | 0 | break; |
286 | 0 | case GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER: |
287 | 0 | c[1] = n; |
288 | 0 | *has_c = TRUE; |
289 | 0 | break; |
290 | 0 | case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: |
291 | 0 | c[0] = n; |
292 | 0 | *has_c = TRUE; |
293 | 0 | break; |
294 | 0 | case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: |
295 | 0 | c[2] = n; |
296 | 0 | *has_c = TRUE; |
297 | 0 | break; |
298 | 0 | case GST_AUDIO_CHANNEL_POSITION_REAR_CENTER: |
299 | 0 | r[1] = n; |
300 | 0 | *has_r = TRUE; |
301 | 0 | break; |
302 | 0 | case GST_AUDIO_CHANNEL_POSITION_REAR_LEFT: |
303 | 0 | r[0] = n; |
304 | 0 | *has_r = TRUE; |
305 | 0 | break; |
306 | 0 | case GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT: |
307 | 0 | r[2] = n; |
308 | 0 | *has_r = TRUE; |
309 | 0 | break; |
310 | 0 | case GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT: |
311 | 0 | s[0] = n; |
312 | 0 | *has_s = TRUE; |
313 | 0 | break; |
314 | 0 | case GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT: |
315 | 0 | s[2] = n; |
316 | 0 | *has_s = TRUE; |
317 | 0 | break; |
318 | 0 | case GST_AUDIO_CHANNEL_POSITION_LFE1: |
319 | 0 | *has_b = TRUE; |
320 | 0 | b[1] = n; |
321 | 0 | break; |
322 | 0 | default: |
323 | 0 | break; |
324 | 0 | } |
325 | 0 | } |
326 | 0 | } |
327 | | |
328 | | static void |
329 | | gst_audio_channel_mixer_fill_one_other (gfloat ** matrix, |
330 | | gint * from_idx, gint * to_idx, gfloat ratio) |
331 | 0 | { |
332 | | |
333 | | /* src & dst have center => passthrough */ |
334 | 0 | if (from_idx[1] != -1 && to_idx[1] != -1) { |
335 | 0 | matrix[from_idx[1]][to_idx[1]] = ratio; |
336 | 0 | } |
337 | | |
338 | | /* src & dst have left => passthrough */ |
339 | 0 | if (from_idx[0] != -1 && to_idx[0] != -1) { |
340 | 0 | matrix[from_idx[0]][to_idx[0]] = ratio; |
341 | 0 | } |
342 | | |
343 | | /* src & dst have right => passthrough */ |
344 | 0 | if (from_idx[2] != -1 && to_idx[2] != -1) { |
345 | 0 | matrix[from_idx[2]][to_idx[2]] = ratio; |
346 | 0 | } |
347 | | |
348 | | /* src has left & dst has center => put into center */ |
349 | 0 | if (from_idx[0] != -1 && to_idx[1] != -1 && from_idx[1] != -1) { |
350 | 0 | matrix[from_idx[0]][to_idx[1]] = 0.5 * ratio; |
351 | 0 | } else if (from_idx[0] != -1 && to_idx[1] != -1 && from_idx[1] == -1) { |
352 | 0 | matrix[from_idx[0]][to_idx[1]] = ratio; |
353 | 0 | } |
354 | | |
355 | | /* src has right & dst has center => put into center */ |
356 | 0 | if (from_idx[2] != -1 && to_idx[1] != -1 && from_idx[1] != -1) { |
357 | 0 | matrix[from_idx[2]][to_idx[1]] = 0.5 * ratio; |
358 | 0 | } else if (from_idx[2] != -1 && to_idx[1] != -1 && from_idx[1] == -1) { |
359 | 0 | matrix[from_idx[2]][to_idx[1]] = ratio; |
360 | 0 | } |
361 | | |
362 | | /* src has center & dst has left => passthrough */ |
363 | 0 | if (from_idx[1] != -1 && to_idx[0] != -1 && from_idx[0] != -1) { |
364 | 0 | matrix[from_idx[1]][to_idx[0]] = 0.5 * ratio; |
365 | 0 | } else if (from_idx[1] != -1 && to_idx[0] != -1 && from_idx[0] == -1) { |
366 | 0 | matrix[from_idx[1]][to_idx[0]] = ratio; |
367 | 0 | } |
368 | | |
369 | | /* src has center & dst has right => passthrough */ |
370 | 0 | if (from_idx[1] != -1 && to_idx[2] != -1 && from_idx[2] != -1) { |
371 | 0 | matrix[from_idx[1]][to_idx[2]] = 0.5 * ratio; |
372 | 0 | } else if (from_idx[1] != -1 && to_idx[2] != -1 && from_idx[2] == -1) { |
373 | 0 | matrix[from_idx[1]][to_idx[2]] = ratio; |
374 | 0 | } |
375 | 0 | } |
376 | | |
377 | 0 | #define RATIO_CENTER_FRONT (1.0 / sqrt (2.0)) |
378 | 0 | #define RATIO_CENTER_SIDE (1.0 / 2.0) |
379 | 0 | #define RATIO_CENTER_REAR (1.0 / sqrt (8.0)) |
380 | | |
381 | | #define RATIO_FRONT_CENTER (1.0 / sqrt (2.0)) |
382 | 0 | #define RATIO_FRONT_SIDE (1.0 / sqrt (2.0)) |
383 | 0 | #define RATIO_FRONT_REAR (1.0 / 2.0) |
384 | | |
385 | | #define RATIO_SIDE_CENTER (1.0 / 2.0) |
386 | | #define RATIO_SIDE_FRONT (1.0 / sqrt (2.0)) |
387 | 0 | #define RATIO_SIDE_REAR (1.0 / sqrt (2.0)) |
388 | | |
389 | 0 | #define RATIO_CENTER_BASS (1.0 / sqrt (2.0)) |
390 | 0 | #define RATIO_FRONT_BASS (1.0) |
391 | 0 | #define RATIO_SIDE_BASS (1.0 / sqrt (2.0)) |
392 | 0 | #define RATIO_REAR_BASS (1.0 / sqrt (2.0)) |
393 | | |
394 | | static void |
395 | | gst_audio_channel_mixer_fill_others (gfloat ** matrix, gint in_channels, |
396 | | GstAudioChannelPosition * in_position, gint out_channels, |
397 | | GstAudioChannelPosition * out_position) |
398 | 0 | { |
399 | 0 | gboolean in_has_front = FALSE, out_has_front = FALSE, |
400 | 0 | in_has_center = FALSE, out_has_center = FALSE, |
401 | 0 | in_has_rear = FALSE, out_has_rear = FALSE, |
402 | 0 | in_has_side = FALSE, out_has_side = FALSE, |
403 | 0 | in_has_bass = FALSE, out_has_bass = FALSE; |
404 | | /* LEFT, RIGHT, MONO */ |
405 | 0 | gint in_f[3] = { -1, -1, -1 }; |
406 | 0 | gint out_f[3] = { -1, -1, -1 }; |
407 | | /* LOC, ROC, CENTER */ |
408 | 0 | gint in_c[3] = { -1, -1, -1 }; |
409 | 0 | gint out_c[3] = { -1, -1, -1 }; |
410 | | /* RLEFT, RRIGHT, RCENTER */ |
411 | 0 | gint in_r[3] = { -1, -1, -1 }; |
412 | 0 | gint out_r[3] = { -1, -1, -1 }; |
413 | | /* SLEFT, INVALID, SRIGHT */ |
414 | 0 | gint in_s[3] = { -1, -1, -1 }; |
415 | 0 | gint out_s[3] = { -1, -1, -1 }; |
416 | | /* INVALID, LFE, INVALID */ |
417 | 0 | gint in_b[3] = { -1, -1, -1 }; |
418 | 0 | gint out_b[3] = { -1, -1, -1 }; |
419 | | |
420 | | /* First see where (if at all) the various channels from/to |
421 | | * which we want to convert are located in our matrix/array. */ |
422 | 0 | gst_audio_channel_mixer_detect_pos (in_channels, in_position, |
423 | 0 | in_f, &in_has_front, |
424 | 0 | in_c, &in_has_center, in_r, &in_has_rear, |
425 | 0 | in_s, &in_has_side, in_b, &in_has_bass); |
426 | 0 | gst_audio_channel_mixer_detect_pos (out_channels, out_position, |
427 | 0 | out_f, &out_has_front, |
428 | 0 | out_c, &out_has_center, out_r, &out_has_rear, |
429 | 0 | out_s, &out_has_side, out_b, &out_has_bass); |
430 | | |
431 | | /* The general idea here is: |
432 | | * - if the source has a channel that the destination doesn't have mix |
433 | | * it into the nearest available destination channel |
434 | | * - if the destination has a channel that the source doesn't have mix |
435 | | * the nearest source channel into the destination channel |
436 | | * |
437 | | * The ratio for the mixing becomes lower as the distance between the |
438 | | * channels gets larger |
439 | | */ |
440 | | |
441 | | /* center <-> front/side/rear */ |
442 | 0 | if (!in_has_center && in_has_front && out_has_center) { |
443 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_f, out_c, |
444 | 0 | RATIO_CENTER_FRONT); |
445 | 0 | } else if (!in_has_center && !in_has_front && in_has_side && out_has_center) { |
446 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_c, |
447 | 0 | RATIO_CENTER_SIDE); |
448 | 0 | } else if (!in_has_center && !in_has_front && !in_has_side && in_has_rear |
449 | 0 | && out_has_center) { |
450 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_r, out_c, |
451 | 0 | RATIO_CENTER_REAR); |
452 | 0 | } else if (in_has_center && !out_has_center && out_has_front) { |
453 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_c, out_f, |
454 | 0 | RATIO_CENTER_FRONT); |
455 | 0 | } else if (in_has_center && !out_has_center && !out_has_front && out_has_side) { |
456 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_c, out_s, |
457 | 0 | RATIO_CENTER_SIDE); |
458 | 0 | } else if (in_has_center && !out_has_center && !out_has_front && !out_has_side |
459 | 0 | && out_has_rear) { |
460 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_c, out_r, |
461 | 0 | RATIO_CENTER_REAR); |
462 | 0 | } |
463 | | |
464 | | /* front <-> center/side/rear */ |
465 | 0 | if (!in_has_front && in_has_center && !in_has_side && out_has_front) { |
466 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_c, out_f, |
467 | 0 | RATIO_CENTER_FRONT); |
468 | 0 | } else if (!in_has_front && !in_has_center && in_has_side && out_has_front) { |
469 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_f, |
470 | 0 | RATIO_FRONT_SIDE); |
471 | 0 | } else if (!in_has_front && in_has_center && in_has_side && out_has_front) { |
472 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_c, out_f, |
473 | 0 | 0.5 * RATIO_CENTER_FRONT); |
474 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_f, |
475 | 0 | 0.5 * RATIO_FRONT_SIDE); |
476 | 0 | } else if (!in_has_front && !in_has_center && !in_has_side && in_has_rear |
477 | 0 | && out_has_front) { |
478 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_r, out_f, |
479 | 0 | RATIO_FRONT_REAR); |
480 | 0 | } else if (in_has_front && out_has_center && !out_has_side && !out_has_front) { |
481 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, |
482 | 0 | in_f, out_c, RATIO_CENTER_FRONT); |
483 | 0 | } else if (in_has_front && !out_has_center && out_has_side && !out_has_front) { |
484 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_f, out_s, |
485 | 0 | RATIO_FRONT_SIDE); |
486 | 0 | } else if (in_has_front && out_has_center && out_has_side && !out_has_front) { |
487 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_f, out_c, |
488 | 0 | 0.5 * RATIO_CENTER_FRONT); |
489 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_f, out_s, |
490 | 0 | 0.5 * RATIO_FRONT_SIDE); |
491 | 0 | } else if (in_has_front && !out_has_center && !out_has_side && !out_has_front |
492 | 0 | && out_has_rear) { |
493 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_f, out_r, |
494 | 0 | RATIO_FRONT_REAR); |
495 | 0 | } |
496 | | |
497 | | /* side <-> center/front/rear */ |
498 | 0 | if (!in_has_side && in_has_front && !in_has_rear && out_has_side) { |
499 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_f, out_s, |
500 | 0 | RATIO_FRONT_SIDE); |
501 | 0 | } else if (!in_has_side && !in_has_front && in_has_rear && out_has_side) { |
502 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_r, out_s, |
503 | 0 | RATIO_SIDE_REAR); |
504 | 0 | } else if (!in_has_side && in_has_front && in_has_rear && out_has_side) { |
505 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_f, out_s, |
506 | 0 | 0.5 * RATIO_FRONT_SIDE); |
507 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_r, out_s, |
508 | 0 | 0.5 * RATIO_SIDE_REAR); |
509 | 0 | } else if (!in_has_side && !in_has_front && !in_has_rear && in_has_center |
510 | 0 | && out_has_side) { |
511 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_c, out_s, |
512 | 0 | RATIO_CENTER_SIDE); |
513 | 0 | } else if (in_has_side && out_has_front && !out_has_rear && !out_has_side) { |
514 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_f, |
515 | 0 | RATIO_FRONT_SIDE); |
516 | 0 | } else if (in_has_side && !out_has_front && out_has_rear && !out_has_side) { |
517 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_r, |
518 | 0 | RATIO_SIDE_REAR); |
519 | 0 | } else if (in_has_side && out_has_front && out_has_rear && !out_has_side) { |
520 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_f, |
521 | 0 | 0.5 * RATIO_FRONT_SIDE); |
522 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_r, |
523 | 0 | 0.5 * RATIO_SIDE_REAR); |
524 | 0 | } else if (in_has_side && !out_has_front && !out_has_rear && out_has_center |
525 | 0 | && !out_has_side) { |
526 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_c, |
527 | 0 | RATIO_CENTER_SIDE); |
528 | 0 | } |
529 | | |
530 | | /* rear <-> center/front/side */ |
531 | 0 | if (!in_has_rear && in_has_side && out_has_rear) { |
532 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_r, |
533 | 0 | RATIO_SIDE_REAR); |
534 | 0 | } else if (!in_has_rear && !in_has_side && in_has_front && out_has_rear) { |
535 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_f, out_r, |
536 | 0 | RATIO_FRONT_REAR); |
537 | 0 | } else if (!in_has_rear && !in_has_side && !in_has_front && in_has_center |
538 | 0 | && out_has_rear) { |
539 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_c, out_r, |
540 | 0 | RATIO_CENTER_REAR); |
541 | 0 | } else if (in_has_rear && !out_has_rear && out_has_side) { |
542 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_r, out_s, |
543 | 0 | RATIO_SIDE_REAR); |
544 | 0 | } else if (in_has_rear && !out_has_rear && !out_has_side && out_has_front) { |
545 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_r, out_f, |
546 | 0 | RATIO_FRONT_REAR); |
547 | 0 | } else if (in_has_rear && !out_has_rear && !out_has_side && !out_has_front |
548 | 0 | && out_has_center) { |
549 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_r, out_c, |
550 | 0 | RATIO_CENTER_REAR); |
551 | 0 | } |
552 | | |
553 | | /* bass <-> any */ |
554 | 0 | if (in_has_bass && !out_has_bass) { |
555 | 0 | if (out_has_center) { |
556 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_b, out_c, |
557 | 0 | RATIO_CENTER_BASS); |
558 | 0 | } |
559 | 0 | if (out_has_front) { |
560 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_b, out_f, |
561 | 0 | RATIO_FRONT_BASS); |
562 | 0 | } |
563 | 0 | if (out_has_side) { |
564 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_b, out_s, |
565 | 0 | RATIO_SIDE_BASS); |
566 | 0 | } |
567 | 0 | if (out_has_rear) { |
568 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_b, out_r, |
569 | 0 | RATIO_REAR_BASS); |
570 | 0 | } |
571 | 0 | } else if (!in_has_bass && out_has_bass) { |
572 | 0 | if (in_has_center) { |
573 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_c, out_b, |
574 | 0 | RATIO_CENTER_BASS); |
575 | 0 | } |
576 | 0 | if (in_has_front) { |
577 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_f, out_b, |
578 | 0 | RATIO_FRONT_BASS); |
579 | 0 | } |
580 | 0 | if (in_has_side) { |
581 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_s, out_b, |
582 | 0 | RATIO_REAR_BASS); |
583 | 0 | } |
584 | 0 | if (in_has_rear) { |
585 | 0 | gst_audio_channel_mixer_fill_one_other (matrix, in_r, out_b, |
586 | 0 | RATIO_REAR_BASS); |
587 | 0 | } |
588 | 0 | } |
589 | 0 | } |
590 | | |
591 | | /* |
592 | | * Normalize output values. |
593 | | */ |
594 | | |
595 | | static void |
596 | | gst_audio_channel_mixer_fill_normalize (gfloat ** matrix, gint in_channels, |
597 | | gint out_channels) |
598 | 0 | { |
599 | 0 | gfloat sum, top = 0; |
600 | 0 | gint i, j; |
601 | |
|
602 | 0 | for (j = 0; j < out_channels; j++) { |
603 | | /* calculate sum */ |
604 | 0 | sum = 0.0; |
605 | 0 | for (i = 0; i < in_channels; i++) { |
606 | 0 | sum += fabs (matrix[i][j]); |
607 | 0 | } |
608 | 0 | if (sum > top) { |
609 | 0 | top = sum; |
610 | 0 | } |
611 | 0 | } |
612 | | |
613 | | /* normalize to mix */ |
614 | 0 | if (top == 0.0) |
615 | 0 | return; |
616 | | |
617 | 0 | for (j = 0; j < out_channels; j++) { |
618 | 0 | for (i = 0; i < in_channels; i++) { |
619 | 0 | matrix[i][j] /= top; |
620 | 0 | } |
621 | 0 | } |
622 | 0 | } |
623 | | |
624 | | static gboolean |
625 | | gst_audio_channel_mixer_fill_special (gfloat ** matrix, gint in_channels, |
626 | | GstAudioChannelPosition * in_position, gint out_channels, |
627 | | GstAudioChannelPosition * out_position) |
628 | 0 | { |
629 | | /* Special, standard conversions here */ |
630 | | |
631 | | /* Mono<->Stereo, just a fast-path */ |
632 | 0 | if (in_channels == 2 && out_channels == 1 && |
633 | 0 | ((in_position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT && |
634 | 0 | in_position[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT) || |
635 | 0 | (in_position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT && |
636 | 0 | in_position[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT)) && |
637 | 0 | out_position[0] == GST_AUDIO_CHANNEL_POSITION_MONO) { |
638 | 0 | matrix[0][0] = 0.5; |
639 | 0 | matrix[1][0] = 0.5; |
640 | 0 | return TRUE; |
641 | 0 | } else if (in_channels == 1 && out_channels == 2 && |
642 | 0 | ((out_position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT && |
643 | 0 | out_position[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT) || |
644 | 0 | (out_position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT && |
645 | 0 | out_position[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT)) && |
646 | 0 | in_position[0] == GST_AUDIO_CHANNEL_POSITION_MONO) { |
647 | 0 | matrix[0][0] = 1.0; |
648 | 0 | matrix[0][1] = 1.0; |
649 | 0 | return TRUE; |
650 | 0 | } |
651 | | |
652 | | /* TODO: 5.1 <-> Stereo and other standard conversions */ |
653 | | |
654 | 0 | return FALSE; |
655 | 0 | } |
656 | | |
657 | | /* |
658 | | * Automagically generate conversion matrix. |
659 | | */ |
660 | | |
661 | | typedef enum |
662 | | { |
663 | | GST_AUDIO_CHANNEL_MIXER_VIRTUAL_INPUT_NONE = 0, |
664 | | GST_AUDIO_CHANNEL_MIXER_VIRTUAL_INPUT_MONO, |
665 | | GST_AUDIO_CHANNEL_MIXER_VIRTUAL_INPUT_STEREO |
666 | | } GstAudioChannelMixerVirtualInput; |
667 | | |
668 | | /* Detects specific input channels configurations introduced in the |
669 | | * audioconvert element (since version 1.26) with the |
670 | | * `GstAudioConvertInputChannelsReorder` configurations. |
671 | | * |
672 | | * If all input channels are positioned to GST_AUDIO_CHANNEL_POSITION_MONO, |
673 | | * the automatic mixing matrix should be configured like if there was only one |
674 | | * virtual input mono channel. This virtual mono channel is the mix of all the |
675 | | * real mono channels. |
676 | | * |
677 | | * If all input channels with an even index are positioned to |
678 | | * GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT and all input channels with an odd |
679 | | * index are positioned to GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, then the |
680 | | * automatic mixing matrix should be configured like if there were only one |
681 | | * virtual input left channel and one virtual input right channel. This virtual |
682 | | * left or right channel is the mix of all the real left or right channels. |
683 | | */ |
684 | | static gboolean |
685 | | gst_audio_channel_mixer_detect_virtual_input_channels (gint channels, |
686 | | GstAudioChannelPosition * position, |
687 | | GstAudioChannelMixerVirtualInput * virtual_input) |
688 | 0 | { |
689 | 0 | g_return_val_if_fail (position != NULL, FALSE); |
690 | 0 | g_return_val_if_fail (virtual_input != NULL, FALSE); |
691 | | |
692 | 0 | *virtual_input = GST_AUDIO_CHANNEL_MIXER_VIRTUAL_INPUT_NONE; |
693 | |
|
694 | 0 | if (channels < 2) |
695 | 0 | return FALSE; |
696 | | |
697 | 0 | static const GstAudioChannelPosition alternate_positions[2] = |
698 | 0 | { GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, |
699 | 0 | GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT |
700 | 0 | }; |
701 | |
|
702 | 0 | gboolean is_mono = TRUE; |
703 | 0 | gboolean is_alternate = TRUE; |
704 | 0 | for (gint i = 0; i < channels; ++i) { |
705 | 0 | if (position[i] != GST_AUDIO_CHANNEL_POSITION_MONO) |
706 | 0 | is_mono = FALSE; |
707 | |
|
708 | 0 | if (position[i] != alternate_positions[i % 2]) |
709 | 0 | is_alternate = FALSE; |
710 | |
|
711 | 0 | if (!is_mono && !is_alternate) |
712 | 0 | return FALSE; |
713 | 0 | } |
714 | | |
715 | 0 | if (is_mono) { |
716 | 0 | g_assert (!is_alternate); |
717 | 0 | *virtual_input = GST_AUDIO_CHANNEL_MIXER_VIRTUAL_INPUT_MONO; |
718 | 0 | return TRUE; |
719 | 0 | } |
720 | | |
721 | 0 | if (is_alternate && (channels > 2)) { |
722 | 0 | g_assert (!is_mono); |
723 | 0 | *virtual_input = GST_AUDIO_CHANNEL_MIXER_VIRTUAL_INPUT_STEREO; |
724 | 0 | return TRUE; |
725 | 0 | } |
726 | | |
727 | 0 | return FALSE; |
728 | 0 | } |
729 | | |
730 | | static void |
731 | | gst_audio_channel_mixer_fill_matrix (gfloat ** matrix, |
732 | | GstAudioChannelMixerFlags flags, gint in_channels, |
733 | | GstAudioChannelPosition * in_position, gint out_channels, |
734 | | GstAudioChannelPosition * out_position) |
735 | 0 | { |
736 | 0 | if (gst_audio_channel_mixer_fill_special (matrix, in_channels, in_position, |
737 | 0 | out_channels, out_position)) |
738 | 0 | return; |
739 | | |
740 | | /* If all input channels are positioned to mono, the mix matrix should be |
741 | | * configured like if there was only one virtual input mono channel. This |
742 | | * virtual mono channel is the mix of all the real input mono channels. |
743 | | * |
744 | | * If all input channels are positioned to left and right alternately, the mix |
745 | | * matrix should be configured like if there were only two virtual input |
746 | | * channels: one left and one right. This virtual left or right channel is the |
747 | | * mix of all the real input left or right channels. |
748 | | */ |
749 | 0 | gint in_size = in_channels; |
750 | 0 | GstAudioChannelMixerVirtualInput virtual_input = |
751 | 0 | GST_AUDIO_CHANNEL_MIXER_VIRTUAL_INPUT_NONE; |
752 | 0 | if (gst_audio_channel_mixer_detect_virtual_input_channels (in_size, |
753 | 0 | in_position, &virtual_input)) { |
754 | 0 | switch (virtual_input) { |
755 | 0 | case GST_AUDIO_CHANNEL_MIXER_VIRTUAL_INPUT_MONO: |
756 | 0 | in_size = 1; |
757 | 0 | break; |
758 | 0 | case GST_AUDIO_CHANNEL_MIXER_VIRTUAL_INPUT_STEREO: |
759 | 0 | in_size = 2; |
760 | 0 | break; |
761 | 0 | default: |
762 | 0 | break; |
763 | 0 | } |
764 | 0 | } |
765 | | |
766 | 0 | gst_audio_channel_mixer_fill_identical (matrix, in_size, in_position, |
767 | 0 | out_channels, out_position, flags); |
768 | |
|
769 | 0 | if (!(flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_UNPOSITIONED_IN)) { |
770 | 0 | gst_audio_channel_mixer_fill_compatible (matrix, in_size, in_position, |
771 | 0 | out_channels, out_position); |
772 | 0 | gst_audio_channel_mixer_fill_others (matrix, in_size, in_position, |
773 | 0 | out_channels, out_position); |
774 | 0 | gst_audio_channel_mixer_fill_normalize (matrix, in_size, out_channels); |
775 | 0 | } |
776 | |
|
777 | 0 | switch (virtual_input) { |
778 | 0 | case GST_AUDIO_CHANNEL_MIXER_VIRTUAL_INPUT_MONO:{ |
779 | 0 | for (gint out = 0; out < out_channels; ++out) |
780 | 0 | matrix[0][out] /= in_channels; |
781 | |
|
782 | 0 | for (gint in = 1; in < in_channels; ++in) |
783 | 0 | memcpy (matrix[in], matrix[0], out_channels * sizeof (gfloat)); |
784 | |
|
785 | 0 | break; |
786 | 0 | } |
787 | | |
788 | 0 | case GST_AUDIO_CHANNEL_MIXER_VIRTUAL_INPUT_STEREO:{ |
789 | 0 | gint right_channels = in_channels >> 1; |
790 | 0 | gint left_channels = right_channels + (in_channels % 2); |
791 | |
|
792 | 0 | for (gint out = 0; out < out_channels; ++out) { |
793 | 0 | matrix[0][out] /= left_channels; |
794 | 0 | matrix[1][out] /= right_channels; |
795 | 0 | } |
796 | |
|
797 | 0 | for (gint in = 2; in < in_channels; ++in) |
798 | 0 | memcpy (matrix[in], matrix[in % 2], out_channels * sizeof (gfloat)); |
799 | |
|
800 | 0 | break; |
801 | 0 | } |
802 | | |
803 | 0 | default: |
804 | 0 | break; |
805 | 0 | } |
806 | 0 | } |
807 | | |
808 | | /* only call mix after mix->matrix is fully set up and normalized */ |
809 | | static void |
810 | | gst_audio_channel_mixer_setup_matrix_int (GstAudioChannelMixer * mix) |
811 | 0 | { |
812 | 0 | gint i, j; |
813 | 0 | gfloat tmp; |
814 | 0 | gfloat factor = (1 << PRECISION_INT); |
815 | |
|
816 | 0 | mix->matrix_int = g_new0 (gint *, mix->in_channels); |
817 | |
|
818 | 0 | for (i = 0; i < mix->in_channels; i++) { |
819 | 0 | mix->matrix_int[i] = g_new (gint, mix->out_channels); |
820 | |
|
821 | 0 | for (j = 0; j < mix->out_channels; j++) { |
822 | 0 | tmp = mix->matrix[i][j] * factor; |
823 | 0 | mix->matrix_int[i][j] = (gint) tmp; |
824 | 0 | } |
825 | 0 | } |
826 | 0 | } |
827 | | |
828 | | static gfloat ** |
829 | | gst_audio_channel_mixer_setup_matrix (GstAudioChannelMixerFlags flags, |
830 | | gint in_channels, GstAudioChannelPosition * in_position, |
831 | | gint out_channels, GstAudioChannelPosition * out_position) |
832 | 0 | { |
833 | 0 | gint i, j; |
834 | 0 | gfloat **matrix = g_new0 (gfloat *, in_channels); |
835 | |
|
836 | 0 | for (i = 0; i < in_channels; i++) { |
837 | 0 | matrix[i] = g_new (gfloat, out_channels); |
838 | 0 | for (j = 0; j < out_channels; j++) |
839 | 0 | matrix[i][j] = 0.; |
840 | 0 | } |
841 | | |
842 | | /* setup the matrix' internal values */ |
843 | 0 | gst_audio_channel_mixer_fill_matrix (matrix, flags, in_channels, in_position, |
844 | 0 | out_channels, out_position); |
845 | |
|
846 | 0 | return matrix; |
847 | 0 | } |
848 | | |
849 | | #define DEFINE_GET_DATA_FUNCS(type) \ |
850 | | static inline type \ |
851 | | _get_in_data_interleaved_##type (const type * in_data[], \ |
852 | 0 | gint sample, gint channel, gint total_channels) \ |
853 | 0 | { \ |
854 | 0 | return in_data[0][sample * total_channels + channel]; \ |
855 | 0 | } \ Unexecuted instantiation: audio-channel-mixer.c:_get_in_data_interleaved_gint16 Unexecuted instantiation: audio-channel-mixer.c:_get_in_data_interleaved_gint32 Unexecuted instantiation: audio-channel-mixer.c:_get_in_data_interleaved_gfloat Unexecuted instantiation: audio-channel-mixer.c:_get_in_data_interleaved_gdouble |
856 | | \ |
857 | | static inline type * \ |
858 | | _get_out_data_interleaved_##type (type * out_data[], \ |
859 | 0 | gint sample, gint channel, gint total_channels) \ |
860 | 0 | { \ |
861 | 0 | return &out_data[0][sample * total_channels + channel]; \ |
862 | 0 | } \ Unexecuted instantiation: audio-channel-mixer.c:_get_out_data_interleaved_gint16 Unexecuted instantiation: audio-channel-mixer.c:_get_out_data_interleaved_gint32 Unexecuted instantiation: audio-channel-mixer.c:_get_out_data_interleaved_gfloat Unexecuted instantiation: audio-channel-mixer.c:_get_out_data_interleaved_gdouble |
863 | | \ |
864 | | static inline type \ |
865 | | _get_in_data_planar_##type (const type * in_data[], \ |
866 | 0 | gint sample, gint channel, gint total_channels) \ |
867 | 0 | { \ |
868 | 0 | (void) total_channels; \ |
869 | 0 | return in_data[channel][sample]; \ |
870 | 0 | } \ Unexecuted instantiation: audio-channel-mixer.c:_get_in_data_planar_gint16 Unexecuted instantiation: audio-channel-mixer.c:_get_in_data_planar_gint32 Unexecuted instantiation: audio-channel-mixer.c:_get_in_data_planar_gfloat Unexecuted instantiation: audio-channel-mixer.c:_get_in_data_planar_gdouble |
871 | | \ |
872 | | static inline type * \ |
873 | | _get_out_data_planar_##type (type * out_data[], \ |
874 | 0 | gint sample, gint channel, gint total_channels) \ |
875 | 0 | { \ |
876 | 0 | (void) total_channels; \ |
877 | 0 | return &out_data[channel][sample]; \ |
878 | 0 | } Unexecuted instantiation: audio-channel-mixer.c:_get_out_data_planar_gint16 Unexecuted instantiation: audio-channel-mixer.c:_get_out_data_planar_gint32 Unexecuted instantiation: audio-channel-mixer.c:_get_out_data_planar_gfloat Unexecuted instantiation: audio-channel-mixer.c:_get_out_data_planar_gdouble |
879 | | |
880 | | #define DEFINE_INTEGER_SPARSE_MIX_FUNC(bits, resbits, inlayout, outlayout) \ |
881 | | static void \ |
882 | | gst_audio_channel_mixer_sparse_mix_int##bits##_##inlayout##_##outlayout ( \ |
883 | | GstAudioChannelMixer * mix, const gint##bits * in_data[], \ |
884 | 0 | gint##bits * out_data[], gint samples) \ |
885 | 0 | { \ |
886 | 0 | gint in, out, n; \ |
887 | 0 | gint##resbits res; \ |
888 | 0 | gint inchannels, outchannels; \ |
889 | 0 | guint offset, count; \ |
890 | 0 | const MixOutEntry *out_entry; \ |
891 | 0 | const MixEntry *entry; \ |
892 | 0 | \ |
893 | 0 | inchannels = mix->in_channels; \ |
894 | 0 | outchannels = mix->out_channels; \ |
895 | 0 | \ |
896 | 0 | n = mix->non_interleaved ? mix->out_channels : 1; \ |
897 | 0 | for (out = 0; out < n; out++) { \ |
898 | 0 | gst_audio_format_info_fill_silence (mix->finfo, \ |
899 | 0 | out_data[out], samples * mix->clear_unit); \ |
900 | 0 | } \ |
901 | 0 | \ |
902 | 0 | for (n = 0; n < samples; n++) { \ |
903 | 0 | for (out = 0; out < mix->num_valid_out_ch; out++) { \ |
904 | 0 | /* convert */ \ |
905 | 0 | out_entry = &mix->out_entry[out]; \ |
906 | 0 | offset = out_entry->offset; \ |
907 | 0 | count = out_entry->count; \ |
908 | 0 | res = 0; \ |
909 | 0 | for (in = 0; in < count; in++) { \ |
910 | 0 | entry = &mix->entry[offset + in]; \ |
911 | 0 | res += \ |
912 | 0 | _get_in_data_##inlayout##_gint##bits (in_data, n, entry->index, inchannels) * \ |
913 | 0 | (gint##resbits) entry->coeff_int; \ |
914 | 0 | } \ |
915 | 0 | /* remove factor from int matrix */ \ |
916 | 0 | res = (res + (1 << (PRECISION_INT - 1))) >> PRECISION_INT; \ |
917 | 0 | *_get_out_data_##outlayout##_gint##bits (out_data, n, out_entry->index, outchannels) = \ |
918 | 0 | CLAMP (res, G_MININT##bits, G_MAXINT##bits); \ |
919 | 0 | } \ |
920 | 0 | } \ |
921 | 0 | } Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_sparse_mix_int16_planar_planar Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_sparse_mix_int16_planar_interleaved Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_sparse_mix_int16_interleaved_planar Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_sparse_mix_int16_interleaved_interleaved Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_sparse_mix_int32_planar_planar Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_sparse_mix_int32_planar_interleaved Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_sparse_mix_int32_interleaved_planar Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_sparse_mix_int32_interleaved_interleaved |
922 | | |
923 | | #define DEFINE_FLOAT_SPARSE_MIX_FUNC(type, inlayout, outlayout) \ |
924 | | static void \ |
925 | | gst_audio_channel_mixer_sparse_mix_##type##_##inlayout##_##outlayout ( \ |
926 | | GstAudioChannelMixer * mix, const g##type * in_data[], \ |
927 | 0 | g##type * out_data[], gint samples) \ |
928 | 0 | { \ |
929 | 0 | gint in, out, n; \ |
930 | 0 | g##type res; \ |
931 | 0 | gint inchannels, outchannels; \ |
932 | 0 | guint offset, count; \ |
933 | 0 | const MixOutEntry *out_entry; \ |
934 | 0 | const MixEntry *entry; \ |
935 | 0 | \ |
936 | 0 | inchannels = mix->in_channels; \ |
937 | 0 | outchannels = mix->out_channels; \ |
938 | 0 | \ |
939 | 0 | n = mix->non_interleaved ? mix->out_channels : 1; \ |
940 | 0 | for (out = 0; out < n; out++) { \ |
941 | 0 | gst_audio_format_info_fill_silence (mix->finfo, \ |
942 | 0 | out_data[out], samples * mix->clear_unit); \ |
943 | 0 | } \ |
944 | 0 | \ |
945 | 0 | for (n = 0; n < samples; n++) { \ |
946 | 0 | for (out = 0; out < mix->num_valid_out_ch; out++) { \ |
947 | 0 | /* convert */ \ |
948 | 0 | out_entry = &mix->out_entry[out]; \ |
949 | 0 | offset = out_entry->offset; \ |
950 | 0 | count = out_entry->count; \ |
951 | 0 | res = 0.0; \ |
952 | 0 | for (in = 0; in < count; in++) { \ |
953 | 0 | entry = &mix->entry[offset + in]; \ |
954 | 0 | res += \ |
955 | 0 | _get_in_data_##inlayout##_g##type (in_data, n, entry->index, inchannels) * \ |
956 | 0 | entry->coeff; \ |
957 | 0 | } \ |
958 | 0 | *_get_out_data_##outlayout##_g##type (out_data, n, out_entry->index, outchannels) = res; \ |
959 | 0 | } \ |
960 | 0 | } \ |
961 | 0 | } Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_sparse_mix_float_planar_planar Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_sparse_mix_float_planar_interleaved Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_sparse_mix_float_interleaved_planar Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_sparse_mix_float_interleaved_interleaved Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_sparse_mix_double_planar_planar Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_sparse_mix_double_planar_interleaved Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_sparse_mix_double_interleaved_planar Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_sparse_mix_double_interleaved_interleaved |
962 | | |
963 | | #define DEFINE_INTEGER_MIX_FUNC(bits, resbits, inlayout, outlayout) \ |
964 | | static void \ |
965 | | gst_audio_channel_mixer_mix_int##bits##_##inlayout##_##outlayout ( \ |
966 | | GstAudioChannelMixer * mix, const gint##bits * in_data[], \ |
967 | 0 | gint##bits * out_data[], gint samples) \ |
968 | 0 | { \ |
969 | 0 | gint in, out, n; \ |
970 | 0 | gint##resbits res; \ |
971 | 0 | gint inchannels, outchannels; \ |
972 | 0 | \ |
973 | 0 | inchannels = mix->in_channels; \ |
974 | 0 | outchannels = mix->out_channels; \ |
975 | 0 | \ |
976 | 0 | for (n = 0; n < samples; n++) { \ |
977 | 0 | for (out = 0; out < outchannels; out++) { \ |
978 | 0 | /* convert */ \ |
979 | 0 | res = 0; \ |
980 | 0 | for (in = 0; in < inchannels; in++) \ |
981 | 0 | res += \ |
982 | 0 | _get_in_data_##inlayout##_gint##bits (in_data, n, in, inchannels) * \ |
983 | 0 | (gint##resbits) mix->matrix_int[in][out]; \ |
984 | 0 | \ |
985 | 0 | /* remove factor from int matrix */ \ |
986 | 0 | res = (res + (1 << (PRECISION_INT - 1))) >> PRECISION_INT; \ |
987 | 0 | *_get_out_data_##outlayout##_gint##bits (out_data, n, out, outchannels) = \ |
988 | 0 | CLAMP (res, G_MININT##bits, G_MAXINT##bits); \ |
989 | 0 | } \ |
990 | 0 | } \ |
991 | 0 | } Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_mix_int16_planar_planar Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_mix_int16_planar_interleaved Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_mix_int16_interleaved_planar Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_mix_int16_interleaved_interleaved Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_mix_int32_planar_planar Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_mix_int32_planar_interleaved Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_mix_int32_interleaved_planar Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_mix_int32_interleaved_interleaved |
992 | | |
993 | | #define DEFINE_FLOAT_MIX_FUNC(type, inlayout, outlayout) \ |
994 | | static void \ |
995 | | gst_audio_channel_mixer_mix_##type##_##inlayout##_##outlayout ( \ |
996 | | GstAudioChannelMixer * mix, const g##type * in_data[], \ |
997 | 0 | g##type * out_data[], gint samples) \ |
998 | 0 | { \ |
999 | 0 | gint in, out, n; \ |
1000 | 0 | g##type res; \ |
1001 | 0 | gint inchannels, outchannels; \ |
1002 | 0 | \ |
1003 | 0 | inchannels = mix->in_channels; \ |
1004 | 0 | outchannels = mix->out_channels; \ |
1005 | 0 | \ |
1006 | 0 | for (n = 0; n < samples; n++) { \ |
1007 | 0 | for (out = 0; out < outchannels; out++) { \ |
1008 | 0 | /* convert */ \ |
1009 | 0 | res = 0.0; \ |
1010 | 0 | for (in = 0; in < inchannels; in++) \ |
1011 | 0 | res += \ |
1012 | 0 | _get_in_data_##inlayout##_g##type (in_data, n, in, inchannels) * \ |
1013 | 0 | mix->matrix[in][out]; \ |
1014 | 0 | \ |
1015 | 0 | *_get_out_data_##outlayout##_g##type (out_data, n, out, outchannels) = res; \ |
1016 | 0 | } \ |
1017 | 0 | } \ |
1018 | 0 | } Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_mix_float_planar_planar Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_mix_float_planar_interleaved Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_mix_float_interleaved_planar Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_mix_float_interleaved_interleaved Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_mix_double_planar_planar Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_mix_double_planar_interleaved Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_mix_double_interleaved_planar Unexecuted instantiation: audio-channel-mixer.c:gst_audio_channel_mixer_mix_double_interleaved_interleaved |
1019 | | |
1020 | | DEFINE_GET_DATA_FUNCS (gint16); |
1021 | | DEFINE_INTEGER_MIX_FUNC (16, 32, interleaved, interleaved); |
1022 | | DEFINE_INTEGER_MIX_FUNC (16, 32, interleaved, planar); |
1023 | | DEFINE_INTEGER_MIX_FUNC (16, 32, planar, interleaved); |
1024 | | DEFINE_INTEGER_MIX_FUNC (16, 32, planar, planar); |
1025 | | |
1026 | | DEFINE_INTEGER_SPARSE_MIX_FUNC (16, 32, interleaved, interleaved); |
1027 | | DEFINE_INTEGER_SPARSE_MIX_FUNC (16, 32, interleaved, planar); |
1028 | | DEFINE_INTEGER_SPARSE_MIX_FUNC (16, 32, planar, interleaved); |
1029 | | DEFINE_INTEGER_SPARSE_MIX_FUNC (16, 32, planar, planar); |
1030 | | |
1031 | | DEFINE_GET_DATA_FUNCS (gint32); |
1032 | | DEFINE_INTEGER_MIX_FUNC (32, 64, interleaved, interleaved); |
1033 | | DEFINE_INTEGER_MIX_FUNC (32, 64, interleaved, planar); |
1034 | | DEFINE_INTEGER_MIX_FUNC (32, 64, planar, interleaved); |
1035 | | DEFINE_INTEGER_MIX_FUNC (32, 64, planar, planar); |
1036 | | |
1037 | | DEFINE_INTEGER_SPARSE_MIX_FUNC (32, 64, interleaved, interleaved); |
1038 | | DEFINE_INTEGER_SPARSE_MIX_FUNC (32, 64, interleaved, planar); |
1039 | | DEFINE_INTEGER_SPARSE_MIX_FUNC (32, 64, planar, interleaved); |
1040 | | DEFINE_INTEGER_SPARSE_MIX_FUNC (32, 64, planar, planar); |
1041 | | |
1042 | | DEFINE_GET_DATA_FUNCS (gfloat); |
1043 | | DEFINE_FLOAT_MIX_FUNC (float, interleaved, interleaved); |
1044 | | DEFINE_FLOAT_MIX_FUNC (float, interleaved, planar); |
1045 | | DEFINE_FLOAT_MIX_FUNC (float, planar, interleaved); |
1046 | | DEFINE_FLOAT_MIX_FUNC (float, planar, planar); |
1047 | | |
1048 | | DEFINE_FLOAT_SPARSE_MIX_FUNC (float, interleaved, interleaved); |
1049 | | DEFINE_FLOAT_SPARSE_MIX_FUNC (float, interleaved, planar); |
1050 | | DEFINE_FLOAT_SPARSE_MIX_FUNC (float, planar, interleaved); |
1051 | | DEFINE_FLOAT_SPARSE_MIX_FUNC (float, planar, planar); |
1052 | | |
1053 | | DEFINE_GET_DATA_FUNCS (gdouble); |
1054 | | DEFINE_FLOAT_MIX_FUNC (double, interleaved, interleaved); |
1055 | | DEFINE_FLOAT_MIX_FUNC (double, interleaved, planar); |
1056 | | DEFINE_FLOAT_MIX_FUNC (double, planar, interleaved); |
1057 | | DEFINE_FLOAT_MIX_FUNC (double, planar, planar); |
1058 | | |
1059 | | DEFINE_FLOAT_SPARSE_MIX_FUNC (double, interleaved, interleaved); |
1060 | | DEFINE_FLOAT_SPARSE_MIX_FUNC (double, interleaved, planar); |
1061 | | DEFINE_FLOAT_SPARSE_MIX_FUNC (double, planar, interleaved); |
1062 | | DEFINE_FLOAT_SPARSE_MIX_FUNC (double, planar, planar); |
1063 | | |
1064 | | static gboolean |
1065 | | gst_audio_channel_mixer_build_sparse_matrix (GstAudioChannelMixer * mix) |
1066 | 0 | { |
1067 | 0 | const gfloat eps = 1e-6f; |
1068 | 0 | gint out, in; |
1069 | 0 | gint offset = 0; |
1070 | 0 | gint total_pairs = 0; |
1071 | 0 | gdouble density; |
1072 | |
|
1073 | 0 | for (out = 0; out < mix->out_channels; out++) { |
1074 | 0 | for (in = 0; in < mix->in_channels; in++) { |
1075 | 0 | if (fabsf (mix->matrix[in][out]) > eps) |
1076 | 0 | total_pairs++; |
1077 | 0 | } |
1078 | 0 | } |
1079 | |
|
1080 | 0 | density = ((double) total_pairs) / (mix->out_channels * mix->in_channels); |
1081 | |
|
1082 | 0 | GST_DEBUG ("nonzero coeff ratio: %.2lf (%d / %d)", density, total_pairs, |
1083 | 0 | mix->out_channels * mix->in_channels); |
1084 | | |
1085 | | /* Sparse matrix mixing involves extra lookup and memset overhead. |
1086 | | * Use sparse optimization only when a sufficient number of zero coefficients |
1087 | | * is detected */ |
1088 | 0 | if (NONZERO_DENSITY_THRESHOLD <= density) |
1089 | 0 | return FALSE; |
1090 | | |
1091 | 0 | mix->out_entry = g_new0 (MixOutEntry, mix->out_channels); |
1092 | 0 | mix->entry = g_new0 (MixEntry, total_pairs); |
1093 | |
|
1094 | 0 | for (out = 0; out < mix->out_channels; out++) { |
1095 | 0 | guint count = 0; |
1096 | 0 | for (in = 0; in < mix->in_channels; in++) { |
1097 | 0 | gfloat coeff = mix->matrix[in][out]; |
1098 | 0 | if (fabsf (coeff) > eps) { |
1099 | 0 | mix->entry[offset].index = in; |
1100 | 0 | if (GST_AUDIO_FORMAT_INFO_IS_INTEGER (mix->finfo)) { |
1101 | 0 | mix->entry[offset].coeff_int = |
1102 | 0 | (gint) (((gfloat) (1 << PRECISION_INT)) * coeff); |
1103 | 0 | } else { |
1104 | 0 | mix->entry[offset].coeff = coeff; |
1105 | 0 | } |
1106 | 0 | offset++; |
1107 | 0 | count++; |
1108 | 0 | } |
1109 | 0 | } |
1110 | |
|
1111 | 0 | if (count > 0) { |
1112 | 0 | MixOutEntry *out_entry = &mix->out_entry[mix->num_valid_out_ch]; |
1113 | 0 | out_entry->index = out; |
1114 | 0 | out_entry->offset = offset - count; |
1115 | 0 | out_entry->count = count; |
1116 | 0 | mix->num_valid_out_ch++; |
1117 | 0 | } |
1118 | 0 | } |
1119 | |
|
1120 | 0 | GST_DEBUG ("Use sparse matrix"); |
1121 | |
|
1122 | 0 | return TRUE; |
1123 | 0 | } |
1124 | | |
1125 | | /** |
1126 | | * gst_audio_channel_mixer_new_with_matrix: (constructor) (skip): |
1127 | | * @flags: #GstAudioChannelMixerFlags |
1128 | | * @in_channels: number of input channels |
1129 | | * @out_channels: number of output channels |
1130 | | * @matrix: (array) (element-type float*) (transfer full) (nullable): channel conversion matrix, m[@in_channels][@out_channels]. |
1131 | | * If identity matrix, passthrough applies. If %NULL, a (potentially truncated) |
1132 | | * identity matrix is generated. |
1133 | | * |
1134 | | * Create a new channel mixer object for the given parameters. |
1135 | | * |
1136 | | * Returns: (transfer full): a new #GstAudioChannelMixer object. |
1137 | | * Free with gst_audio_channel_mixer_free() after usage. |
1138 | | * |
1139 | | * Since: 1.14 |
1140 | | */ |
1141 | | GstAudioChannelMixer * |
1142 | | gst_audio_channel_mixer_new_with_matrix (GstAudioChannelMixerFlags flags, |
1143 | | GstAudioFormat format, |
1144 | | gint in_channels, gint out_channels, gfloat ** matrix) |
1145 | 0 | { |
1146 | 0 | GstAudioChannelMixer *mix; |
1147 | 0 | gboolean use_sparse; |
1148 | |
|
1149 | 0 | g_return_val_if_fail (format == GST_AUDIO_FORMAT_S16 |
1150 | 0 | || format == GST_AUDIO_FORMAT_S32 |
1151 | 0 | || format == GST_AUDIO_FORMAT_F32 |
1152 | 0 | || format == GST_AUDIO_FORMAT_F64, NULL); |
1153 | 0 | g_return_val_if_fail (in_channels > 0, NULL); |
1154 | 0 | g_return_val_if_fail (out_channels > 0, NULL); |
1155 | | |
1156 | 0 | mix = g_new0 (GstAudioChannelMixer, 1); |
1157 | 0 | mix->in_channels = in_channels; |
1158 | 0 | mix->out_channels = out_channels; |
1159 | |
|
1160 | 0 | if (!matrix) { |
1161 | | /* Generate (potentially truncated) identity matrix */ |
1162 | 0 | gint i, j; |
1163 | |
|
1164 | 0 | mix->matrix = g_new0 (gfloat *, in_channels); |
1165 | |
|
1166 | 0 | for (i = 0; i < in_channels; i++) { |
1167 | 0 | mix->matrix[i] = g_new (gfloat, out_channels); |
1168 | 0 | for (j = 0; j < out_channels; j++) { |
1169 | 0 | mix->matrix[i][j] = i == j ? 1.0 : 0.0; |
1170 | 0 | } |
1171 | 0 | } |
1172 | 0 | } else { |
1173 | 0 | mix->matrix = matrix; |
1174 | 0 | } |
1175 | |
|
1176 | 0 | mix->finfo = gst_audio_format_get_info (format); |
1177 | |
|
1178 | 0 | use_sparse = gst_audio_channel_mixer_build_sparse_matrix (mix); |
1179 | 0 | if (use_sparse) { |
1180 | 0 | if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT) { |
1181 | 0 | mix->non_interleaved = TRUE; |
1182 | 0 | mix->clear_unit = mix->finfo->width / 8; |
1183 | 0 | } else { |
1184 | 0 | mix->clear_unit = mix->finfo->width * out_channels / 8; |
1185 | 0 | } |
1186 | 0 | } else if (GST_AUDIO_FORMAT_INFO_IS_INTEGER (mix->finfo)) { |
1187 | 0 | gst_audio_channel_mixer_setup_matrix_int (mix); |
1188 | 0 | } |
1189 | |
|
1190 | 0 | #ifndef GST_DISABLE_GST_DEBUG |
1191 | | /* debug */ |
1192 | 0 | { |
1193 | 0 | GString *s; |
1194 | 0 | gint i, j; |
1195 | |
|
1196 | 0 | s = g_string_new ("Matrix for"); |
1197 | 0 | g_string_append_printf (s, " %d -> %d: ", |
1198 | 0 | mix->in_channels, mix->out_channels); |
1199 | 0 | g_string_append (s, "{"); |
1200 | 0 | for (i = 0; i < mix->in_channels; i++) { |
1201 | 0 | if (i != 0) |
1202 | 0 | g_string_append (s, ","); |
1203 | 0 | g_string_append (s, " {"); |
1204 | 0 | for (j = 0; j < mix->out_channels; j++) { |
1205 | 0 | if (j != 0) |
1206 | 0 | g_string_append (s, ","); |
1207 | 0 | g_string_append_printf (s, " %f", mix->matrix[i][j]); |
1208 | 0 | } |
1209 | 0 | g_string_append (s, " }"); |
1210 | 0 | } |
1211 | 0 | g_string_append (s, " }"); |
1212 | 0 | GST_DEBUG ("%s", s->str); |
1213 | 0 | g_string_free (s, TRUE); |
1214 | 0 | } |
1215 | 0 | #endif |
1216 | |
|
1217 | 0 | switch (format) { |
1218 | 0 | case GST_AUDIO_FORMAT_S16: |
1219 | 0 | if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_IN) { |
1220 | 0 | if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT) { |
1221 | 0 | mix->func = (MixerFunc) |
1222 | 0 | gst_audio_channel_mixer_mix_int16_planar_planar; |
1223 | 0 | if (use_sparse) { |
1224 | 0 | mix->func = (MixerFunc) |
1225 | 0 | gst_audio_channel_mixer_sparse_mix_int16_planar_planar; |
1226 | 0 | } |
1227 | 0 | } else { |
1228 | 0 | mix->func = (MixerFunc) |
1229 | 0 | gst_audio_channel_mixer_mix_int16_planar_interleaved; |
1230 | 0 | if (use_sparse) { |
1231 | 0 | mix->func = (MixerFunc) |
1232 | 0 | gst_audio_channel_mixer_sparse_mix_int16_planar_interleaved; |
1233 | 0 | } |
1234 | 0 | } |
1235 | 0 | } else { |
1236 | 0 | if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT) { |
1237 | 0 | mix->func = (MixerFunc) |
1238 | 0 | gst_audio_channel_mixer_mix_int16_interleaved_planar; |
1239 | 0 | if (use_sparse) { |
1240 | 0 | mix->func = (MixerFunc) |
1241 | 0 | gst_audio_channel_mixer_sparse_mix_int16_interleaved_planar; |
1242 | 0 | } |
1243 | 0 | } else { |
1244 | 0 | mix->func = (MixerFunc) |
1245 | 0 | gst_audio_channel_mixer_mix_int16_interleaved_interleaved; |
1246 | 0 | if (use_sparse) { |
1247 | 0 | mix->func = (MixerFunc) |
1248 | 0 | gst_audio_channel_mixer_sparse_mix_int16_interleaved_interleaved; |
1249 | 0 | } |
1250 | 0 | } |
1251 | 0 | } |
1252 | 0 | break; |
1253 | 0 | case GST_AUDIO_FORMAT_S32: |
1254 | 0 | if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_IN) { |
1255 | 0 | if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT) { |
1256 | 0 | mix->func = (MixerFunc) |
1257 | 0 | gst_audio_channel_mixer_mix_int32_planar_planar; |
1258 | 0 | if (use_sparse) { |
1259 | 0 | mix->func = (MixerFunc) |
1260 | 0 | gst_audio_channel_mixer_sparse_mix_int32_planar_planar; |
1261 | 0 | } |
1262 | 0 | } else { |
1263 | 0 | mix->func = (MixerFunc) |
1264 | 0 | gst_audio_channel_mixer_mix_int32_planar_interleaved; |
1265 | 0 | if (use_sparse) { |
1266 | 0 | mix->func = (MixerFunc) |
1267 | 0 | gst_audio_channel_mixer_sparse_mix_int32_planar_interleaved; |
1268 | 0 | } |
1269 | 0 | } |
1270 | 0 | } else { |
1271 | 0 | if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT) { |
1272 | 0 | mix->func = (MixerFunc) |
1273 | 0 | gst_audio_channel_mixer_mix_int32_interleaved_planar; |
1274 | 0 | if (use_sparse) { |
1275 | 0 | mix->func = (MixerFunc) |
1276 | 0 | gst_audio_channel_mixer_sparse_mix_int32_interleaved_planar; |
1277 | 0 | } |
1278 | 0 | } else { |
1279 | 0 | mix->func = (MixerFunc) |
1280 | 0 | gst_audio_channel_mixer_mix_int32_interleaved_interleaved; |
1281 | 0 | if (use_sparse) { |
1282 | 0 | mix->func = (MixerFunc) |
1283 | 0 | gst_audio_channel_mixer_sparse_mix_int32_interleaved_interleaved; |
1284 | 0 | } |
1285 | 0 | } |
1286 | 0 | } |
1287 | 0 | break; |
1288 | 0 | case GST_AUDIO_FORMAT_F32: |
1289 | 0 | if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_IN) { |
1290 | 0 | if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT) { |
1291 | 0 | mix->func = (MixerFunc) |
1292 | 0 | gst_audio_channel_mixer_mix_float_planar_planar; |
1293 | 0 | if (use_sparse) { |
1294 | 0 | mix->func = (MixerFunc) |
1295 | 0 | gst_audio_channel_mixer_sparse_mix_float_planar_planar; |
1296 | 0 | } |
1297 | 0 | } else { |
1298 | 0 | mix->func = (MixerFunc) |
1299 | 0 | gst_audio_channel_mixer_mix_float_planar_interleaved; |
1300 | 0 | if (use_sparse) { |
1301 | 0 | mix->func = (MixerFunc) |
1302 | 0 | gst_audio_channel_mixer_sparse_mix_float_planar_interleaved; |
1303 | 0 | } |
1304 | 0 | } |
1305 | 0 | } else { |
1306 | 0 | if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT) { |
1307 | 0 | mix->func = (MixerFunc) |
1308 | 0 | gst_audio_channel_mixer_mix_float_interleaved_planar; |
1309 | 0 | if (use_sparse) { |
1310 | 0 | mix->func = (MixerFunc) |
1311 | 0 | gst_audio_channel_mixer_sparse_mix_float_interleaved_planar; |
1312 | 0 | } |
1313 | 0 | } else { |
1314 | 0 | mix->func = (MixerFunc) |
1315 | 0 | gst_audio_channel_mixer_mix_float_interleaved_interleaved; |
1316 | 0 | if (use_sparse) { |
1317 | 0 | mix->func = (MixerFunc) |
1318 | 0 | gst_audio_channel_mixer_sparse_mix_float_interleaved_interleaved; |
1319 | 0 | } |
1320 | 0 | } |
1321 | 0 | } |
1322 | 0 | break; |
1323 | 0 | case GST_AUDIO_FORMAT_F64: |
1324 | 0 | if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_IN) { |
1325 | 0 | if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT) { |
1326 | 0 | mix->func = (MixerFunc) |
1327 | 0 | gst_audio_channel_mixer_mix_double_planar_planar; |
1328 | 0 | if (use_sparse) { |
1329 | 0 | mix->func = (MixerFunc) |
1330 | 0 | gst_audio_channel_mixer_sparse_mix_double_planar_planar; |
1331 | 0 | } |
1332 | 0 | } else { |
1333 | 0 | mix->func = (MixerFunc) |
1334 | 0 | gst_audio_channel_mixer_mix_double_planar_interleaved; |
1335 | 0 | if (use_sparse) { |
1336 | 0 | mix->func = (MixerFunc) |
1337 | 0 | gst_audio_channel_mixer_sparse_mix_double_planar_interleaved; |
1338 | 0 | } |
1339 | 0 | } |
1340 | 0 | } else { |
1341 | 0 | if (flags & GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT) { |
1342 | 0 | mix->func = (MixerFunc) |
1343 | 0 | gst_audio_channel_mixer_mix_double_interleaved_planar; |
1344 | 0 | if (use_sparse) { |
1345 | 0 | mix->func = (MixerFunc) |
1346 | 0 | gst_audio_channel_mixer_sparse_mix_double_interleaved_planar; |
1347 | 0 | } |
1348 | 0 | } else { |
1349 | 0 | mix->func = (MixerFunc) |
1350 | 0 | gst_audio_channel_mixer_mix_double_interleaved_interleaved; |
1351 | 0 | if (use_sparse) { |
1352 | 0 | mix->func = (MixerFunc) |
1353 | 0 | gst_audio_channel_mixer_sparse_mix_double_interleaved_interleaved; |
1354 | 0 | } |
1355 | 0 | } |
1356 | 0 | } |
1357 | 0 | break; |
1358 | 0 | default: |
1359 | 0 | g_assert_not_reached (); |
1360 | 0 | break; |
1361 | 0 | } |
1362 | 0 | return mix; |
1363 | 0 | } |
1364 | | |
1365 | | /** |
1366 | | * gst_audio_channel_mixer_new: (constructor) (skip): |
1367 | | * @flags: #GstAudioChannelMixerFlags |
1368 | | * @in_channels: number of input channels |
1369 | | * @in_position: (array length=in_channels): positions of input channels |
1370 | | * @out_channels: number of output channels |
1371 | | * @out_position: (array length=out_channels): positions of output channels |
1372 | | * |
1373 | | * Create a new channel mixer object for the given parameters. |
1374 | | * |
1375 | | * Returns: (transfer full): a new #GstAudioChannelMixer object. |
1376 | | * Free with gst_audio_channel_mixer_free() after usage. |
1377 | | */ |
1378 | | GstAudioChannelMixer * |
1379 | | gst_audio_channel_mixer_new (GstAudioChannelMixerFlags flags, |
1380 | | GstAudioFormat format, |
1381 | | gint in_channels, |
1382 | | GstAudioChannelPosition * in_position, |
1383 | | gint out_channels, GstAudioChannelPosition * out_position) |
1384 | 0 | { |
1385 | 0 | gfloat **matrix; |
1386 | |
|
1387 | 0 | g_return_val_if_fail (format == GST_AUDIO_FORMAT_S16 |
1388 | 0 | || format == GST_AUDIO_FORMAT_S32 |
1389 | 0 | || format == GST_AUDIO_FORMAT_F32 |
1390 | 0 | || format == GST_AUDIO_FORMAT_F64, NULL); |
1391 | | |
1392 | 0 | matrix = |
1393 | 0 | gst_audio_channel_mixer_setup_matrix (flags, in_channels, in_position, |
1394 | 0 | out_channels, out_position); |
1395 | 0 | return gst_audio_channel_mixer_new_with_matrix (flags, format, in_channels, |
1396 | 0 | out_channels, matrix); |
1397 | 0 | } |
1398 | | |
1399 | | /** |
1400 | | * gst_audio_channel_mixer_is_passthrough: |
1401 | | * @mix: a #GstAudioChannelMixer |
1402 | | * |
1403 | | * Check if @mix is in passthrough. |
1404 | | * |
1405 | | * Only N x N mix identity matrices are considered passthrough, |
1406 | | * this is determined by comparing the contents of the matrix |
1407 | | * with 0.0 and 1.0. |
1408 | | * |
1409 | | * As this is floating point comparisons, if the values have been |
1410 | | * generated, they should be rounded up or down by explicit |
1411 | | * assignment of 0.0 or 1.0 to values within a user-defined |
1412 | | * epsilon, this code doesn't make assumptions as to what may |
1413 | | * constitute an appropriate epsilon. |
1414 | | * |
1415 | | * Returns: %TRUE is @mix is passthrough. |
1416 | | */ |
1417 | | gboolean |
1418 | | gst_audio_channel_mixer_is_passthrough (GstAudioChannelMixer * mix) |
1419 | 0 | { |
1420 | 0 | gint i, j; |
1421 | 0 | gboolean res; |
1422 | | |
1423 | | /* only NxN matrices can be identities */ |
1424 | 0 | if (mix->in_channels != mix->out_channels) |
1425 | 0 | return FALSE; |
1426 | | |
1427 | 0 | res = TRUE; |
1428 | |
|
1429 | 0 | for (i = 0; i < mix->in_channels; i++) { |
1430 | 0 | for (j = 0; j < mix->out_channels; j++) { |
1431 | 0 | if ((i == j && mix->matrix[i][j] != 1.0f) || |
1432 | 0 | (i != j && mix->matrix[i][j] != 0.0f)) { |
1433 | 0 | res = FALSE; |
1434 | 0 | break; |
1435 | 0 | } |
1436 | 0 | } |
1437 | 0 | } |
1438 | |
|
1439 | 0 | return res; |
1440 | 0 | } |
1441 | | |
1442 | | /** |
1443 | | * gst_audio_channel_mixer_samples: |
1444 | | * @mix: a #GstAudioChannelMixer |
1445 | | * @in: (array) (element-type gpointer): input samples |
1446 | | * @out: (array) (element-type gpointer): output samples |
1447 | | * @samples: number of samples |
1448 | | * |
1449 | | * In case the samples are interleaved, @in and @out must point to an |
1450 | | * array with a single element pointing to a block of interleaved samples. |
1451 | | * |
1452 | | * If non-interleaved samples are used, @in and @out must point to an |
1453 | | * array with pointers to memory blocks, one for each channel. |
1454 | | * |
1455 | | * Perform channel mixing on @in_data and write the result to @out_data. |
1456 | | * @in_data and @out_data need to be in @format and @layout. |
1457 | | */ |
1458 | | void |
1459 | | gst_audio_channel_mixer_samples (GstAudioChannelMixer * mix, |
1460 | | const gpointer in[], gpointer out[], gint samples) |
1461 | 0 | { |
1462 | 0 | g_return_if_fail (mix != NULL); |
1463 | 0 | g_return_if_fail (mix->matrix != NULL); |
1464 | | |
1465 | 0 | mix->func (mix, in, out, samples); |
1466 | 0 | } |