SAF
Loading...
Searching...
No Matches
array2sh_internal.c
Go to the documentation of this file.
1/*
2 * Copyright 2017-2018 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
49
50#include "array2sh_internal.h"
51
53
55 { { 45.0f, 35.264f},
56 { -45.0f, -35.264f},
57 { 135.0f, -35.264f},
58 { -135.0f, 35.264f},
59 { -59.5674172539630f, -69.7763953095265f},
60 { -107.629493132161f, 10.0801907586439f},
61 { 59.5651193014662f, 69.7750019669784f},
62 { -72.3715136529922f, -10.0909592824967f},
63 { 120.418832045969f, -69.7630732527928f},
64 { 72.3705387467529f, 10.0908849630425f},
65 { 10.5699823270029f, 17.3489915451677f},
66 { -169.426851150349f, 17.3476277272366f},
67 { -30.7728814706980f, 68.2546206344998f},
68 { 101.529181734204f, 18.5653877408229f},
69 { 149.250234431564f, 68.2619792676765f},
70 { 18.9183978612362f, -10.9197242075287f},
71 { 78.4729799557400f, -18.5558892226653f},
72 { -18.9161109968480f, 10.9195275229078f},
73 { -78.4723066816396f, 18.5565169424192f},
74 { -161.085243560336f, -10.9220975953767f},
75 { -101.530063994217f, -18.5648176173994f},
76 { 161.085855454581f, 10.9237834974412f},
77 { 30.7790837424989f, -68.2562996173874f},
78 { -149.255648697558f, -68.2594892022576f},
79 { 56.4638592172755f, 41.2645742763346f},
80 { 46.4626904606488f, 24.5386896277818f},
81 { -123.528839291756f, 41.2565889648213f},
82 { 32.1932295461248f, 38.8027347956192f},
83 { 133.534545076742f, -24.5310453346374f},
84 { -32.1935458377634f, -38.8041545028931f},
85 { -133.533477606995f, 24.5319420428588f},
86 { -147.797881221742f, 38.7980990097138f},
87 { -46.4626630742447f, -24.5400749997299f},
88 { 147.799544204027f, -38.7965984357137f},
89 { -56.4649576930965f, -41.2658215291547f},
90 { 123.530547454259f, -41.2555316199835f},
91 { 84.7385596473317f, 27.3132538556364f},
92 { 27.4072860474693f, 4.67593317316088f},
93 { -95.2612516264878f, 27.3036967398252f},
94 { 10.0607591758466f, 62.2287426217158f},
95 { 152.592710471291f, -4.67007594116539f},
96 { -10.0604693351594f, -62.2317767049329f},
97 { -152.592615409799f, 4.67188744845125f},
98 { -169.918387889403f, 62.2272712110016f},
99 { -27.4047652820400f, -4.67684895484045f},
100 { 169.918493477140f, -62.2246416140826f},
101 { -84.7390238025025f, -27.3120311727342f},
102 { 95.2622793653641f, -27.3027426350066f},
103 { 136.272846725200f, -0.726139068263599f},
104 { -1.05456977390436f, -46.2698051615206f},
105 { -43.7263256019408f, -0.737666959763443f},
106 { -91.0084089447853f, 43.7170295842697f},
107 { -178.936314291048f, 46.2682278552731f},
108 { 91.0100541861312f, -43.7163112498585f},
109 { 178.938490558618f, -46.2666218304286f},
110 { 88.9860902890987f, 43.7285680951606f},
111 { 1.05095788619312f, 46.2682407577363f},
112 { -88.9851906686018f, -43.7278600822776f},
113 { -136.273314433943f, 0.726484363994244f},
114 { 43.7273191822901f, 0.733967099281741f},
115 { 55.2321077221357f, 10.8187456245361f},
116 { 13.0867814081219f, 34.0654299770448f},
117 { -124.767898552651f, 10.8099467408727f},
118 { 71.4751302534919f, 53.7984109774923f},
119 { 166.905549455530f, -34.0625862919868f},
120 { -71.4765158530160f, -53.8002805142714f},
121 { -166.904587570338f, 34.0633858486074f},
122 { -108.515564585463f, 53.7864186881779f},
123 { -13.0869589355977f, -34.0644403811701f},
124 { 108.517274377512f, -53.7853093207457f},
125 { -55.2337689585088f, -10.8200079671294f},
126 { 124.767519417537f, -10.8096092876561f},
127 { -105.493359369733f, -68.1345228940484f},
128 { -111.151973933085f, -5.71277564515985f},
129 { 74.5244447329726f, -68.1205741555505f},
130 { -173.885132403874f, -21.0401043953034f},
131 { -68.8521018079939f, 5.70313885775370f},
132 { 173.885050918764f, 21.0419786777516f},
133 { 68.8513569996117f, -5.70297394874866f},
134 { 6.12041448982713f, -21.0387333986971f},
135 { 111.151515131908f, 5.71357243375474f},
136 { -6.11913621739966f, 21.0397756114589f},
137 { 105.487055344890f, 68.1331981897040f},
138 { -74.5188619841755f, 68.1214716326340f},
139 { 35.2822222163987f, -15.1801841162885f},
140 { -25.1732516880600f, 51.9826514887145f},
141 { -144.722913967824f, -15.1859965048827f},
142 { 108.386507250934f, 33.8824333892584f},
143 { -154.839566662519f, -51.9856067440744f},
144 { -108.385758587361f, -33.8826426573830f},
145 { 154.840828582605f, 51.9884126722963f},
146 { -71.6110721306039f, 33.8714084774643f},
147 { 25.1699762263473f, -51.9843011897216f},
148 { 71.6126173250777f, -33.8705490198003f},
149 { -35.2810594358470f, 15.1763090469469f},
150 { 144.722474074749f, 15.1863112389402f},
151 { -125.277803641269f, -28.5561977838671f},
152 { -146.317489805034f, -30.4888082061806f},
153 { 54.7220842889970f, -28.5463137051834f},
154 { -133.292600372594f, -45.8199818171681f},
155 { -33.6854220654234f, 30.4794807422719f},
156 { 133.295169860873f, 45.8200619710773f},
157 { 33.6830420572493f, -30.4828689919959f},
158 { 46.7098267598019f, -45.8106991831118f},
159 { 146.318736830907f, 30.4889588527258f},
160 { -46.7107531920589f, 45.8077510168776f},
161 { 125.278543193551f, 28.5567151663926f},
162 { -54.7226679373581f, 28.5424675755949f},
163 { -144.402199049652f, 54.7141577397485f},
164 { 112.382216591782f, -28.0100385212576f},
165 { 35.5834085793474f, 54.7193853715013f},
166 { -29.9155874729000f, -19.6471282266303f},
167 { 67.6163420587218f, 28.0211878299760f},
168 { 29.9151078604395f, 19.6461577548916f},
169 { -67.6164804090156f, -28.0221553885221f},
170 { 150.081679085309f, -19.6396879622134f},
171 { -112.381365317985f, 28.0105570315277f},
172 { -150.081127534239f, 19.6412999777121f},
173 { 144.402936805264f, -54.7127597303097f},
174 { -35.5837994140428f, -54.7203576209200f},
175 { 68.5348504928164f, -52.8527273534571f},
176 { -54.8218918027732f, 12.7570858971645f},
177 { -111.464947340552f, -52.8652964226689f},
178 { 164.510735700738f, 34.1892226850382f},
179 { -125.181243085238f, -12.7710900404128f},
180 { -164.510172672569f, -34.1865270720665f},
181 { 125.180742749445f, 12.7714932994183f},
182 { -15.4967238792034f, 34.1855682977571f}};
183
189(
190 void* const hA2sh,
191 int order
192)
193{
194 array2sh_data *pData = (array2sh_data*)(hA2sh);
195 int band, n, i;
196 int o[MAX_SH_ORDER+2];
197
198 for(n=0; n<order+2; n++)
199 o[n] = n*n;
200 for(band=0; band<HYBRID_BANDS; band++)
201 for(n=0; n < order+1; n++)
202 for(i=o[n]; i < o[n+1]; i++)
203 pData->bN_inv_R[band][i] = pData->bN_inv[band][n];
204}
205
207(
208 void* const hA2sh
209)
210{
211 array2sh_data *pData = (array2sh_data*)(hA2sh);
212 array2sh_arrayPars* arraySpecs = (array2sh_arrayPars*)(pData->arraySpecs);
213 int new_nSH, nSH;
214
215 new_nSH = (pData->new_order+1)*(pData->new_order+1);
216 nSH = (pData->order+1)*(pData->order+1);
217 if(pData->hSTFT==NULL)
218 afSTFT_create(&(pData->hSTFT), arraySpecs->newQ, new_nSH, HOP_SIZE, 0, 1, AFSTFT_BANDS_CH_TIME);
219 else if(arraySpecs->newQ != arraySpecs->Q || nSH != new_nSH){
220 afSTFT_channelChange(pData->hSTFT, arraySpecs->newQ, new_nSH);
221 afSTFT_clearBuffers(pData->hSTFT);
222 pData->reinitSHTmatrixFLAG = 1; /* filters will need to be updated too */
223 }
224 arraySpecs->Q = arraySpecs->newQ;
225}
226
228(
229 void* const hA2sh
230)
231{
232 array2sh_data *pData = (array2sh_data*)(hA2sh);
233 array2sh_arrayPars* arraySpecs = (array2sh_arrayPars*)(pData->arraySpecs);
234 int i, j, band, n, order, nSH;
235 double alpha, beta, g_lim, regPar;
236 double kr[HYBRID_BANDS], kR[HYBRID_BANDS];
237 float sensorCoords_deg_local[MAX_NUM_SENSORS][2];
238 float* Y_mic, *pinv_Y_mic;
239 float_complex* pinv_Y_mic_cmplx, *diag_bN_inv_R;
240 const float_complex calpha = cmplxf(1.0f, 0.0f); const float_complex cbeta = cmplxf(0.0f, 0.0f);
241
242 /* prep */
243 order = pData->new_order;
244 nSH = (order+1)*(order+1);
245 arraySpecs->R = SAF_MIN(arraySpecs->R, arraySpecs->r);
246 for(band=0; band<HYBRID_BANDS; band++){
247 kr[band] = 2.0*SAF_PId*(pData->freqVector[band])*(arraySpecs->r)/pData->c;
248 kR[band] = 2.0*SAF_PId*(pData->freqVector[band])*(arraySpecs->R)/pData->c;
249 }
250 for(i=0; i<arraySpecs->Q; i++){
251 sensorCoords_deg_local[i][0] = arraySpecs->sensorCoords_deg[i][0];
252 sensorCoords_deg_local[i][1] = arraySpecs->sensorCoords_deg[i][1];
253 }
254
255 /* Spherical harmponic weights for each sensor direction */
256 Y_mic = malloc1d(nSH*(arraySpecs->Q)*sizeof(float));
257 getRSH(order, (float*)sensorCoords_deg_local, arraySpecs->Q, Y_mic); /* nSH x Q */
258 pinv_Y_mic = malloc1d( arraySpecs->Q * nSH *sizeof(float));
259 utility_spinv(NULL, Y_mic, nSH, arraySpecs->Q, pinv_Y_mic);
260 pinv_Y_mic_cmplx = malloc1d((arraySpecs->Q) * nSH *sizeof(float_complex));
261 for(i=0; i<(arraySpecs->Q)*nSH; i++)
262 pinv_Y_mic_cmplx[i] = cmplxf(pinv_Y_mic[i], 0.0f);
263
264 /* ------------------------------------------------------------------------------ */
265 /* Encoding filters based on the regularised inversion of the modal coefficients: */
266 /* ------------------------------------------------------------------------------ */
267 if ( (pData->filterType==FILTER_SOFT_LIM) || (pData->filterType==FILTER_TIKHONOV) ){
268 /* Compute modal responses */
269 free(pData->bN);
270 pData->bN = malloc1d((HYBRID_BANDS)*(order+1)*sizeof(double_complex));
271 ARRAY2SH_ARRAY_TYPES arrayType = arraySpecs->arrayType;
272 switch(arrayType){
273 case ARRAY_CYLINDRICAL: {
274 ARRAY2SH_WEIGHT_TYPES weightType = arraySpecs->weightType;
275 switch (weightType){
277 case WEIGHT_RIGID_CARD: saf_print_error("weightType is not supported"); break;
278 case WEIGHT_RIGID_DIPOLE: saf_print_error("weightType is not supported"); break;
280 case WEIGHT_OPEN_CARD: saf_print_error("weightType is not supported"); break;
281 case WEIGHT_OPEN_DIPOLE: saf_print_error("weightType is not supported"); break;
282 }
283 break;
284 }
285 case ARRAY_SPHERICAL: {
286 ARRAY2SH_WEIGHT_TYPES weightType = arraySpecs->weightType;
287 switch (weightType){
288 case WEIGHT_OPEN_OMNI: sphModalCoeffs(order, kr, HYBRID_BANDS, ARRAY_CONSTRUCTION_OPEN, 1.0, pData->bN); break;
294 /* if sensors are flushed with the rigid baffle: */
295 if(arraySpecs->R == arraySpecs->r )
296 sphModalCoeffs(order, kr, HYBRID_BANDS, ARRAY_CONSTRUCTION_RIGID, 1.0, pData->bN);
297
298 /* if sensors protrude from the rigid baffle: */
299 else{
300 if (arraySpecs->weightType == WEIGHT_RIGID_OMNI)
301 sphScattererModalCoeffs(order, kr, kR, HYBRID_BANDS, pData->bN);
302 else if (arraySpecs->weightType == WEIGHT_RIGID_CARD)
303 sphScattererDirModalCoeffs(order, kr, kR, HYBRID_BANDS, 0.5, pData->bN);
304 else if (arraySpecs->weightType == WEIGHT_RIGID_DIPOLE)
305 sphScattererDirModalCoeffs(order, kr, kR, HYBRID_BANDS, 0.0, pData->bN);
306 }
307 break;
308 }
309 break;
310 }
311 }
312
313 for(band=0; band<HYBRID_BANDS; band++)
314 for(n=0; n < order+1; n++)
315 pData->bN[band*(order+1)+n] = ccdiv(pData->bN[band*(order+1)+n], cmplx(4.0*SAF_PId, 0.0)); /* 4pi term */
316
317 /* direct inverse */
318 regPar = pData->regPar;
319 for(band=0; band<HYBRID_BANDS; band++)
320 for(n=0; n < order+1; n++)
321 pData->bN_modal[band][n] = ccdiv(cmplx(1.0,0.0), (pData->bN[band*(order+1)+n]));
322
323 /* regularised inverse */
324 if (pData->filterType == FILTER_SOFT_LIM){
325 /* Bernschutz, B., Porschmann, C., Spors, S., Weinzierl, S., Versterkung, B., 2011. Soft-limiting der
326 modalen amplitudenverst?rkung bei sph?rischen mikrofonarrays im plane wave decomposition verfahren.
327 Proceedings of the 37. Deutsche Jahrestagung fur Akustik (DAGA 2011) */
328 g_lim = sqrt(arraySpecs->Q)*pow(10.0,(regPar/20.0));
329 for(band=0; band<HYBRID_BANDS; band++)
330 for(n=0; n < order+1; n++)
331 pData->bN_inv[band][n] = crmul(pData->bN_modal[band][n], (2.0*g_lim*cabs(pData->bN[band*(order+1)+n]) / SAF_PId)
332 * atan(SAF_PId / (2.0*g_lim*cabs(pData->bN[band*(order+1)+n]))) );
333 }
334 else if(pData->filterType == FILTER_TIKHONOV){
335 /* Moreau, S., Daniel, J., Bertet, S., 2006, 3D sound field recording with higher order ambisonics-objective
336 measurements and validation of spherical microphone. In Audio Engineering Society Convention 120. */
337 alpha = sqrt(arraySpecs->Q)*pow(10.0,(regPar/20.0));
338 for(band=0; band<HYBRID_BANDS; band++){
339 for(n=0; n < order+1; n++){
340 beta = sqrt((1.0-sqrt(1.0-1.0/ pow(alpha,2.0)))/(1.0+sqrt(1.0-1.0/pow(alpha,2.0))));
341 pData->bN_inv[band][n] = ccdiv(conj(pData->bN[band*(order+1)+n]), cmplx((pow(cabs(pData->bN[band*(order+1)+n]), 2.0) + pow(beta, 2.0)),0.0));
342 }
343 }
344 }
345
346 /* diag(filters) * Y */
347 array2sh_replicate_order(hA2sh, order); /* replicate orders */
348
349 diag_bN_inv_R = calloc1d(nSH*nSH, sizeof(float_complex));
350 for(band=0; band<HYBRID_BANDS; band++){
351 for(i=0; i<nSH; i++)
352 diag_bN_inv_R[i*nSH+i] = cmplxf((float)creal(pData->bN_inv_R[band][i]), (float)cimag(pData->bN_inv_R[band][i]));
353 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasTrans, nSH, (arraySpecs->Q), nSH, &calpha,
354 diag_bN_inv_R, nSH,
355 pinv_Y_mic_cmplx, nSH, &cbeta,
356 (float_complex*)pData->W[band], MAX_NUM_SENSORS);
357 }
358 free(diag_bN_inv_R);
359 }
360
361 /* ------------------------------------------------------------- */
362 /* Encoding filters based on a linear-phase filter-bank approach */
363 /* ------------------------------------------------------------- */
364 else if ( (pData->filterType==FILTER_Z_STYLE) || (pData->filterType==FILTER_Z_STYLE_MAXRE) ) {
365 /* Zotter, F. A Linear-Phase Filter-Bank Approach to Process Rigid Spherical Microphone Array Recordings. */
366 double normH;
367 float f_lim[MAX_SH_ORDER+1];
368 double H[HYBRID_BANDS][MAX_SH_ORDER+1];
369 double_complex Hs[HYBRID_BANDS][MAX_SH_ORDER+1];
370
371 /* find suitable cut-off frequencies */
372 ARRAY2SH_WEIGHT_TYPES weightType = arraySpecs->weightType;
373 switch (weightType){
374 case WEIGHT_OPEN_OMNI: sphArrayNoiseThreshold(order, arraySpecs->Q, arraySpecs->r, pData->c, ARRAY_CONSTRUCTION_OPEN, 1.0, pData->regPar, f_lim); break;
375 case WEIGHT_OPEN_CARD: sphArrayNoiseThreshold(order, arraySpecs->Q, arraySpecs->r, pData->c, ARRAY_CONSTRUCTION_OPEN_DIRECTIONAL, 0.5, pData->regPar, f_lim); break;
376 case WEIGHT_OPEN_DIPOLE: sphArrayNoiseThreshold(order, arraySpecs->Q, arraySpecs->r, pData->c, ARRAY_CONSTRUCTION_OPEN_DIRECTIONAL, 0.0, pData->regPar, f_lim); break;
380 /* Currently no support for estimating the noise cut-off frequencies for rigid scatterers. */
381 sphArrayNoiseThreshold(order, arraySpecs->Q, arraySpecs->r, pData->c, ARRAY_CONSTRUCTION_RIGID, 1.0, pData->regPar, f_lim); break;
382 }
383
384 /* design prototype filterbank */
385 for(band=0; band<HYBRID_BANDS; band++){
386 normH = 0.0;
387 for (n=0; n<order+1; n++){
388 if (n==0)
389 H[band][n] = 1.0/(1.0+ pow((double)(pData->freqVector[band]/f_lim[n]),2.0));
390 else if (n==order)
391 H[band][n] = pow((double)(pData->freqVector[band]/f_lim[n-1]), (double)order+1.0 ) /
392 (1.0 + pow((double)(pData->freqVector[band]/f_lim[n-1]), (double)order+1.0));
393 else
394 H[band][n] = pow((double)(pData->freqVector[band]/f_lim[n-1]), (double)n+1.0 ) /
395 (1.0 + pow((double)(pData->freqVector[band]/f_lim[n-1]), (double)n+1.0)) *
396 (1.0 / (1.0 + pow((double)(pData->freqVector[band]/f_lim[n]), (double)n+2.0)));
397 normH += H[band][n];
398 }
399 /* normalise */
400 for (n=0; n<order+1; n++)
401 H[band][n] = H[band][n]/normH;
402 }
403
404 /* compute inverse radial response */
405 free(pData->bN);
406 pData->bN = malloc1d((HYBRID_BANDS)*(order+1)*sizeof(double_complex));
407 ARRAY2SH_ARRAY_TYPES arrayType = arraySpecs->arrayType;
408 switch(arrayType){
409 case ARRAY_CYLINDRICAL:{
410 ARRAY2SH_WEIGHT_TYPES weightType = arraySpecs->arrayType;
411 switch (weightType){
413 case WEIGHT_RIGID_CARD: /* not supported */ break;
414 case WEIGHT_RIGID_DIPOLE: /* not supported */ break;
416 case WEIGHT_OPEN_CARD: /* not supported */ break;
417 case WEIGHT_OPEN_DIPOLE: /* not supported */ break;
418 }
419 break;
420 }
421 case ARRAY_SPHERICAL:{
422 ARRAY2SH_WEIGHT_TYPES weightType = arraySpecs->arrayType;
423 switch (weightType){
424 case WEIGHT_OPEN_OMNI: sphModalCoeffs(order, kr, HYBRID_BANDS, ARRAY_CONSTRUCTION_OPEN, 1.0, pData->bN); break;
430 /* if sensors are flushed with the rigid baffle: */
431 if(arraySpecs->R == arraySpecs->r )
432 sphModalCoeffs(order, kr, HYBRID_BANDS, ARRAY_CONSTRUCTION_RIGID, 1.0, pData->bN);
433
434 /* if sensors protrude from the rigid baffle: */
435 else{
436 if (arraySpecs->weightType == WEIGHT_RIGID_OMNI)
437 sphScattererModalCoeffs(order, kr, kR, HYBRID_BANDS, pData->bN);
438 else if (arraySpecs->weightType == WEIGHT_RIGID_CARD)
439 sphScattererDirModalCoeffs(order, kr, kR, HYBRID_BANDS, 0.5, pData->bN);
440 else if (arraySpecs->weightType == WEIGHT_RIGID_DIPOLE)
441 sphScattererDirModalCoeffs(order, kr, kR, HYBRID_BANDS, 0.0, pData->bN);
442 }
443 break;
444 }
445 break;
446 }
447 }
448
449 /* direct inverse (only required for GUI) */
450 for(band=0; band<HYBRID_BANDS; band++)
451 for(n=0; n < order+1; n++)
452 pData->bN_modal[band][n] = ccdiv(cmplx(4.0*SAF_PId, 0.0), pData->bN[band*(order+1)+n]);
453
454 /* phase shift */
455 for(band=0; band<HYBRID_BANDS; band++)
456 for (n=0; n<order+1; n++)
457 Hs[band][n] = ccmul(cexp(cmplx(0.0, kr[band])), ccdiv(cmplx(4.0*SAF_PId, 0.0), pData->bN[band*(order+1)+n]));
458
459 /* apply max-re order weighting and diffuse equalisation (not the same as "array2sh_apply_diff_EQ") */
460 float* wn;
461 double W[MAX_SH_ORDER+1][MAX_SH_ORDER+1];
462 double EN, scale;
463 int nSH_n;
464 memset(W, 0, (MAX_SH_ORDER+1)*(MAX_SH_ORDER+1)*sizeof(double));
465 for (n=0; n<order+1; n++){
466 nSH_n = (n+1)*(n+1);
467 wn = calloc1d(nSH_n*nSH_n, sizeof(float));
468 if(pData->filterType==FILTER_Z_STYLE)
469 for (i=0; i<n+1; i++)
470 wn[(i*i)*nSH_n+(i*i)] = 1.0f;
471 else if(pData->filterType==FILTER_Z_STYLE_MAXRE)
472 getMaxREweights(n, 1, wn);
473 scale = 0.0;
474 for (i=0; i<n+1; i++)
475 scale += (double)(2*i+1)*pow((double)wn[(i*i)*nSH_n + (i*i)], 2.0);
476 for (i=0; i<n+1; i++)
477 W[i][n] = (double)wn[(i*i)*nSH_n + (i*i)]/ sqrt(scale);
478 free(wn);
479 }
480 EN=W[0][n-1];
481 for (n=0; n<order+1; n++)
482 for (i=0; i<order+1; i++)
483 W[i][n] /= EN;
484
485 /* apply bandpass filterbank to the inverse array response to regularise it */
486 double HW[HYBRID_BANDS];
487 double H_np[HYBRID_BANDS][MAX_SH_ORDER+1];
488 double W_np[MAX_SH_ORDER+1];
489 for (n=0; n<order+1; n++){
490 for(band=0; band< HYBRID_BANDS; band++)
491 for (i=n, j=0; i<order+1; i++, j++)
492 H_np[band][j] = H[band][i];
493 for (i=n, j=0; i<order+1; i++, j++)
494 W_np[j] = W[n][i];
495 cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasTrans, HYBRID_BANDS, 1, order+1-n, 1.0,
496 (const double*)H_np, MAX_SH_ORDER+1,
497 (const double*)W_np, MAX_SH_ORDER+1, 0.0,
498 (double*)HW, 1);
499 for(band=0; band<HYBRID_BANDS; band++)
500 pData->bN_inv[band][n] = crmul(Hs[band][n], HW[band]);
501 }
502
503 /* diag(filters) * Y */
504 array2sh_replicate_order(hA2sh, order); /* replicate orders */
505 diag_bN_inv_R = calloc1d(nSH*nSH, sizeof(float_complex));
506 for(band=0; band<HYBRID_BANDS; band++){
507 for(i=0; i<nSH; i++)
508 diag_bN_inv_R[i*nSH+i] = cmplxf((float)creal(pData->bN_inv_R[band][i]), (float)cimag(pData->bN_inv_R[band][i])); /* double->single */
509 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasTrans, nSH, (arraySpecs->Q), nSH, &calpha,
510 diag_bN_inv_R, nSH,
511 pinv_Y_mic_cmplx, nSH, &cbeta,
512 (float_complex*)pData->W[band], MAX_NUM_SENSORS);
513 }
514 free(diag_bN_inv_R);
515 }
516
517 pData->order = order;
518
519 if(pData->enableDiffEQpastAliasing)
521
522 free(Y_mic);
523 free(pinv_Y_mic);
524 free(pinv_Y_mic_cmplx);
525}
526
527/* Based on a MatLab script by Archontis Politis, 2019 */
528void array2sh_apply_diff_EQ(void* const hA2sh)
529{
530 array2sh_data *pData = (array2sh_data*)(hA2sh);
531 array2sh_arrayPars* arraySpecs = (array2sh_arrayPars*)(pData->arraySpecs);
532 int i, j, band, array_order, idxf_alias, nSH;
533 float f_max, kR_max, f_alias, f_f_alias;
534 double_complex* dM_diffcoh_s;
535 const double_complex calpha = cmplx(1.0, 0.0); const double_complex cbeta = cmplx(0.0, 0.0);
536 double kr[HYBRID_BANDS];
537 double* dM_diffcoh;
538 float sensorCoords_rad_local[MAX_NUM_SENSORS][2];
539
540 if(arraySpecs->arrayType==ARRAY_CYLINDRICAL)
541 return; /* unsupported */
542
543 /* prep */
544 nSH = (pData->order+1)*(pData->order+1);
545 dM_diffcoh = malloc1d((arraySpecs->Q)*(arraySpecs->Q)* (HYBRID_BANDS) * sizeof(double_complex));
546 dM_diffcoh_s = malloc1d((arraySpecs->Q)*(arraySpecs->Q) * sizeof(double_complex));
547 f_max = 20e3f;
548 kR_max = 2.0f*SAF_PI*f_max*(arraySpecs->r)/pData->c;
549 array_order = SAF_MIN((int)(ceilf(2.0f*kR_max)+0.01f), 28); /* Cap at around 28, as Bessels at 30+ can be numerically unstable */
550 for(band=0; band<HYBRID_BANDS; band++)
551 kr[band] = 2.0*SAF_PId*(pData->freqVector[band])*(arraySpecs->r)/pData->c;
552 for(i=0; i<arraySpecs->Q; i++){
553 sensorCoords_rad_local[i][0] = arraySpecs->sensorCoords_rad[i][0];
554 sensorCoords_rad_local[i][1] = arraySpecs->sensorCoords_rad[i][1];
555 }
556
557 /* Get theoretical diffuse coherence matrix */
558 ARRAY2SH_ARRAY_TYPES arrayType = arraySpecs->arrayType;
559 switch(arrayType){
561 return; /* Unsupported */
562 break;
563 case ARRAY_SPHERICAL:{
564 ARRAY2SH_WEIGHT_TYPES weightType = arraySpecs->weightType;
565 switch (weightType){
566 case WEIGHT_RIGID_OMNI: /* Does not handle the case where kr != kR ! */
567 sphDiffCohMtxTheory(array_order, (float*)sensorCoords_rad_local, arraySpecs->Q, ARRAY_CONSTRUCTION_RIGID, 1.0, kr, HYBRID_BANDS, dM_diffcoh);
568 break;
570 sphDiffCohMtxTheory(array_order, (float*)sensorCoords_rad_local, arraySpecs->Q, ARRAY_CONSTRUCTION_RIGID_DIRECTIONAL, 0.5, kr, HYBRID_BANDS, dM_diffcoh);
571 break;
573 sphDiffCohMtxTheory(array_order, (float*)sensorCoords_rad_local, arraySpecs->Q, ARRAY_CONSTRUCTION_RIGID_DIRECTIONAL, 0.0, kr, HYBRID_BANDS, dM_diffcoh);
574 break;
575 case WEIGHT_OPEN_OMNI:
576 sphDiffCohMtxTheory(array_order, (float*)sensorCoords_rad_local, arraySpecs->Q, ARRAY_CONSTRUCTION_OPEN, 1.0, kr, HYBRID_BANDS, dM_diffcoh);
577 break;
578 case WEIGHT_OPEN_CARD:
579 sphDiffCohMtxTheory(array_order, (float*)sensorCoords_rad_local, arraySpecs->Q, ARRAY_CONSTRUCTION_OPEN_DIRECTIONAL, 0.5, kr, HYBRID_BANDS, dM_diffcoh);
580 break;
582 sphDiffCohMtxTheory(array_order, (float*)sensorCoords_rad_local, arraySpecs->Q, ARRAY_CONSTRUCTION_OPEN_DIRECTIONAL, 0.0, kr, HYBRID_BANDS, dM_diffcoh);
583 break;
584 }
585 break;
586 }
587 }
588
589 /* determine band index for the spatial aliasing limit */
590 f_alias = sphArrayAliasLim(arraySpecs->r, pData->c, pData->order);
591 idxf_alias = 1;
592 f_f_alias = 1e13f;
593 for(band=0; band<HYBRID_BANDS; band++){
594 if( fabsf(pData->freqVector[band]-f_alias) < f_f_alias){
595 f_f_alias = fabsf(pData->freqVector[band]-f_alias);
596 idxf_alias = band;
597 }
598 }
599
600 /* baseline */
601 for(i=0; i<arraySpecs->Q; i++)
602 for(j=0; j<arraySpecs->Q; j++)
603 dM_diffcoh_s[i*(arraySpecs->Q)+j] = cmplx(dM_diffcoh[i*(arraySpecs->Q)* (HYBRID_BANDS) + j*(HYBRID_BANDS) + (idxf_alias)], 0.0);
604 for(i=0; i<nSH; i++)
605 for(j=0; j<arraySpecs->Q; j++)
606 pData->W_tmp[i*MAX_NUM_SENSORS+j]= cmplx((double)crealf(pData->W[idxf_alias][i][j]), (double)cimagf(pData->W[idxf_alias][i][j]));
607 cblas_zgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nSH, (arraySpecs->Q), (arraySpecs->Q), &calpha,
608 pData->W_tmp, MAX_NUM_SENSORS,
609 dM_diffcoh_s, (arraySpecs->Q), &cbeta,
610 pData->E_diff, MAX_NUM_SENSORS);
611 cblas_zgemm(CblasRowMajor, CblasNoTrans, CblasConjTrans, nSH, nSH, (arraySpecs->Q), &calpha,
612 pData->E_diff, MAX_NUM_SENSORS,
613 pData->W_tmp, MAX_NUM_SENSORS, &cbeta,
614 pData->L_diff_fal, MAX_NUM_SH_SIGNALS);
615 for(i=0; i<nSH; i++)
616 pData->L_diff_fal[i*MAX_NUM_SH_SIGNALS+i] = crmul(pData->L_diff_fal[i*MAX_NUM_SH_SIGNALS+i], 1.0/(4.0*SAF_PId)); /* only care about the diagonal entries */
617
618 /* diffuse-field equalise bands above aliasing. */
619 for(band = SAF_MAX(idxf_alias,0)+1; band<HYBRID_BANDS; band++){
620 for(i=0; i<arraySpecs->Q; i++)
621 for(j=0; j<arraySpecs->Q; j++)
622 dM_diffcoh_s[i*(arraySpecs->Q)+j] = cmplx(dM_diffcoh[i*(arraySpecs->Q)* (HYBRID_BANDS) + j*(HYBRID_BANDS) + (band)], 0.0);
623 for(i=0; i<nSH; i++)
624 for(j=0; j<arraySpecs->Q; j++)
625 pData->W_tmp[i*MAX_NUM_SENSORS+j]= cmplx((double)crealf(pData->W[band][i][j]), (double)cimagf(pData->W[band][i][j]));
626 cblas_zgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nSH, (arraySpecs->Q), (arraySpecs->Q), &calpha,
627 pData->W_tmp, MAX_NUM_SENSORS,
628 dM_diffcoh_s, (arraySpecs->Q), &cbeta,
629 pData->E_diff, MAX_NUM_SENSORS);
630 cblas_zgemm(CblasRowMajor, CblasNoTrans, CblasConjTrans, nSH, nSH, (arraySpecs->Q), &calpha,
631 pData->E_diff, MAX_NUM_SENSORS,
632 pData->W_tmp, MAX_NUM_SENSORS, &cbeta,
633 pData->L_diff, MAX_NUM_SH_SIGNALS);
634 for(i=0; i<nSH; i++)
635 for(j=0; j<nSH; j++)
636 pData->L_diff[i*MAX_NUM_SH_SIGNALS+j] = i==j? csqrt(cradd(ccdiv(pData->L_diff_fal[i*MAX_NUM_SH_SIGNALS+j], crmul(pData->L_diff[i*MAX_NUM_SH_SIGNALS+j], 1.0/(4.0*SAF_PId))), 2.23e-10)): cmplx(0.0,0.0);
637 cblas_zgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nSH, (arraySpecs->Q), nSH, &calpha,
638 pData->L_diff, MAX_NUM_SH_SIGNALS,
639 pData->W_tmp, MAX_NUM_SENSORS, &cbeta,
640 pData->W_diffEQ_tmp, MAX_NUM_SENSORS);
641 for(i=0; i<nSH; i++)
642 for(j=0; j<arraySpecs->Q; j++)
643 pData->W[band][i][j] = cmplxf((float)creal(pData->W_diffEQ_tmp[i*MAX_NUM_SENSORS+j]), (float)cimag(pData->W_diffEQ_tmp[i*MAX_NUM_SENSORS+j]));
644 }
645
647
648 free(dM_diffcoh);
649 free(dM_diffcoh_s);
650}
651
652void array2sh_calculate_mag_curves(void* const hA2sh)
653{
654 array2sh_data *pData = (array2sh_data*)(hA2sh);
655 int band, n;
656
657 for(band = 0; band <HYBRID_BANDS; band++){
658 for(n = 0; n <pData->order+1; n++){
659 pData->bN_inv_dB[band][n] = 20.0f * (float)log10(cabs(pData->bN_inv[band][n]));
660 pData->bN_modal_dB[band][n] = 20.0f * (float)log10(cabs(pData->bN_modal[band][n]));
661 }
662 }
663}
664
666{
667 array2sh_data *pData = (array2sh_data*)(hA2sh);
668 array2sh_arrayPars* arraySpecs = (array2sh_arrayPars*)(pData->arraySpecs);
669 int band, i, j, simOrder, order, nSH;
670 double kr[HYBRID_BANDS];
671 double kR[HYBRID_BANDS];
672 float* Y_grid_real;
673 float_complex* Y_grid, *H_array, *Wshort;
674
675 saf_assert(pData->W != NULL, "The initCodec function must have been called prior to calling array2sh_evaluateSHTfilters()");
676
677 strcpy(pData->progressBarText,"Simulating microphone array");
678 pData->progressBar0_1 = 0.35f;
679
680 /* simulate the current array by firing 812 plane-waves around the surface of a theoretical version of the array
681 * and ascertaining the transfer function for each */
682 simOrder = (int)(2.0f*SAF_PI*MAX_EVAL_FREQ_HZ*(arraySpecs->r)/pData->c)+1;
683 for(band=0; band<HYBRID_BANDS; band++){
684 kr[band] = 2.0*SAF_PId*(pData->freqVector[band])*(arraySpecs->r)/pData->c;
685 kR[band] = 2.0*SAF_PId*(pData->freqVector[band])*(arraySpecs->R)/pData->c;
686 }
687 H_array = malloc1d((HYBRID_BANDS) * (arraySpecs->Q) * 812*sizeof(float_complex));
688 ARRAY2SH_ARRAY_TYPES arrayType = arraySpecs->arrayType;
689 switch(arrayType){
690 case ARRAY_SPHERICAL:{
691 ARRAY2SH_WEIGHT_TYPES weightType = arraySpecs->weightType;
692 switch(weightType){
693 default:
695 simulateSphArray(simOrder, kr, kR, HYBRID_BANDS, (float*)arraySpecs->sensorCoords_rad, arraySpecs->Q,
696 (float*)__geosphere_ico_9_0_dirs_deg, 812, ARRAY_CONSTRUCTION_RIGID, 1.0, H_array);
697 break;
699 simulateSphArray(simOrder, kr, kR, HYBRID_BANDS, (float*)arraySpecs->sensorCoords_rad, arraySpecs->Q,
701 break;
703 simulateSphArray(simOrder, kr, kR, HYBRID_BANDS, (float*)arraySpecs->sensorCoords_rad, arraySpecs->Q,
705 break;
706 case WEIGHT_OPEN_OMNI:
707 simulateSphArray(simOrder, kr, NULL, HYBRID_BANDS, (float*)arraySpecs->sensorCoords_rad, arraySpecs->Q,
708 (float*)__geosphere_ico_9_0_dirs_deg, 812, ARRAY_CONSTRUCTION_OPEN, 1.0, H_array);
709 break;
710 case WEIGHT_OPEN_CARD:
711 simulateSphArray(simOrder, kr, NULL, HYBRID_BANDS, (float*)arraySpecs->sensorCoords_rad, arraySpecs->Q,
713 break;
715 simulateSphArray(simOrder, kr, NULL, HYBRID_BANDS, (float*)arraySpecs->sensorCoords_rad, arraySpecs->Q,
717 break;
718 }
719 break;
720 }
721 case ARRAY_CYLINDRICAL:{
722 ARRAY2SH_WEIGHT_TYPES weightType = arraySpecs->weightType;
723 switch(weightType){
724 default:
728 simulateCylArray(simOrder, kr, HYBRID_BANDS, (float*)arraySpecs->sensorCoords_rad, arraySpecs->Q, (float*)__geosphere_ico_9_0_dirs_deg, 812, ARRAY_CONSTRUCTION_RIGID, H_array);
729 break;
731 case WEIGHT_OPEN_CARD:
732 case WEIGHT_OPEN_OMNI:
733 simulateCylArray(simOrder, kr, HYBRID_BANDS, (float*)arraySpecs->sensorCoords_rad, arraySpecs->Q, (float*)__geosphere_ico_9_0_dirs_deg, 812, ARRAY_CONSTRUCTION_OPEN, H_array);
734 break;
735 }
736 break;
737 }
738 }
739
740 strcpy(pData->progressBarText,"Evaluating encoding performance");
741 pData->progressBar0_1 = 0.8f;
742
743 /* generate ideal (real) spherical harmonics to compare with */
744 order = pData->order;
745 nSH = (order+1)*(order+1);
746 Y_grid_real = malloc1d(nSH*812*sizeof(float));
747 getRSH(order, (float*)__geosphere_ico_9_0_dirs_deg, 812, Y_grid_real);
748 Y_grid = malloc1d(nSH*812*sizeof(float_complex));
749 for(i=0; i<nSH*812; i++)
750 Y_grid[i] = cmplxf(Y_grid_real[i], 0.0f); /* "evaluateSHTfilters" function requires complex data type */
751
752 /* compare the spherical harmonics obtained from encoding matrix 'W' with the ideal patterns */
753 Wshort = malloc1d(HYBRID_BANDS*nSH*(arraySpecs->Q)*sizeof(float_complex));
754 for(band=0; band<HYBRID_BANDS; band++)
755 for(i=0; i<nSH; i++)
756 for(j=0; j<(arraySpecs->Q); j++)
757 Wshort[band*nSH*(arraySpecs->Q) + i*(arraySpecs->Q) + j] = pData->W[band][i][j];
758 evaluateSHTfilters(order, Wshort, arraySpecs->Q, HYBRID_BANDS, H_array, 812, Y_grid, pData->cSH, pData->lSH);
759
760 free(Y_grid_real);
761 free(Y_grid);
762 free(H_array);
763 free(Wshort);
764}
765
766void array2sh_createArray(void ** const hPars)
767{
769 *hPars = (void*)pars;
770}
771
772void array2sh_destroyArray(void ** const hPars)
773{
774 array2sh_arrayPars *pars = (array2sh_arrayPars*)(*hPars);
775 if(pars!=NULL) {
776 free(pars);
777 pars=NULL;
778 }
779}
780
782(
783 void* const hPars,
785 _Atomic_INT32* arrayOrder,
786 int firstInitFlag
787)
788{
789 array2sh_arrayPars *pars = (array2sh_arrayPars*)(hPars);
790 int ch, i, Q;
791
792 switch(preset){
793 default:
794 case MICROPHONE_ARRAY_PRESET_DEFAULT:
795 (*arrayOrder) = 1;
796 Q = 4;
797 pars->r = 0.02f;
798 pars->R = 0.02f;
801 for(ch=0; ch<Q; ch++){
802 for(i=0; i<2; i++){
804 pars->sensorCoords_deg[ch][i] = pars->sensorCoords_rad[ch][i] * (180.0f/SAF_PI);
805 }
806 }
807 break;
808 case MICROPHONE_ARRAY_PRESET_AALTO_HYDROPHONE:
809 (*arrayOrder) = 1;
810 Q = 4;
811 pars->r = 0.173f;
812 pars->R = 0.173f;
815 for(ch=0; ch<Q; ch++){
816 for(i=0; i<2; i++){
818 pars->sensorCoords_deg[ch][i] = pars->sensorCoords_rad[ch][i] * (180.0f/SAF_PI);
819 }
820 }
821 break;
822 case MICROPHONE_ARRAY_PRESET_SENNHEISER_AMBEO:
823 (*arrayOrder) = 1;
824 Q = 4;
825 pars->r = 0.014f;
826 pars->R = 0.014f;
829 for(ch=0; ch<Q; ch++){
830 for(i=0; i<2; i++){
832 pars->sensorCoords_deg[ch][i] = pars->sensorCoords_rad[ch][i] * (180.0f/SAF_PI);
833 }
834 }
835 break;
836 case MICROPHONE_ARRAY_PRESET_CORE_SOUND_TETRAMIC:
837 (*arrayOrder) = 1;
838 Q = 4;
839 pars->r = 0.02f;
840 pars->R = 0.02f;
843 for(ch=0; ch<Q; ch++){
844 for(i=0; i<2; i++){
846 pars->sensorCoords_deg[ch][i] = pars->sensorCoords_rad[ch][i] * (180.0f/SAF_PI);
847 }
848 }
849 break;
850 case MICROPHONE_ARRAY_PRESET_ZOOM_H3VR_PRESET:
851 (*arrayOrder) = 1;
852 Q = 4;
853 pars->r = 0.012f;
854 pars->R = 0.012f;
857 for(ch=0; ch<Q; ch++){
858 for(i=0; i<2; i++){
859 pars->sensorCoords_rad[ch][i] = __Zoom_H3VR_coords_rad[ch][i];
860 pars->sensorCoords_deg[ch][i] = pars->sensorCoords_rad[ch][i] * (180.0f/SAF_PI);
861 }
862 }
863 break;
864 case MICROPHONE_ARRAY_PRESET_SOUND_FIELD_SPS200:
865 (*arrayOrder) = 1;
866 Q = 4;
867 pars->r = 0.02f;
868 pars->R = 0.02f;
871 for(ch=0; ch<Q; ch++){
872 for(i=0; i<2; i++){
874 pars->sensorCoords_deg[ch][i] = pars->sensorCoords_rad[ch][i] * (180.0f/SAF_PI);
875 }
876 }
877 break;
878 case MICROPHONE_ARRAY_PRESET_ZYLIA_1D:
879 (*arrayOrder) = 3;
880 Q = 19;
881 pars->r = 0.049f;
882 pars->R = 0.049f;
885 for(ch=0; ch<Q; ch++){
886 for(i=0; i<2; i++){
887 pars->sensorCoords_rad[ch][i] = __Zylia1D_coords_rad[ch][i];
888 pars->sensorCoords_deg[ch][i] = pars->sensorCoords_rad[ch][i] * (180.0f/SAF_PI);
889 }
890 }
891 break;
892 case MICROPHONE_ARRAY_PRESET_EIGENMIKE32:
893 (*arrayOrder) = 4;
894 Q = 32;
895 pars->r = 0.042f;
896 pars->R = 0.042f;
899 for(ch=0; ch<Q; ch++){
900 for(i=0; i<2; i++){
901 pars->sensorCoords_rad[ch][i] = __Eigenmike32_coords_rad[ch][i];
902 pars->sensorCoords_deg[ch][i] = pars->sensorCoords_rad[ch][i] * (180.0f/SAF_PI);
903 }
904 }
905 break;
906 case MICROPHONE_ARRAY_PRESET_EIGENMIKE64:
907 (*arrayOrder) = 6;
908 Q = 64;
909 pars->r = 0.042f;
910 pars->R = 0.042f;
913 for(ch=0; ch<Q; ch++){
914 for(i=0; i<2; i++){
915 pars->sensorCoords_rad[ch][i] = __Eigenmike64_coords_rad[ch][i];
916 pars->sensorCoords_deg[ch][i] = pars->sensorCoords_rad[ch][i] * (180.0f/SAF_PI);
917 }
918 }
919 break;
920 case MICROPHONE_ARRAY_PRESET_DTU_MIC:
921 (*arrayOrder) = 6;
922 Q = 52;
923 pars->r = 0.05f;
924 pars->R = 0.05f;
927 for(ch=0; ch<Q; ch++){
928 for(i=0; i<2; i++){
929 pars->sensorCoords_rad[ch][i] = __DTU_mic_coords_rad[ch][i];
930 pars->sensorCoords_deg[ch][i] = pars->sensorCoords_rad[ch][i] * (180.0f/SAF_PI);
931 }
932 }
933 break;
934 }
935
936 /* Fill remaining slots with default coords */
937 for(; ch<MAX_NUM_SENSORS_IN_PRESET; ch++){
938 for(i=0; i<2; i++){
940 pars->sensorCoords_rad[ch][i] = pars->sensorCoords_deg[ch][i] * (SAF_PI/180.0f);
941 }
942 }
943
944 /* For dynamically changing the number of TFT channels */
945 if(firstInitFlag==1){
946 pars->Q = Q;
947 pars->newQ = pars->Q;
948 }
949 else
950 pars->newQ = Q;
951}
952
#define MAX_SH_ORDER
Maximum supported Ambisonic order.
Definition _common.h:56
#define MAX_NUM_INPUTS
Maximum number of input channels supported.
Definition _common.h:261
#define MAX_NUM_SH_SIGNALS
Maximum number of spherical harmonic components/signals supported.
Definition _common.h:267
void afSTFT_clearBuffers(void *const hSTFT)
Flushes time-domain buffers with zeros.
Definition afSTFTlib.c:519
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_channelChange(void *const hSTFT, int new_nCHin, int new_nCHout)
Re-allocates memory to support a change in the number of input/output channels.
Definition afSTFTlib.c:477
@ AFSTFT_BANDS_CH_TIME
nBands x nChannels x nTimeHops
Definition afSTFTlib.h:80
#define HOP_SIZE
STFT hop size.
#define HYBRID_BANDS
Number of frequency bands.
const int array2sh_defaultNumSensors
Default number of microphones.
ARRAY2SH_MICROPHONE_ARRAY_PRESETS
Available microphone array presets.
Definition array2sh.h:105
@ FILTER_Z_STYLE
Encoding filters based on a linear-phase filter- bank approach [3].
Definition array2sh.h:140
@ FILTER_SOFT_LIM
Encoding filters based on a 'soft-limiting' regularised inversion of the modal responses [1].
Definition array2sh.h:135
@ FILTER_Z_STYLE_MAXRE
Same as FILTER_Z_STYLE, only it also has max_rE weights baked in.
Definition array2sh.h:142
@ FILTER_TIKHONOV
Encoding filters based on a 'Tikhonov' regularised inversion of the modal responses [2].
Definition array2sh.h:138
ARRAY2SH_WEIGHT_TYPES
List of supported sensor directivities and array construction types.
Definition array2sh.h:166
@ WEIGHT_RIGID_OMNI
Rigid baffle construction with omni sensors.
Definition array2sh.h:167
@ WEIGHT_OPEN_OMNI
Open array construction with omni sensors.
Definition array2sh.h:171
@ WEIGHT_OPEN_DIPOLE
Open array construction with dipole sensors.
Definition array2sh.h:173
@ WEIGHT_RIGID_CARD
Rigid baffle construction with cardioid sensors.
Definition array2sh.h:168
@ WEIGHT_RIGID_DIPOLE
Rigid baffle construction with dipole sensors.
Definition array2sh.h:170
@ WEIGHT_OPEN_CARD
Open array construction with cardioid sensors.
Definition array2sh.h:172
const float array2sh_defaultSensorsDirections[MAX_NUM_INPUTS][2]
Default microphone directions (degrees)
ARRAY2SH_ARRAY_TYPES
List of supported array types.
Definition array2sh.h:156
@ ARRAY_CYLINDRICAL
Cylindrial arrangement of sensors (open/rigid)
Definition array2sh.h:158
@ ARRAY_SPHERICAL
Spherical arrangement of sensors (open/rigid)
Definition array2sh.h:157
@ EVAL_STATUS_NOT_EVALUATED
Encoder has not been evaluated.
Definition array2sh.h:189
void array2sh_evaluateSHTfilters(void *hA2sh)
Evaluates the spherical harmonic transform performance with the currently configured microphone/hydro...
void array2sh_calculate_sht_matrix(void *const hA2sh)
Computes the spherical harmonic transform (SHT) matrix, to spatially encode input microphone/hydropho...
void array2sh_initTFT(void *const hA2sh)
Initialise the filterbank used by array2sh.
void array2sh_initArray(void *const hPars, ARRAY2SH_MICROPHONE_ARRAY_PRESETS preset, _Atomic_INT32 *arrayOrder, int firstInitFlag)
Intialises an instance of a struct based on a preset, which contains the array configuration data.
void array2sh_calculate_mag_curves(void *const hA2sh)
Computes the magnitude responses of the equalisation filters; the absolute values of the regularised ...
void array2sh_apply_diff_EQ(void *const hA2sh)
Applies diffuse-field equalisation at frequencies above the spatial aliasing limit.
void array2sh_destroyArray(void **const hPars)
Destroys an instance of a struct, which contains the array configuration data.
static void array2sh_replicate_order(void *const hA2sh, int order)
Takes the bNs computed up to N+1, and replicates them to be of length (N+1)^2 (replicating the 1st or...
void array2sh_createArray(void **const hPars)
Creates an instance of a struct, which contains the array configuration data.
Spatially encodes spherical microphone array signals into spherical harmonic signals (aka: Ambisonic ...
#define MAX_NUM_SENSORS
Maximum permitted number of inputs/sensors.
#define MAX_NUM_SENSORS_IN_PRESET
Maximum permitted number of inputs/sensors.
#define MAX_EVAL_FREQ_HZ
Up to which frequency should the evaluation be accurate.
void getRSH(int N, float *dirs_deg, int nDirs, float *Y)
Computes real-valued spherical harmonics [1] for each given direction on the unit sphere.
Definition saf_hoa.c:119
void getMaxREweights(int order, int diagMtxFlag, float *a_n)
Computes the weights required to manipulate a hyper-cardioid beam-pattern, such that it has maximum e...
Definition saf_hoa.c:236
void cylModalCoeffs(int order, double *kr, int nBands, ARRAY_CONSTRUCTION_TYPES arrayType, double_complex *b_N)
Calculates the modal coefficients for open/rigid cylindrical arrays.
Definition saf_sh.c:2267
void evaluateSHTfilters(int order, float_complex *M_array2SH, int nSensors, int nBands, float_complex *H_array, int nDirs, float_complex *Y_grid, float *cSH, float *lSH)
Generates some objective measures, which evaluate the performance of spatial encoding filters.
Definition saf_sh.c:2847
void sphModalCoeffs(int order, double *kr, int nBands, ARRAY_CONSTRUCTION_TYPES arrayType, double dirCoeff, double_complex *b_N)
Calculates the modal coefficients for open/rigid spherical arrays.
Definition saf_sh.c:2370
float sphArrayAliasLim(float r, float c, int maxN)
Returns a simple estimate of the spatial aliasing limit (the kR = maxN rule)
Definition saf_sh.c:2332
void sphArrayNoiseThreshold(int maxN, int Nsensors, float r, float c, ARRAY_CONSTRUCTION_TYPES arrayType, double dirCoeff, float maxG_db, float *f_lim)
Computes the frequencies (per order), at which the noise of a SHT of a SMA exceeds a specified maximu...
Definition saf_sh.c:2342
void simulateCylArray(int order, double *kr, int nBands, float *sensor_dirs_rad, int N_sensors, float *src_dirs_deg, int N_srcs, ARRAY_CONSTRUCTION_TYPES arrayType, float_complex *H_array)
Simulates a cylindrical microphone array, returning the transfer functions for each (plane wave) sour...
Definition saf_sh.c:2717
void simulateSphArray(int order, double *kr, double *kR, int nBands, float *sensor_dirs_rad, int N_sensors, float *src_dirs_deg, int N_srcs, ARRAY_CONSTRUCTION_TYPES arrayType, double dirCoeff, float_complex *H_array)
Simulates a spherical microphone array, returning the transfer functions for each (plane wave) source...
Definition saf_sh.c:2769
void sphDiffCohMtxTheory(int order, float *sensor_dirs_rad, int N_sensors, ARRAY_CONSTRUCTION_TYPES arrayType, double dirCoeff, double *kr, int nBands, double *M_diffcoh)
Calculates the theoretical diffuse coherence matrix for a spherical array.
Definition saf_sh.c:2570
void sphScattererModalCoeffs(int order, double *kr, double *kR, int nBands, double_complex *b_N)
Calculates the modal coefficients for a rigid spherical scatterer with omni-directional sensors.
Definition saf_sh.c:2454
void sphScattererDirModalCoeffs(int order, double *kr, double *kR, int nBands, double dirCoeff, double_complex *b_N)
Calculates the modal coefficients for a rigid spherical scatterer with directional sensors.
Definition saf_sh.c:2503
@ ARRAY_CONSTRUCTION_RIGID_DIRECTIONAL
Rigid baffle, directional sensors.
Definition saf_sh.h:70
@ ARRAY_CONSTRUCTION_RIGID
Rigid baffle, omni-directional sensors.
Definition saf_sh.h:68
@ ARRAY_CONSTRUCTION_OPEN_DIRECTIONAL
Open array, directional sensors.
Definition saf_sh.h:67
@ ARRAY_CONSTRUCTION_OPEN
Open array, omni-directional sensors.
Definition saf_sh.h:65
#define saf_print_error(message)
Macro to print a error message along with the filename and line number.
const float __Aalto_Hydrophone_coords_rad[4][2]
Sensor array coordinates for the custom hydrophone array made at Aalto University [1].
#define saf_assert(x, message)
Macro to make an assertion, along with a string explaining its purpose.
#define SAF_PI
pi constant (single precision)
const float __Eigenmike64_coords_rad[64][2]
Sensor array coordinates for the Eigenmike64.
const float __Sennheiser_Ambeo_coords_rad[4][2]
Sensor array coordinates for the Sennheiser Ambeo.
const float __Eigenmike32_coords_rad[32][2]
Sensor array coordinates for the Eigenmike32.
#define SAF_MAX(a, b)
Returns the maximum of the two values.
const float __default_SENSORcoords128_deg[128][2]
Default sensor array coordinates.
#define SAF_PId
pi constant (double precision)
const float __geosphere_ico_9_0_dirs_deg[812][2]
Directions [azimuth, Elevation] in degrees, for ico geosphere, degree: 9.
void utility_spinv(void *const hWork, const float *inM, const int dim1, const int dim2, float *outM)
General matrix pseudo-inverse (the svd way): single precision, i.e.
const float __Sound_field_SPS200_coords_rad[4][2]
Sensor array coordinates for the Sound-field SPS200.
#define SAF_MIN(a, b)
Returns the minimum of the two values.
const float __Zylia1D_coords_rad[19][2]
Sensor array coordinates for the Zylia mic.
const float __Zoom_H3VR_coords_rad[4][2]
Sensor array coordinates for the Zoom H3VR.
const float __Core_Sound_TetraMic_coords_rad[4][2]
Sensor array coordinates for the Core Sound TetraMic.
const float __DTU_mic_coords_rad[52][2]
Sensor array coordinates for the custom 52-sensor array built at the Technical University of Denmark ...
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
Contains variables for describing the microphone/hydrophone array.
_Atomic_FLOAT32 r
radius of sensors
_Atomic_ARRAY2SH_WEIGHT_TYPES weightType
see ARRAY2SH_WEIGHT_TYPES
_Atomic_ARRAY2SH_ARRAY_TYPES arrayType
see ARRAY2SH_ARRAY_TYPES
_Atomic_FLOAT32 R
radius of scatterer (only for rigid arrays)
_Atomic_INT32 Q
Current number of sensors.
_Atomic_INT32 newQ
New number of sensors (current value replaced by this after next re-init)
_Atomic_FLOAT32 sensorCoords_deg[MAX_NUM_SENSORS][2]
Sensor directions in degrees.
_Atomic_FLOAT32 sensorCoords_rad[MAX_NUM_SENSORS][2]
Sensor directions in radians.
Main structure for array2sh.
_Atomic_FLOAT32 progressBar0_1
Current (re)initialisation progress, between [0..1].
_Atomic_FLOAT32 regPar
regularisation upper gain limit, dB;
float freqVector[HYBRID_BANDS]
frequency vector
double_complex * bN
Temp vector for the modal coefficients.
char * progressBarText
Current (re)initialisation step, string.
double_complex bN_inv[HYBRID_BANDS][MAX_SH_ORDER+1]
1/bN_modal
_Atomic_INT32 reinitSHTmatrixFLAG
0: do not reinit; 1: reinit;
void * hSTFT
filterbank handle
float ** bN_modal_dB
modal responses / no regulaisation; HYBRID_BANDS x (MAX_SH_ORDER +1)
void * arraySpecs
array configuration
_Atomic_INT32 enableDiffEQpastAliasing
0: disabled, 1: enabled
double_complex bN_modal[HYBRID_BANDS][MAX_SH_ORDER+1]
Current modal coeffients.
double_complex bN_inv_R[HYBRID_BANDS][MAX_NUM_SH_SIGNALS]
1/bN_modal with regularisation
float * cSH
spatial correlation; HYBRID_BANDS x 1
_Atomic_INT32 new_order
new encoding order (current value will be replaced by this after next re-init)
_Atomic_FLOAT32 c
speed of sound, m/s
_Atomic_ARRAY2SH_FILTER_TYPES filterType
encoding filter approach
_Atomic_ARRAY2SH_EVAL_STATUS evalStatus
see ARRAY2SH_EVAL_STATUS
float ** bN_inv_dB
modal responses / with regularisation; HYBRID_BANDS x (MAX_SH_ORDER +1)
float * lSH
level difference; HYBRID_BANDS x 1
_Atomic_INT32 order
current encoding order
float_complex W[HYBRID_BANDS][MAX_NUM_SH_SIGNALS][MAX_NUM_SENSORS]
Encoding weights.