1 /* Spa
2 *
3 * Copyright © 2018 Wim Taymans
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 */
24
25 #include <string.h>
26 #include <stdio.h>
27 #include <math.h>
28
29 #include <spa/param/audio/format-utils.h>
30 #include <spa/support/cpu.h>
31 #include <spa/support/log.h>
32 #include <spa/utils/defs.h>
33
34 #define VOLUME_MIN 0.0f
35 #define VOLUME_NORM 1.0f
36
37 #include "channelmix-ops.h"
38
39 #undef SPA_LOG_TOPIC_DEFAULT
40 #define SPA_LOG_TOPIC_DEFAULT log_topic
41 struct spa_log_topic *log_topic = &SPA_LOG_TOPIC(0, "spa.channelmix");
42
43 #define _M(ch) (1UL << SPA_AUDIO_CHANNEL_ ## ch)
44 #define MASK_MONO _M(FC)|_M(MONO)|_M(UNKNOWN)
45 #define MASK_STEREO _M(FL)|_M(FR)|_M(UNKNOWN)
46 #define MASK_QUAD _M(FL)|_M(FR)|_M(RL)|_M(RR)|_M(UNKNOWN)
47 #define MASK_3_1 _M(FL)|_M(FR)|_M(FC)|_M(LFE)
48 #define MASK_5_1 _M(FL)|_M(FR)|_M(FC)|_M(LFE)|_M(SL)|_M(SR)|_M(RL)|_M(RR)
49 #define MASK_7_1 _M(FL)|_M(FR)|_M(FC)|_M(LFE)|_M(SL)|_M(SR)|_M(RL)|_M(RR)
50
51 #define ANY ((uint32_t)-1)
52 #define EQ ((uint32_t)-2)
53
54 typedef void (*channelmix_func_t) (struct channelmix *mix, uint32_t n_dst, void * SPA_RESTRICT dst[n_dst],
55 uint32_t n_src, const void * SPA_RESTRICT src[n_src], uint32_t n_samples);
56
57 static const struct channelmix_info {
58 uint32_t src_chan;
59 uint64_t src_mask;
60 uint32_t dst_chan;
61 uint64_t dst_mask;
62
63 channelmix_func_t process;
64 uint32_t cpu_flags;
65 const char *name;
66 } channelmix_table[] =
67 {
68 #if defined (HAVE_SSE)
69 { 2, MASK_MONO, 2, MASK_MONO, channelmix_copy_sse, SPA_CPU_FLAG_SSE, "copy_sse" },
70 { 2, MASK_STEREO, 2, MASK_STEREO, channelmix_copy_sse, SPA_CPU_FLAG_SSE, "copy_sse" },
71 { EQ, 0, EQ, 0, channelmix_copy_sse, SPA_CPU_FLAG_SSE, "copy_sse" },
72 #endif
73 { 2, MASK_MONO, 2, MASK_MONO, channelmix_copy_c, 0, "copy_c" },
74 { 2, MASK_STEREO, 2, MASK_STEREO, channelmix_copy_c, 0, "copy_c" },
75 { EQ, 0, EQ, 0, channelmix_copy_c, 0 },
76
77 { 1, MASK_MONO, 2, MASK_STEREO, channelmix_f32_1_2_c, 0, "f32_1_2_c" },
78 { 2, MASK_STEREO, 1, MASK_MONO, channelmix_f32_2_1_c, 0, "f32_2_1_c" },
79 { 4, MASK_QUAD, 1, MASK_MONO, channelmix_f32_4_1_c, 0, "f32_4_1_c" },
80 { 4, MASK_3_1, 1, MASK_MONO, channelmix_f32_3p1_1_c, 0, "f32_3p1_1_c" },
81 #if defined (HAVE_SSE)
82 { 2, MASK_STEREO, 4, MASK_QUAD, channelmix_f32_2_4_sse, SPA_CPU_FLAG_SSE, "f32_2_4_sse" },
83 #endif
84 { 2, MASK_STEREO, 4, MASK_QUAD, channelmix_f32_2_4_c, 0, "f32_2_4_c" },
85 { 2, MASK_STEREO, 4, MASK_3_1, channelmix_f32_2_3p1_c, 0, "f32_2_3p1_c" },
86 { 2, MASK_STEREO, 6, MASK_5_1, channelmix_f32_2_5p1_c, 0, "f32_2_5p1_c" },
87 #if defined (HAVE_SSE)
88 { 6, MASK_5_1, 2, MASK_STEREO, channelmix_f32_5p1_2_sse, SPA_CPU_FLAG_SSE, "f32_5p1_2_sse" },
89 #endif
90 { 6, MASK_5_1, 2, MASK_STEREO, channelmix_f32_5p1_2_c, 0, "f32_5p1_2_c" },
91 #if defined (HAVE_SSE)
92 { 6, MASK_5_1, 4, MASK_QUAD, channelmix_f32_5p1_4_sse, SPA_CPU_FLAG_SSE, "f32_5p1_4_sse" },
93 #endif
94 { 6, MASK_5_1, 4, MASK_QUAD, channelmix_f32_5p1_4_c, 0, "f32_5p1_4_c" },
95
96 #if defined (HAVE_SSE)
97 { 6, MASK_5_1, 4, MASK_3_1, channelmix_f32_5p1_3p1_sse, SPA_CPU_FLAG_SSE, "f32_5p1_3p1_sse" },
98 #endif
99 { 6, MASK_5_1, 4, MASK_3_1, channelmix_f32_5p1_3p1_c, 0, "f32_5p1_3p1_c" },
100
101 { 8, MASK_7_1, 2, MASK_STEREO, channelmix_f32_7p1_2_c, 0, "f32_7p1_2_c" },
102 { 8, MASK_7_1, 4, MASK_QUAD, channelmix_f32_7p1_4_c, 0, "f32_7p1_4_c" },
103 { 8, MASK_7_1, 4, MASK_3_1, channelmix_f32_7p1_3p1_c, 0, "f32_7p1_3p1_c" },
104
105 { ANY, 0, ANY, 0, channelmix_f32_n_m_c, 0, "f32_n_m_c" },
106 };
107
108 #define MATCH_CHAN(a,b) ((a) == ANY || (a) == (b))
109 #define MATCH_CPU_FLAGS(a,b) ((a) == 0 || ((a) & (b)) == a)
110 #define MATCH_MASK(a,b) ((a) == 0 || ((a) & (b)) == (b))
111
find_channelmix_info(uint32_t src_chan,uint64_t src_mask,uint32_t dst_chan,uint64_t dst_mask,uint32_t cpu_flags)112 static const struct channelmix_info *find_channelmix_info(uint32_t src_chan, uint64_t src_mask,
113 uint32_t dst_chan, uint64_t dst_mask, uint32_t cpu_flags)
114 {
115 size_t i;
116 for (i = 0; i < SPA_N_ELEMENTS(channelmix_table); i++) {
117 if (!MATCH_CPU_FLAGS(channelmix_table[i].cpu_flags, cpu_flags))
118 continue;
119
120 if (src_chan == dst_chan && src_mask == dst_mask)
121 return &channelmix_table[i];
122
123 if (MATCH_CHAN(channelmix_table[i].src_chan, src_chan) &&
124 MATCH_CHAN(channelmix_table[i].dst_chan, dst_chan) &&
125 MATCH_MASK(channelmix_table[i].src_mask, src_mask) &&
126 MATCH_MASK(channelmix_table[i].dst_mask, dst_mask))
127 return &channelmix_table[i];
128 }
129 return NULL;
130 }
131
132 #define SQRT3_2 1.224744871f /* sqrt(3/2) */
133 #define SQRT1_2 0.707106781f
134 #define SQRT2 1.414213562f
135
136 #define MATRIX_NORMAL 0
137 #define MATRIX_DOLBY 1
138 #define MATRIX_DPLII 2
139
140 #define _CH(ch) ((SPA_AUDIO_CHANNEL_ ## ch)-3)
141 #define _MASK(ch) (1ULL << _CH(ch))
142 #define FRONT (_MASK(FC))
143 #define STEREO (_MASK(FL)|_MASK(FR))
144 #define REAR (_MASK(RL)|_MASK(RR))
145 #define SIDE (_MASK(SL)|_MASK(SR))
146
make_matrix(struct channelmix * mix)147 static int make_matrix(struct channelmix *mix)
148 {
149 float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS] = {{ 0.0f }};
150 uint64_t src_mask = mix->src_mask;
151 uint64_t dst_mask = mix->dst_mask;
152 uint64_t unassigned;
153 uint32_t i, j, ic, jc, matrix_encoding = MATRIX_NORMAL;
154 float clev = SQRT1_2;
155 float slev = SQRT1_2;
156 float llev = 0.5f;
157 float maxsum = 0.0f;
158 bool do_upmix = SPA_FLAG_IS_SET(mix->options, CHANNELMIX_OPTION_UPMIX);
159 #define _MATRIX(s,d) matrix[_CH(s)][_CH(d)]
160
161 spa_log_debug(mix->log, "src-mask:%08"PRIx64" dst-mask:%08"PRIx64,
162 src_mask, dst_mask);
163
164 /* move the MONO mask to FRONT so that the lower bits can be shifted
165 * away. */
166 if ((src_mask & (1Ull << SPA_AUDIO_CHANNEL_MONO)) != 0) {
167 if (mix->src_chan == 1)
168 src_mask = 0;
169 else
170 src_mask |= (1ULL << SPA_AUDIO_CHANNEL_FC);
171 }
172 if ((dst_mask & (1Ull << SPA_AUDIO_CHANNEL_MONO)) != 0)
173 dst_mask |= (1ULL << SPA_AUDIO_CHANNEL_FC);
174
175 /* shift so that bit 0 is FL */
176 src_mask >>= 3;
177 dst_mask >>= 3;
178
179 /* unknown channels or just 1 channel */
180 if (src_mask == 0 || dst_mask == 0) {
181 if (mix->src_chan == 1) {
182 /* one FC/MONO src goes everywhere */
183 spa_log_debug(mix->log, "distribute FC/MONO");
184 for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
185 matrix[i][0]= 1.0f;
186 } else if (mix->dst_chan == 1) {
187 /* one FC/MONO dst get average of everything */
188 spa_log_debug(mix->log, "average FC/MONO");
189 for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
190 matrix[0][i]= 1.0f / mix->src_chan;
191 } else {
192 /* just pair channels */
193 spa_log_debug(mix->log, "pairing channels");
194 for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
195 matrix[i][i]= 1.0f;
196 }
197 src_mask = dst_mask = ~0LU;
198 goto done;
199 } else {
200 spa_log_debug(mix->log, "matching channels");
201 for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) {
202 if ((src_mask & dst_mask & (1ULL << i))) {
203 spa_log_debug(mix->log, "matched %u", i);
204 matrix[i][i]= 1.0f;
205 }
206 }
207 }
208
209 unassigned = src_mask & ~dst_mask;
210
211 spa_log_debug(mix->log, "unassigned downmix %08" PRIx64, unassigned);
212
213 if (unassigned & FRONT){
214 if ((dst_mask & STEREO) == STEREO){
215 spa_log_debug(mix->log, "assign FC to STEREO");
216 if(src_mask & STEREO) {
217 _MATRIX(FL,FC) += clev;
218 _MATRIX(FR,FC) += clev;
219 } else {
220 _MATRIX(FL,FC) += SQRT1_2;
221 _MATRIX(FR,FC) += SQRT1_2;
222 }
223 } else {
224 spa_log_warn(mix->log, "can't assign FC");
225 }
226 }
227
228 if (unassigned & STEREO){
229 if (dst_mask & FRONT) {
230 spa_log_debug(mix->log, "assign STEREO to FC");
231 _MATRIX(FC,FL) += SQRT1_2;
232 _MATRIX(FC,FR) += SQRT1_2;
233 if (src_mask & FRONT)
234 _MATRIX(FC,FC) = clev * SQRT2;
235 } else {
236 spa_log_warn(mix->log, "can't assign STEREO");
237 }
238 }
239
240 if (unassigned & _MASK(RC)) {
241 if (dst_mask & REAR){
242 spa_log_debug(mix->log, "assign RC to RL+RR");
243 _MATRIX(RL,RC) += SQRT1_2;
244 _MATRIX(RR,RC) += SQRT1_2;
245 } else if (dst_mask & SIDE) {
246 spa_log_debug(mix->log, "assign RC to SL+SR");
247 _MATRIX(SL,RC) += SQRT1_2;
248 _MATRIX(SR,RC) += SQRT1_2;
249 } else if(dst_mask & STEREO) {
250 spa_log_debug(mix->log, "assign RC to FL+FR");
251 if (matrix_encoding == MATRIX_DOLBY ||
252 matrix_encoding == MATRIX_DPLII) {
253 if (unassigned & (_MASK(RL)|_MASK(RR))) {
254 _MATRIX(FL,RC) -= slev * SQRT1_2;
255 _MATRIX(FR,RC) += slev * SQRT1_2;
256 } else {
257 _MATRIX(FL,RC) -= slev;
258 _MATRIX(FR,RC) += slev;
259 }
260 } else {
261 _MATRIX(FL,RC) += slev * SQRT1_2;
262 _MATRIX(FR,RC) += slev * SQRT1_2;
263 }
264 } else if (dst_mask & FRONT) {
265 spa_log_debug(mix->log, "assign RC to FC");
266 _MATRIX(FC,RC) += slev * SQRT1_2;
267 } else {
268 spa_log_warn(mix->log, "can't assign RC");
269 }
270 }
271
272 if (unassigned & REAR) {
273 if (dst_mask & _MASK(RC)) {
274 spa_log_debug(mix->log, "assign RL+RR to RC");
275 _MATRIX(RC,RL) += SQRT1_2;
276 _MATRIX(RC,RR) += SQRT1_2;
277 } else if (dst_mask & SIDE) {
278 spa_log_debug(mix->log, "assign RL+RR to SL+SR");
279 if (src_mask & SIDE) {
280 _MATRIX(SL,RL) += SQRT1_2;
281 _MATRIX(SR,RR) += SQRT1_2;
282 } else {
283 _MATRIX(SL,RL) += 1.0f;
284 _MATRIX(SR,RR) += 1.0f;
285 }
286 } else if (dst_mask & STEREO) {
287 spa_log_debug(mix->log, "assign RL+RR to FL+FR %f", slev);
288 if (matrix_encoding == MATRIX_DOLBY) {
289 _MATRIX(FL,RL) -= slev * SQRT1_2;
290 _MATRIX(FL,RR) -= slev * SQRT1_2;
291 _MATRIX(FR,RL) += slev * SQRT1_2;
292 _MATRIX(FR,RR) += slev * SQRT1_2;
293 } else if (matrix_encoding == MATRIX_DPLII) {
294 _MATRIX(FL,RL) -= slev * SQRT3_2;
295 _MATRIX(FL,RR) -= slev * SQRT1_2;
296 _MATRIX(FR,RL) += slev * SQRT1_2;
297 _MATRIX(FR,RR) += slev * SQRT3_2;
298 } else {
299 _MATRIX(FL,RL) += slev;
300 _MATRIX(FR,RR) += slev;
301 }
302 } else if (dst_mask & FRONT) {
303 spa_log_debug(mix->log, "assign RL+RR to FC");
304 _MATRIX(FC,RL)+= slev * SQRT1_2;
305 _MATRIX(FC,RR)+= slev * SQRT1_2;
306 } else {
307 spa_log_warn(mix->log, "can't assign RL");
308 }
309 }
310
311 if (unassigned & SIDE) {
312 if (dst_mask & REAR) {
313 spa_log_debug(mix->log, "assign SL+SR to RL+RR");
314 if (src_mask & _MASK(RL)) {
315 _MATRIX(RL,SL) += SQRT1_2;
316 _MATRIX(RR,SR) += SQRT1_2;
317 } else {
318 _MATRIX(RL,SL) += 1.0f;
319 _MATRIX(RR,SR) += 1.0f;
320 }
321 } else if (dst_mask & _MASK(RC)) {
322 spa_log_debug(mix->log, "assign SL+SR to RC");
323 _MATRIX(RC,SL)+= SQRT1_2;
324 _MATRIX(RC,SR)+= SQRT1_2;
325 } else if (dst_mask & STEREO) {
326 spa_log_debug(mix->log, "assign SL+SR to FL+FR");
327 if (matrix_encoding == MATRIX_DOLBY) {
328 _MATRIX(FL,SL) -= slev * SQRT1_2;
329 _MATRIX(FL,SR) -= slev * SQRT1_2;
330 _MATRIX(FR,SL) += slev * SQRT1_2;
331 _MATRIX(FR,SR) += slev * SQRT1_2;
332 } else if (matrix_encoding == MATRIX_DPLII) {
333 _MATRIX(FL,SL) -= slev * SQRT3_2;
334 _MATRIX(FL,SR) -= slev * SQRT1_2;
335 _MATRIX(FR,SL) += slev * SQRT1_2;
336 _MATRIX(FR,SR) += slev * SQRT3_2;
337 } else {
338 _MATRIX(FL,SL) += slev;
339 _MATRIX(FR,SR) += slev;
340 }
341 } else if (dst_mask & FRONT) {
342 spa_log_debug(mix->log, "assign SL+SR to FC");
343 _MATRIX(FC,SL) += slev * SQRT1_2;
344 _MATRIX(FC,SR) += slev * SQRT1_2;
345 } else {
346 spa_log_warn(mix->log, "can't assign SL");
347 }
348 }
349
350 if (unassigned & _MASK(FLC)) {
351 if (dst_mask & STEREO) {
352 spa_log_debug(mix->log, "assign FLC+FRC to FL+FR");
353 _MATRIX(FL,FLC)+= 1.0f;
354 _MATRIX(FR,FRC)+= 1.0f;
355 } else if(dst_mask & FRONT) {
356 spa_log_debug(mix->log, "assign FLC+FRC to FC");
357 _MATRIX(FC,FLC)+= SQRT1_2;
358 _MATRIX(FC,FRC)+= SQRT1_2;
359 } else {
360 spa_log_warn(mix->log, "can't assign FLC");
361 }
362 }
363 if (unassigned & _MASK(LFE) &&
364 SPA_FLAG_IS_SET(mix->options, CHANNELMIX_OPTION_MIX_LFE)) {
365 if (dst_mask & FRONT) {
366 spa_log_debug(mix->log, "assign LFE to FC");
367 _MATRIX(FC,LFE) += llev;
368 } else if (dst_mask & STEREO) {
369 spa_log_debug(mix->log, "assign LFE to FL+FR");
370 _MATRIX(FL,LFE) += llev * SQRT1_2;
371 _MATRIX(FR,LFE) += llev * SQRT1_2;
372 } else {
373 spa_log_warn(mix->log, "can't assign LFE");
374 }
375 }
376
377 if (!do_upmix)
378 goto done;
379
380 unassigned = dst_mask & ~src_mask;
381
382 spa_log_debug(mix->log, "unassigned upmix %08" PRIx64, unassigned);
383
384 if (unassigned & FRONT) {
385 if ((src_mask & STEREO) == STEREO) {
386 spa_log_debug(mix->log, "produce FC from STEREO");
387 _MATRIX(FC,FL) += clev;
388 _MATRIX(FC,FR) += clev;
389 } else {
390 spa_log_warn(mix->log, "can't produce FC");
391 }
392 }
393 if (unassigned & _MASK(LFE) && mix->lfe_cutoff > 0.0f) {
394 if ((src_mask & STEREO) == STEREO) {
395 spa_log_debug(mix->log, "produce LFE from STEREO");
396 _MATRIX(LFE,FL) += llev;
397 _MATRIX(LFE,FR) += llev;
398 } else if ((src_mask & FRONT) == FRONT) {
399 spa_log_debug(mix->log, "produce LFE from FC");
400 _MATRIX(LFE,FC) += llev;
401 } else {
402 spa_log_warn(mix->log, "can't produce LFE");
403 }
404 }
405 if (unassigned & SIDE) {
406 if ((src_mask & REAR) == REAR) {
407 spa_log_debug(mix->log, "produce SIDE from REAR");
408 _MATRIX(SL,RL) += 1.0f;
409 _MATRIX(SR,RR) += 1.0f;
410 } else if ((src_mask & STEREO) == STEREO) {
411 spa_log_debug(mix->log, "produce SIDE from STEREO");
412 _MATRIX(SL,FL) += 1.0f;
413 _MATRIX(SR,FR) += 1.0f;
414 } else if ((src_mask & FRONT) == FRONT) {
415 spa_log_debug(mix->log, "produce SIDE from FC");
416 _MATRIX(SL,FC) += clev;
417 _MATRIX(SR,FC) += clev;
418 }
419 }
420 if (unassigned & REAR) {
421 if ((src_mask & SIDE) == SIDE) {
422 spa_log_debug(mix->log, "produce REAR from SIDE");
423 _MATRIX(RL,SL) += 1.0f;
424 _MATRIX(RR,SR) += 1.0f;
425 } else if ((src_mask & STEREO) == STEREO) {
426 spa_log_debug(mix->log, "produce REAR from STEREO");
427 _MATRIX(RL,FL) += 1.0f;
428 _MATRIX(RR,FR) += 1.0f;
429 } else if ((src_mask & FRONT) == FRONT) {
430 spa_log_debug(mix->log, "produce REAR from FC");
431 _MATRIX(RL,FC) += clev;
432 _MATRIX(RR,FC) += clev;
433 }
434 }
435
436 done:
437 for (jc = 0, ic = 0, i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) {
438 float sum = 0.0f;
439 if ((dst_mask & (1UL << i)) == 0)
440 continue;
441 for (jc = 0, j = 0; j < SPA_AUDIO_MAX_CHANNELS; j++) {
442 if ((src_mask & (1UL << j)) == 0)
443 continue;
444 mix->matrix_orig[ic][jc++] = matrix[i][j];
445 sum += fabs(matrix[i][j]);
446 }
447 maxsum = SPA_MAX(maxsum, sum);
448 if (i == _CH(LFE) && do_upmix && mix->lfe_cutoff > 0.0f) {
449 spa_log_debug(mix->log, "channel %d is LFE", ic);
450 lr4_set(&mix->lr4[ic], BQ_LOWPASS, mix->lfe_cutoff / mix->freq);
451 mix->lr4_info[ic] = 1;
452 } else {
453 mix->lr4_info[ic] = 0;
454 }
455 ic++;
456 }
457 if (SPA_FLAG_IS_SET(mix->options, CHANNELMIX_OPTION_NORMALIZE) &&
458 maxsum > 1.0f) {
459 for (i = 0; i < ic; i++)
460 for (j = 0; j < jc; j++)
461 mix->matrix_orig[i][j] /= maxsum;
462 }
463 return 0;
464 }
465
impl_channelmix_set_volume(struct channelmix * mix,float volume,bool mute,uint32_t n_channel_volumes,float * channel_volumes)466 static void impl_channelmix_set_volume(struct channelmix *mix, float volume, bool mute,
467 uint32_t n_channel_volumes, float *channel_volumes)
468 {
469 float volumes[SPA_AUDIO_MAX_CHANNELS];
470 float vol = mute ? 0.0f : volume, t;
471 uint32_t i, j;
472 uint32_t src_chan = mix->src_chan;
473 uint32_t dst_chan = mix->dst_chan;
474
475 spa_log_debug(mix->log, "volume:%f mute:%d n_volumes:%d", volume, mute, n_channel_volumes);
476
477 /** apply global volume to channels */
478 for (i = 0; i < n_channel_volumes; i++) {
479 volumes[i] = channel_volumes[i] * vol;
480 spa_log_debug(mix->log, "%d: %f * %f = %f", i, channel_volumes[i], vol, volumes[i]);
481 }
482
483 /** apply volumes per channel */
484 if (n_channel_volumes == src_chan) {
485 for (i = 0; i < dst_chan; i++) {
486 for (j = 0; j < src_chan; j++) {
487 mix->matrix[i][j] = mix->matrix_orig[i][j] * volumes[j];
488 }
489 }
490 } else if (n_channel_volumes == dst_chan) {
491 for (i = 0; i < dst_chan; i++) {
492 for (j = 0; j < src_chan; j++) {
493 mix->matrix[i][j] = mix->matrix_orig[i][j] * volumes[i];
494 }
495 }
496 }
497
498 SPA_FLAG_SET(mix->flags, CHANNELMIX_FLAG_ZERO);
499 SPA_FLAG_SET(mix->flags, CHANNELMIX_FLAG_EQUAL);
500 SPA_FLAG_SET(mix->flags, CHANNELMIX_FLAG_COPY);
501
502 t = 0.0;
503 for (i = 0; i < dst_chan; i++) {
504 for (j = 0; j < src_chan; j++) {
505 float v = mix->matrix[i][j];
506 spa_log_debug(mix->log, "%d %d: %f", i, j, v);
507 if (i == 0 && j == 0)
508 t = v;
509 else if (t != v)
510 SPA_FLAG_CLEAR(mix->flags, CHANNELMIX_FLAG_EQUAL);
511 if (v != 0.0)
512 SPA_FLAG_CLEAR(mix->flags, CHANNELMIX_FLAG_ZERO);
513 if ((i == j && v != 1.0f) ||
514 (i != j && v != 0.0f))
515 SPA_FLAG_CLEAR(mix->flags, CHANNELMIX_FLAG_COPY);
516 }
517 }
518 SPA_FLAG_UPDATE(mix->flags, CHANNELMIX_FLAG_IDENTITY,
519 dst_chan == src_chan && SPA_FLAG_IS_SET(mix->flags, CHANNELMIX_FLAG_COPY));
520
521 spa_log_debug(mix->log, "flags:%08x", mix->flags);
522 }
523
impl_channelmix_free(struct channelmix * mix)524 static void impl_channelmix_free(struct channelmix *mix)
525 {
526 mix->process = NULL;
527 }
528
channelmix_init(struct channelmix * mix)529 int channelmix_init(struct channelmix *mix)
530 {
531 const struct channelmix_info *info;
532
533 info = find_channelmix_info(mix->src_chan, mix->src_mask, mix->dst_chan, mix->dst_mask,
534 mix->cpu_flags);
535 if (info == NULL)
536 return -ENOTSUP;
537
538 spa_log_debug(mix->log, "selected %s", info->name);
539
540 mix->free = impl_channelmix_free;
541 mix->process = info->process;
542 mix->set_volume = impl_channelmix_set_volume;
543 mix->cpu_flags = info->cpu_flags;
544 return make_matrix(mix);
545 }
546