SAF
Loading...
Searching...
No Matches
test__utilities_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
24
25#include "saf_test.h"
26
28 for (int i = 0; i < 2; i++)
30 for (int i = 0; i < 3; i++)
32 for (int i = 0; i < 4; i++)
34 for (int i = 0; i < 5; i++)
36 for (int i = 0; i < 7; i++)
38 for (int i = 0; i < 9; i++)
40 for (int i = 0; i < 7; i++)
42 for (int i = 0; i < 9; i++)
44 for (int i = 0; i < 11; i++)
46 for (int i = 0; i < 13; i++)
48 for (int i = 0; i < 13; i++)
50 for (int i = 0; i < 15; i++)
52}
53
54void test__cylindricalBesselFunctions(void){ // TODO: may as well check the derivatives too...
55 int i;
56 double J_n[10], Y_n[10];
57
58 /* Config */
59 const float acceptedTolerance = 0.00001f;
60 int testOrder = 7; /* note, REF values hardcoded for order 7 */
61 double z[10] = {0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}; /* note, REF values hardcoded for these values */
62
63 /* Reference values computed in MATLAB with:
64 * J_n = besselj(N, z);
65 * y_n = bessely(N, z);
66 */
67 double J_nREF[10] = {0.0, 1.50232581743681e-06, 0.000174944074868274, 0.00254729445180469, 0.0151760694220584, 0.0533764101558907, 0.129586651841481, 0.233583569505696, 0.320589077979826, 0.327460879242453};
68 double Y_nREF[10] = {0.0, -30588.9570521240, -271.548025367994, -19.8399354089864, -3.70622393164077, -1.26289883576932, -0.656590825719075, -0.405371018606768, -0.200063904600409, 0.0172445799076681};
69
70 /* test bessel_Jn */
71 bessel_Jn(testOrder, z, 10, J_n, NULL);
72 for(i=0; i<10; i++)
73 TEST_ASSERT_TRUE(fabs(J_n[i]-J_nREF[i])<acceptedTolerance);
74
75 /* test bessel_Yn */
76 bessel_Yn(testOrder, z, 10, Y_n, NULL);
77 for(i=0; i<10; i++)
78 TEST_ASSERT_TRUE(fabs(Y_n[i]-Y_nREF[i])<acceptedTolerance);
79}
80
81void test__sphericalBesselFunctions(void){ // TODO: may as well check the derivatives too...
82 int i, successFlag;
83 double j_n[10], i_n[10], y_n[10], k_n[10];
84
85 /* Config */
86 const float acceptedTolerance = 0.00001f;
87 int testOrder = 7; /* note, REF values hardcoded for order 7 */
88 double z[10] = {0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}; /* note, REF values hardcoded for these values */
89
90 /* Reference values computed in MATLAB with:
91 * j_n = sqrt(pi./(2*z)).*besselj(N+0.5, z);
92 * i_n = sqrt(pi./(2*z)).*besseli(N+0.5, z);
93 * y_n = sqrt(pi./(2*z)).*bessely(N+0.5, z);
94 * k_n = sqrt(pi./(2*z)).*besselk(N+0.5, z);
95 */
96 double j_nREF[10] = {0.0, 4.79013419873948e-07, 5.60965570334894e-05, 0.000824843253217635, 0.00498650846172602, 0.0179027781779895, 0.0447223808293482, 0.0839226228445072, 0.122272711565833, 0.137946585027486};
97 double i_nREF[10] = {0.0, 5.08036087257580e-07, 7.09794452304064e-05, 0.00140087680258227, 0.0127983365433790, 0.0783315436379810, 0.377879458299915, 1.56419501808402, 5.83626393050750, 20.2384754394417};
98 double y_nREF[10] = {0.0, -140452.852366906, -617.054329642527, -29.4761692244538, -3.98778927238432, -1.02739463881260, -0.425887203702750, -0.237025274765842, -0.132622247946352, -0.0402143438632017};
99 double k_nREF[10] = {0.0, 204287.522076393, 712.406907885478, 23.1153112578315, 1.80293583642309, 0.222213613092395, 0.0360276414091966, 0.00698538879470478, 0.00153285534574965, 0.000367847412220325};
100
101 /* test bessel_jn */
102 successFlag = bessel_jn(testOrder, z, 10, j_n, NULL);
103 TEST_ASSERT_TRUE(successFlag);
104 for(i=0; i<10; i++)
105 TEST_ASSERT_TRUE(fabs(j_n[i]-j_nREF[i])<acceptedTolerance);
106
107 /* test bessel_in */
108 successFlag = bessel_in(testOrder, z, 10, i_n, NULL);
109 TEST_ASSERT_TRUE(successFlag);
110 for(i=0; i<10; i++)
111 TEST_ASSERT_TRUE(fabs(i_n[i]-i_nREF[i])<acceptedTolerance);
112
113 /* test bessel_yn */
114 successFlag = bessel_yn(testOrder, z, 10, y_n, NULL);
115 TEST_ASSERT_TRUE(successFlag);
116 for(i=0; i<10; i++)
117 TEST_ASSERT_TRUE(fabs(y_n[i]-y_nREF[i])<acceptedTolerance);
118
119 /* test bessel_kn */
120 successFlag = bessel_kn(testOrder, z, 10, k_n, NULL);
121 TEST_ASSERT_TRUE(successFlag);
122 for(i=0; i<10; i++)
123 TEST_ASSERT_TRUE(fabs(k_n[i]-k_nREF[i])<acceptedTolerance);
124}
125
126void test__cart2sph(void){
127 const float acceptedTolerance = 0.00001f;
128 float cordCar [100][3];
129 float cordSph [100][3];
130 float cordCarTest [100][3];
131
132 /* Generate some random Cartesian coordinates */
133 rand_m1_1((float*) cordCar, 100*3);
134
135 /* rad */
136 cart2sph((float*) cordCar, 100, SAF_FALSE, (float*) cordSph);
137 sph2cart((float*) cordSph, 100, SAF_FALSE, (float*) cordCarTest);
138 for (int i=0; i<100; i++)
139 for (int j=0; j<3; j++)
140 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, cordCar[i][j], cordCarTest[i][j]);
141
142 /* deg */
143 cart2sph((float*) cordCar, 100, SAF_TRUE, (float*) cordSph);
144 sph2cart((float*) cordSph, 100, SAF_TRUE, (float*) cordCarTest);
145 for (int i=0; i<100; i++)
146 for (int j=0; j<3; j++)
147 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, cordCar[i][j], cordCarTest[i][j]);
148}
149
151 int nMesh;
152 int* mesh;
153
154 /* Not really a unit test... You have to copy the mesh indices into e.g. Matlab, plot, and see... */
155
156 /* 2D 3 points */
157 float three_xy[3][2] =
158 { {7.0f,7.0f},{2.0f,7.0f},{2.0f,1.0f} };
159 mesh = NULL;
160 delaunaynd((float*)three_xy, 3, 2/*nDims*/, &mesh, &nMesh);
161 free(mesh);
162
163 /* 2D 4 points */
164 float four_xy[4][2] =
165 { {7.0f,7.0f},{2.0f,7.0f},{2.0f,1.0f},{7.0f,1.0f} };
166 mesh = NULL;
167 delaunaynd((float*)four_xy, 4, 2/*nDims*/, &mesh, &nMesh);
168 free(mesh);
169
170 /* 2D Square */
171 float square_xy[26][2] =
172 { {-1.0f,-1.0f},{-1.0f,-0.5f},{-1.0f,0.0f},{-1.0f,0.5f},{-1.0f,1.0f},{-0.5f,-1.0f},{-0.5f,-0.5f},{-0.5f,0.0f},{-0.5f,0.5f},
173 {-0.5f,1.0f},{0.0f,-1.0f},{0.0f,-0.5f},{0.0f,0.0f},{0.0f,0.5f},{0.0f,1.0f},{0.5f,-1.0f},
174 {0.5f,-0.5f},{0.5f,0.0f},{0.5f,0.5f},{0.5f,1.0f},{1.0f,-1.0f},{1.0f,-0.5f},
175 {1.0f,0.0f},{1.0f,0.5f},{1.0f,1.0f},{0.0f,0.0f} };
176 mesh = NULL;
177 delaunaynd((float*)square_xy, 26, 2/*nDims*/, &mesh, &nMesh);
178 free(mesh);
179
180 /* 3D Cube */
181 float cube_xyz[8][3] =
182 { {-1.0f,-1.0f,-1.0f},{-1.0f,1.0f,-1.0f},{1.0f,-1.0f,-1.0f},{1.0f,1.0f,-1.0f},
183 {-1.0f,-1.0f,1.0f}, {-1.0f,1.0f,1.0f}, {1.0f,-1.0f,1.0f}, {1.0f,1.0f,1.0f} };
184 mesh = NULL;
185 delaunaynd((float*)cube_xyz, 8, 3/*nDims*/, &mesh, &nMesh);
186 free(mesh);
187
188 /* 3D Cube with a point in the centre */
189 float cube_xyz2[9][3] =
190 { {-1.0f,-1.0f,-1.0f},{-1.0f,1.0f,-1.0f},{1.0f,-1.0f,-1.0f},{1.0f,1.0f,-1.0f},
191 {-1.0f,-1.0f,1.0f}, {-1.0f,1.0f,1.0f}, {1.0f,-1.0f,1.0f}, {1.0f,1.0f,1.0f}, {0.0f,0.0f,0.0f} };
192 mesh = NULL;
193 delaunaynd((float*)cube_xyz2, 9, 3/*nDims*/, &mesh, &nMesh);
194 free(mesh);
195}
196
198 int i, j;
199 float norm;
200 float rot[3][3], rot2[3][3], residual[9], ypr[3], test_ypr[3];
201 quaternion_data Q, Q1, Q2;
202
203 for(i=0; i<1000; i++){
204 /* Randomise the quaternion values */
205 rand_m1_1((float*)Q.Q, 4);
206
207 /* Normalise to make it valid */
208 norm = L2_norm((float*)Q.Q, 4);
209 Q.w /= norm;
210 Q.x /= norm;
211 Q.y /= norm;
212 Q.z /= norm;
213 /* Q.w = 0; Q.x = 0.0000563298236; Q.y = 0.947490811; Q.z = -0.319783032; // Problem case! */
214
215 /* Convert to rotation matrix, then back, then to rotation matrix again */
218 quaternion2rotationMatrix(&Q1, rot2);
219
220 /* Ensure that the difference between them is 0 */
221 utility_svvsub((float*)rot, (float*)rot2, 9, residual);
222 for(j=0; j<9; j++)
223 TEST_ASSERT_TRUE(fabsf(residual[j])<1e-3f);
224
225 /* Testing that quaternion2euler() and euler2Quaternion() are invertable */
226 quaternion2euler(&Q1, 1, EULER_ROTATION_YAW_PITCH_ROLL, &ypr[0], &ypr[1], &ypr[2]);
227 euler2Quaternion(ypr[0], ypr[1], ypr[2], 1, EULER_ROTATION_YAW_PITCH_ROLL, &Q2);
228 quaternion2euler(&Q2, 1, EULER_ROTATION_YAW_PITCH_ROLL, &test_ypr[0], &test_ypr[1], &test_ypr[2]);
229 for(j=0; j<3; j++)
230 TEST_ASSERT_TRUE(fabsf(test_ypr[j]-ypr[j])<1e-2f);
231 }
232}
233
235 int frame, winsize, hopsize, nFrames, ch, i, nBands, nTimesSlots, band;
236 void* hSTFT;
237 float** insig, **outsig, **inframe, **outframe;
238 float_complex*** inspec, ***outspec;
239
240 /* prep */
241 const float acceptedTolerance = 0.000001f;
242 const int fs = 48000;
243 const int signalLength = 1*fs;
244 const int framesize = 512;
245 const int nCHin = 62;
246 const int nCHout = 64;
247 insig = (float**)malloc2d(nCHin,signalLength,sizeof(float)); /* One second long */
248 outsig = (float**)malloc2d(nCHout,signalLength,sizeof(float));
249 inframe = (float**)malloc2d(nCHin,framesize,sizeof(float));
250 outframe = (float**)malloc2d(nCHout,framesize,sizeof(float));
251 rand_m1_1(FLATTEN2D(insig), nCHin*signalLength); /* populate with random numbers */
252
253 /* Set-up STFT for 50% overlapping */
254 winsize = 128;
255 hopsize = winsize/2;
256 nBands = winsize+1;
257 nTimesSlots = framesize/hopsize;
258 inspec = (float_complex***)malloc3d(nBands, nCHin, nTimesSlots, sizeof(float_complex));
259 outspec = (float_complex***)malloc3d(nBands, nCHout, nTimesSlots, sizeof(float_complex));
260 saf_stft_create(&hSTFT, winsize, hopsize, nCHin, nCHout, SAF_STFT_BANDS_CH_TIME);
261 saf_stft_channelChange(hSTFT, 123, 7); /* messing about */
262 saf_stft_flushBuffers(hSTFT); /* messing about */
263 saf_stft_channelChange(hSTFT, nCHin, nCHout); /* change back */
264
265 /* Pass insig through STFT, block-wise processing */
266 nFrames = (int)((float)signalLength/(float)framesize);
267 for(frame = 0; frame<nFrames; frame++){
268 /* Forward */
269 for(ch=0; ch<nCHin; ch++)
270 memcpy(inframe[ch], &insig[ch][frame*framesize], framesize*sizeof(float));
271 saf_stft_forward(hSTFT, inframe, framesize, inspec);
272
273 /* Copy first channel of inspec to all outspec channels */
274 for(band=0; band<nBands; band++)
275 for(ch=0; ch<nCHout; ch++)
276 memcpy(outspec[band][ch], inspec[band][0], nTimesSlots*sizeof(float_complex));
277
278 /* Backward */
279 saf_stft_backward(hSTFT, outspec, framesize, outframe);
280 for(ch=0; ch<nCHout; ch++)
281 memcpy(&outsig[ch][frame*framesize], outframe[ch], framesize*sizeof(float));
282 }
283
284 /* Check that input==output (given some numerical precision) */
285 for(i=0; i<signalLength-framesize; i++)
286 TEST_ASSERT_TRUE( fabsf(insig[0][i] - outsig[0][i+hopsize]) <= acceptedTolerance );
287
288 /* Clean-up */
289 saf_stft_destroy(&hSTFT);
290 free(insig);
291 free(outsig);
292 free(inframe);
293 free(outframe);
294 free(inspec);
295 free(outspec);
296}
297
299 int frame, winsize, hopsize, nFrames, ch, i, nBands, nTimesSlots, band;
300 void* hSTFT;
301 float** insig, **outsig, **inframe, **outframe;
302 float_complex*** inspec, ***outspec;
303
304 /* prep */
305 const float acceptedTolerance = 0.000001f;
306 const int fs = 48000;
307 const int framesize = 128;
308 const int nCHin = 62;
309 const int nCHout = 64;
310 insig = (float**)malloc2d(nCHin,fs,sizeof(float)); /* One second long */
311 outsig = (float**)malloc2d(nCHout,fs,sizeof(float));
312 inframe = (float**)malloc2d(nCHin,framesize,sizeof(float));
313 outframe = (float**)malloc2d(nCHout,framesize,sizeof(float));
314 rand_m1_1(FLATTEN2D(insig), nCHin*fs); /* populate with random numbers */
315
316 /* Set-up STFT suitable for LTI filtering applications */
317 winsize = hopsize = 128;
318 nBands = winsize+1;
319 nTimesSlots = framesize/hopsize;
320 inspec = (float_complex***)malloc3d(nBands, nCHin, nTimesSlots, sizeof(float_complex));
321 outspec = (float_complex***)malloc3d(nBands, nCHout, nTimesSlots, sizeof(float_complex));
322 saf_stft_create(&hSTFT, winsize, hopsize, nCHin, nCHout, SAF_STFT_BANDS_CH_TIME);
323
324 /* Pass insig through STFT, block-wise processing */
325 nFrames = (int)((float)fs/(float)framesize);
326 for(frame = 0; frame<nFrames; frame++){
327 /* Forward */
328 for(ch=0; ch<nCHin; ch++)
329 memcpy(inframe[ch], &insig[ch][frame*framesize], framesize*sizeof(float));
330 saf_stft_forward(hSTFT, inframe, framesize, inspec);
331
332 /* Copy first channel of inspec to all outspec channels */
333 for(band=0; band<nBands; band++)
334 for(ch=0; ch<nCHout; ch++)
335 memcpy(outspec[band][ch], inspec[band][0], nTimesSlots*sizeof(float_complex));
336
337 /* Backward */
338 saf_stft_backward(hSTFT, outspec, framesize, outframe);
339 for(ch=0; ch<nCHout; ch++)
340 memcpy(&outsig[ch][frame*framesize], outframe[ch], framesize*sizeof(float));
341 }
342
343 /* Check that input==output (given some numerical precision) */
344 for(i=0; i<fs-framesize; i++)
345 TEST_ASSERT_TRUE( fabsf(insig[0][i] - outsig[63][i]) <= acceptedTolerance );
346
347 /* Clean-up */
348 saf_stft_destroy(&hSTFT);
349 free(insig);
350 free(outsig);
351 free(inframe);
352 free(outframe);
353 free(inspec);
354 free(outspec);
355}
356
358 int i, frame;
359 float** inputTD, **outputTD, **inputFrameTD, **outputFrameTD;
360 float*** filters;
361 void* hMatrixConv;
362
363 /* config */
364 const int signalLength = 48000;
365 const int hostBlockSize = 2048;
366 const int filterLength = 512;
367 const int nInputs = 32;
368 const int nOutputs = 40;
369
370 /* prep */
371 inputTD = (float**)malloc2d(nInputs, signalLength, sizeof(float));
372 outputTD = (float**)malloc2d(nOutputs, signalLength, sizeof(float));
373 inputFrameTD = (float**)malloc2d(nInputs, hostBlockSize, sizeof(float));
374 outputFrameTD = (float**)calloc2d(nOutputs, hostBlockSize, sizeof(float));
375 filters = (float***)malloc3d(nOutputs, nInputs, filterLength, sizeof(float));
376 rand_m1_1(FLATTEN3D(filters), nOutputs*nInputs*filterLength);
377 rand_m1_1(FLATTEN2D(inputTD), nInputs*signalLength);
378 saf_matrixConv_create(&hMatrixConv, hostBlockSize, FLATTEN3D(filters), filterLength,
379 nInputs, nOutputs, SAF_TRUE);
380
381 /* Apply */
382 for(frame = 0; frame<(int)signalLength/hostBlockSize; frame++){
383 for(i = 0; i<nInputs; i++)
384 memcpy(inputFrameTD[i], &inputTD[i][frame*hostBlockSize], hostBlockSize*sizeof(float));
385
386 saf_matrixConv_apply(hMatrixConv, FLATTEN2D(inputFrameTD), FLATTEN2D(outputFrameTD));
387
388 for(i = 0; i<nOutputs; i++)
389 memcpy(&outputTD[i][frame*hostBlockSize], outputFrameTD[i], hostBlockSize*sizeof(float));
390 }
391
392 /* Clean-up */
393 free(inputTD);
394 free(outputTD);
395 free(inputFrameTD);
396 free(outputFrameTD);
397 free(filters);
398 saf_matrixConv_destroy(&hMatrixConv);
399}
400
401void test__saf_rfft(void){
402 int i, j, N;
403 float* x_td, *test;
404 float_complex* x_fd;
405 void *hFFT;
406
407 /* Config */
408 const float acceptedTolerance = 0.00001f;
409 const int fftSizesToTest[24] =
410 {16,256,512,1024,2048,4096,8192,16384,32768,65536,1048576, /* 2^x */
411 80,160,320,640,1280,240,480,960,1920,3840,7680,15360,30720 }; /* non-2^x, (but still supported by vDSP) */
412
413 /* Loop over the different FFT sizes */
414 for (i=0; i<24; i++){
415 N = fftSizesToTest[i];
416
417 /* prep */
418 x_td = malloc1d(N*sizeof(float));
419 test = malloc1d(N*sizeof(float));
420 x_fd = malloc1d((N/2+1)*sizeof(float_complex));
421 rand_m1_1(x_td, N); /* populate with random numbers */
422 saf_rfft_create(&hFFT, N);
423
424 /* forward and backward transform */
425 saf_rfft_forward(hFFT, x_td, x_fd);
426 saf_rfft_backward(hFFT, x_fd, test);
427
428 /* Check that, x_td==test */
429 for(j=0; j<N; j++)
430 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, x_td[j], test[j]);
431
432 /* clean-up */
433 saf_rfft_destroy(&hFFT);
434 free(x_fd);
435 free(x_td);
436 free(test);
437 }
438}
439
440void test__saf_fft(void){
441 int i, j, N;
442 float_complex* x_td, *test;
443 float_complex* x_fd;
444 void *hFFT;
445
446 /* Config */
447 const float acceptedTolerance = 0.00001f;
448 const int fftSizesToTest[24] =
449 {16,256,512,1024,2048,4096,8192,16384,32768,65536,1048576, /* 2^x */
450 80,160,320,640,1280,240,480,960,1920,3840,7680,15360,30720 }; /* non-2^x, (but still supported by vDSP) */
451
452 /* Loop over the different FFT sizes */
453 for (i=0; i<24; i++){
454 N = fftSizesToTest[i];
455
456 /* prep */
457 x_td = malloc1d(N*sizeof(float_complex));
458 test = malloc1d(N*sizeof(float_complex));
459 x_fd = malloc1d(N*sizeof(float_complex));
460 rand_m1_1((float*)x_td, N*2); /* populate with random numbers */
461 saf_fft_create(&hFFT, N);
462
463 /* forward and backward transform */
464 saf_fft_forward(hFFT, x_td, x_fd);
465 saf_fft_backward(hFFT, x_fd, test);
466
467 /* Check that, x_td==test */
468 for(j=0; j<N; j++){
469 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, crealf(x_td[j]), crealf(test[j]));
470 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, cimagf(x_td[j]), cimagf(test[j]));
471 }
472
473 /* clean-up */
474 saf_fft_destroy(&hFFT);
475 free(x_fd);
476 free(x_td);
477 free(test);
478 }
479}
480
481void test__qmf(void){
482 int frame, nFrames, ch, i, nBands, procDelay, band, nHops;
483 void* hQMF;
484 float* freqVector;
485 float** insig, **outsig, **inframe, **outframe;
486 float_complex*** inspec, ***outspec;
487
488 /* prep */
489 const float acceptedTolerance = 0.01f;
490 const int fs = 48000;
491 const int signalLength = 1*fs;
492 const int framesize = 512;
493 const int hopsize = 128;
494 const int nCHin = 60;
495 const int hybridMode = 1;
496 const int nCHout = 64;
497 insig = (float**)malloc2d(nCHin,signalLength,sizeof(float)); /* One second long */
498 outsig = (float**)malloc2d(nCHout,signalLength,sizeof(float));
499 inframe = (float**)malloc2d(nCHin,framesize,sizeof(float));
500 outframe = (float**)malloc2d(nCHout,framesize,sizeof(float));
501 rand_m1_1(FLATTEN2D(insig), nCHin*signalLength); /* populate with random numbers */
502
503 /* Set-up */
504 nHops = framesize/hopsize;
505 qmf_create(&hQMF, nCHin, nCHout, hopsize, hybridMode, QMF_BANDS_CH_TIME);
506 procDelay = qmf_getProcDelay(hQMF);
507 nBands = qmf_getNBands(hQMF);
508 freqVector = malloc1d(nBands*sizeof(float));
509 qmf_getCentreFreqs(hQMF, (float)fs, nBands, freqVector);
510 inspec = (float_complex***)malloc3d(nBands, nCHin, nHops, sizeof(float_complex));
511 outspec = (float_complex***)malloc3d(nBands, nCHout, nHops, sizeof(float_complex));
512
513 /* Pass insig through the QMF filterbank, block-wise processing */
514 nFrames = (int)((float)signalLength/(float)framesize);
515 for(frame = 0; frame<nFrames; frame++){
516 /* QMF Analysis */
517 for(ch=0; ch<nCHin; ch++)
518 memcpy(inframe[ch], &insig[ch][frame*framesize], framesize*sizeof(float));
519 qmf_analysis(hQMF, inframe, framesize, inspec);
520
521 /* Copy first channel of inspec to all outspec channels */
522 for(band=0; band<nBands; band++)
523 for(ch=0; ch<nCHout; ch++)
524 memcpy(outspec[band][ch], inspec[band][0], nHops*sizeof(float_complex));
525
526 /* QMF Synthesis */
527 qmf_synthesis(hQMF, outspec, framesize, outframe);
528 for(ch=0; ch<nCHout; ch++)
529 memcpy(&outsig[ch][frame*framesize], outframe[ch], framesize*sizeof(float));
530 }
531
532 /* Check that input==output (given some numerical precision) - channel 0 */
533 for(i=0; i<signalLength-procDelay-framesize; i++)
534 TEST_ASSERT_TRUE( fabsf(insig[0][i] - outsig[0][i+procDelay]) <= acceptedTolerance );
535
536 /* Clean-up */
537 qmf_destroy(&hQMF);
538 free(insig);
539 free(outsig);
540 free(inframe);
541 free(outframe);
542 free(inspec);
543 free(outspec);
544 free(freqVector);
545}
546
548 float* inputData, *outputData;
549 void* hPS, *hFFT;
550 float frequency;
551 int i, ind;
552
553 /* Config */
554 const int sampleRate = 48000;
555 const int FFTsize = 8192;
556 const int osfactor = 4;
557 const int nSamples = 8*FFTsize;
558
559 /* prep */
560 smb_pitchShift_create(&hPS, 1, FFTsize, osfactor, (float)sampleRate);
561 inputData = malloc1d(nSamples*sizeof(float));
562 outputData = calloc1d(nSamples,sizeof(float));
563 frequency = (float)sampleRate/8.0f;
564 for(i=0; i<nSamples; i++) /* sine tone at quarter Nyquist: */
565 inputData[i] = sinf(2.0f * SAF_PI * (float)i * frequency/(float)sampleRate);
566
567 /* Pitch shift down one octave */
568 smb_pitchShift_apply(hPS, 0.5, nSamples, inputData, outputData);
569
570 /* Take FFT, the bin with the highest energy should correspond to 1/8 Nyquist */
571 float_complex* out_fft; // [nSamples / 2 + 1];
572 out_fft = malloc1d((nSamples / 2 + 1) * sizeof(float_complex));
573 saf_rfft_create(&hFFT, nSamples);
574 saf_rfft_forward(hFFT, outputData, out_fft);
575 utility_cimaxv(out_fft, nSamples/2+1, &ind);
576 TEST_ASSERT_TRUE(ind == nSamples/16);
577
578 /* clean-up */
580 saf_rfft_destroy(&hFFT);
581 free(inputData);
582 free(outputData);
583 free(out_fft);
584}
585
586void test__sortf(void){
587 float* values;
588 int* sortedIdx;
589 int i;
590
591 /* Config */
592 const int numValues = 10000;
593
594 /* Prep */
595 sortedIdx = malloc1d(numValues*sizeof(int));
596 values = malloc1d(numValues*sizeof(float));
597 rand_m1_1(values, numValues); /* populate with random numbers */
598
599 /* Sort in accending order */
600 sortf(values, NULL, sortedIdx, numValues, 0);
601
602 /* Check that the next value is either the same or greater than current value */
603 for(i=0; i<numValues-1; i++)
604 TEST_ASSERT_TRUE(values[sortedIdx[i]]<=values[sortedIdx[i+1]]);
605
606 /* Sort in decending order */
607 sortf(values, NULL, sortedIdx, numValues, 1);
608
609 /* Check that the next value is either the same or less than current value */
610 for(i=0; i<numValues-1; i++)
611 TEST_ASSERT_TRUE(values[sortedIdx[i]]>=values[sortedIdx[i+1]]);
612
613 /* clean-up */
614 free(values);
615 free(sortedIdx);
616}
617
618void test__sortz(void){
619 int i;
620 const int N = 36;
621 double_complex vals[36] ={
622 cmplx(1.0, 1.0), cmplx(7.0, 1.0), cmplx(10.0, 5.0),
623 cmplx(12.0, 4.0), cmplx(4.0, 4.0), cmplx(8.0, 0.0),
624 cmplx(10.0, -1.0), cmplx(7.0, 5.0), cmplx(7.0, 2.0),
625 cmplx(5.0, 1.0), cmplx(4.0, -1.0), cmplx(32.0, 3.0),
626 cmplx(32.0, 32.5), cmplx(25.0, 0.0), cmplx(2.0, -2.0),
627 cmplx(7.0, -2.0), cmplx(1.0, -1.0), cmplx(12.0, -1.0),
628 cmplx(2.0, -1.0), cmplx(4.0, 2.0), cmplx(10.0, 6.0),
629 cmplx(5.0, 2.0), cmplx(32.0, 1.5), cmplx(7.0, -10.0),
630 cmplx(1.0, -1.5), cmplx(4.0, 25.0), cmplx(3.0, 2.0),
631 cmplx(1.0, 4.5), cmplx(10.0, 5.0), cmplx(10.0, 2.0),
632 cmplx(10.0, -3.5), cmplx(30.0, -10.0), cmplx(7.0, -12.0),
633 cmplx(1.0, -13.5), cmplx(12.0, -12.0), cmplx(32.0, 23.0)
634 };
635 double_complex sorted_vals[36];
636
637 /* Sort assending order */
638 sortz(vals, sorted_vals, N, 0);
639
640 /* Check that the next real(value) is either the same or greater than current real(value) */
641 for(i=0; i<N-1; i++)
642 TEST_ASSERT_TRUE(creal(sorted_vals[i])<=creal(sorted_vals[i+1]));
643
644 /* Check that if the next real(value) is the same as the current real(value), then
645 * the next imag(value) is greater that the current image(value)*/
646 for(i=0; i<N-1; i++)
647 if(fabs(creal(sorted_vals[i])-creal(sorted_vals[i+1])) < 0.00001 )
648 TEST_ASSERT_TRUE(cimag(sorted_vals[i])<=cimag(sorted_vals[i+1]));
649
650 /* Sort decending order */
651 sortz(vals, sorted_vals, N, 1);
652
653 /* Check that the next real(value) is either the same or smaller than current real(value) */
654 for(i=0; i<N-1; i++)
655 TEST_ASSERT_TRUE(creal(sorted_vals[i])>=creal(sorted_vals[i+1]));
656
657 /* Check that if the next real(value) is the same as the current real(value), then
658 * the next imag(value) is smaller that the current image(value)*/
659 for(i=0; i<N-1; i++)
660 if(fabs(creal(sorted_vals[i])-creal(sorted_vals[i+1])) < 0.00001 )
661 TEST_ASSERT_TRUE(cimag(sorted_vals[i])>=cimag(sorted_vals[i+1]));
662}
663
665 int i;
666 const int N = 36;
667 double_complex vals[36] ={
668 cmplx(1.0, 1.0), cmplx(7.0, 1.0), cmplx(10.0, 5.0),
669 cmplx(12.0, 4.0), cmplx(4.0, 4.0), cmplx(8.0, 0.0),
670 cmplx(10.0, -1.0), cmplx(7.0, 5.0), cmplx(7.0, 2.0),
671 cmplx(5.0, 1.0), cmplx(4.0, -1.0), cmplx(32.0, 3.0),
672 cmplx(32.0, 32.5), cmplx(25.0, 0.0), cmplx(2.0, -2.0),
673 cmplx(7.0, -2.0), cmplx(1.0, -1.0), cmplx(12.0, -1.0),
674 cmplx(2.0, -1.0), cmplx(4.0, 2.0), cmplx(10.0, 6.0),
675 cmplx(5.0, 0.0), cmplx(32.0, 1.5), cmplx(7.0, -10.0),
676 cmplx(1.0, -1.5), cmplx(4.0, 25.0), cmplx(3.0, 2.0),
677 cmplx(1.0, 0.0), cmplx(10.0, 5.0), cmplx(10.0, 2.0),
678 cmplx(10.0, -3.5), cmplx(30.0, -10.0), cmplx(7.0, -12.0),
679 cmplx(1.0, -13.5), cmplx(12.0, -12.0), cmplx(32.0, 23.0)
680 };
681 double_complex sorted_vals[36];
682
683 /* Sort assending order */
684 cmplxPairUp(vals, sorted_vals, N);
685
686 /* Check that the next real(value) is either the same or greater than current real(value),
687 * Ignoring purely real numbers */
688 for(i=0; i<N-1; i++)
689 if( !(fabs(cimag(sorted_vals[i])) < 0.0001) && !(fabs(cimag(sorted_vals[i+1])) < 0.0001) )
690 TEST_ASSERT_TRUE(creal(sorted_vals[i])<=creal(sorted_vals[i+1]));
691
692 /* Check that the next real(value) is either the same or greater than current real(value),
693 * Only considering purely real numbers */
694 for(i=0; i<N-1; i++)
695 if( (fabs(cimag(sorted_vals[i])) < 0.0001) && (fabs(cimag(sorted_vals[i+1])) < 0.0001) )
696 TEST_ASSERT_TRUE(creal(sorted_vals[i])<=creal(sorted_vals[i+1]));
697
698 /* Check that if the next real(value) is the same as the current real(value), then
699 * the next imag(value) is greater that the current image(value)
700 * Ignoring purely real numbers */
701 for(i=0; i<N-1; i++)
702 if(fabs(creal(sorted_vals[i])-creal(sorted_vals[i+1])) < 0.00001 )
703 if( !(fabs(cimag(sorted_vals[i])) < 0.0001) && !(fabs(cimag(sorted_vals[i+1])) < 0.0001) )
704 TEST_ASSERT_TRUE(cimag(sorted_vals[i])<=cimag(sorted_vals[i+1]));
705}
706
708 int i, it, td, nDirs;
709 float* dirs_deg, *weights;
710 float sum, tmp, scale;
711
712 /* Config */
713 const float acceptedTolerance = 0.01f;
714 const int nIterations = 100;
715
716 /* Loop over T-designs */
717 for(td=2; td<21; td++){
718 dirs_deg = (float*)__HANDLES_Tdesign_dirs_deg[td];
720
721 /* Compute weights */
722 weights = malloc1d(nDirs*sizeof(float));
723 getVoronoiWeights(dirs_deg, nDirs, 0, weights);
724
725 /* Assert that they sum to 4PI */
726 sum = sumf(weights, nDirs);
727 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, 4.0f*SAF_PI, sum);
728
729 /* Due to the uniform arrangement, all the weights should be the same */
730 for(i=1; i<nDirs; i++)
731 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, weights[0], weights[i]);
732
733 /* clean-up */
734 free(weights);
735 }
736
737 /* Loop over some random arrangement of points */
738 for(it=0; it<nIterations; it++){
739 rand_0_1(&tmp, 1);
740 nDirs = (int)(tmp*190.0f + 10.0f); /* random number between 10..200 */
741
742 /* Random dirs (-180..180 azi, -180..180 elev) */
743 dirs_deg = malloc1d(nDirs*2*sizeof(float));
744 rand_m1_1(dirs_deg, nDirs*2);
745 scale = 180.0f;
746 utility_svsmul(dirs_deg, &scale, nDirs*2, dirs_deg);
747
748 /* Compute weights */
749 weights = malloc1d(nDirs*sizeof(float));
750 getVoronoiWeights(dirs_deg, nDirs, 0, weights);
751
752 /* Assert that they sum to 4PI */
753 sum = sumf(weights, nDirs);
754 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, 4.0f*SAF_PI, sum);
755
756 /* clean-up */
757 free(dirs_deg);
758 free(weights);
759 }
760}
761
762void test__unique_i(void){
763 int i, nUnique;
764 int* uniqueVals;
765 int* uniqueInds;
766
767 /* test1 */
768 int input[6] = {1, 2, 2, 10, 11, 12};
769 int uniqueVals_ref[5] = {1, 2, 10, 11, 12};
770 int uniqueInds_ref[5] = {0, 2, 3, 4, 5};
771 unique_i(input, 6, &uniqueVals, &uniqueInds, &nUnique);
772 TEST_ASSERT_EQUAL(5, nUnique);
773 for(i=0; i<nUnique; i++){
774 TEST_ASSERT_EQUAL(uniqueVals_ref[i], uniqueVals[i]);
775 TEST_ASSERT_EQUAL(uniqueInds_ref[i], uniqueInds[i]);
776 }
777 free(uniqueVals);
778 free(uniqueInds);
779
780 /* test2 */
781 int input2[12] = {1, 10, 1, 3, 1, 3, 4, 7, 8, 10, 10, 2};
782 int uniqueVals_ref2[7] = {1, 3, 4, 7, 8, 10, 2};
783 int uniqueInds_ref2[7] = {4, 5, 6, 7, 8, 10, 11};
784 unique_i(input2, 12, &uniqueVals, &uniqueInds, &nUnique);
785 TEST_ASSERT_EQUAL(7, nUnique);
786 for(i=0; i<nUnique; i++){
787 TEST_ASSERT_EQUAL(uniqueVals_ref2[i], uniqueVals[i]);
788 TEST_ASSERT_EQUAL(uniqueInds_ref2[i], uniqueInds[i]);
789 }
790 free(uniqueVals);
791 free(uniqueInds);
792}
793
795 int c, band, nBands, idx, hopIdx, i;
796 void* hDecor, *hSTFT;
797 float icc, tmp, tmp2;
798 float* freqVector;
799 float** inputTimeDomainData, **outputTimeDomainData, **tempHop;
800 float_complex*** inTFframe, ***outTFframe;
801
802 /* config */
803 const float acceptedICC = 0.05f;
804 const int nCH = 24;
805 const int nTestHops = 800;
806 const int hopSize = 128;
807 const int procDelay = hopSize*12 + 12; // from afSTFT + decor
808 const int lSig = nTestHops*hopSize+procDelay;
809 const float fs = 48e3f;
810 nBands = hopSize+5;
811
812 /* audio buffers */
813 inputTimeDomainData = (float**) calloc2d(1, lSig, sizeof(float));
814 outputTimeDomainData = (float**) calloc2d(nCH, lSig, sizeof(float));
815 inTFframe = (float_complex***)malloc3d(nBands, nCH, 1, sizeof(float_complex));
816 outTFframe = (float_complex***)malloc3d(nBands, nCH, 1, sizeof(float_complex));
817 tempHop = (float**) malloc2d(nCH, hopSize, sizeof(float));
818
819 /* Initialise afSTFT and input data */
820 afSTFT_create(&hSTFT, 1, nCH, hopSize, 0, 1, AFSTFT_BANDS_CH_TIME);
821 rand_m1_1(FLATTEN2D(inputTimeDomainData), 1*lSig); /* populate with random numbers */
822 freqVector = malloc1d(nBands*sizeof(float));
823 afSTFT_getCentreFreqs(hSTFT, fs, nBands, freqVector);
824
825 /* setup decorrelator */
826 int orders[4] = {20, 15, 6, 6}; /* 20th order up to 700Hz, 15th->2.4kHz, 6th->4kHz, 3rd->12kHz, NONE(only delays)->Nyquist */
827 //float freqCutoffs[4] = {600.0f, 2.6e3f, 4.5e3f, 12e3f};
828 float freqCutoffs[4] = {900.0f, 6.8e3f, 12e3f, 24e3f};
829 const int maxDelay = 12;
830 latticeDecorrelator_create(&hDecor, fs, hopSize, freqVector, nBands, nCH, orders, freqCutoffs, 4, maxDelay, 0, 0.75f);
831
832 /* Processing loop */
833 idx = 0;
834 hopIdx = 0;
835 while(idx<lSig-hopSize*2){
836 for(c=0; c<1; c++)
837 memcpy(tempHop[c], &(inputTimeDomainData[c][hopIdx*hopSize]), hopSize*sizeof(float));
838
839 /* forward TF transform, and replicate to all channels */
840 afSTFT_forward(hSTFT, tempHop, hopSize, inTFframe);
841 for(band=0; band<nBands; band++)
842 for(i=1; i<nCH;i++)
843 inTFframe[band][i][0] = inTFframe[band][0][0];
844
845 /* decorrelate */
846 latticeDecorrelator_apply(hDecor, inTFframe, 1, outTFframe);
847
848 /* backward TF transform */
849 afSTFT_backward(hSTFT, outTFframe, hopSize, tempHop);
850
851 /* Copy frame to output TD buffer */
852 for(c=0; c<nCH; c++)
853 memcpy(&(outputTimeDomainData[c][hopIdx*hopSize]), tempHop[c], hopSize*sizeof(float));
854 idx+=hopSize;
855 hopIdx++;
856 }
857
858 /* Compensate for processing delay, and check that the inter-channel correlation
859 * coefficient is below the accepted threshold (ideally 0, if fully
860 * decorrelated...) */
861 for(c=0; c<nCH; c++){
862 utility_svvdot(inputTimeDomainData[0], &outputTimeDomainData[c][procDelay], (lSig-procDelay), &icc);
863 utility_svvdot(inputTimeDomainData[0], inputTimeDomainData[0], (lSig-procDelay), &tmp);
864 utility_svvdot(&outputTimeDomainData[c][procDelay], &outputTimeDomainData[c][procDelay], (lSig-procDelay), &tmp2);
865
866 icc = icc/sqrtf(tmp*tmp2); /* normalise */
867 TEST_ASSERT_TRUE(fabsf(icc)<acceptedICC);
868 }
869#if 0
870 /* Check for mutually decorrelated channels... */
871 int c2;
872 for(c=0; c<nCH; c++){
873 for(c2=0; c2<nCH; c2++){
874 utility_svvdot(&outputTimeDomainData[c][procDelay], &outputTimeDomainData[c2][procDelay], (lSig-procDelay), &icc);
875 utility_svvdot(&outputTimeDomainData[c2][procDelay], &outputTimeDomainData[c2][procDelay], (lSig-procDelay), &tmp);
876 utility_svvdot(&outputTimeDomainData[c][procDelay], &outputTimeDomainData[c][procDelay], (lSig-procDelay), &tmp2);
877
878 if (c!=c2){
879 icc = icc/sqrtf(tmp*tmp2); /* normalise */
880 TEST_ASSERT_TRUE(fabsf(icc)<acceptedICC);
881 }
882 }
883 }
884#endif
885
886 /* Clean-up */
888 free(inTFframe);
889 free(outTFframe);
890 free(tempHop);
891 afSTFT_destroy(&hSTFT);
892 free(inputTimeDomainData);
893 free(outputTimeDomainData);
894}
895
897 int i;
898 float fs, cutoff_freq, cutoff_freq2;
899 int order;
900
901 /* Config */
902 const double acceptedTolerance = 0.00001f;
903
904 /* 1st order Low-pass filter */
905 fs = 48e3f;
906 cutoff_freq = 3000.0f;
907 order = 1;
908 double a_test1[2], b_test1[2];
909 butterCoeffs(BUTTER_FILTER_LPF, order, cutoff_freq, 0.0f, fs, (double*)b_test1, (double*)a_test1);
910 const double a_ref1[2] = {1,-0.668178637919299};
911 const double b_ref1[2] = {0.165910681040351,0.165910681040351};
912 for(i=0; i<2; i++){ /* Compare with the values given by Matlab's butter function */
913 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, a_test1[i], a_ref1[i]);
914 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, b_test1[i], b_ref1[i]);
915 }
916
917 /* 2nd order Low-pass filter */
918 fs = 48e3f;
919 cutoff_freq = 12000.0f;
920 order = 2;
921 double a_test2[3], b_test2[3];
922 butterCoeffs(BUTTER_FILTER_LPF, order, cutoff_freq, 0.0f, fs, (double*)b_test2, (double*)a_test2);
923 const double a_ref2[3] = {1.0,-2.22044604925031e-16,0.171572875253810};
924 const double b_ref2[3] = {0.292893218813452,0.585786437626905,0.292893218813452};
925 for(i=0; i<3; i++){ /* Compare with the values given by Matlab's butter function */
926 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, a_test2[i], a_ref2[i]);
927 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, b_test2[i], b_ref2[i]);
928 }
929
930 /* 3rd order Low-pass filter */
931 fs = 48e3f;
932 cutoff_freq = 200.0f;
933 order = 3;
934 double a_test3[4], b_test3[4];
935 butterCoeffs(BUTTER_FILTER_LPF, order, cutoff_freq, 0.0f, fs, (double*)b_test3, (double*)a_test3);
936 const double a_ref3[4] = {1.0,-2.94764161678340,2.89664496645376,-0.948985866903327};
937 const double b_ref3[4] = {2.18534587909103e-06,6.55603763727308e-06,6.55603763727308e-06,2.18534587909103e-06};
938 for(i=0; i<4; i++){ /* Compare with the values given by Matlab's butter function */
939 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, a_test3[i], a_ref3[i]);
940 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, b_test3[i], b_ref3[i]);
941 }
942
943 /* 6th order Low-pass filter */
944 fs = 48e3f;
945 cutoff_freq = 1e3f;
946 order = 6;
947 double a_test4[7], b_test4[7];
948 butterCoeffs(BUTTER_FILTER_LPF, order, cutoff_freq, 0.0f, fs, (double*)b_test4, (double*)a_test4);
949 const double a_ref4[7] = {1,-5.49431292177096,12.5978414666894,-15.4285267903275,10.6436770055305,-3.92144696766748,0.602772146971300};
950 const double b_ref4[7] = {6.15535184628202e-08,3.69321110776921e-07,9.23302776942303e-07,1.23107036925640e-06,9.23302776942303e-07,3.69321110776921e-07,6.15535184628202e-08};
951 for(i=0; i<7; i++){ /* Compare with the values given by Matlab's butter function */
952 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, a_test4[i], a_ref4[i]);
953 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, b_test4[i], b_ref4[i]);
954 }
955
956 /* 3rd order High-pass filter */
957 fs = 48e3f;
958 cutoff_freq = 3000.0f;
959 order = 3;
960 double a_test5[4], b_test5[4];
961 butterCoeffs(BUTTER_FILTER_HPF, order, cutoff_freq, 0.0f, fs, (double*)b_test5, (double*)a_test5);
962 const double a_ref5[4] = {1,-2.21916861831167,1.71511783003340,-0.453545933365530};
963 const double b_ref5[4] = {0.673479047713825,-2.02043714314147,2.02043714314147,-0.673479047713825};
964 for(i=0; i<4; i++){ /* Compare with the values given by Matlab's butter function */
965 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, a_test5[i], a_ref5[i]);
966 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, b_test5[i], b_ref5[i]);
967 }
968
969 /* 4th order High-pass filter */
970 fs = 48e3f;
971 cutoff_freq = 100.0;
972 order = 4;
973 double a_test6[5], b_test6[5];
974 butterCoeffs(BUTTER_FILTER_HPF, order, cutoff_freq, 0.0f, fs, (double*)b_test6, (double*)a_test6);
975 const double a_ref6[5] = {1.0,-3.96579438007005,5.89796693861409,-3.89854491737242,0.966372387692057};
976 const double b_ref6[5] = {0.983042413984288,-3.93216965593715,5.89825448390573,-3.93216965593715,0.983042413984288};
977 for(i=0; i<5; i++){ /* Compare with the values given by Matlab's butter function */
978 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, a_test6[i], a_ref6[i]);
979 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, b_test6[i], b_ref6[i]);
980 }
981
982 /* 2nd order Band-pass filter */
983 fs = 48e3f;
984 cutoff_freq = 100.0;
985 cutoff_freq2 = 400.0;
986 order = 2;
987 double a_test7[5], b_test7[5];
988 butterCoeffs(BUTTER_FILTER_BPF, order, cutoff_freq, cutoff_freq2, fs, (double*)b_test7, (double*)a_test7);
989 const double a_ref7[5] = {1.0,-3.94312581006024,5.83226704209421,-3.83511871130750,0.945977936232284};
990 const double b_ref7[5] = {0.000375069616051004,0.0,-0.000750139232102008,0.0,0.000375069616051004};
991 for(i=0; i<5; i++){ /* Compare with the values given by Matlab's butter function */
992 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, a_test7[i], a_ref7[i]);
993 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, b_test7[i], b_ref7[i]);
994 }
995
996 /* 3rd order Band-stop filter */
997 fs = 48e3f;
998 cutoff_freq = 240.0;
999 cutoff_freq2 = 1600.0;
1000 order = 3;
1001 double a_test9[7], b_test9[7];
1002 butterCoeffs(BUTTER_FILTER_BSF, order, cutoff_freq, cutoff_freq2, fs, (double*)b_test9, (double*)a_test9);
1003 const double a_ref9[7] = {1,-5.62580309774365,13.2124846784594,-16.5822627287366,11.7304049556188,-4.43493124452282,0.700107676775329};
1004 const double b_ref9[7] = {0.836724592951539,-5.00379660039217,12.4847741945760,-16.6354041344203,12.4847741945760,-5.00379660039217,0.836724592951539};
1005 for(i=0; i<7; i++){ /* Compare with the values given by Matlab's butter function */
1006 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, a_test9[i], a_ref9[i]);
1007 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, b_test9[i], b_ref9[i]);
1008 }
1009}
1010
1011/* Test evalIIRTransferFunction()
1012 * Coefficients for the first 7 tests below are taken from the butterworth tests
1013 * above, so can be compared against the mag/phase results given by MATLAB's
1014 * freqz function. The 8th test loop evaluates results for 1st order shelving
1015 * filters with coefficients generated by the DVF filter functions. The last
1016 * test loop runs those same tests, but with the floating point version of
1017 * evalIIRTransferFunctionf() (valid for low order filters)
1018 */
1020 int i, nCoeffs;
1021 float phase_ref, mag_ref, mag_ref_db;
1022
1023 /* Config */
1024 const double phaseTolerance = 0.0174533f * 5.f; /* ~ 1 degree * mul */
1025 const double magToleranceDb = 0.1f; /* tolerance in dB, for a target magnitude of 0dB */
1026 const float errScale = 2.f / 120.f; /* tolerance grows for lower dB target: toleranceLevel/atLevel.
1027 * e.g. 2/120 = 2dB tolerance for -120 target dB */
1028 const int nFreqs = 10;
1029 const float freqs[10] = {147.21423f,270.49564f,411.40091f,687.90202f,1395.3072f,2024.3936f,3696.9416f,6784.4745f,9798.67f,17594.058f};
1030 float mag[10], phase[10];
1031 float fs = 44.1e3f;
1032
1033 /* evalIIRTransferFunction(): coeffs of type double */
1034
1035 /* Test 1 * 1st order Low-pass filter */
1036 nCoeffs = 2;
1037 const double a_t1[2] = {1,-0.6681786};
1038 const double b_t1[2] = {0.1659107,0.1659107};
1039 const double mag_ref1[10] = {0.99861294,0.99533929,0.98931332,0.97092312,0.89393904,0.80765661,0.59366549,0.35440473,0.23070415,0.06521195};
1040 const double phase_ref1[10] = {-0.052676048,-0.096585083,-0.14632679,-0.24173907,-0.46473796,-0.63062928,-0.93519006,-1.2085189,-1.337995,-1.5055381};
1041 evalIIRTransferFunction((double*)b_t1, (double*)a_t1, nCoeffs, (float*)freqs, nFreqs, fs, 0, (float*)mag, (float*)phase);
1042 for(i = 0; i < nFreqs; i++) {
1043 phase_ref = (float)phase_ref1[i];
1044 mag_ref = (float)mag_ref1[i];
1045 mag_ref_db = 20.f*log10f(mag_ref);
1046 TEST_ASSERT_FLOAT_WITHIN(magToleranceDb + errScale*fabsf(mag_ref_db), mag_ref_db, 20*log10(mag[i]));
1047 TEST_ASSERT_FLOAT_WITHIN(phaseTolerance, phase_ref, phase[i]);
1048 }
1049
1050 /* Test 2 * 2nd order Low-pass filter */
1051 nCoeffs = 3;
1052 const double a_t2[3] = {1,-0,0.1715729};
1053 const double b_t2[3] = {0.2928932,0.5857864,0.2928932};
1054 const float mag_ref2[10] = {0.99999991f,0.99999985f,0.99999955f,0.99999702f,0.99995046f,0.99977761f,0.99736787f,0.96409579f,0.81776268f,0.1073164f};
1055 const float phase_ref2[10] = {-0.014832279f,-0.027258003f,-0.041470589f,-0.069414188f,-0.14150066f,-0.20679977f,-0.39012494f,-0.7974393f,-1.3261562f,-2.6614056f};
1056 evalIIRTransferFunction((double*)b_t2, (double*)a_t2, nCoeffs, (float*)freqs, nFreqs, fs, 0, (float*)mag, (float*)phase);
1057 for(i = 0; i < nFreqs; i++) {
1058 phase_ref = phase_ref2[i];
1059 mag_ref = mag_ref2[i];
1060 mag_ref_db = 20.f*log10f(mag_ref);
1061 TEST_ASSERT_FLOAT_WITHIN(magToleranceDb + errScale*fabsf(mag_ref_db), mag_ref_db, 20*log10(mag[i]));
1062 TEST_ASSERT_FLOAT_WITHIN(phaseTolerance, phase_ref, phase[i]);
1063 }
1064
1065 /* Test 3 * 3rd order Low-pass filter */
1066 nCoeffs = 4;
1067 const double a_t3[4] = {1,-2.9476416,2.896645,-0.9489859};
1068 const double b_t3[4] = {2.2e-06,6.6e-06,6.6e-06,2.2e-06};
1069 const float mag_ref3[10] = {0.8954383f,0.3011618f,0.0892913f,0.0191409f,0.0022769f,0.0007374f,0.0001152f,1.56e-05f,3.8e-06f,1e-07f};
1070 const float phase_ref3[10] = {-1.8249735f,3.0678618f,2.4995092f,2.1114704f,1.8340934f,1.751328f,1.6679375f,1.6206872f,1.6020054f,1.579398f};
1071 evalIIRTransferFunction((double*)b_t3, (double*)a_t3, nCoeffs, (float*)freqs, nFreqs, fs, 0, (float*)mag, (float*)phase);
1072 for(i = 0; i < nFreqs; i++) {
1073 phase_ref = phase_ref3[i];
1074 mag_ref = mag_ref3[i];
1075 mag_ref_db = 20.f*log10f(mag_ref);
1076 TEST_ASSERT_FLOAT_WITHIN(magToleranceDb + errScale*fabsf(mag_ref_db), mag_ref_db, 20*log10(mag[i]));
1077 TEST_ASSERT_FLOAT_WITHIN(phaseTolerance, phase_ref, phase[i]);
1078 }
1079
1080 /* Test 4 * 6th order Low-pass filter */
1081 nCoeffs = 7;
1082 const double a_t4[7] = {1,-5.49431292177096,12.5978414666894,-15.4285267903275,10.6436770055305,-3.92144696766748,0.6027721469713};
1083 const double b_t4[7] = {6.15535184628202e-08,3.69321110776921e-07,9.23302776942303e-07,1.2310703692564e-06,9.23302776942303e-07,3.69321110776921e-07,6.15535184628202e-08};
1084 const float mag_ref4[10] = {0.9999999834907868f,0.9999997831836054f,0.9999679556869572f,0.9849426248859378f,0.08033081621985783f,0.008452216914022819f,0.0002063542729228268f,3.793812554381118e-06f,2.274031694371124e-07f,9.970589432354785e-11f};
1085 const float phase_ref4[10] = {-0.6201852189230334f,-1.148525513374147f,-1.774695369143539f,3.109543344373707f,-0.4296773811384472f,-1.349824316530828f,-2.195405632723407f,-2.65814688739603f,-2.839508904295157f,-3.058387834019209f};
1086 evalIIRTransferFunction((double*)b_t4, (double*)a_t4, nCoeffs, (float*)freqs, nFreqs, fs, 0, (float*)mag, (float*)phase);
1087 for(i = 0; i < nFreqs; i++) {
1088 float phase_ref = phase_ref4[i];
1089 float mag_ref = mag_ref4[i];
1090 float mag_ref_db = 20.f*log10f(mag_ref);
1091 TEST_ASSERT_FLOAT_WITHIN(magToleranceDb + errScale*fabsf(mag_ref_db), mag_ref_db, 20*log10(mag[i]));
1092 TEST_ASSERT_FLOAT_WITHIN(phaseTolerance, phase_ref, phase[i]);
1093 }
1094
1095 /* Test 5 * 3rd order High-pass filter */
1096 nCoeffs = 4;
1097 const double a_t5[4] = {1,-2.2191686,1.7151178,-0.4535459};
1098 const double b_t5[4] = {0.673479,-2.0204371,2.0204371,-0.673479};
1099 const float mag_ref5[10] = {0.0001466f,0.0009096f,0.0032014f,0.0149875f,0.125037f,0.362653f,0.927991f,0.9985214f,0.9999112f,0.9999999f};
1100 const float phase_ref5[10] = {-1.6762949f,-1.7648759f,-1.866651f,-2.0692621f,-2.6256366f,3.0800183f,1.6530258f,0.7789431f,0.4789307f,0.1307956f};
1101 evalIIRTransferFunction((double*)b_t5, (double*)a_t5, nCoeffs, (float*)freqs, nFreqs, fs, 0, (float*)mag, (float*)phase);
1102 for(i = 0; i < nFreqs; i++) {
1103 float phase_ref = phase_ref5[i];
1104 float mag_ref = mag_ref5[i];
1105 float mag_ref_db = 20.f*log10f(mag_ref);
1106 TEST_ASSERT_FLOAT_WITHIN(magToleranceDb + errScale*fabsf(mag_ref_db), mag_ref_db, 20*log10(mag[i]));
1107 TEST_ASSERT_FLOAT_WITHIN(phaseTolerance, phase_ref, phase[i]);
1108 }
1109
1110 /* Test 6 * 4th order High-pass filter
1111 * 400 Hz cut (differs from butterworth test above) */
1112 nCoeffs = 5;
1113 const double a_t6[5] = {1,-3.863184622426,5.598835456747838,-3.607752453919942,0.872108645089876};
1114 const double b_t6[5] = {4.3909323578772e-07,1.75637294315089e-06,2.63455941472633e-06,1.75637294315089e-06,4.3909323578772e-07};
1115 const float mag_ref6[10] = {0.9996691528983467f,0.9595570109649983f,0.5370184819357747f,0.08100263003740536f,0.004753436194609436f,0.001057169058757887f,8.896712774518116e-05f,6.197328265811134e-06f,9.491865964914827e-07f,5.478157027512644e-09f};
1116 const float phase_ref6[10] = {-1.072517623166929f,-2.13344694428915f,2.732267641095127f,1.462991201859678f,0.6929733816699927f,0.4733493046075806f,0.2541184532330854f,0.130425028023503f,0.08157492611996242f,0.02248140228360206f};
1117 evalIIRTransferFunction((double*)b_t6, (double*)a_t6, nCoeffs, (float*)freqs, nFreqs, fs, 0, (float*)mag, (float*)phase);
1118 for(i = 0; i < nFreqs; i++) {
1119 float phase_ref = phase_ref6[i];
1120 float mag_ref = mag_ref6[i];
1121 float mag_ref_db = 20.f*log10f(mag_ref);
1122 TEST_ASSERT_FLOAT_WITHIN(magToleranceDb + errScale*fabsf(mag_ref_db), mag_ref_db, 20*log10(mag[i]));
1123 TEST_ASSERT_FLOAT_WITHIN(phaseTolerance, phase_ref, phase[i]);
1124 }
1125
1126 /* Test 7 * 2nd order Band-pass filter */
1127 nCoeffs = 5;
1128 const double a_t7[5] = {1,-3.9431258,5.832267,-3.8351187,0.9459779};
1129 const double b_t7[5] = {0.0003751,0,-0.0007501,0,0.0003751};
1130 const float mag_ref7[10] = {0.7829909f,0.9051549f,0.5636772f,0.1816557f,0.0400635f,0.0185759f,0.0053305f,0.0014022f,0.0005484f,4.16e-05f};
1131 const float phase_ref7[10] = {0.4017825f,-0.7852502f,-1.8127451f,-2.4983166f,-2.8544848f,-2.9475768f,-3.0381483f,-3.0886103f,-3.1084696f,-3.1324667f};
1132 evalIIRTransferFunction((double*)b_t7, (double*)a_t7, nCoeffs, (float*)freqs, nFreqs, fs, 0, (float*)mag, (float*)phase); for(i=0; i<nFreqs; i++){
1133 float phase_ref = phase_ref7[i];
1134 float mag_ref = mag_ref7[i];
1135 float mag_ref_db = 20.f*log10f(mag_ref);
1136 TEST_ASSERT_FLOAT_WITHIN(magToleranceDb + errScale*fabsf(mag_ref_db), mag_ref_db, 20*log10(mag[i]));
1137 TEST_ASSERT_FLOAT_WITHIN(phaseTolerance, phase_ref, phase[i]);
1138 }
1139
1140 /* Test the response for 12 settings used by the DVF filters
1141 * (1st order sheving filter) */
1142 const int nTest = 12;
1143 nCoeffs = 2;
1144 double as_dvf[12][2] = { /* nTest x nCoeffs */
1145 {1,-0.95864619},{1,-0.96599375},{1,-0.9648155},{1,-0.84969467},{1,-0.93327999},{1,-0.95974372},
1146 {1,-0.83460338},{1,-0.74744027},{1,-0.67445272},{1,-0.76911048},{1,-0.64266857},{1,-0.54043336}
1147 };
1148 double bs_dvf[12][2] = { /* nTest x nCoeffs */
1149 {8.0841171,-7.5217374},{2.1404179,-2.0439339},{1.3371466,-1.2837154},{0.73353449,-0.52570938},
1150 {1.1312827,-1.0324739},{1.1334695,-1.0826754},{0.18784397,-0.098191093},{0.43493823,-0.24012268},
1151 {0.72850398,-0.42469436},{0.1577158,-0.075691788},{0.34545179,-0.16259809},{0.60618525,-0.27852873}
1152 };
1153 const float mags_dvf[12][10] = { /* nTest x nFreqs */
1154 {12.68472f,11.390262f,10.245287f,9.081507f,8.2881923f,8.1239126f,8.0139907f,7.9799796f,7.9724969f,7.9680408f},
1155 {2.6652868f,2.469902f,2.3321513f,2.2179604f,2.1523794f,2.139901f,2.1317514f,2.1292619f,2.1287161f,2.1283915f},
1156 {1.473629f,1.4224625f,1.3865239f,1.35693f,1.3400517f,1.3368519f,1.3347643f,1.3341269f,1.3339872f,1.3339041f},
1157 {1.3740782f,1.3545086f,1.3209933f,1.2349497f,1.0204791f,0.89934391f,0.76424296f,0.70524874f,0.69060103f,0.68154193f},
1158 {1.4538524f,1.4034324f,1.3412611f,1.2506542f,1.1632856f,1.1414561f,1.125968f,1.1210229f,1.1199249f,1.1192693f},
1159 {1.2358328f,1.2022487f,1.1755623f,1.151321f,1.1364575f,1.1335478f,1.1316331f,1.1310458f,1.130917f,1.1308403f},
1160 {0.53871826f,0.53107297f,0.51772931f,0.48194542f,0.3814555f,0.3150888f,0.22673701f,0.17897735f,0.16548424f,0.15666687f},
1161 {0.76984932f,0.76629998f,0.75986062f,0.74092839f,0.6717326f,0.60914565f,0.49873472f,0.42504312f,0.40260097f,0.38760994f},
1162 {0.93261062f,0.93115748f,0.92849149f,0.92042692f,0.88786149f,0.85374491f,0.78101307f,0.72261208f,0.7032242f,0.68987066f},
1163 {0.3542684f,0.3519694f,0.34781956f,0.33577027f,0.29342253f,0.25693916f,0.1950882f,0.15408756f,0.14134236f,0.13268952f},
1164 {0.51134341f,0.51045389f,0.50881513f,0.50380352f,0.48269293f,0.45893406f,0.4014785f,0.34644275f,0.32577048f,0.31064565f},
1165 {0.71281398f,0.71244818f,0.7117705f,0.70966741f,0.70027544f,0.68857561f,0.65428371f,0.61109598f,0.59151358f,0.57580457f}
1166 };
1167 const float phases_dvf[12][10] = { /* nTest x nFreqs */
1168 {-0.17782001f,-0.24874011f,-0.2637155f,-0.22716735f,-0.13811864f,-0.098858451f,-0.054719219f,-0.028348151f,-0.01776687f,-0.0049023852f},
1169 {-0.11818797f,-0.14315719f,-0.13342746f,-0.10042039f,-0.055483624f,-0.038914731f,-0.021247104f,-0.010960723f,-0.006863078f,-0.001892663f},
1170 {-0.054685172f,-0.064793725f,-0.059294582f,-0.043867626f,-0.023978567f,-0.016782288f,-0.0091501707f,-0.0047182622f,-0.0029540703f,-0.00081461202f},
1171 {-0.064892969f,-0.11661773f,-0.17043929f,-0.25417001f,-0.34353751f,-0.33903683f,-0.25655254f,-0.15106232f,-0.097685198f,-0.027478557f},
1172 {-0.069273584f,-0.10993957f,-0.13346817f,-0.13654806f,-0.096248577f,-0.071345378f,-0.040468966f,-0.0211288f,-0.013264997f,-0.0036639447f},
1173 {-0.042920762f,-0.054369797f,-0.052367865f,-0.040534678f,-0.022767208f,-0.01601877f,-0.0087641883f,-0.0045240297f,-0.0028331222f,-0.00078136769f},
1174 {-0.082361693f,-0.14918816f,-0.22112994f,-0.34301241f,-0.52772513f,-0.5813414f,-0.53771111f,-0.3682489f,-0.25049777f,-0.073003569f},
1175 {-0.036111073f,-0.06587829f,-0.098882791f,-0.15880356f,-0.2712657f,-0.32156729f,-0.32729224f,-0.23402028f,-0.16071005f,-0.047082247f},
1176 {-0.014103031f,-0.025780252f,-0.03883755f,-0.063048578f,-0.11207771f,-0.13775677f,-0.14908283f,-0.11045335f,-0.07655027f,-0.022550556f},
1177 {-0.050350716f,-0.09181969f,-0.13772611f,-0.22079247f,-0.37596653f,-0.44682619f,-0.46554397f,-0.34629266f,-0.24235026f,-0.072094835f},
1178 {-0.019043671f,-0.03486822f,-0.052685779f,-0.086317621f,-0.15955383f,-0.2051296f,-0.24897791f,-0.20818672f,-0.15155625f,-0.046353162f},
1179 {-0.006828925f,-0.012518705f,-0.018958244f,-0.031275577f,-0.059563883f,-0.079317458f,-0.10560439f,-0.097624085f,-0.0740598f,-0.023377524f}
1180 };
1181 for(int t = 0; t < nTest; t++) {
1182 float phase_ref, mag_ref, mag_ref_db;
1183 double* pAs = &as_dvf[t][0];
1184 double* pBs = &bs_dvf[t][0];
1185 evalIIRTransferFunction(pBs, pAs, nCoeffs, (float*)freqs, nFreqs, fs, 0, (float*)mag, (float*)phase);
1186 for(i = 0; i < nFreqs; i++){
1187 phase_ref = phases_dvf[t][i];
1188 mag_ref = mags_dvf[t][i];
1189 mag_ref_db = 20.f*log10f(mag_ref);
1190 TEST_ASSERT_FLOAT_WITHIN(magToleranceDb + errScale*fabsf(mag_ref_db), mag_ref_db, 20*log10(mag[i]));
1191 TEST_ASSERT_FLOAT_WITHIN(phaseTolerance, phase_ref, phase[i]);
1192 }
1193 }
1194 /* using the same parameters as above, test evalIIRTransferFunctionf():
1195 * coeffs of type float */
1196 float as_dvf_f[12][2] = { /* nTest x nCoeffs */
1197 {1.f,-0.95864619f},{1.f,-0.96599375f},{1.f,-0.9648155f},{1.f,-0.84969467f},{1.f,-0.93327999f},{1.f,-0.95974372f},
1198 {1.f,-0.83460338f},{1.f,-0.74744027f},{1.f,-0.67445272f},{1.f,-0.76911048f},{1.f,-0.64266857f},{1.f,-0.54043336f}
1199 };
1200 float bs_dvf_f[12][2] = { /* nTest x nCoeffs */
1201 {8.0841171f,-7.5217374f},{2.1404179f,-2.0439339f},{1.3371466f,-1.2837154f},{0.73353449f,-0.52570938f},
1202 {1.1312827f,-1.0324739f},{1.1334695f,-1.0826754f},{0.18784397f,-0.098191093f},{0.43493823f,-0.24012268f},
1203 {0.72850398f,-0.42469436f},{0.1577158f,-0.075691788f},{0.34545179f,-0.16259809f},{0.60618525f,-0.27852873f}
1204 };
1205 for(int t = 0; t < nTest; t++) {
1206 float* pAs = &as_dvf_f[t][0];
1207 float* pBs = &bs_dvf_f[t][0];
1208 evalIIRTransferFunctionf(pBs, pAs, nCoeffs, (float*)freqs, nFreqs, fs, 0, (float*)mag, (float*)phase);
1209 for(i = 0; i < nFreqs; i++){
1210 phase_ref = phases_dvf[t][i];
1211 mag_ref = mags_dvf[t][i];
1212 mag_ref_db = 20.f*log10f(mag_ref);
1213 TEST_ASSERT_FLOAT_WITHIN(magToleranceDb + errScale*fabsf(mag_ref_db), mag_ref_db, 20*log10(mag[i]));
1214 TEST_ASSERT_FLOAT_WITHIN(phaseTolerance, phase_ref, phase[i]);
1215 }
1216 }
1217
1218}
1219
1220
1222 void* hFaF;
1223 int i, band;
1224 float* inSig, *outSig;
1225 float** outFrame, **outSig_bands;
1226 void* hFFT;
1227 float_complex* insig_fft, *outsig_fft;
1228
1229 /* Config */
1230 const float acceptedTolerance_dB = 0.5f;
1231 const int signalLength = 256;
1232 const int frameSize = 256;//16;
1233 float fs = 48e3;
1234 int order = 3;
1235 float fc[6] = {176.776695296637f, 353.553390593274f, 707.106781186547f, 1414.21356237309f, 2828.42712474619f, 5656.85424949238f};
1236 inSig = malloc1d(signalLength * sizeof(float));
1237 outSig_bands = (float**)malloc2d(7, signalLength, sizeof(float));
1238 outSig = calloc1d(signalLength, sizeof(float));
1239
1240 insig_fft = malloc1d((signalLength / 2 + 1) * sizeof(float_complex));
1241 outsig_fft = malloc1d((signalLength / 2 + 1) * sizeof(float_complex));
1242
1243 /* Impulse */
1244 memset(inSig, 0, signalLength*sizeof(float));
1245 inSig[0] = 1.0f;
1246
1247 /* Pass impulse through filterbank */
1248 outFrame = (float**)malloc2d(7, frameSize, sizeof(float));
1249 faf_IIRFilterbank_create(&hFaF, order, (float*)fc, 6, fs, frameSize);
1250 for(i=0; i< signalLength/frameSize; i++){
1251 faf_IIRFilterbank_apply(hFaF, &inSig[i*frameSize], outFrame, frameSize);
1252 for(band=0; band<7; band++)
1253 memcpy(&outSig_bands[band][i*frameSize], outFrame[band], frameSize*sizeof(float));
1254 }
1256
1257 /* Sum the individual bands */
1258 for(band=0; band<7; band++)
1259 cblas_saxpy(signalLength, 1.0f, outSig_bands[band], 1, outSig, 1);
1260
1261 /* Check that the magnitude difference between input and output is below 0.5dB */
1262 saf_rfft_create(&hFFT, signalLength);
1263 saf_rfft_forward(hFFT, inSig, insig_fft);
1264 saf_rfft_forward(hFFT, outSig, outsig_fft);
1265 for(i=0; i<signalLength/2+1; i++)
1266 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance_dB, 0.0f, 20.0f * log10f(cabsf( ccdivf(outsig_fft[i],insig_fft[i]) )));
1267
1268 /* Now the same thing, but for 1st order */
1269 order = 1;
1270 faf_IIRFilterbank_create(&hFaF, order, (float*)fc, 6, fs, frameSize);
1271 for(i=0; i< signalLength/frameSize; i++){
1272 faf_IIRFilterbank_apply(hFaF, &inSig[i*frameSize], outFrame, frameSize);
1273 for(band=0; band<7; band++)
1274 memcpy(&outSig_bands[band][i*frameSize], outFrame[band], frameSize*sizeof(float));
1275 }
1277 memset(outSig, 0, signalLength*sizeof(float));
1278 for(band=0; band<7; band++)
1279 cblas_saxpy(signalLength, 1.0f, outSig_bands[band], 1, outSig, 1);
1280 saf_rfft_forward(hFFT, outSig, outsig_fft);
1281 for(i=0; i<signalLength/2+1; i++)
1282 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance_dB, 0.0f, 20.0f * log10f(cabsf(ccdivf(outsig_fft[i], insig_fft[i]))));
1283
1284 /* clean-up */
1285 saf_rfft_destroy(&hFFT);
1286 free(outFrame);
1287 free(inSig);
1288 free(outSig_bands);
1289 free(outSig);
1290 free(insig_fft);
1291 free(outsig_fft);
1292}
1293
1294void test__gexpm(void){
1295 int i, j;
1296 float outM[6][6];
1297
1298 /* prep */
1299 const float acceptedTolerance = 0.0001f;
1300 const float inM[6][6] = {
1301 {-0.376858200853762f,0.656790634216694f,0.124479178614046f,-0.334752428307223f,1.50745241578235f,0.0290651989052969f},
1302 {0.608382058262806f,0.581930485432986f,3.23135406998058f,-0.712003744668929f,-1.33872571354702f,-0.334742482743222f},
1303 {-0.795741418256672f,0.690709474622409f,0.620971281129248f,1.38749471231620f,0.897245329198841f,-0.0693670166113321f},
1304 {0.179789913109994f,-1.06135084902804f,-1.10032635271188f,0.612441344250358f,-2.43213807790664f,-0.479265889956047f},
1305 {-0.277441781278754f,-0.0732116130293688f,-0.572551795688137f,1.02024767389969f,0.167385894565923f,1.45210312619277f},
1306 {-0.205305770089918f,-1.59783032780633f,1.08539265129120f,0.460057585947626f,-1.02420974042838f,1.04117461500218f}
1307 };
1308 const float outM_ref[6][6] = {
1309 {0.385163650730121f,0.0865151585709784f,0.898406722231524f,0.877640791713973f,0.435244824708340f,0.888866982998854f},
1310 {-0.664938511314777f,5.02943129352875f,8.24444951891833f,2.23840978101979f,-0.942669833528886f,-2.38535530623266f},
1311 {-0.388189314743059f,0.429308537172675f,1.13870842882926f,1.60875776611798f,-1.44249911796405f,-1.51822150286392f},
1312 {1.05630187656688f,0.256606570814868f,-2.42701873560847f,-1.42372526577009f,-0.335273289873574f,-1.94362909671742f},
1313 {0.0261470437116839f,-3.03329326250434f,-3.50207776203591f,0.412043775125377f,-0.536000387729306f,1.61801775548557f},
1314 {-0.292024827617294f,-4.31537192033477f,-3.99160103133879f,0.312499067924889f,-1.46924802440347f,1.98522802303672f}
1315 };
1316
1317 /* Compute matrix exponential */
1318 gexpm((float*)inM, 6, 0, (float*)outM);
1319
1320 /* Check that output of SAF's gexpm, is similar to Matlab's expm: */
1321 for(i=0; i<6; i++)
1322 for(j=0; j<6; j++)
1323 TEST_ASSERT_TRUE( fabsf(outM[i][j] - outM_ref[i][j]) <= acceptedTolerance );
1324}
1325
1326/* Target values are generated by MATLAB functions in `generate_coeffs_for_plugin_tests.m`
1327 * (corresponding function names are noted above each data set), which is not included in this
1328 * repository but are in the author's development repository `nearfield_rangeextrapolation`.
1329 */
1331
1332 /* setup */
1333 const float acceptedTolerance = 0.00001f;
1334 const float acceptedTolerance_fc = 0.1f;
1335 const int nTheta = 19;
1336 const int nRho = 5;
1337 const float rho[5] = {1.150000f,1.250000f,1.570000f,2.381000f,3.990000f};
1338 const float g0_ref[5][19] = { /* testRhoCoeffs_g_0 */
1339 {22.670282f,17.717752f,11.902597f,7.906282f,4.720884f,2.217061f,0.134088f,-1.613730f,-3.095960f,-5.279052f,-5.433653f,-6.342905f,-7.107677f,-7.744796f,-8.236084f,-8.613662f,-8.864276f,-9.065870f,-9.089385f},
1340 {18.295933f,15.510441f,11.452312f,7.951083f,4.997235f,2.609075f,0.592613f,-1.107964f,-2.557504f,-4.547256f,-4.853912f,-5.750024f,-6.504702f,-7.133244f,-7.621092f,-7.993574f,-8.244015f,-8.438287f,-8.467470f},
1341 {11.937032f,11.093339f,9.245757f,7.118216f,4.990070f,3.083402f,1.371444f,-0.121838f,-1.427296f,-2.979742f,-3.542803f,-4.381065f,-5.091220f,-5.683427f,-6.149122f,-6.508598f,-6.748356f,-6.923465f,-6.961620f},
1342 {6.676990f,6.451424f,5.818377f,4.924700f,3.861979f,2.760683f,1.662668f,0.629080f,-0.327831f,-1.328149f,-1.970549f,-2.649238f,-3.234743f,-3.727775f,-4.122829f,-4.433178f,-4.640902f,-4.783351f,-4.823625f},
1343 {3.628860f,3.534311f,3.298166f,2.922799f,2.438587f,1.888286f,1.296135f,0.698518f,0.112899f,-0.473626f,-0.960644f,-1.428032f,-1.841763f,-2.196404f,-2.487131f,-2.717121f,-2.873915f,-2.978235f,-3.010937f}
1344 };
1345 const float gInf_ref[5][19] = { /* testRhoCoeffs_g_inf */
1346 {-4.643651f,-4.225287f,-4.134752f,-4.386332f,-5.244711f,-6.439307f,-7.659091f,-8.887172f,-10.004796f,-10.694171f,-11.190476f,-10.876569f,-10.140292f,-9.913242f,-9.411469f,-8.981807f,-8.723677f,-8.529900f,-8.574359f},
1347 {-4.128221f,-3.834507f,-3.575000f,-3.637788f,-4.278932f,-5.310000f,-6.609705f,-7.815000f,-8.925450f,-9.646588f,-10.000000f,-9.784733f,-9.301643f,-8.862963f,-8.370815f,-7.953778f,-7.693305f,-7.500645f,-7.518260f},
1348 {-3.094135f,-2.963709f,-2.721834f,-2.573043f,-2.793627f,-3.414652f,-4.403297f,-5.518539f,-6.578461f,-7.332562f,-7.702192f,-7.582977f,-7.376856f,-6.745349f,-6.279895f,-5.891862f,-5.636418f,-5.456323f,-5.437006f},
1349 {-1.937289f,-1.889079f,-1.765709f,-1.620800f,-1.598110f,-1.815613f,-2.314443f,-3.041183f,-3.857777f,-4.533446f,-4.931544f,-4.962571f,-4.717069f,-4.357935f,-3.971281f,-3.646312f,-3.422461f,-3.269044f,-3.231471f},
1350 {-1.126412f,-1.103440f,-1.049199f,-0.969714f,-0.917898f,-0.962176f,-1.182409f,-1.566237f,-2.065834f,-2.552771f,-2.884909f,-2.977707f,-2.811758f,-2.629199f,-2.355800f,-2.118920f,-1.949860f,-1.834291f,-1.800638f}
1351 };
1352 const float fc_ref[5][19] = { /* testRhoCoeffs_f_c */
1353 {525.636204f,409.078426f,427.552571f,936.671283f,1635.128987f,2622.394035f,3167.199181f,3899.649293f,4176.703569f,4361.226917f,4634.448076f,4516.401848f,4567.834168f,4685.234222f,4908.786495f,4966.258562f,4936.982049f,4927.963688f,5210.861482f},
1354 {410.072475f,389.319631f,398.844102f,613.394238f,1116.223303f,2095.651724f,2847.557763f,3726.141143f,4080.406901f,4304.960791f,4463.911798f,4449.375495f,4501.166349f,4623.582375f,4757.884246f,4911.093999f,4935.074404f,4940.266143f,5155.085794f},
1355 {358.247441f,352.931439f,352.752741f,402.566754f,590.733021f,1127.131294f,2007.589994f,3160.896502f,3808.131027f,4155.246718f,4336.853155f,4375.553567f,4406.656373f,4543.636509f,4649.878140f,4849.374583f,4974.986343f,5006.214905f,5164.504029f},
1356 {318.842699f,318.199637f,315.776327f,326.423309f,364.498469f,500.548368f,980.626776f,2174.301881f,3296.777215f,3904.656864f,4203.152454f,4329.347194f,4338.652755f,4492.976051f,4579.879128f,4849.678327f,5052.801340f,5116.753611f,5267.402018f},
1357 {297.454930f,297.570719f,296.701047f,300.362959f,308.255747f,342.596563f,509.934390f,1379.970914f,2702.827191f,3646.599635f,4078.866661f,4301.570222f,4303.807248f,4472.223890f,4535.654099f,4855.399825f,5119.558700f,5210.329993f,5380.750972f}
1358 };
1359 /* params to test */
1360 float g0, gInf, fc;
1361
1362 for(int ri = 0; ri < nRho; ri++){
1363 float r = rho[ri];
1364 for(int ti = 0; ti < nTheta; ti++){
1365 calcDVFShelfParams(ti, r, &g0, &gInf, &fc);
1366 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, g0_ref[ri][ti], g0);
1367 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, gInf_ref[ri][ti], gInf);
1368 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance_fc, fc_ref[ri][ti], fc);
1369 }
1370 }
1371}
1372
1373/* Parameter interpolation is implicitly checked in test__dvf_dvfShelfCoeffs. */
1375
1376 /* interpDVFShelfParams() calls calcDVFShelfParams() twice to generate the
1377 * high shelf parameters for the nearests angles in the lookup table. Those
1378 * parameters are subsequently interpolated. So the success of
1379 * interpHighShelfCoeffs() relies first on the calcDVFShelfParams(),
1380 * so that should be tested first. */
1381
1382 /* setup */
1383 const float acceptedTolerance = 0.0001f;
1384 const float acceptedTolerance_frq = 0.01f;
1385 const int nTheta = 6;
1386 const float theta[6] = {0.000000f,2.300000f,47.614000f,98.600000f,166.200000f,180.000000f};
1387 const int nRho = 5;
1388 const float rho[5] = {1.150000f,1.250000f,1.570000f,2.381000f,3.990000f};
1389 const float iG0_ref[5][6] = { /* testShelfParamsInterp_iG_0 */
1390 {22.670282f,21.531200f,2.814473f,-5.412009f,-8.989264f,-9.089385f},
1391 {18.295933f,17.655270f,3.178890f,-4.810981f,-8.364464f,-8.467470f},
1392 {11.937032f,11.742982f,3.538333f,-3.463974f,-6.856924f,-6.961620f},
1393 {6.676990f,6.625110f,3.023452f,-1.880613f,-4.729220f,-4.823625f},
1394 {3.628860f,3.607114f,2.019588f,-0.892461f,-2.938593f,-3.010937f}
1395 };
1396 const float iGInf_ref[5][6] = { /* testShelfParamsInterp_iG_inf */
1397 {-4.643651f,-4.547427f,-6.154277f,-11.120993f,-8.603536f,-8.574359f},
1398 {-4.128221f,-4.060667f,-5.063987f,-9.950522f,-7.573856f,-7.518260f},
1399 {-3.094135f,-3.064137f,-3.266476f,-7.650444f,-5.524759f,-5.437006f},
1400 {-1.937289f,-1.926201f,-1.763717f,-4.875811f,-3.327342f,-3.231471f},
1401 {-1.126412f,-1.121129f,-0.951611f,-2.838410f,-1.878207f,-1.800638f}
1402 };
1403 const float iFc_ref[5][6] = { /* testShelfParamsInterp_if_c */
1404 {525.636204f,498.827915f,2386.832594f,4596.197114f,4931.390665f,5210.861482f},
1405 {410.072475f,405.299321f,1861.960103f,4441.658657f,4938.293282f,5155.085794f},
1406 {358.247441f,357.024760f,999.146666f,4311.428254f,4994.348051f,5164.504029f},
1407 {318.842699f,318.694795f,468.086862f,4161.363071f,5092.451748f,5267.402018f},
1408 {297.454930f,297.481562f,334.402844f,4018.349277f,5175.836901f,5380.750972f}
1409 };
1410 /* High shelf parameters to be tested */
1411 float iG0, iGInf, iFc;
1412
1413 for(int ri = 0; ri < nRho; ri++){
1414 float r = rho[ri];
1415 for(int ti = 0; ti < nTheta; ti++){
1416 interpDVFShelfParams(theta[ti], r, &iG0, &iGInf, &iFc);
1417 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, iG0, iG0_ref[ri][ti]);
1418 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, iGInf, iGInf_ref[ri][ti]);
1419 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance_frq, iFc, iFc_ref[ri][ti]);
1420 }
1421 }
1422}
1423
1425 /* setup */
1426 const float acceptedTolerance = 0.00001f;
1427 const int nTheta = 6;
1428 const float theta[6] = {0.000000f,2.300000f,47.614000f,98.600000f,166.200000f,180.000000f};
1429 const int nRho = 5;
1430 const float rho[5] = {1.150000f,1.250000f,1.570000f,2.381000f,3.990000f};
1431 const float fs = 44100;
1432 const float b0_ref[5][6] = { /* testIIRCoefs_b0 */
1433 {8.084117f,7.162779f,0.733534f,0.181211f,0.157716f,0.157753f},
1434 {5.162983f,4.832104f,0.847478f,0.218379f,0.188114f,0.188100f},
1435 {2.787888f,2.735502f,1.052975f,0.322169f,0.274301f,0.274359f},
1436 {1.733188f,1.725027f,1.162720f,0.508950f,0.432220f,0.432405f},
1437 {1.337147f,1.334601f,1.133469f,0.693396f,0.606185f,0.606313f}
1438 };
1439 const float b1_ref[5][6] = { /* testIIRCoefs_b1 */
1440 {-7.521737f,-6.689086f,-0.525709f,-0.092147f,-0.075692f,-0.072026f},
1441 {-4.880667f,-4.570874f,-0.654751f,-0.113974f,-0.090171f,-0.086752f},
1442 {-2.654257f,-2.604818f,-0.917824f,-0.171818f,-0.130191f,-0.126320f},
1443 {-1.659057f,-1.651278f,-1.090421f,-0.278191f,-0.201595f,-0.195404f},
1444 {-1.283715f,-1.281267f,-1.082675f,-0.387883f,-0.278529f,-0.268341f}
1445 };
1446 const float a1_ref[5][6] = { /* testIIRCoefs_a1 */
1447 {-0.958646f,-0.960287f,-0.849695f,-0.833925f,-0.769110f,-0.755889f},
1448 {-0.965649f,-0.965782f,-0.866341f,-0.818335f,-0.743436f,-0.731349f},
1449 {-0.966189f,-0.966188f,-0.910070f,-0.775971f,-0.682649f,-0.670043f},
1450 {-0.965632f,-0.965605f,-0.948954f,-0.713458f,-0.602472f,-0.587018f},
1451 {-0.964816f,-0.964791f,-0.959744f,-0.661426f,-0.540433f,-0.522001f}
1452 };
1453
1454 float b0, b1, a1; /* IIR coeffs to be tested */
1455 float iG0, iGInf, iFc;
1456
1457 for(int ri = 0; ri < nRho; ri++){
1458 float r = rho[ri];
1459 for(int ti = 0; ti < nTheta; ti++){
1460 interpDVFShelfParams(theta[ti], r, &iG0, &iGInf, &iFc);
1461 dvfShelfCoeffs(iG0, iGInf, iFc, fs, &b0, &b1, &a1);
1462 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, b0, b0_ref[ri][ti]);
1463 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, b1, b1_ref[ri][ti]);
1464 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, a1, a1_ref[ri][ti]);
1465 }
1466 }
1467}
void afSTFT_create(void **const phSTFT, int nCHin, int nCHout, int hopsize, int lowDelayMode, int hybridmode, AFSTFT_FDDATA_FORMAT format)
Creates an instance of afSTFT.
Definition afSTFTlib.c:143
void afSTFT_forward(void *const hSTFT, float **dataTD, int framesize, float_complex ***dataFD)
Performs forward afSTFT transform.
Definition afSTFTlib.c:230
void afSTFT_getCentreFreqs(void *const hSTFT, float fs, int nBands, float *freqVector)
Returns current frequency vector.
Definition afSTFTlib.c:546
void afSTFT_destroy(void **const phSTFT)
Destroys an instance of afSTFT.
Definition afSTFTlib.c:199
void afSTFT_backward(void *const hSTFT, float_complex ***dataFD, int framesize, float **dataTD)
Performs backward afSTFT transform.
Definition afSTFTlib.c:348
@ AFSTFT_BANDS_CH_TIME
nBands x nChannels x nTimeHops
Definition afSTFTlib.h:80
void faf_IIRFilterbank_destroy(void **phFaF)
Destroys an instance of the Favrot & Faller filterbank.
void smb_pitchShift_create(void **hSmb, int nCH, int fftFrameSize, int osamp, float sampleRate)
Creates an instance of SMB PitchShifter.
void saf_matrixConv_apply(void *const hMC, float *inputSig, float *outputSig)
Performs the matrix convolution.
const int __protools_mapping_5p0_to_discrete[5]
Indices to map a 5p0 bus to a discrete bus in ProTools.
void saf_stft_backward(void *const hSTFT, float_complex ***dataFD, int framesize, float **dataTD)
Performs the backward-STFT operation for the current frame.
#define SAF_PI
pi constant (single precision)
const int __protools_mapping_7p0p4_to_discrete[11]
Indices to map a 7p0p4 bus to a discrete bus in ProTools.
void saf_stft_forward(void *const hSTFT, float **dataTD, int framesize, float_complex ***dataFD)
Performs the forward-STFT operation for the current frame.
const float * __HANDLES_Tdesign_dirs_deg[21]
minimum T-design HANDLES (up to degree 21 only).
void sortf(float *in_vec, float *out_vec, int *new_idices, int len, int descendFLAG)
Sort a vector of floating-point values into ascending/decending order (optionally returning the new i...
const int __protools_mapping_discrete_to_7p0p4[11]
Indices to map a discrete bus to a 7p0p4 bus in ProTools.
const int __protools_mapping_discrete_to_9p0p4[13]
Indices to map a discrete bus to a 9p0p4 bus in ProTools.
#define SAF_FALSE
Boolean false.
void quaternion2rotationMatrix(quaternion_data *Q, float R[3][3])
Constructs a 3x3 rotation matrix based on a quaternion.
void interpDVFShelfParams(float theta, float rho, float *iG0, float *iGInf, float *iFc)
Calculate the shelving filter parameters for the Distance Variation Function filter from the source (...
void bessel_Jn(int N, double *z, int nZ, double *J_n, double *dJ_n)
Computes the values of the (cylindrical) Bessel function of the first kind (Jn) and it's derivative (...
void qmf_getCentreFreqs(void *const hQMF, float fs, int nBands, float *centreFreq)
Computes the QMF/hybrid-QMF centre frequencies.
void saf_fft_backward(void *const hFFT, float_complex *inputFD, float_complex *outputTD)
Performs the backward-FFT operation; use for complex to complex transformations.
void euler2Quaternion(float alpha, float beta, float gamma, int degreesFlag, EULER_ROTATION_CONVENTIONS convention, quaternion_data *Q)
Converts Euler angles to a quaternion.
void unique_i(int *input, int nInputs, int **uniqueVals, int **uniqueInds, int *nUnique)
Finds the unique values (and their indices) of the input vector.
const int __protools_mapping_discrete_to_7p0[7]
Indices to map a discrete bus to a 7p0 bus in ProTools.
void getVoronoiWeights(float *dirs_deg, int nDirs, int diagFLAG, float *weights)
Computes the integration weights, based on the areas of each face of the corresponding Voronoi diagra...
void utility_svvsub(const float *a, const float *b, const int len, float *c)
Single-precision, vector-vector subtraction, i.e.
void cmplxPairUp(double_complex *in_vec, double_complex *out_vec, int len)
Pairs up complex numbers and sorts them in ascending order based on their real parts first,...
void saf_fft_create(void **const phFFT, int N)
Creates an instance of saf_fft; complex<->complex FFT.
const int __protools_mapping_discrete_to_LCR[3]
Indices to map a discrete bus to a LCR bus in ProTools.
int qmf_getProcDelay(void *const hQMF)
Returns the processing delay in samples.
void saf_stft_channelChange(void *const hSTFT, int new_nCHin, int new_nCHout)
Changes the number of input/output channels.
void smb_pitchShift_apply(void *hSmb, float pitchShift, int frameSize, float *indata, float *outdata)
Performs pitch shifting of the input signals, while retaining the same time duration as the original ...
void qmf_synthesis(void *const hQMF, float_complex ***dataFD, int framesize, float **dataTD)
Performs QMF synthesis of the input frequency-domain signals.
void saf_matrixConv_create(void **const phMC, int hopSize, float *H, int length_h, int nCHin, int nCHout, int usePartFLAG)
Creates an instance of matrixConv.
void quaternion2euler(quaternion_data *Q, int degreesFlag, EULER_ROTATION_CONVENTIONS convention, float *alpha, float *beta, float *gamma)
Converts a quaternion to Euler angles.
void faf_IIRFilterbank_create(void **phFaF, int order, float *fc, int nCutoffFreq, float sampleRate, int maxNumSamples)
Computes a bank of IIR filter coefficients required to divide a signal into frequency bands,...
void rand_0_1(float *vector, int length)
Generates random numbers between 0 and 1 and stores them in the input vector.
const int __protools_mapping_stereo_to_discrete[2]
Indices to map a stereo bus to a discrete bus in ProTools.
void utility_cimaxv(const float_complex *a, const int len, int *index)
Single-precision, complex, index of maximum absolute value in a vector, i.e.
void saf_rfft_create(void **const phFFT, int N)
Creates an instance of saf_rfft; real<->half-complex (conjugate-symmetric) FFT.
#define SAF_TRUE
Boolean true.
void sph2cart(float *sph, int nDirs, int anglesInDegreesFLAG, float *cart)
Converts spherical coordinates to Cartesian coordinates.
const int __protools_mapping_discrete_to_7p0p6[13]
Indices to map a discrete bus to a 7p0p6 bus in ProTools.
const int __protools_mapping_discrete_to_Quad[4]
Indices to map a discrete bus to a Quad bus in ProTools.
void smb_pitchShift_destroy(void **const hSmb)
Destroys an instance of SMB PitchShifter.
void qmf_analysis(void *const hQMF, float **dataTD, int framesize, float_complex ***dataFD)
Performs QMF analysis of the input time-domain signals.
void saf_fft_forward(void *const hFFT, float_complex *inputTD, float_complex *outputFD)
Performs the forward-FFT operation; use for complex to complex transformations.
void gexpm(float *D, int sizeD, int m1, float *Y)
Numerically solves first-order, linear, homogeneous differential equation systems,...
void evalIIRTransferFunctionf(float *b_coeff, float *a_coeff, int nCoeffs, float *freqs, int nFreqs, float fs, int mag2dB, float *magnitude, float *phase_rad)
Computes magnitude and phase response of an IIR filter from its coefficients (floats) at user-specifi...
const int __protools_mapping_7p0p2_to_discrete[9]
Indices to map a 7p0p2 bus to a discrete bus in ProTools.
void rotationMatrix2quaternion(float R[3][3], quaternion_data *Q)
Calculates the quaternion corresponding to a 3x3 rotation matrix.
const int __protools_mapping_5p0p2_to_discrete[7]
Indices to map a 5p0p2 bus to a discrete bus in ProTools.
const int __protools_mapping_LCR_to_discrete[3]
Indices to map a LCR bus to a discrete bus in ProTools.
void qmf_destroy(void **const phQMF)
Destroys an instance of the qmf filterbank.
const int __protools_mapping_discrete_to_5p0p4[9]
Indices to map a discrete bus to a 5p0p4 bus in ProTools.
void latticeDecorrelator_apply(void *hDecor, float_complex ***inFrame, int nTimeSlots, float_complex ***decorFrame)
Applies the lattice all-pass-filter-based multi-channel signal decorrelator.
void saf_matrixConv_destroy(void **const phMC)
Destroys an instance of matrixConv.
void saf_rfft_forward(void *const hFFT, float *inputTD, float_complex *outputFD)
Performs the forward-FFT operation; use for real to complex (conjugate symmetric) transformations.
const int __protools_mapping_7p0p6_to_discrete[13]
Indices to map a 7p0p6 bus to a discrete bus in ProTools.
const int __protools_mapping_discrete_to_9p0p6[15]
Indices to map a discrete bus to a 9p0p6 bus in ProTools.
void saf_rfft_backward(void *const hFFT, float_complex *inputFD, float *outputTD)
Performs the backward-FFT operation; use for complex (conjugate symmetric) to real transformations.
float sumf(float *values, int nValues)
Returns the sum of all values.
void saf_rfft_destroy(void **const phFFT)
Destroys an instance of saf_rfft.
int qmf_getNBands(void *const hQMF)
Returns the number of frequency bands.
void latticeDecorrelator_create(void **phDecor, float fs, int hopsize, float *freqVector, int nBands, int nCH, int *orders, float *freqCutoffs, int nCutoffs, int maxDelay, int lookupOffset, float enComp_coeff)
Creates an instance of the lattice all-pass-filter-based multi-channel signal decorrelator.
void saf_stft_destroy(void **const phSTFT)
Destroys an instance of saf_stft.
void butterCoeffs(BUTTER_FILTER_TYPES filterType, int order, float cutoff1, float cutoff2, float sampleRate, double *b_coeffs, double *a_coeffs)
Computes Butterworth IIR filter coefficients [1].
void sortz(double_complex *in_vec, double_complex *out_vec, int len, int descendFLAG)
Sort a vector of complex double floating-point values into ascending/ decending order.
void dvfShelfCoeffs(float g0, float gInf, float fc, float fs, float *b0, float *b1, float *a1)
Calculate the DVF filter coefficients from shelving filter parameters.
const int __protools_mapping_9p0p4_to_discrete[13]
Indices to map a 9p0p4 bus to a discrete bus in ProTools.
const int __protools_mapping_discrete_to_7p0p2[9]
Indices to map a discrete bus to a 7p0p2 bus in ProTools.
const int __Tdesign_nPoints_per_degree[21]
Number of points in each t-design (up to degree 21 only).
const int __protools_mapping_discrete_to_5p0[5]
Indices to map a discrete bus to a 5p0 bus in ProTools.
const int __protools_mapping_5p0p4_to_discrete[9]
Indices to map a 5p0p4 bus to a discrete bus in ProTools.
int bessel_yn(int N, double *z, int nZ, double *y_n, double *dy_n)
Computes the values of the spherical Bessel function of the second kind (yn) and it's derivative (dyn...
int bessel_jn(int N, double *z, int nZ, double *j_n, double *dj_n)
Computes the values of the spherical Bessel function of the first kind (jn) and it's derivative (djn)
void cart2sph(float *cart, int nDirs, int anglesInDegreesFLAG, float *sph)
Converts Cartesian coordinates to spherical coordinates.
void qmf_create(void **const phQMF, int nCHin, int nCHout, int hopsize, int hybridmode, QMF_FDDATA_FORMAT format)
Creates an instance of the qmf filterbank.
const int __protools_mapping_discrete_to_5p0p2[7]
Indices to map a discrete bus to a 5p0p2 bus in ProTools.
int bessel_kn(int N, double *z, int nZ, double *k_n, double *dk_n)
Computes the values of the modified spherical Bessel function of the second kind (kn) and it's deriva...
void evalIIRTransferFunction(double *b_coeff, double *a_coeff, int nCoeffs, float *freqs, int nFreqs, float fs, int mag2dB, float *magnitude, float *phase_rad)
Computes magnitude and phase response of an IIR filter from its coefficients at user-specified freque...
void latticeDecorrelator_destroy(void **phDecor)
Destroys an instance of the lattice all-pass-filter-based multi-channel signal decorrelator.
const int __protools_mapping_Quad_to_discrete[4]
Indices to map a Quad bus to a discrete bus in ProTools.
const int __protools_mapping_7p0_to_discrete[7]
Indices to map a 7p0 bus to a discrete bus in ProTools.
const int __protools_mapping_discrete_to_stereo[2]
Indices to map a discrete bus to a stereo bus in ProTools.
float L2_norm(float *v, int lenV)
Returns the L2 (Euclidean) norm of an arbitrary length vector.
const int __protools_mapping_9p0p6_to_discrete[15]
Indices to map a 9p0p9 bus to a discrete bus in ProTools.
void utility_svvdot(const float *a, const float *b, const int len, float *c)
Single-precision, vector-vector dot product, i.e.
void delaunaynd(const float *points, const int nPoints, const int nd, int **DT, int *nDT)
Computes the Delaunay triangulation of an arrangement of points in N-dimensional space.
void bessel_Yn(int N, double *z, int nZ, double *Y_n, double *dY_n)
Computes the values of the (cylindrical) Bessel function of the second kind (Yn) and it's derivative ...
void saf_stft_flushBuffers(void *const hSTFT)
Flushes the internal buffers with zeros.
void saf_fft_destroy(void **const phFFT)
Destroys an instance of saf_fft.
void utility_svsmul(float *a, const float *s, const int len, float *c)
Single-precision, multiplies each element in vector 'a' with a scalar 's', i.e.
void rand_m1_1(float *vector, int length)
Generates random numbers between -1 and 1 and stores them in the input vector.
void saf_stft_create(void **const phSTFT, int winsize, int hopsize, int nCHin, int nCHout, SAF_STFT_FDDATA_FORMAT FDformat)
Creates an instance of saf_stft.
void calcDVFShelfParams(int i, float rhoIn, float *g0, float *gInf, float *fc)
Calculate the high shelf gains and cutoff parameters, given a azimuth index i and distance rho.
int bessel_in(int N, double *z, int nZ, double *i_n, double *di_n)
Computes the values of the modified spherical Bessel function of the first kind (in) and it's derivat...
void faf_IIRFilterbank_apply(void *hFaF, float *inSig, float **outBands, int nSamples)
Applies the Favrot & Faller filterbank.
@ BUTTER_FILTER_BSF
band-stop filter
@ BUTTER_FILTER_LPF
low-pass filter
@ BUTTER_FILTER_HPF
high-pass filter
@ BUTTER_FILTER_BPF
band-pass filter
@ EULER_ROTATION_YAW_PITCH_ROLL
yaw-pitch-roll, 'zyx'
@ SAF_STFT_BANDS_CH_TIME
nBands x nChannels x nTimeHops
@ QMF_BANDS_CH_TIME
nBands x nChannels x nTimeHops
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 * malloc1d(size_t dim1_data_size)
1-D malloc (same as malloc, but with error checking)
Definition md_malloc.c:59
void * calloc1d(size_t dim1, size_t data_size)
1-D calloc (same as calloc, but with error checking)
Definition md_malloc.c:69
void *** malloc3d(size_t dim1, size_t dim2, size_t dim3, size_t data_size)
3-D malloc (contiguously allocated, so use free() as usual to deallocate)
Definition md_malloc.c:151
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
#define FLATTEN3D(A)
Use this macro when passing a 3-D dynamic multi-dimensional array to memset, memcpy or any other func...
Definition md_malloc.h:72
Unit test program for the Spatial_Audio_Framework.
Quaternion data structure.
_Atomic_FLOAT32 z
Z value of the quaternion [-1..1].
_Atomic_FLOAT32 y
Y value of the quaternion [-1..1].
_Atomic_FLOAT32 x
X value of the quaternion [-1..1].
_Atomic_FLOAT32 w
W value of the quaternion [-1..1].
_Atomic_FLOAT32 Q[4]
WXYZ values of the quaternion [-1..1].
void test__protoolsRemappingIndices(void)
Testing that the remapping indices are reversable.
void test__saf_fft(void)
Testing the forward and backward complex-complex FFT (saf_fft)
void test__evalIIRTransferFunction(void)
Testing that the magnitudes and phases returned by evalIIRTransferFunction() are numerically similar ...
void test__getVoronoiWeights(void)
Testing that the weights from the getVoronoiWeights() function sum to 4pi and that the weights are al...
void test__sphericalBesselFunctions(void)
Testing bessel_jn(), bessel_in(), bessel_yn(), and bessel_kn()
void test__saf_stft_50pc_overlap(void)
Testing for perfect reconstruction of the saf_stft (when configured for 50% window overlap)
void test__butterCoeffs(void)
Testing that the coefficients computed with butterCoeffs() are numerically similar to the "butter" fu...
void test__sortf(void)
Testing the sortf() function (sorting real floating point numbers)
void test__cmplxPairUp(void)
Testing the cmplxPairUp() function (grouping up conjugate symmetric values)
void test__smb_pitchShifter(void)
Testing that the smb_pitchShifter can shift the energy of input spectra by one octave down.
void test__unique_i(void)
Testing that the unique_i() function operates correctly.
void test__saf_rfft(void)
Testing the forward and backward real-(half)complex FFT (saf_rfft)
void test__cylindricalBesselFunctions(void)
Testing bessel_Jn(), bessel_Yn()
void test__dvf_dvfShelfCoeffs(void)
Test the generation of high shelf coeffs based on shelf gain and fc parameters.
void test__delaunaynd(void)
Testing that the delaunaynd() function can triangulate basic shapes.
void test__faf_IIRFilterbank(void)
Testing that the faf_IIRFilterbank can reconstruct the original signal power.
void test__qmf(void)
Testing the (near)-perfect reconstruction performance of the QMF filterbank.
void test__gexpm(void)
Testing computing the matrix exponential - comparing the output to that of the "expm" function in Mat...
void test__dvf_interpDVFShelfParams(void)
Test the interpolation of high shelf parameters based on distance and incidence angle parameters.
void test__sortz(void)
Testing the sortz() function (sorting complex double-floating point numbers)
void test__cart2sph(void)
Testing cart2sph() and sph2cart() are reversible.
void test__dvf_calcDVFShelfParams(void)
Calculate high shelf parameters, g0, gInf, fc, from the lookup table coefficients (10 degree steps)
void test__saf_matrixConv(void)
Testing the saf_matrixConv.
void test__latticeDecorrelator(void)
Testing the performance of the latticeDecorrelator, verifying that the inter- channel cross-correlati...
void test__saf_stft_LTI(void)
Testing for perfect reconstruction of the saf_stft (when configured for linear time-invariant (LTI) f...
void test__quaternion(void)
Testing that quaternion2rotationMatrix() and rotationMatrix2quaternion() are reversible.