SAF
Loading...
Searching...
No Matches
test__cdf4sap_module.c
Go to the documentation of this file.
1/*
2 * Copyright 2020-2021 Leo McCormack
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10 * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
13 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14 * PERFORMANCE OF THIS SOFTWARE.
15 */
16
25#include "saf_test.h"
26
28 int i, j, it, nCHin, nCHout, lenSig;
29 float reg, tmp;
30 float** Q, **x, **y, **z, **Cx, **Cy, **Cz, **M, **Cr, **Mr, **Q_Cx, **Cp;
31 float** decor, **z_r, **eye_nCHout;
32 void* hCdf, *hCdf_res;
33
34 /* Config */
35 const float acceptedTolerance = 0.1f; /* Due to regularisation, the result will never be exact */
36 /* However, this is a very generous tolerance value. If the number of input
37 * and output channels are similar, then this tolerance can be much lower
38 * (0.00001). The error is only ever high when there is a large discrepency
39 * between the number of input and output channels. */
40 const int nIterations = 1000;
41
42 /* Loop through iterations */
43 for(it=0; it<nIterations; it++){
44 rand_0_1(&tmp, 1);
45 nCHin = (int)(tmp*12.0f + 4.1f); /* random number between 4 and 16 */
46 rand_0_1(&tmp, 1);
47 nCHout = (int)(tmp*12.0f + 4.1f); /* random number between 4 and 16 */
48 rand_0_1(&tmp, 1);
49 lenSig = (int)(tmp*384.0f + 128.1f); /* random number between 128 and 512 */
50
51 /* Define prototype decoder and compute input signal covariance matrix */
52 Q = (float**)calloc2d(nCHout, nCHin, sizeof(float));
53 for(i=0; i<SAF_MIN(nCHin, nCHout); i++)
54 Q[i][i] = 1.0f; /* Identity */
55 x = (float**)malloc2d(nCHin, lenSig, sizeof(float));
56 rand_m1_1(FLATTEN2D(x), nCHin*lenSig);
57 Cx = (float**)malloc2d(nCHin, nCHin, sizeof(float));
58 cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasTrans, nCHin, nCHin, lenSig, 1.0f,
59 FLATTEN2D(x), lenSig,
60 FLATTEN2D(x), lenSig, 0.0f,
61 FLATTEN2D(Cx), nCHin);
62
63 /* Compute target covariance matrix */
64 y = (float**)malloc2d(nCHout, lenSig, sizeof(float));
65 rand_m1_1(FLATTEN2D(y), nCHout*lenSig);
66 Cy = (float**)malloc2d(nCHout, nCHout, sizeof(float));
67 cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasTrans, nCHout, nCHout, lenSig, 1.0f,
68 FLATTEN2D(y), lenSig,
69 FLATTEN2D(y), lenSig, 0.0f,
70 FLATTEN2D(Cy), nCHout);
71
72 /* Compute optimal mixing matrix - with energy compensation enabled */
73 M = (float**)malloc2d(nCHout, nCHin, sizeof(float));
74 reg = 0.2f;
75 cdf4sap_create(&hCdf, nCHin, nCHout);
76 formulate_M_and_Cr(hCdf, FLATTEN2D(Cx), FLATTEN2D(Cy), FLATTEN2D(Q), 1, reg, FLATTEN2D(M), NULL);
77
78 /* Apply mixing matrix to 'x' and assert that it's covariance matrix matches
79 * the target covariance matrix */
80 z = (float**)malloc2d(nCHout, lenSig, sizeof(float));
81 cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nCHout, lenSig, nCHin, 1.0f,
82 FLATTEN2D(M), nCHin,
83 FLATTEN2D(x), lenSig, 0.0f,
84 FLATTEN2D(z), lenSig);
85 Cz = (float**)malloc2d(nCHout, nCHout, sizeof(float));
86 cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasTrans, nCHout, nCHout, lenSig, 1.0f,
87 FLATTEN2D(z), lenSig,
88 FLATTEN2D(z), lenSig, 0.0f,
89 FLATTEN2D(Cz), nCHout);
90 if(nCHin>=nCHout){
91 for(i=0; i<nCHout; i++)
92 for(j=0; j<nCHout; j++)
93 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, Cy[i][j], Cz[i][j]);
94 }
95 else{ /* if nCHin<nCHout, then only the diagonal elements will match */
96 for(i=0; i<nCHout; i++)
97 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, Cy[i][i], Cz[i][i]);
98 }
99
100 /* Determine prototype covariance matrix */
101 Q_Cx = (float**)malloc2d(nCHout, nCHin, sizeof(float));
102 cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nCHout, nCHin, nCHin, 1.0f,
103 FLATTEN2D(Q), nCHin,
104 FLATTEN2D(Cx), nCHin, 0.0f,
105 FLATTEN2D(Q_Cx), nCHin);
106 Cp = (float**)malloc2d(nCHout, nCHout, sizeof(float));
107 cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasTrans, nCHout, nCHout, nCHin, 1.0f,
108 FLATTEN2D(Q_Cx), nCHin,
109 FLATTEN2D(Q), nCHin, 0.0f,
110 FLATTEN2D(Cp), nCHout);
111 for(i=0; i<nCHout; i++)
112 for(j=0; j<nCHout; j++)
113 if(i!=j)
114 Cp[i][j] = 0.0f; /* Zero non-diagonal elements */
115
116 /* Create perfectly incoherent frame. Note, in practice this would instead
117 * be a decorrelated version of the prototype signals, [i.e.
118 * decorrelate(Q*x) ]*/
119 decor = (float**)malloc2d(nCHout, lenSig, sizeof(float));
120 rand_m1_1(FLATTEN2D(decor), nCHout*lenSig);
121
122 /* Now compute optimal mixing matrix, but this time also including the
123 * residual mixing matrix */
124 M = (float**)malloc2d(nCHout, nCHin, sizeof(float));
125 reg = 0.2f;
126 Cr = (float**)malloc2d(nCHout, nCHout, sizeof(float));
127 formulate_M_and_Cr(hCdf, FLATTEN2D(Cx), FLATTEN2D(Cy), FLATTEN2D(Q), 0, reg, FLATTEN2D(M), FLATTEN2D(Cr));
128 cdf4sap_create(&hCdf_res, nCHout, nCHout);
129 Mr = (float**)calloc2d(nCHout, nCHout, sizeof(float));
130 eye_nCHout = (float**)calloc2d(nCHout, nCHout, sizeof(float));
131 for(i=0; i<nCHout; i++)
132 eye_nCHout[i][i] = 1.0f;
133 formulate_M_and_Cr(hCdf_res, FLATTEN2D(Cp), FLATTEN2D(Cr), FLATTEN2D(eye_nCHout), 0, reg, FLATTEN2D(Mr), NULL);
134
135 /* Apply mixing matrix to x, and residual mixing matrix to the decorrelated
136 * prototype signals, and sum */
137 cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nCHout, lenSig, nCHin, 1.0f,
138 FLATTEN2D(M), nCHin,
139 FLATTEN2D(x), lenSig, 0.0f,
140 FLATTEN2D(z), lenSig);
141 z_r = (float**)malloc2d(nCHout, lenSig, sizeof(float));
142 cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nCHout, lenSig, nCHout, 1.0f,
143 FLATTEN2D(Mr), nCHout,
144 FLATTEN2D(decor), lenSig, 0.0f,
145 FLATTEN2D(z_r), lenSig);
146 cblas_saxpy(nCHout*lenSig, 1.0f, FLATTEN2D(z_r), 1, FLATTEN2D(z), 1);
147
148 /* Assert that the covariance matrix of 'z' matches the target covariance
149 * matrix */
150 if(nCHin>=nCHout){
151 for(i=0; i<nCHout; i++)
152 for(j=0; j<nCHout; j++)
153 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, Cy[i][j], Cz[i][j]);
154 }
155 else{ /* if nCHin<nCHout, then only the diagonal elements will match */
156 for(i=0; i<nCHout; i++)
157 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, Cy[i][i], Cz[i][i]);
158 }
159
160 /* Clean-up */
161 cdf4sap_destroy(&hCdf);
162 cdf4sap_destroy(&hCdf_res);
163 free(Q);
164 free(x);
165 free(y);
166 free(z);
167 free(Cx);
168 free(Cy);
169 free(Cz);
170 free(M);
171 free(Cr);
172 free(Mr);
173 free(Q_Cx);
174 free(Cp);
175 free(decor);
176 free(z_r);
177 free(eye_nCHout);
178 }
179}
180
182 int i, j, it, nCHin, nCHout, lenSig;
183 float reg, tmp;
184 float_complex** Q, **x, **y, **z, **Cx, **Cy, **Cz, **M, **Cr, **Mr, **Q_Cx, **Cp;
185 float_complex** decor, **z_r, **eye_nCHout;
186 void* hCdf, *hCdf_res;
187 const float_complex calpha = cmplxf(1.0f, 0.0f), cbeta = cmplxf(0.0f, 0.0f);
188
189 /* Config */
190 const float acceptedTolerance = 0.1f; /* Due to regularisation, the result will never be exact */
191 /* However, this is a very generous tolerance value. If the number of input
192 * and output channels are similar, then this tolerance can be much lower
193 * (0.00001). The error is only ever high when there is a large discrepency
194 * between the number of input and output channels. */
195 const int nIterations = 300;
196
197 /* Loop through iterations */
198 for(it=0; it<nIterations; it++){
199 rand_0_1(&tmp, 1);
200 nCHin = (int)(tmp*12.0f + 4.1f); /* random number between 4 and 16 */
201 rand_0_1(&tmp, 1);
202 nCHout = (int)(tmp*12.0f + 4.1f); /* random number between 4 and 16 */
203 rand_0_1(&tmp, 1);
204 lenSig = (int)(tmp*384.0f + 128.1f); /* random number between 128 and 512 */
205
206 /* Define prototype decoder and compute input signal covariance matrix */
207 Q = (float_complex**)calloc2d(nCHout, nCHin, sizeof(float_complex));
208 for(i=0; i<SAF_MIN(nCHin, nCHout); i++)
209 Q[i][i] = cmplxf(1.0f, 0.0f); /* Identity */
210 x = (float_complex**)malloc2d(nCHin, lenSig, sizeof(float_complex));
211 rand_cmplx_m1_1(FLATTEN2D(x), nCHin*lenSig);
212 Cx = (float_complex**)malloc2d(nCHin, nCHin, sizeof(float_complex));
213 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasConjTrans, nCHin, nCHin, lenSig, &calpha,
214 FLATTEN2D(x), lenSig,
215 FLATTEN2D(x), lenSig, &cbeta,
216 FLATTEN2D(Cx), nCHin);
217
218 /* Compute target covariance matrix */
219 y = (float_complex**)malloc2d(nCHout, lenSig, sizeof(float_complex));
220 rand_cmplx_m1_1(FLATTEN2D(y), nCHout*lenSig);
221 Cy = (float_complex**)malloc2d(nCHout, nCHout, sizeof(float_complex));
222 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasConjTrans, nCHout, nCHout, lenSig, &calpha,
223 FLATTEN2D(y), lenSig,
224 FLATTEN2D(y), lenSig, &cbeta,
225 FLATTEN2D(Cy), nCHout);
226
227 /* Compute optimal mixing matrix - with energy compensation enabled */
228 M = (float_complex**)malloc2d(nCHout, nCHin, sizeof(float_complex));
229 reg = 0.2f;
230 cdf4sap_cmplx_create(&hCdf, nCHin, nCHout);
231 formulate_M_and_Cr_cmplx(hCdf, FLATTEN2D(Cx), FLATTEN2D(Cy), FLATTEN2D(Q), 1, reg, FLATTEN2D(M), NULL);
232
233 /* Apply mixing matrix to 'x' and assert that it's covariance matrix matches
234 * the target covariance matrix */
235 z = (float_complex**)malloc2d(nCHout, lenSig, sizeof(float_complex));
236 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nCHout, lenSig, nCHin, &calpha,
237 FLATTEN2D(M), nCHin,
238 FLATTEN2D(x), lenSig, &cbeta,
239 FLATTEN2D(z), lenSig);
240 Cz = (float_complex**)malloc2d(nCHout, nCHout, sizeof(float_complex));
241 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasConjTrans, nCHout, nCHout, lenSig, &calpha,
242 FLATTEN2D(z), lenSig,
243 FLATTEN2D(z), lenSig, &cbeta,
244 FLATTEN2D(Cz), nCHout);
245 if(nCHin>=nCHout){
246 for(i=0; i<nCHout; i++){
247 for(j=0; j<nCHout; j++){
248 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, crealf(Cy[i][j]), crealf(Cz[i][j]));
249 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, cimagf(Cy[i][j]), cimagf(Cz[i][j]));
250 }
251 }
252 }
253 else{ /* if nCHin<nCHout, then only the diagonal elements will match */
254 for(i=0; i<nCHout; i++){
255 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, crealf(Cy[i][i]), crealf(Cz[i][i]));
256 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, cimagf(Cy[i][i]), cimagf(Cz[i][i]));
257 }
258 }
259
260 /* Determine prototype covariance matrix */
261 Q_Cx = (float_complex**)malloc2d(nCHout, nCHin, sizeof(float_complex));
262 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nCHout, nCHin, nCHin, &calpha,
263 FLATTEN2D(Q), nCHin,
264 FLATTEN2D(Cx), nCHin, &cbeta,
265 FLATTEN2D(Q_Cx), nCHin);
266 Cp = (float_complex**)malloc2d(nCHout, nCHout, sizeof(float_complex));
267 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasConjTrans, nCHout, nCHout, nCHin, &calpha,
268 FLATTEN2D(Q_Cx), nCHin,
269 FLATTEN2D(Q), nCHin, &cbeta,
270 FLATTEN2D(Cp), nCHout);
271 for(i=0; i<nCHout; i++)
272 for(j=0; j<nCHout; j++)
273 if(i!=j)
274 Cp[i][j] = cmplxf(0.0f, 0.0f); /* Zero non-diagonal elements */
275
276 /* Create perfectly incoherent frame. Note, in practice this would instead
277 * be a decorrelated version of the prototype signals, [i.e.
278 * decorrelate(Q*x) ]*/
279 decor = (float_complex**)malloc2d(nCHout, lenSig, sizeof(float_complex));
280 rand_cmplx_m1_1(FLATTEN2D(decor), nCHout*lenSig);
281
282 /* Now compute optimal mixing matrix, but this time also including the
283 * residual mixing matrix */
284 M = (float_complex**)malloc2d(nCHout, nCHin, sizeof(float_complex));
285 reg = 0.2f;
286 Cr = (float_complex**)malloc2d(nCHout, nCHout, sizeof(float_complex));
287 formulate_M_and_Cr_cmplx(hCdf, FLATTEN2D(Cx), FLATTEN2D(Cy), FLATTEN2D(Q), 0, reg, FLATTEN2D(M), FLATTEN2D(Cr));
288 cdf4sap_cmplx_create(&hCdf_res, nCHout, nCHout);
289 Mr = (float_complex**)calloc2d(nCHout, nCHout, sizeof(float_complex));
290 eye_nCHout = (float_complex**)calloc2d(nCHout, nCHout, sizeof(float_complex));
291 for(i=0; i<nCHout; i++)
292 eye_nCHout[i][i] = cmplxf(1.0f, 0.0f);
293 formulate_M_and_Cr_cmplx(hCdf_res, FLATTEN2D(Cp), FLATTEN2D(Cr), FLATTEN2D(eye_nCHout), 0, reg, FLATTEN2D(Mr), NULL);
294
295 /* Apply mixing matrix to x, and residual mixing matrix to the decorrelated
296 * prototype signals, and sum */
297 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nCHout, lenSig, nCHin, &calpha,
298 FLATTEN2D(M), nCHin,
299 FLATTEN2D(x), lenSig, &cbeta,
300 FLATTEN2D(z), lenSig);
301 z_r = (float_complex**)malloc2d(nCHout, lenSig, sizeof(float_complex));
302 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nCHout, lenSig, nCHout, &calpha,
303 FLATTEN2D(Mr), nCHout,
304 FLATTEN2D(decor), lenSig, &cbeta,
305 FLATTEN2D(z_r), lenSig);
306 cblas_saxpy(/*re+im*/2*nCHout*lenSig, 1.0f, (const float*)FLATTEN2D(z_r), 1, (float*)FLATTEN2D(z), 1);
307
308 /* Assert that the covariance matrix of 'z' matches the target covariance
309 * matrix */
310 if(nCHin>=nCHout){
311 for(i=0; i<nCHout; i++){
312 for(j=0; j<nCHout; j++){
313 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, crealf(Cy[i][j]), crealf(Cz[i][j]));
314 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, cimagf(Cy[i][j]), cimagf(Cz[i][j]));
315 }
316 }
317 }
318 else{ /* if nCHin<nCHout, then only the diagonal elements will match */
319 for(i=0; i<nCHout; i++){
320 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, crealf(Cy[i][i]), crealf(Cz[i][i]));
321 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, cimagf(Cy[i][i]), cimagf(Cz[i][i]));
322 }
323 }
324
325 /* Clean-up */
327 cdf4sap_cmplx_destroy(&hCdf_res);
328 free(Q);
329 free(x);
330 free(y);
331 free(z);
332 free(Cx);
333 free(Cy);
334 free(Cz);
335 free(M);
336 free(Cr);
337 free(Mr);
338 free(Q_Cx);
339 free(Cp);
340 free(decor);
341 free(z_r);
342 free(eye_nCHout);
343 }
344}
345
void formulate_M_and_Cr_cmplx(void *const hCdf, float_complex *Cx, float_complex *Cy, float_complex *Q, int useEnergyFLAG, float reg, float_complex *M, float_complex *Cr)
Computes the optimal mixing matrices.
void cdf4sap_create(void **const phCdf, int nXcols, int nYcols)
Creates an instance of the Covariance Domain Framework.
Definition saf_cdf4sap.c:85
void cdf4sap_cmplx_destroy(void **const phCdf)
Destroys an instance of the Covariance Domain Framework.
void cdf4sap_destroy(void **const phCdf)
Destroys an instance of the Covariance Domain Framework.
void cdf4sap_cmplx_create(void **const phCdf, int nXcols, int nYcols)
Creates an instance of the Covariance Domain Framework.
void formulate_M_and_Cr(void *const hCdf, float *Cx, float *Cy, float *Q, int useEnergyFLAG, float reg, float *M, float *Cr)
Computes the optimal mixing matrices.
void rand_0_1(float *vector, int length)
Generates random numbers between 0 and 1 and stores them in the input vector.
void rand_cmplx_m1_1(float_complex *vector, int length)
Generates random numbers between -1 and 1 and stores them in the input vector for both the real and i...
#define SAF_MIN(a, b)
Returns the minimum of the two values.
void rand_m1_1(float *vector, int length)
Generates random numbers between -1 and 1 and stores them in the input vector.
void ** malloc2d(size_t dim1, size_t dim2, size_t data_size)
2-D malloc (contiguously allocated, so use free() as usual to deallocate)
Definition md_malloc.c:89
void ** calloc2d(size_t dim1, size_t dim2, size_t data_size)
2-D calloc (contiguously allocated, so use free() as usual to deallocate)
Definition md_malloc.c:102
#define FLATTEN2D(A)
Use this macro when passing a 2-D dynamic multi-dimensional array to memset, memcpy or any other func...
Definition md_malloc.h:65
Unit test program for the Spatial_Audio_Framework.
void test__formulate_M_and_Cr(void)
Testing the formulate_M_and_Cr() function, and verifying that the output mixing matrices yield signal...
void test__formulate_M_and_Cr_cmplx(void)
Testing the formulate_M_and_Cr_cmplx() function, and verifying that the output mixing matrices yield ...