1 /* Spa
2  *
3  * Copyright © 2019 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 <stdlib.h>
28 #include <unistd.h>
29 #include <errno.h>
30 #include <time.h>
31 
32 #include <spa/support/log-impl.h>
33 #include <spa/debug/mem.h>
34 
35 SPA_LOG_IMPL(logger);
36 
37 #define MATRIX(...) (float[]) { __VA_ARGS__ }
38 
39 #include "channelmix-ops.c"
dump_matrix(struct channelmix * mix,float * coeff)40 static void dump_matrix(struct channelmix *mix, float *coeff)
41 {
42 	uint32_t i, j;
43 
44 	for (i = 0; i < mix->dst_chan; i++) {
45 		for (j = 0; j < mix->src_chan; j++) {
46 			float v = mix->matrix_orig[i][j];
47 			spa_log_debug(mix->log, "%d %d: %f <-> %f", i, j, v, *coeff);
48 			spa_assert_se(fabs(v - *coeff) < 0.000001);
49 			coeff++;
50 		}
51 	}
52 }
53 
test_mix(uint32_t src_chan,uint32_t src_mask,uint32_t dst_chan,uint32_t dst_mask,float * coeff)54 static void test_mix(uint32_t src_chan, uint32_t src_mask, uint32_t dst_chan, uint32_t dst_mask, float *coeff)
55 {
56 	struct channelmix mix;
57 
58 	spa_log_debug(&logger.log, "start %d->%d (%08x -> %08x)", src_chan, dst_chan, src_mask, dst_mask);
59 
60 	spa_zero(mix);
61 	mix.src_chan = src_chan;
62 	mix.dst_chan = dst_chan;
63 	mix.src_mask = src_mask;
64 	mix.dst_mask = dst_mask;
65 	mix.log = &logger.log;
66 
67 	channelmix_init(&mix);
68 	dump_matrix(&mix, coeff);
69 }
70 
test_1_N_MONO(void)71 static void test_1_N_MONO(void)
72 {
73 	test_mix(1, _M(MONO), 2, _M(FL)|_M(FR),
74 			MATRIX(1.0, 1.0));
75 	test_mix(1, _M(MONO), 3, _M(FL)|_M(FR)|_M(LFE),
76 			MATRIX(1.0, 1.0, 1.0));
77 	test_mix(1, _M(MONO), 4, _M(FL)|_M(FR)|_M(LFE)|_M(FC),
78 			MATRIX(1.0, 1.0, 1.0, 1.0));
79 	test_mix(1, _M(MONO), 4, _M(FL)|_M(FR)|_M(RL)|_M(RR),
80 			MATRIX(1.0, 1.0, 1.0, 1.0));
81 	test_mix(1, _M(MONO), 12, 0,
82 			MATRIX(1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
83 			       1.0, 1.0, 1.0, 1.0, 1.0, 1.0));
84 }
85 
test_1_N_FC(void)86 static void test_1_N_FC(void)
87 {
88 	test_mix(1, _M(FC), 2, _M(FL)|_M(FR),
89 			MATRIX(0.707107, 0.707107));
90 	test_mix(1, _M(FC), 3, _M(FL)|_M(FR)|_M(LFE),
91 			MATRIX(0.707107, 0.707107, 0.0));
92 	test_mix(1, _M(FC), 4, _M(FL)|_M(FR)|_M(LFE)|_M(FC),
93 			MATRIX(0.0, 0.0, 1.0, 0.0));
94 	test_mix(1, _M(FC), 4, _M(FL)|_M(FR)|_M(RL)|_M(RR),
95 			MATRIX(0.707107, 0.707107, 0.0, 0.0));
96 	test_mix(1, _M(FC), 12, 0,
97 			MATRIX(1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
98 			       1.0, 1.0, 1.0, 1.0, 1.0, 1.0));
99 }
100 
test_N_1(void)101 static void test_N_1(void)
102 {
103 	test_mix(1, _M(MONO), 1, _M(MONO),
104 			MATRIX(1.0));
105 	test_mix(1, _M(MONO), 1, _M(FC),
106 			MATRIX(1.0));
107 	test_mix(1, _M(FC), 1, _M(MONO),
108 			MATRIX(1.0));
109 	test_mix(1, _M(FC), 1, _M(FC),
110 			MATRIX(1.0));
111 	test_mix(2, _M(FL)|_M(FR), 1, _M(MONO),
112 			MATRIX(0.707107, 0.707107));
113 	test_mix(12, 0, 1, _M(MONO),
114 			MATRIX(0.083333, 0.083333, 0.083333, 0.083333, 0.083333, 0.083333,
115 			       0.083333, 0.083333, 0.083333, 0.083333, 0.083333, 0.0833333));
116 }
117 
test_3p1_N(void)118 static void test_3p1_N(void)
119 {
120 	test_mix(4, _M(FL)|_M(FR)|_M(LFE)|_M(FC), 1, _M(MONO),
121 			MATRIX(0.707107, 0.707107, 1.0, 0.0));
122 	test_mix(4, _M(FL)|_M(FR)|_M(LFE)|_M(FC), 2, _M(FL)|_M(FR),
123 			MATRIX(1.0, 0.0, 0.707107, 0.0,
124 			       0.0, 1.0, 0.707107, 0.0 ));
125 	test_mix(4, _M(FL)|_M(FR)|_M(LFE)|_M(FC), 3, _M(FL)|_M(FR)|_M(LFE),
126 			MATRIX(1.0, 0.0, 0.707107, 0.0,
127 			       0.0, 1.0, 0.707107, 0.0,
128 			       0.0, 0.0, 0.0, 1.0 ));
129 	test_mix(4, _M(FL)|_M(FR)|_M(LFE)|_M(FC), 4, _M(FL)|_M(FR)|_M(LFE)|_M(FC),
130 			MATRIX(1.0, 0.0, 0.0, 0.0,
131 			       0.0, 1.0, 0.0, 0.0,
132 			       0.0, 0.0, 1.0, 0.0,
133 			       0.0, 0.0, 0.0, 1.0,));
134 	test_mix(4, _M(FL)|_M(FR)|_M(LFE)|_M(FC), 4, _M(FL)|_M(FR)|_M(RL)|_M(RR),
135 			MATRIX(1.0, 0.0, 0.707107, 0.0,
136 			       0.0, 1.0, 0.707107, 0.0,
137 			       0.0, 0.0, 0.0, 0.0,
138 			       0.0, 0.0, 0.0, 0.0,));
139 }
140 
test_4_N(void)141 static void test_4_N(void)
142 {
143 	test_mix(4, _M(FL)|_M(FR)|_M(RL)|_M(RR), 1, _M(MONO),
144 			MATRIX(0.707107, 0.707107, 0.5, 0.5));
145 	test_mix(4, _M(FL)|_M(FR)|_M(SL)|_M(SR), 1, _M(MONO),
146 			MATRIX(0.707107, 0.707107, 0.5, 0.5));
147 	test_mix(4, _M(FL)|_M(FR)|_M(RL)|_M(RR), 2, _M(FL)|_M(FR),
148 			MATRIX(1.0, 0.0, 0.707107, 0.0,
149 			       0.0, 1.0, 0.0, 0.707107));
150 	test_mix(4, _M(FL)|_M(FR)|_M(SL)|_M(SR), 2, _M(FL)|_M(FR),
151 			MATRIX(1.0, 0.0, 0.707107, 0.0,
152 			       0.0, 1.0, 0.0, 0.707107));
153 	test_mix(4, _M(FL)|_M(FR)|_M(RL)|_M(RR), 3, _M(FL)|_M(FR)|_M(LFE),
154 			MATRIX(1.0, 0.0, 0.707107, 0.0,
155 			       0.0, 1.0, 0.0, 0.707107,
156 			       0.0, 0.0, 0.0, 0.0));
157 	test_mix(4, _M(FL)|_M(FR)|_M(RL)|_M(RR), 4, _M(FL)|_M(FR)|_M(RL)|_M(RR),
158 			MATRIX(1.0, 0.0, 0.0, 0.0,
159 			       0.0, 1.0, 0.0, 0.0,
160 			       0.0, 0.0, 1.0, 0.0,
161 			       0.0, 0.0, 0.0, 1.0));
162 	test_mix(4, _M(FL)|_M(FR)|_M(RL)|_M(RR), 4, _M(FL)|_M(FR)|_M(LFE)|_M(FC),
163 			MATRIX(1.0, 0.0, 0.707107, 0.0,
164 			       0.0, 1.0, 0.0, 0.707107,
165 			       0.0, 0.0, 0.0, 0.0,
166 			       0.0, 0.0, 0.0, 0.0));
167 }
168 
test_5p1_N(void)169 static void test_5p1_N(void)
170 {
171 	test_mix(6, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR), 1, _M(MONO),
172 			MATRIX(0.707107, 0.707107, 1.0, 0.0, 0.5, 0.5));
173 	test_mix(6, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR), 2, _M(FL)|_M(FR),
174 			MATRIX(1.0, 0.0, 0.707107, 0.0, 0.707107, 0.0,
175 			       0.0, 1.0, 0.707107, 0.0, 0.0, 0.707107));
176 	test_mix(6, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(RL)|_M(RR), 2, _M(FL)|_M(FR),
177 			MATRIX(1.0, 0.0, 0.707107, 0.0, 0.707107, 0.0,
178 			       0.0, 1.0, 0.707107, 0.0, 0.0, 0.707107));
179 	test_mix(6, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR), 3, _M(FL)|_M(FR)|_M(LFE),
180 			MATRIX(1.0, 0.0, 0.707107, 0.0, 0.707107, 0.0,
181 			       0.0, 1.0, 0.707107, 0.0, 0.0, 0.707107,
182 			       0.0, 0.0, 0.0, 1.0, 0.0, 0.0));
183 	test_mix(6, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR), 4, _M(FL)|_M(FR)|_M(LFE)|_M(FC),
184 			MATRIX(1.0, 0.0, 0.0, 0.0, 0.707107, 0.0,
185 			       0.0, 1.0, 0.0, 0.0, 0.0, 0.707107,
186 			       0.0, 0.0, 1.0, 0.0, 0.0, 0.0,
187 			       0.0, 0.0, 0.0, 1.0, 0.0, 0.0));
188 	test_mix(6, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR), 4, _M(FL)|_M(FR)|_M(RL)|_M(RR),
189 			MATRIX(1.0, 0.0, 0.707107, 0.0, 0.0, 0.0,
190 			       0.0, 1.0, 0.707107, 0.0, 0.0, 0.0,
191 			       0.0, 0.0, 0.0, 0.0, 1.0, 0.0,
192 			       0.0, 0.0, 0.0, 0.0, 0.0, 1.0));
193 	test_mix(6, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR), 5, _M(FL)|_M(FR)|_M(FC)|_M(SL)|_M(SR),
194 			MATRIX(1.0, 0.0, 0.0, 0.0, 0.0, 0.0,
195 			       0.0, 1.0, 0.0, 0.0, 0.0, 0.0,
196 			       0.0, 0.0, 1.0, 0.0, 0.0, 0.0,
197 			       0.0, 0.0, 0.0, 0.0, 1.0, 0.0,
198 			       0.0, 0.0, 0.0, 0.0, 0.0, 1.0));
199 	test_mix(6, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR), 6, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR),
200 			MATRIX(1.0, 0.0, 0.0, 0.0, 0.0, 0.0,
201 			       0.0, 1.0, 0.0, 0.0, 0.0, 0.0,
202 			       0.0, 0.0, 1.0, 0.0, 0.0, 0.0,
203 			       0.0, 0.0, 0.0, 1.0, 0.0, 0.0,
204 			       0.0, 0.0, 0.0, 0.0, 1.0, 0.0,
205 			       0.0, 0.0, 0.0, 0.0, 0.0, 1.0));
206 }
207 
test_7p1_N(void)208 static void test_7p1_N(void)
209 {
210 	test_mix(8, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR)|_M(RL)|_M(RR), 1, _M(MONO),
211 			MATRIX(0.707107, 0.707107, 1.0, 0.0, 0.5, 0.5, 0.5, 0.5));
212 	test_mix(8, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR)|_M(RL)|_M(RR), 2, _M(FL)|_M(FR),
213 			MATRIX(1.0, 0.0, 0.707107, 0.0, 0.707107, 0.0, 0.707107, 0.0,
214 			       0.0, 1.0, 0.707107, 0.0, 0.0, 0.707107, 0.0, 0.707107));
215 }
216 
main(int argc,char * argv[])217 int main(int argc, char *argv[])
218 {
219 	logger.log.level = SPA_LOG_LEVEL_TRACE;
220 
221 	test_1_N_MONO();
222 	test_1_N_FC();
223 	test_N_1();
224 	test_3p1_N();
225 	test_4_N();
226 	test_5p1_N();
227 	test_7p1_N();
228 
229 	return 0;
230 }
231