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