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