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
57(
58 void* const hA2sh,
59 int order
60)
61{
62 array2sh_data *pData = (array2sh_data*)(hA2sh);
63 int band, n, i;
64 int o[MAX_SH_ORDER+2];
65
66 for(n=0; n<order+2; n++)
67 o[n] = n*n;
68 for(band=0; band<HYBRID_BANDS; band++)
69 for(n=0; n < order+1; n++)
70 for(i=o[n]; i < o[n+1]; i++)
71 pData->bN_inv_R[band][i] = pData->bN_inv[band][n];
72}
73
75(
76 void* const hA2sh
77)
78{
79 array2sh_data *pData = (array2sh_data*)(hA2sh);
80 array2sh_arrayPars* arraySpecs = (array2sh_arrayPars*)(pData->arraySpecs);
81 int new_nSH, nSH;
82
83 new_nSH = (pData->new_order+1)*(pData->new_order+1);
84 nSH = (pData->order+1)*(pData->order+1);
85 if(pData->hSTFT==NULL)
86 afSTFT_create(&(pData->hSTFT), arraySpecs->newQ, new_nSH, HOP_SIZE, 0, 1, AFSTFT_BANDS_CH_TIME);
87 else if(arraySpecs->newQ != arraySpecs->Q || nSH != new_nSH){
88 afSTFT_channelChange(pData->hSTFT, arraySpecs->newQ, new_nSH);
90 pData->reinitSHTmatrixFLAG = 1; /* filters will need to be updated too */
91 }
92 arraySpecs->Q = arraySpecs->newQ;
93}
94
96(
97 void* const hA2sh
98)
99{
100 array2sh_data *pData = (array2sh_data*)(hA2sh);
101 array2sh_arrayPars* arraySpecs = (array2sh_arrayPars*)(pData->arraySpecs);
102 int i, j, band, n, order, nSH;
103 double alpha, beta, g_lim, regPar;
104 double kr[HYBRID_BANDS], kR[HYBRID_BANDS];
105 float sensorCoords_deg_local[MAX_NUM_SENSORS][2];
106 float* Y_mic, *pinv_Y_mic;
107 float_complex* pinv_Y_mic_cmplx, *diag_bN_inv_R;
108 const float_complex calpha = cmplxf(1.0f, 0.0f); const float_complex cbeta = cmplxf(0.0f, 0.0f);
109
110 /* prep */
111 order = pData->new_order;
112 nSH = (order+1)*(order+1);
113 arraySpecs->R = SAF_MIN(arraySpecs->R, arraySpecs->r);
114 for(band=0; band<HYBRID_BANDS; band++){
115 kr[band] = 2.0*SAF_PId*(pData->freqVector[band])*(arraySpecs->r)/pData->c;
116 kR[band] = 2.0*SAF_PId*(pData->freqVector[band])*(arraySpecs->R)/pData->c;
117 }
118 for(i=0; i<arraySpecs->Q; i++){
119 sensorCoords_deg_local[i][0] = arraySpecs->sensorCoords_deg[i][0];
120 sensorCoords_deg_local[i][1] = arraySpecs->sensorCoords_deg[i][1];
121 }
122
123 /* Spherical harmponic weights for each sensor direction */
124 Y_mic = malloc1d(nSH*(arraySpecs->Q)*sizeof(float));
125 getRSH(order, (float*)sensorCoords_deg_local, arraySpecs->Q, Y_mic); /* nSH x Q */
126 pinv_Y_mic = malloc1d( arraySpecs->Q * nSH *sizeof(float));
127 utility_spinv(NULL, Y_mic, nSH, arraySpecs->Q, pinv_Y_mic);
128 pinv_Y_mic_cmplx = malloc1d((arraySpecs->Q) * nSH *sizeof(float_complex));
129 for(i=0; i<(arraySpecs->Q)*nSH; i++)
130 pinv_Y_mic_cmplx[i] = cmplxf(pinv_Y_mic[i], 0.0f);
131
132 /* ------------------------------------------------------------------------------ */
133 /* Encoding filters based on the regularised inversion of the modal coefficients: */
134 /* ------------------------------------------------------------------------------ */
135 if ( (pData->filterType==FILTER_SOFT_LIM) || (pData->filterType==FILTER_TIKHONOV) ){
136 /* Compute modal responses */
137 free(pData->bN);
138 pData->bN = malloc1d((HYBRID_BANDS)*(order+1)*sizeof(double_complex));
139 switch(arraySpecs->arrayType){
141 switch (arraySpecs->weightType){
143 case WEIGHT_RIGID_CARD: saf_print_error("weightType is not supported"); break;
144 case WEIGHT_RIGID_DIPOLE: saf_print_error("weightType is not supported"); break;
146 case WEIGHT_OPEN_CARD: saf_print_error("weightType is not supported"); break;
147 case WEIGHT_OPEN_DIPOLE: saf_print_error("weightType is not supported"); break;
148 }
149 break;
150 case ARRAY_SPHERICAL:
151 switch (arraySpecs->weightType){
152 case WEIGHT_OPEN_OMNI: sphModalCoeffs(order, kr, HYBRID_BANDS, ARRAY_CONSTRUCTION_OPEN, 1.0, pData->bN); break;
158 /* if sensors are flushed with the rigid baffle: */
159 if(arraySpecs->R == arraySpecs->r )
160 sphModalCoeffs(order, kr, HYBRID_BANDS, ARRAY_CONSTRUCTION_RIGID, 1.0, pData->bN);
161
162 /* if sensors protrude from the rigid baffle: */
163 else{
164 if (arraySpecs->weightType == WEIGHT_RIGID_OMNI)
165 sphScattererModalCoeffs(order, kr, kR, HYBRID_BANDS, pData->bN);
166 else if (arraySpecs->weightType == WEIGHT_RIGID_CARD)
167 sphScattererDirModalCoeffs(order, kr, kR, HYBRID_BANDS, 0.5, pData->bN);
168 else if (arraySpecs->weightType == WEIGHT_RIGID_DIPOLE)
169 sphScattererDirModalCoeffs(order, kr, kR, HYBRID_BANDS, 0.0, pData->bN);
170 }
171 break;
172 }
173 break;
174 }
175
176 for(band=0; band<HYBRID_BANDS; band++)
177 for(n=0; n < order+1; n++)
178 pData->bN[band*(order+1)+n] = ccdiv(pData->bN[band*(order+1)+n], cmplx(4.0*SAF_PId, 0.0)); /* 4pi term */
179
180 /* direct inverse */
181 regPar = pData->regPar;
182 for(band=0; band<HYBRID_BANDS; band++)
183 for(n=0; n < order+1; n++)
184 pData->bN_modal[band][n] = ccdiv(cmplx(1.0,0.0), (pData->bN[band*(order+1)+n]));
185
186 /* regularised inverse */
187 if (pData->filterType == FILTER_SOFT_LIM){
188 /* Bernschutz, B., Porschmann, C., Spors, S., Weinzierl, S., Versterkung, B., 2011. Soft-limiting der
189 modalen amplitudenverst?rkung bei sph?rischen mikrofonarrays im plane wave decomposition verfahren.
190 Proceedings of the 37. Deutsche Jahrestagung fur Akustik (DAGA 2011) */
191 g_lim = sqrt(arraySpecs->Q)*pow(10.0,(regPar/20.0));
192 for(band=0; band<HYBRID_BANDS; band++)
193 for(n=0; n < order+1; n++)
194 pData->bN_inv[band][n] = crmul(pData->bN_modal[band][n], (2.0*g_lim*cabs(pData->bN[band*(order+1)+n]) / SAF_PId)
195 * atan(SAF_PId / (2.0*g_lim*cabs(pData->bN[band*(order+1)+n]))) );
196 }
197 else if(pData->filterType == FILTER_TIKHONOV){
198 /* Moreau, S., Daniel, J., Bertet, S., 2006, 3D sound field recording with higher order ambisonics-objective
199 measurements and validation of spherical microphone. In Audio Engineering Society Convention 120. */
200 alpha = sqrt(arraySpecs->Q)*pow(10.0,(regPar/20.0));
201 for(band=0; band<HYBRID_BANDS; band++){
202 for(n=0; n < order+1; n++){
203 beta = sqrt((1.0-sqrt(1.0-1.0/ pow(alpha,2.0)))/(1.0+sqrt(1.0-1.0/pow(alpha,2.0))));
204 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));
205 }
206 }
207 }
208
209 /* diag(filters) * Y */
210 array2sh_replicate_order(hA2sh, order); /* replicate orders */
211
212 diag_bN_inv_R = calloc1d(nSH*nSH, sizeof(float_complex));
213 for(band=0; band<HYBRID_BANDS; band++){
214 for(i=0; i<nSH; i++)
215 diag_bN_inv_R[i*nSH+i] = cmplxf((float)creal(pData->bN_inv_R[band][i]), (float)cimag(pData->bN_inv_R[band][i]));
216 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasTrans, nSH, (arraySpecs->Q), nSH, &calpha,
217 diag_bN_inv_R, nSH,
218 pinv_Y_mic_cmplx, nSH, &cbeta,
219 pData->W[band], MAX_NUM_SENSORS);
220 }
221 free(diag_bN_inv_R);
222 }
223
224 /* ------------------------------------------------------------- */
225 /* Encoding filters based on a linear-phase filter-bank approach */
226 /* ------------------------------------------------------------- */
227 else if ( (pData->filterType==FILTER_Z_STYLE) || (pData->filterType==FILTER_Z_STYLE_MAXRE) ) {
228 /* Zotter, F. A Linear-Phase Filter-Bank Approach to Process Rigid Spherical Microphone Array Recordings. */
229 double normH;
230 float f_lim[MAX_SH_ORDER+1];
231 double H[HYBRID_BANDS][MAX_SH_ORDER+1];
232 double_complex Hs[HYBRID_BANDS][MAX_SH_ORDER+1];
233
234 /* find suitable cut-off frequencies */
235 switch (arraySpecs->weightType){
236 case WEIGHT_OPEN_OMNI: sphArrayNoiseThreshold(order, arraySpecs->Q, arraySpecs->r, pData->c, ARRAY_CONSTRUCTION_OPEN, 1.0, pData->regPar, f_lim); break;
237 case WEIGHT_OPEN_CARD: sphArrayNoiseThreshold(order, arraySpecs->Q, arraySpecs->r, pData->c, ARRAY_CONSTRUCTION_OPEN_DIRECTIONAL, 0.5, pData->regPar, f_lim); break;
238 case WEIGHT_OPEN_DIPOLE: sphArrayNoiseThreshold(order, arraySpecs->Q, arraySpecs->r, pData->c, ARRAY_CONSTRUCTION_OPEN_DIRECTIONAL, 0.0, pData->regPar, f_lim); break;
242 /* Currently no support for estimating the noise cut-off frequencies for rigid scatterers. */
243 sphArrayNoiseThreshold(order, arraySpecs->Q, arraySpecs->r, pData->c, ARRAY_CONSTRUCTION_RIGID, 1.0, pData->regPar, f_lim); break;
244 }
245
246 /* design prototype filterbank */
247 for(band=0; band<HYBRID_BANDS; band++){
248 normH = 0.0;
249 for (n=0; n<order+1; n++){
250 if (n==0)
251 H[band][n] = 1.0/(1.0+ pow((double)(pData->freqVector[band]/f_lim[n]),2.0));
252 else if (n==order)
253 H[band][n] = pow((double)(pData->freqVector[band]/f_lim[n-1]), (double)order+1.0 ) /
254 (1.0 + pow((double)(pData->freqVector[band]/f_lim[n-1]), (double)order+1.0));
255 else
256 H[band][n] = pow((double)(pData->freqVector[band]/f_lim[n-1]), (double)n+1.0 ) /
257 (1.0 + pow((double)(pData->freqVector[band]/f_lim[n-1]), (double)n+1.0)) *
258 (1.0 / (1.0 + pow((double)(pData->freqVector[band]/f_lim[n]), (double)n+2.0)));
259 normH += H[band][n];
260 }
261 /* normalise */
262 for (n=0; n<order+1; n++)
263 H[band][n] = H[band][n]/normH;
264 }
265
266 /* compute inverse radial response */
267 free(pData->bN);
268 pData->bN = malloc1d((HYBRID_BANDS)*(order+1)*sizeof(double_complex));
269 switch(arraySpecs->arrayType){
271 switch (arraySpecs->weightType){
273 case WEIGHT_RIGID_CARD: /* not supported */ break;
274 case WEIGHT_RIGID_DIPOLE: /* not supported */ break;
276 case WEIGHT_OPEN_CARD: /* not supported */ break;
277 case WEIGHT_OPEN_DIPOLE: /* not supported */ break;
278 }
279 break;
280 case ARRAY_SPHERICAL:
281 switch (arraySpecs->weightType){
282 case WEIGHT_OPEN_OMNI: sphModalCoeffs(order, kr, HYBRID_BANDS, ARRAY_CONSTRUCTION_OPEN, 1.0, pData->bN); break;
288 /* if sensors are flushed with the rigid baffle: */
289 if(arraySpecs->R == arraySpecs->r )
290 sphModalCoeffs(order, kr, HYBRID_BANDS, ARRAY_CONSTRUCTION_RIGID, 1.0, pData->bN);
291
292 /* if sensors protrude from the rigid baffle: */
293 else{
294 if (arraySpecs->weightType == WEIGHT_RIGID_OMNI)
295 sphScattererModalCoeffs(order, kr, kR, HYBRID_BANDS, pData->bN);
296 else if (arraySpecs->weightType == WEIGHT_RIGID_CARD)
297 sphScattererDirModalCoeffs(order, kr, kR, HYBRID_BANDS, 0.5, pData->bN);
298 else if (arraySpecs->weightType == WEIGHT_RIGID_DIPOLE)
299 sphScattererDirModalCoeffs(order, kr, kR, HYBRID_BANDS, 0.0, pData->bN);
300 }
301 break;
302 }
303 break;
304 }
305
306 /* direct inverse (only required for GUI) */
307 for(band=0; band<HYBRID_BANDS; band++)
308 for(n=0; n < order+1; n++)
309 pData->bN_modal[band][n] = ccdiv(cmplx(4.0*SAF_PId, 0.0), pData->bN[band*(order+1)+n]);
310
311 /* phase shift */
312 for(band=0; band<HYBRID_BANDS; band++)
313 for (n=0; n<order+1; n++)
314 Hs[band][n] = ccmul(cexp(cmplx(0.0, kr[band])), ccdiv(cmplx(4.0*SAF_PId, 0.0), pData->bN[band*(order+1)+n]));
315
316 /* apply max-re order weighting and diffuse equalisation (not the same as "array2sh_apply_diff_EQ") */
317 float* wn;
318 double W[MAX_SH_ORDER+1][MAX_SH_ORDER+1];
319 double EN, scale;
320 int nSH_n;
321 memset(W, 0, (MAX_SH_ORDER+1)*(MAX_SH_ORDER+1)*sizeof(double));
322 for (n=0; n<order+1; n++){
323 nSH_n = (n+1)*(n+1);
324 wn = calloc1d(nSH_n*nSH_n, sizeof(float));
325 if(pData->filterType==FILTER_Z_STYLE)
326 for (i=0; i<n+1; i++)
327 wn[(i*i)*nSH_n+(i*i)] = 1.0f;
328 else if(pData->filterType==FILTER_Z_STYLE_MAXRE)
329 getMaxREweights(n, 1, wn);
330 scale = 0.0;
331 for (i=0; i<n+1; i++)
332 scale += (double)(2*i+1)*pow((double)wn[(i*i)*nSH_n + (i*i)], 2.0);
333 for (i=0; i<n+1; i++)
334 W[i][n] = (double)wn[(i*i)*nSH_n + (i*i)]/ sqrt(scale);
335 free(wn);
336 }
337 EN=W[0][n-1];
338 for (n=0; n<order+1; n++)
339 for (i=0; i<order+1; i++)
340 W[i][n] /= EN;
341
342 /* apply bandpass filterbank to the inverse array response to regularise it */
343 double HW[HYBRID_BANDS];
344 double H_np[HYBRID_BANDS][MAX_SH_ORDER+1];
345 double W_np[MAX_SH_ORDER+1];
346 for (n=0; n<order+1; n++){
347 for(band=0; band< HYBRID_BANDS; band++)
348 for (i=n, j=0; i<order+1; i++, j++)
349 H_np[band][j] = H[band][i];
350 for (i=n, j=0; i<order+1; i++, j++)
351 W_np[j] = W[n][i];
352 cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasTrans, HYBRID_BANDS, 1, order+1-n, 1.0,
353 (const double*)H_np, MAX_SH_ORDER+1,
354 (const double*)W_np, MAX_SH_ORDER+1, 0.0,
355 (double*)HW, 1);
356 for(band=0; band<HYBRID_BANDS; band++)
357 pData->bN_inv[band][n] = crmul(Hs[band][n], HW[band]);
358 }
359
360 /* diag(filters) * Y */
361 array2sh_replicate_order(hA2sh, order); /* replicate orders */
362 diag_bN_inv_R = calloc1d(nSH*nSH, sizeof(float_complex));
363 for(band=0; band<HYBRID_BANDS; band++){
364 for(i=0; i<nSH; i++)
365 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 */
366 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasTrans, nSH, (arraySpecs->Q), nSH, &calpha,
367 diag_bN_inv_R, nSH,
368 pinv_Y_mic_cmplx, nSH, &cbeta,
369 pData->W[band], MAX_NUM_SENSORS);
370 }
371 free(diag_bN_inv_R);
372 }
373
374 pData->order = order;
375
376 if(pData->enableDiffEQpastAliasing)
378
379 free(Y_mic);
380 free(pinv_Y_mic);
381 free(pinv_Y_mic_cmplx);
382}
383
384/* Based on a MatLab script by Archontis Politis, 2019 */
385void array2sh_apply_diff_EQ(void* const hA2sh)
386{
387 array2sh_data *pData = (array2sh_data*)(hA2sh);
388 array2sh_arrayPars* arraySpecs = (array2sh_arrayPars*)(pData->arraySpecs);
389 int i, j, band, array_order, idxf_alias, nSH;
390 float f_max, kR_max, f_alias, f_f_alias;
391 double_complex* dM_diffcoh_s;
392 const double_complex calpha = cmplx(1.0, 0.0); const double_complex cbeta = cmplx(0.0, 0.0);
393 double kr[HYBRID_BANDS];
394 double* dM_diffcoh;
395 float sensorCoords_rad_local[MAX_NUM_SENSORS][2];
396
397 if(arraySpecs->arrayType==ARRAY_CYLINDRICAL)
398 return; /* unsupported */
399
400 /* prep */
401 nSH = (pData->order+1)*(pData->order+1);
402 dM_diffcoh = malloc1d((arraySpecs->Q)*(arraySpecs->Q)* (HYBRID_BANDS) * sizeof(double_complex));
403 dM_diffcoh_s = malloc1d((arraySpecs->Q)*(arraySpecs->Q) * sizeof(double_complex));
404 f_max = 20e3f;
405 kR_max = 2.0f*SAF_PI*f_max*(arraySpecs->r)/pData->c;
406 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 */
407 for(band=0; band<HYBRID_BANDS; band++)
408 kr[band] = 2.0*SAF_PId*(pData->freqVector[band])*(arraySpecs->r)/pData->c;
409 for(i=0; i<arraySpecs->Q; i++){
410 sensorCoords_rad_local[i][0] = arraySpecs->sensorCoords_rad[i][0];
411 sensorCoords_rad_local[i][1] = arraySpecs->sensorCoords_rad[i][1];
412 }
413
414 /* Get theoretical diffuse coherence matrix */
415 switch(arraySpecs->arrayType){
417 return; /* Unsupported */
418 break;
419 case ARRAY_SPHERICAL:
420 switch (arraySpecs->weightType){
421 case WEIGHT_RIGID_OMNI: /* Does not handle the case where kr != kR ! */
422 sphDiffCohMtxTheory(array_order, (float*)sensorCoords_rad_local, arraySpecs->Q, ARRAY_CONSTRUCTION_RIGID, 1.0, kr, HYBRID_BANDS, dM_diffcoh);
423 break;
425 sphDiffCohMtxTheory(array_order, (float*)sensorCoords_rad_local, arraySpecs->Q, ARRAY_CONSTRUCTION_RIGID_DIRECTIONAL, 0.5, kr, HYBRID_BANDS, dM_diffcoh);
426 break;
428 sphDiffCohMtxTheory(array_order, (float*)sensorCoords_rad_local, arraySpecs->Q, ARRAY_CONSTRUCTION_RIGID_DIRECTIONAL, 0.0, kr, HYBRID_BANDS, dM_diffcoh);
429 break;
430 case WEIGHT_OPEN_OMNI:
431 sphDiffCohMtxTheory(array_order, (float*)sensorCoords_rad_local, arraySpecs->Q, ARRAY_CONSTRUCTION_OPEN, 1.0, kr, HYBRID_BANDS, dM_diffcoh);
432 break;
433 case WEIGHT_OPEN_CARD:
434 sphDiffCohMtxTheory(array_order, (float*)sensorCoords_rad_local, arraySpecs->Q, ARRAY_CONSTRUCTION_OPEN_DIRECTIONAL, 0.5, kr, HYBRID_BANDS, dM_diffcoh);
435 break;
437 sphDiffCohMtxTheory(array_order, (float*)sensorCoords_rad_local, arraySpecs->Q, ARRAY_CONSTRUCTION_OPEN_DIRECTIONAL, 0.0, kr, HYBRID_BANDS, dM_diffcoh);
438 break;
439 }
440 break;
441 }
442
443 /* determine band index for the spatial aliasing limit */
444 f_alias = sphArrayAliasLim(arraySpecs->r, pData->c, pData->order);
445 idxf_alias = 1;
446 f_f_alias = 1e13f;
447 for(band=0; band<HYBRID_BANDS; band++){
448 if( fabsf(pData->freqVector[band]-f_alias) < f_f_alias){
449 f_f_alias = fabsf(pData->freqVector[band]-f_alias);
450 idxf_alias = band;
451 }
452 }
453
454 /* baseline */
455 for(i=0; i<arraySpecs->Q; i++)
456 for(j=0; j<arraySpecs->Q; j++)
457 dM_diffcoh_s[i*(arraySpecs->Q)+j] = cmplx(dM_diffcoh[i*(arraySpecs->Q)* (HYBRID_BANDS) + j*(HYBRID_BANDS) + (idxf_alias)], 0.0);
458 for(i=0; i<nSH; i++)
459 for(j=0; j<arraySpecs->Q; j++)
460 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]));
461 cblas_zgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nSH, (arraySpecs->Q), (arraySpecs->Q), &calpha,
462 pData->W_tmp, MAX_NUM_SENSORS,
463 dM_diffcoh_s, (arraySpecs->Q), &cbeta,
464 pData->E_diff, MAX_NUM_SENSORS);
465 cblas_zgemm(CblasRowMajor, CblasNoTrans, CblasConjTrans, nSH, nSH, (arraySpecs->Q), &calpha,
466 pData->E_diff, MAX_NUM_SENSORS,
467 pData->W_tmp, MAX_NUM_SENSORS, &cbeta,
468 pData->L_diff_fal, MAX_NUM_SH_SIGNALS);
469 for(i=0; i<nSH; i++)
470 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 */
471
472 /* diffuse-field equalise bands above aliasing. */
473 for(band = SAF_MAX(idxf_alias,0)+1; band<HYBRID_BANDS; band++){
474 for(i=0; i<arraySpecs->Q; i++)
475 for(j=0; j<arraySpecs->Q; j++)
476 dM_diffcoh_s[i*(arraySpecs->Q)+j] = cmplx(dM_diffcoh[i*(arraySpecs->Q)* (HYBRID_BANDS) + j*(HYBRID_BANDS) + (band)], 0.0);
477 for(i=0; i<nSH; i++)
478 for(j=0; j<arraySpecs->Q; j++)
479 pData->W_tmp[i*MAX_NUM_SENSORS+j]= cmplx((double)crealf(pData->W[band][i][j]), (double)cimagf(pData->W[band][i][j]));
480 cblas_zgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nSH, (arraySpecs->Q), (arraySpecs->Q), &calpha,
481 pData->W_tmp, MAX_NUM_SENSORS,
482 dM_diffcoh_s, (arraySpecs->Q), &cbeta,
483 pData->E_diff, MAX_NUM_SENSORS);
484 cblas_zgemm(CblasRowMajor, CblasNoTrans, CblasConjTrans, nSH, nSH, (arraySpecs->Q), &calpha,
485 pData->E_diff, MAX_NUM_SENSORS,
486 pData->W_tmp, MAX_NUM_SENSORS, &cbeta,
487 pData->L_diff, MAX_NUM_SH_SIGNALS);
488 for(i=0; i<nSH; i++)
489 for(j=0; j<nSH; j++)
490 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);
491 cblas_zgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nSH, (arraySpecs->Q), nSH, &calpha,
492 pData->L_diff, MAX_NUM_SH_SIGNALS,
493 pData->W_tmp, MAX_NUM_SENSORS, &cbeta,
494 pData->W_diffEQ_tmp, MAX_NUM_SENSORS);
495 for(i=0; i<nSH; i++)
496 for(j=0; j<arraySpecs->Q; j++)
497 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]));
498 }
499
501
502 free(dM_diffcoh);
503 free(dM_diffcoh_s);
504}
505
506void array2sh_calculate_mag_curves(void* const hA2sh)
507{
508 array2sh_data *pData = (array2sh_data*)(hA2sh);
509 int band, n;
510
511 for(band = 0; band <HYBRID_BANDS; band++){
512 for(n = 0; n <pData->order+1; n++){
513 pData->bN_inv_dB[band][n] = 20.0f * (float)log10(cabs(pData->bN_inv[band][n]));
514 pData->bN_modal_dB[band][n] = 20.0f * (float)log10(cabs(pData->bN_modal[band][n]));
515 }
516 }
517}
518
520{
521 array2sh_data *pData = (array2sh_data*)(hA2sh);
522 array2sh_arrayPars* arraySpecs = (array2sh_arrayPars*)(pData->arraySpecs);
523 int band, i, j, simOrder, order, nSH;
524 double kr[HYBRID_BANDS];
525 double kR[HYBRID_BANDS];
526 float* Y_grid_real;
527 float_complex* Y_grid, *H_array, *Wshort;
528
529 saf_assert(pData->W != NULL, "The initCodec function must have been called prior to calling array2sh_evaluateSHTfilters()");
530
531 strcpy(pData->progressBarText,"Simulating microphone array");
532 pData->progressBar0_1 = 0.35f;
533
534 /* simulate the current array by firing 812 plane-waves around the surface of a theoretical version of the array
535 * and ascertaining the transfer function for each */
536 simOrder = (int)(2.0f*SAF_PI*MAX_EVAL_FREQ_HZ*(arraySpecs->r)/pData->c)+1;
537 for(band=0; band<HYBRID_BANDS; band++){
538 kr[band] = 2.0*SAF_PId*(pData->freqVector[band])*(arraySpecs->r)/pData->c;
539 kR[band] = 2.0*SAF_PId*(pData->freqVector[band])*(arraySpecs->R)/pData->c;
540 }
541 H_array = malloc1d((HYBRID_BANDS) * (arraySpecs->Q) * 812*sizeof(float_complex));
542 switch(arraySpecs->arrayType){
543 case ARRAY_SPHERICAL:
544 switch(arraySpecs->weightType){
545 default:
547 simulateSphArray(simOrder, kr, kR, HYBRID_BANDS, (float*)arraySpecs->sensorCoords_rad, arraySpecs->Q,
548 (float*)__geosphere_ico_9_0_dirs_deg, 812, ARRAY_CONSTRUCTION_RIGID, 1.0, H_array);
549 break;
551 simulateSphArray(simOrder, kr, kR, HYBRID_BANDS, (float*)arraySpecs->sensorCoords_rad, arraySpecs->Q,
553 break;
555 simulateSphArray(simOrder, kr, kR, HYBRID_BANDS, (float*)arraySpecs->sensorCoords_rad, arraySpecs->Q,
557 break;
558 case WEIGHT_OPEN_OMNI:
559 simulateSphArray(simOrder, kr, NULL, HYBRID_BANDS, (float*)arraySpecs->sensorCoords_rad, arraySpecs->Q,
560 (float*)__geosphere_ico_9_0_dirs_deg, 812, ARRAY_CONSTRUCTION_OPEN, 1.0, H_array);
561 break;
562 case WEIGHT_OPEN_CARD:
563 simulateSphArray(simOrder, kr, NULL, HYBRID_BANDS, (float*)arraySpecs->sensorCoords_rad, arraySpecs->Q,
565 break;
567 simulateSphArray(simOrder, kr, NULL, HYBRID_BANDS, (float*)arraySpecs->sensorCoords_rad, arraySpecs->Q,
569 break;
570 }
571 break;
572
574 switch(arraySpecs->weightType){
575 default:
579 simulateCylArray(simOrder, kr, HYBRID_BANDS, (float*)arraySpecs->sensorCoords_rad, arraySpecs->Q, (float*)__geosphere_ico_9_0_dirs_deg, 812, ARRAY_CONSTRUCTION_RIGID, H_array);
580 break;
582 case WEIGHT_OPEN_CARD:
583 case WEIGHT_OPEN_OMNI:
584 simulateCylArray(simOrder, kr, HYBRID_BANDS, (float*)arraySpecs->sensorCoords_rad, arraySpecs->Q, (float*)__geosphere_ico_9_0_dirs_deg, 812, ARRAY_CONSTRUCTION_OPEN, H_array);
585 break;
586 }
587 break;
588 }
589
590 strcpy(pData->progressBarText,"Evaluating encoding performance");
591 pData->progressBar0_1 = 0.8f;
592
593 /* generate ideal (real) spherical harmonics to compare with */
594 order = pData->order;
595 nSH = (order+1)*(order+1);
596 Y_grid_real = malloc1d(nSH*812*sizeof(float));
597 getRSH(order, (float*)__geosphere_ico_9_0_dirs_deg, 812, Y_grid_real);
598 Y_grid = malloc1d(nSH*812*sizeof(float_complex));
599 for(i=0; i<nSH*812; i++)
600 Y_grid[i] = cmplxf(Y_grid_real[i], 0.0f); /* "evaluateSHTfilters" function requires complex data type */
601
602 /* compare the spherical harmonics obtained from encoding matrix 'W' with the ideal patterns */
603 Wshort = malloc1d(HYBRID_BANDS*nSH*(arraySpecs->Q)*sizeof(float_complex));
604 for(band=0; band<HYBRID_BANDS; band++)
605 for(i=0; i<nSH; i++)
606 for(j=0; j<(arraySpecs->Q); j++)
607 Wshort[band*nSH*(arraySpecs->Q) + i*(arraySpecs->Q) + j] = pData->W[band][i][j];
608 evaluateSHTfilters(order, Wshort, arraySpecs->Q, HYBRID_BANDS, H_array, 812, Y_grid, pData->cSH, pData->lSH);
609
610 free(Y_grid_real);
611 free(Y_grid);
612 free(H_array);
613 free(Wshort);
614}
615
616void array2sh_createArray(void ** const hPars)
617{
619 *hPars = (void*)pars;
620}
621
622void array2sh_destroyArray(void ** const hPars)
623{
624 array2sh_arrayPars *pars = (array2sh_arrayPars*)(*hPars);
625 if(pars!=NULL) {
626 free(pars);
627 pars=NULL;
628 }
629}
630
632(
633 void* const hPars,
635 _Atomic_INT32* arrayOrder,
636 int firstInitFlag
637)
638{
639 array2sh_arrayPars *pars = (array2sh_arrayPars*)(hPars);
640 int ch, i, Q;
641
642 switch(preset){
643 default:
644 case MICROPHONE_ARRAY_PRESET_DEFAULT:
645 (*arrayOrder) = 1;
646 Q = 4;
647 pars->r = 0.02f;
648 pars->R = 0.02f;
651 for(ch=0; ch<Q; ch++){
652 for(i=0; i<2; i++){
654 pars->sensorCoords_deg[ch][i] = pars->sensorCoords_rad[ch][i] * (180.0f/SAF_PI);
655 }
656 }
657 break;
658 case MICROPHONE_ARRAY_PRESET_AALTO_HYDROPHONE:
659 (*arrayOrder) = 1;
660 Q = 4;
661 pars->r = 0.173f;
662 pars->R = 0.173f;
665 for(ch=0; ch<Q; ch++){
666 for(i=0; i<2; i++){
668 pars->sensorCoords_deg[ch][i] = pars->sensorCoords_rad[ch][i] * (180.0f/SAF_PI);
669 }
670 }
671 break;
672 case MICROPHONE_ARRAY_PRESET_SENNHEISER_AMBEO:
673 (*arrayOrder) = 1;
674 Q = 4;
675 pars->r = 0.014f;
676 pars->R = 0.014f;
679 for(ch=0; ch<Q; ch++){
680 for(i=0; i<2; i++){
682 pars->sensorCoords_deg[ch][i] = pars->sensorCoords_rad[ch][i] * (180.0f/SAF_PI);
683 }
684 }
685 break;
686 case MICROPHONE_ARRAY_PRESET_CORE_SOUND_TETRAMIC:
687 (*arrayOrder) = 1;
688 Q = 4;
689 pars->r = 0.02f;
690 pars->R = 0.02f;
693 for(ch=0; ch<Q; ch++){
694 for(i=0; i<2; i++){
696 pars->sensorCoords_deg[ch][i] = pars->sensorCoords_rad[ch][i] * (180.0f/SAF_PI);
697 }
698 }
699 break;
700 case MICROPHONE_ARRAY_PRESET_ZOOM_H3VR_PRESET:
701 (*arrayOrder) = 1;
702 Q = 4;
703 pars->r = 0.012f;
704 pars->R = 0.012f;
707 for(ch=0; ch<Q; ch++){
708 for(i=0; i<2; i++){
709 pars->sensorCoords_rad[ch][i] = __Zoom_H3VR_coords_rad[ch][i];
710 pars->sensorCoords_deg[ch][i] = pars->sensorCoords_rad[ch][i] * (180.0f/SAF_PI);
711 }
712 }
713 break;
714 case MICROPHONE_ARRAY_PRESET_SOUND_FIELD_SPS200:
715 (*arrayOrder) = 1;
716 Q = 4;
717 pars->r = 0.02f;
718 pars->R = 0.02f;
721 for(ch=0; ch<Q; ch++){
722 for(i=0; i<2; i++){
724 pars->sensorCoords_deg[ch][i] = pars->sensorCoords_rad[ch][i] * (180.0f/SAF_PI);
725 }
726 }
727 break;
728 case MICROPHONE_ARRAY_PRESET_ZYLIA_1D:
729 (*arrayOrder) = 3;
730 Q = 19;
731 pars->r = 0.049f;
732 pars->R = 0.049f;
735 for(ch=0; ch<Q; ch++){
736 for(i=0; i<2; i++){
737 pars->sensorCoords_rad[ch][i] = __Zylia1D_coords_rad[ch][i];
738 pars->sensorCoords_deg[ch][i] = pars->sensorCoords_rad[ch][i] * (180.0f/SAF_PI);
739 }
740 }
741 break;
742 case MICROPHONE_ARRAY_PRESET_EIGENMIKE32:
743 (*arrayOrder) = 4;
744 Q = 32;
745 pars->r = 0.042f;
746 pars->R = 0.042f;
749 for(ch=0; ch<Q; ch++){
750 for(i=0; i<2; i++){
751 pars->sensorCoords_rad[ch][i] = __Eigenmike32_coords_rad[ch][i];
752 pars->sensorCoords_deg[ch][i] = pars->sensorCoords_rad[ch][i] * (180.0f/SAF_PI);
753 }
754 }
755 break;
756 case MICROPHONE_ARRAY_PRESET_EIGENMIKE64:
757 (*arrayOrder) = 6;
758 Q = 64;
759 pars->r = 0.042f;
760 pars->R = 0.042f;
763 for(ch=0; ch<Q; ch++){
764 for(i=0; i<2; i++){
765 pars->sensorCoords_rad[ch][i] = __Eigenmike64_coords_rad[ch][i];
766 pars->sensorCoords_deg[ch][i] = pars->sensorCoords_rad[ch][i] * (180.0f/SAF_PI);
767 }
768 }
769 break;
770 case MICROPHONE_ARRAY_PRESET_DTU_MIC:
771 (*arrayOrder) = 6;
772 Q = 52;
773 pars->r = 0.05f;
774 pars->R = 0.05f;
777 for(ch=0; ch<Q; ch++){
778 for(i=0; i<2; i++){
779 pars->sensorCoords_rad[ch][i] = __DTU_mic_coords_rad[ch][i];
780 pars->sensorCoords_deg[ch][i] = pars->sensorCoords_rad[ch][i] * (180.0f/SAF_PI);
781 }
782 }
783 break;
784 }
785
786 /* Fill remaining slots with default coords */
787 for(; ch<MAX_NUM_SENSORS_IN_PRESET; ch++){
788 for(i=0; i<2; i++){
790 pars->sensorCoords_rad[ch][i] = pars->sensorCoords_deg[ch][i] * (SAF_PI/180.0f);
791 }
792 }
793
794 /* For dynamically changing the number of TFT channels */
795 if(firstInitFlag==1){
796 pars->Q = Q;
797 pars->newQ = pars->Q;
798 }
799 else
800 pars->newQ = Q;
801}
802
#define MAX_SH_ORDER
Maximum supported Ambisonic order.
Definition _common.h:56
#define MAX_NUM_SH_SIGNALS
Maximum number of spherical harmonic components/signals supported.
Definition _common.h:243
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.
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
@ 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
@ 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.