SAF
Loading...
Searching...
No Matches
saf_hoa_internal.c
Go to the documentation of this file.
1/*
2 * Copyright 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
34#include "../saf_hoa/saf_hoa.h"
36
37/* ========================================================================== */
38/* Loudspeaker Ambisonic Decoders */
39/* ========================================================================== */
40
42(
43 int order,
44 float* ls_dirs_deg,
45 int nLS,
46 float* decMtx
47)
48{
49 int i, j, nSH;
50 float scale;
51 float* Y_ls, *U, *V, *U_tr, *V_tr;
52
53 nSH = ORDER2NSH(order);
54 scale = 1.0f/SQRT4PI;
55
56 /* Prep */
57 Y_ls = malloc1d(nSH*nLS*sizeof(float));
58 U = malloc1d(nSH*nSH*sizeof(float));
59 V = malloc1d(nLS*nLS*sizeof(float));
60 getRSH(order, ls_dirs_deg, nLS, Y_ls);
61 cblas_sscal(nLS*nSH, scale, Y_ls, 1);
62 utility_ssvd(NULL, Y_ls, nSH, nLS, U, NULL, V, NULL);
63
64 /* Apply truncation */
65 if(nSH>nLS){
66 /* truncate the U matrix */
67 U_tr = malloc1d(nSH*nLS*sizeof(float));
68 for(i=0; i<nSH; i++)
69 for(j=0; j<nLS; j++)
70 U_tr[i*nLS+j] = U[i*nSH+j];
71 cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasTrans, nLS, nSH, nLS, 1.0f,
72 V, nLS,
73 U_tr, nLS, 0.0f,
74 decMtx, nSH);
75 free(U_tr);
76 }
77 else{
78 /* truncate the V matrix (NOT V^T!) */
79 V_tr = malloc1d(nLS*nSH*sizeof(float));
80 for(i=0; i<nLS; i++)
81 for(j=0; j<nSH; j++)
82 V_tr[i*nSH+j] = V[i*nLS+j];
83 cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasTrans, nLS, nSH, nSH, 1.0f,
84 V_tr, nSH,
85 U, nSH, 0.0f,
86 decMtx, nSH);
87 free(V_tr);
88 }
89
90 /* Apply normalisation, and scale by number of loudspeakers */
91 scale = sqrtf(4.0f*SAF_PI/(float)nLS);
92 utility_svsmul(decMtx, &scale, nLS*nSH, decMtx);
93
94 /* clean-up */
95 free(U);
96 free(V);
97 free(Y_ls);
98}
99
101(
102 int order,
103 float* ls_dirs_deg,
104 int nLS,
105 float* decMtx
106)
107{
108 int nDirs_td, N_gtable, nGroups, nSH;
109 float scale;
110 float* Y_td, *G_td, *t_dirs;
111
112 nSH = ORDER2NSH(order);
113 scale = 1.0f/SQRT4PI;
114
115#if 0
116 /* define a sufficiently dense t-design for this decoding order, as to
117 * conserve omni energy */
118 t = 4*order;
119 G_td = NULL;
120 if(t<=21){
121 /* suitable for up to 5th order */
122 nDirs_td = __Tdesign_nPoints_per_degree[t-1];
123 t_dirs = (float*)__HANDLES_Tdesign_dirs_deg[t-1];
124 }
125 else if (order > 7){
126 /* suitable for >7th order */
127 nDirs_td = 5100; /* Minimum t-design of degree 100 has 5100 points */
128 t_dirs = (float*)__Tdesign_degree_100_dirs_deg;
129 }
130 else{
131 /* suitable for 6th & 7th order */
132 nDirs_td = 480; /* Minimum t-design of degree 30 has 480 points (sufficient for up to 7th order) */
133 t_dirs = (float*)__Tdesign_degree_30_dirs_deg;
134 }
135#else
136 nDirs_td = 5100; /* Minimum t-design of degree 100 has 5100 points */
137 t_dirs = (float*)__Tdesign_degree_100_dirs_deg;
138#endif
139
140 /* calculate vbap gains and SH matrix for this t-design */
141 generateVBAPgainTable3D_srcs(t_dirs, nDirs_td, ls_dirs_deg, nLS, 0, 0, 0.0f, &G_td, &N_gtable, &nGroups);
142 Y_td = malloc1d(nSH*nDirs_td*sizeof(float));
143 getRSH(order, t_dirs, nDirs_td, Y_td);
144 cblas_sscal(nDirs_td*nSH, scale, Y_td, 1);
145
146 /* AllRAD decoder is simply (G_td * T_td * 1/nDirs_td) */
147 cblas_sgemm(CblasRowMajor, CblasTrans, CblasTrans, nLS, nSH, nDirs_td, 1.0f,
148 G_td, nLS,
149 Y_td, nDirs_td, 0.0f,
150 decMtx, nSH);
151 cblas_sscal(nLS*nSH, (4.0f*SAF_PI)/(float)nDirs_td, decMtx, 1);
152
153 free(Y_td);
154 free(G_td);
155}
156
157
158/* ========================================================================== */
159/* Binaural Ambisonic Decoders */
160/* ========================================================================== */
161
163(
164 float_complex* hrtfs, /* the HRTFs; FLAT: N_bands x 2 x N_dirs */
165 float* hrtf_dirs_deg,
166 int N_dirs,
167 int N_bands,
168 int order,
169 float* weights,
170 float_complex* decMtx /* N_bands x 2 x (order+1)^2 */
171)
172{
173 int i, j, nSH, band;
174 float* Y_tmp;
175 float_complex* W, *Y_na, *Yna_W, *Yna_W_Yna, *Yna_W_H, *B;
176 const float_complex calpha = cmplxf(1.0f, 0.0f), cbeta = cmplxf(0.0f, 0.0f);
177
178 nSH = ORDER2NSH(order);
179
180 /* SH */
181 Y_tmp = malloc1d(nSH*N_dirs*sizeof(float));
182 Y_na = malloc1d(nSH*N_dirs*sizeof(float_complex));
183 B = malloc1d(nSH * 2 * sizeof(float_complex));
184 getRSH(order, hrtf_dirs_deg, N_dirs, Y_tmp);
185 for(i=0; i<nSH*N_dirs; i++)
186 Y_na[i] = cmplxf(Y_tmp[i], 0.0f);
187 free(Y_tmp);
188
189 /* compute decoding matrix, incorporating integration weights */
190 W = calloc1d(N_dirs*N_dirs, sizeof(float_complex));
191 if(weights!=NULL)
192 for(i=0; i<N_dirs; i++)
193 W[i*N_dirs+i] = cmplxf(weights[i], 0.0f);
194 else
195 for(i=0; i<N_dirs; i++)
196 W[i*N_dirs+i] = cmplxf(1.0f/(float)N_dirs, 0.0f);
197
198 /* calculate decoding matrix per band */
199 Yna_W = malloc1d(nSH * N_dirs*sizeof(float_complex));
200 Yna_W_Yna = malloc1d(nSH * nSH * sizeof(float_complex));
201 Yna_W_H = malloc1d(nSH * 2 * sizeof(float_complex));
202 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nSH, N_dirs, N_dirs, &calpha,
203 Y_na, N_dirs,
204 W, N_dirs, &cbeta,
205 Yna_W, N_dirs);
206 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasTrans, nSH, nSH, N_dirs, &calpha,
207 Yna_W, N_dirs,
208 Y_na, N_dirs, &cbeta,
209 Yna_W_Yna, nSH);
210 for(band=0; band<N_bands; band++){
211 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasConjTrans, nSH, 2, N_dirs, &calpha,
212 Yna_W, N_dirs,
213 &hrtfs[band*2*N_dirs], N_dirs, &cbeta,
214 Yna_W_H, 2);
215 utility_cglslv(NULL, Yna_W_Yna, nSH, Yna_W_H, 2, B);
216 for(i=0; i<nSH; i++)
217 for(j=0; j<2; j++)
218 decMtx[band*2*nSH + j*nSH + i] = conjf(B[i*2+j]); /* ^H */
219 }
220
221 /* clean-up */
222 free(W);
223 free(Yna_W);
224 free(Yna_W_Yna);
225 free(Yna_W_H);
226 free(Y_na);
227 free(B);
228}
229
231(
232 float_complex* hrtfs, /* the HRTFs; FLAT: N_bands x 2 x N_dirs */
233 float* hrtf_dirs_deg,
234 int N_dirs,
235 int N_bands,
236 int order,
237 float* weights,
238 float_complex* decMtx /* N_bands x 2 x (order+1)^2 */
239)
240{
241 int i, j, nSH, band;
242 float Gh;
243 float* Y_tmp;
244 float_complex* W, *Y_na, *Yna_W, *Yna_W_Yna, *Yna_W_H, *B_ls, *hrtfs_ls, *H_W;
245 float_complex C_ref[2][2], C_ls[2][2];
246 const float_complex calpha = cmplxf(1.0f, 0.0f), cbeta = cmplxf(0.0f, 0.0f);
247
248 nSH = ORDER2NSH(order);
249
250 /* integration weights */
251 W = calloc1d(N_dirs*N_dirs, sizeof(float_complex));
252 if(weights!=NULL)
253 for(i=0; i<N_dirs; i++)
254 W[i*N_dirs+i] = cmplxf(weights[i], 0.0f);
255 else
256 for(i=0; i<N_dirs; i++)
257 W[i*N_dirs+i] = cmplxf(1.0f/(float)N_dirs, 0.0f);
258
259 /* SH */
260 Y_tmp = malloc1d(nSH*N_dirs*sizeof(float));
261 Y_na = malloc1d(nSH*N_dirs*sizeof(float_complex));
262 getRSH(order, hrtf_dirs_deg, N_dirs, Y_tmp);
263 for(i=0; i<nSH*N_dirs; i++)
264 Y_na[i] = cmplxf(Y_tmp[i], 0.0f);
265 free(Y_tmp);
266
267 /* calculate decoding matrix per band */
268 Yna_W = malloc1d(nSH * N_dirs*sizeof(float_complex));
269 Yna_W_Yna = malloc1d(nSH * nSH * sizeof(float_complex));
270 Yna_W_H = malloc1d(nSH * 2 * sizeof(float_complex));
271 B_ls = malloc1d(nSH * 2 * sizeof(float_complex));
272 hrtfs_ls = malloc1d(2*N_dirs*sizeof(float_complex));
273 H_W = malloc1d(2*N_dirs*sizeof(float_complex));
274 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nSH, N_dirs, N_dirs, &calpha,
275 Y_na, N_dirs,
276 W, N_dirs, &cbeta,
277 Yna_W, N_dirs);
278 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasTrans, nSH, nSH, N_dirs, &calpha,
279 Yna_W, N_dirs,
280 Y_na, N_dirs, &cbeta,
281 Yna_W_Yna, nSH);
282 for(band=0; band<N_bands; band++){
283 /* find least-squares decoding matrix */
284 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasConjTrans, nSH, 2, N_dirs, &calpha,
285 Yna_W, N_dirs,
286 &hrtfs[band*2*N_dirs], N_dirs, &cbeta,
287 Yna_W_H, 2);
288 utility_cglslv(NULL, Yna_W_Yna, nSH, Yna_W_H, 2, B_ls);
289 cblas_cgemm(CblasRowMajor, CblasConjTrans, CblasNoTrans, 2, N_dirs, nSH, &calpha,
290 B_ls, 2,
291 Y_na, N_dirs, &cbeta,
292 hrtfs_ls, N_dirs);
293
294 /* Diffuse-field responses */
295 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, 2, N_dirs, N_dirs, &calpha,
296 &hrtfs[band*2*N_dirs], N_dirs,
297 W, N_dirs, &cbeta,
298 H_W, N_dirs);
299 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasConjTrans, 2, 2, N_dirs, &calpha,
300 H_W, N_dirs,
301 &hrtfs[band*2*N_dirs], N_dirs, &cbeta,
302 (float_complex*)C_ref, 2);
303 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, 2, N_dirs, N_dirs, &calpha,
304 hrtfs_ls, N_dirs,
305 W, N_dirs, &cbeta,
306 H_W, N_dirs);
307 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasConjTrans, 2, 2, N_dirs, &calpha,
308 H_W, N_dirs,
309 hrtfs_ls, N_dirs, &cbeta,
310 (float_complex*)C_ls, 2);
311
312 /* Diffuse-Equalisation factor */
313 Gh = (sqrtf(crealf(C_ref[0][0])/(crealf(C_ls[0][0])+2.23e-7f)) +
314 sqrtf(crealf(C_ref[1][1])/(crealf(C_ls[1][1])+2.23e-7f))) /2.0f;
315
316 /* apply diff-EQ */
317 for(i=0; i<nSH; i++)
318 for(j=0; j<2; j++)
319 decMtx[band*2*nSH + j*nSH + i] = crmulf(conjf(B_ls[i*2+j]), Gh); /* ^H */
320 }
321
322 free(W);
323 free(Y_na);
324 free(Yna_W);
325 free(Yna_W_Yna);
326 free(Yna_W_H);
327 free(B_ls);
328 free(hrtfs_ls);
329 free(H_W);
330}
331
333(
334 float_complex* hrtfs, /* the HRTFs; FLAT: N_bands x 2 x N_dirs */
335 float* hrtf_dirs_deg,
336 int N_dirs,
337 int N_bands,
338 int order,
339 float* weights,
340 float_complex* decMtx /* N_bands x 2 x (order+1)^2 */
341)
342{
343 int band, i, j, nSH, nSH_nh, Nh_max, Nh, K_td;
344 float* hrtf_dirs_rad, *W, *cnd_num, *Y_nh, *Y_na, *tdirs_deg, *Y_td, *Ynh_Ytd, *tmp;
345 float_complex* Y_td_cmplx, *W_Ynh_Ytd, *hrtfs_td, *B;
346 const float_complex calpha = cmplxf(1.0f, 0.0f), cbeta = cmplxf(0.0f, 0.0f);
347
348 nSH = ORDER2NSH(order);
349
350 /* integration weights */
351 W = calloc1d(N_dirs*N_dirs, sizeof(float));
352 if(weights!=NULL)
353 for(i=0; i<N_dirs; i++)
354 W[i*N_dirs+i] = weights[i]/(4.0f*SAF_PI);
355 else
356 for(i=0; i<N_dirs; i++)
357 W[i*N_dirs+i] = 1.0f/(float)N_dirs;
358
359 /* find SH-order for interpolation of the HRTF set */
360 Nh_max = SAF_MIN((int)(sqrtf((float)N_dirs)-1.0f), 20); /* Cap to something more sensible if needed... */
361 hrtf_dirs_rad = malloc1d(N_dirs*2*sizeof(float));
362 cnd_num = malloc1d((Nh_max+1)*sizeof(float));
363 for(i=0; i<N_dirs; i++){ /* [azi, elev] degrees, to: [azi, inclination] radians */
364 hrtf_dirs_rad[i*2] = hrtf_dirs_deg[i*2]*(SAF_PI/180.0f);
365 hrtf_dirs_rad[i*2+1] = SAF_PI/2.0f - hrtf_dirs_deg[i*2+1]*(SAF_PI/180.0f);
366 }
367 checkCondNumberSHTReal(Nh_max, hrtf_dirs_rad, N_dirs, weights, cnd_num);
368 for(i=0, Nh=0; i<Nh_max+1; i++)
369 Nh = cnd_num[i] < 100.0f ? i : Nh;
370 saf_assert(Nh>=order, "Input order exceeds the modal order of the spatial grid");
371 nSH_nh = (Nh+1)*(Nh+1);
372 Y_nh = malloc1d(nSH_nh*N_dirs*sizeof(float));
373 getRSH(Nh, hrtf_dirs_deg, N_dirs, Y_nh);
374 Y_na = malloc1d(nSH * N_dirs *sizeof(float));
375 for(i=0; i<nSH; i++)
376 for(j=0; j<N_dirs; j++)
377 Y_na[i*N_dirs+j] = Y_nh[i*N_dirs+j];
378
379 /* Get t-design SH for ambisonic signals */
380 tdirs_deg = (float*)__HANDLES_Tdesign_dirs_deg[2*order-1];
381 K_td = __Tdesign_nPoints_per_degree[2*order-1];
382 Y_td = malloc1d(nSH_nh*K_td*sizeof(float));
383 getRSH(Nh, tdirs_deg, K_td, Y_td);
384 Y_td_cmplx = malloc1d(nSH_nh*K_td*sizeof(float_complex));
385 for(i=0; i<nSH_nh*K_td; i++)
386 Y_td_cmplx[i] = cmplxf(Y_td[i], 0.0f);
387
388 /* calculate decoding matrix per band */
389 Ynh_Ytd = malloc1d(N_dirs * K_td * sizeof(float));
390 tmp = malloc1d(N_dirs * K_td * sizeof(float));
391 W_Ynh_Ytd = malloc1d(N_dirs * K_td * sizeof(float_complex));
392 hrtfs_td = malloc1d(2*K_td*sizeof(float_complex));
393 B = malloc1d(nSH * 2 * sizeof(float_complex));
394 for(band=0; band<N_bands; band++){
395 cblas_sgemm(CblasRowMajor, CblasTrans, CblasNoTrans, N_dirs, K_td, nSH_nh, 1.0f,
396 Y_nh, N_dirs,
397 Y_td, K_td, 0.0f,
398 Ynh_Ytd, K_td);
399 cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, N_dirs, K_td, N_dirs, 1.0f,
400 W, N_dirs,
401 Ynh_Ytd, K_td, 0.0f,
402 tmp, K_td);
403 for(i=0; i<N_dirs*K_td; i++)
404 W_Ynh_Ytd[i] = cmplxf(tmp[i], 0.0f);
405 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, 2, K_td, N_dirs, &calpha,
406 &hrtfs[band*2*N_dirs], N_dirs,
407 W_Ynh_Ytd, K_td, &cbeta,
408 hrtfs_td, K_td);
409 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasConjTrans, nSH, 2, K_td, &calpha,
410 Y_td_cmplx, K_td,
411 hrtfs_td, K_td, &cbeta,
412 B, 2);
413 for(i=0; i<nSH; i++)
414 for(j=0; j<2; j++)
415 decMtx[band*2*nSH + j*nSH + i] = crmulf(conjf(B[i*2+j]), 1.0f/(float)K_td); /* ^H */
416 }
417
418 free(hrtf_dirs_rad);
419 free(W);
420 free(cnd_num);
421 free(Y_nh);
422 free(Y_na);
423 free(Y_td);
424 free(Y_td_cmplx);
425 free(Ynh_Ytd);
426 free(tmp);
427 free(W_Ynh_Ytd);
428 free(hrtfs_td);
429 free(B);
430}
431
433(
434 float_complex* hrtfs, /* the HRTFs; FLAT: N_bands x 2 x N_dirs */
435 float* hrtf_dirs_deg,
436 int N_dirs,
437 int N_bands,
438 int order,
439 float* freqVector,
440 float* itd_s,
441 float* weights,
442 float_complex* decMtx /* N_bands x 2 x (order+1)^2 */
443)
444{
445 int i, j, nSH, band, band_cutoff;
446 float cutoff, minVal;
447 float* Y_tmp;
448 float_complex* W, *Y_na, *hrtfs_mod, *Yna_W, *Yna_W_Yna, *Yna_W_H, *B;
449 const float_complex calpha = cmplxf(1.0f, 0.0f), cbeta = cmplxf(0.0f, 0.0f);
450
451 nSH = ORDER2NSH(order);
452
453 /* integration weights */
454 W = calloc1d(N_dirs*N_dirs, sizeof(float_complex));
455 if(weights!=NULL)
456 for(i=0; i<N_dirs; i++)
457 W[i*N_dirs+i] = cmplxf(weights[i], 0.0f);
458 else
459 for(i=0; i<N_dirs; i++)
460 W[i*N_dirs+i] = cmplxf(1.0f/(float)N_dirs, 0.0f);
461
462 /* SH */
463 Y_tmp = malloc1d(nSH*N_dirs*sizeof(float));
464 Y_na = malloc1d(nSH*N_dirs*sizeof(float_complex));
465 getRSH(order, hrtf_dirs_deg, N_dirs, Y_tmp);
466 for(i=0; i<nSH*N_dirs; i++)
467 Y_na[i] = cmplxf(Y_tmp[i], 0.0f);
468 free(Y_tmp);
469
470 /* find band index for cutoff frequency */
471 cutoff = 1.5e3f;
472 minVal = 2.23e10f;
473 for(band=0, band_cutoff=0; band<N_bands; band++){
474 if(minVal>fabsf(freqVector[band]-cutoff)){
475 minVal = fabsf(freqVector[band]-cutoff);
476 band_cutoff = band;
477 }
478 }
479 saf_assert(band_cutoff>0, "Frequency vector is wrong!");
480
481 /* calculate decoding matrix per band */
482 Yna_W = malloc1d(nSH * N_dirs*sizeof(float_complex));
483 Yna_W_Yna = malloc1d(nSH * nSH * sizeof(float_complex));
484 Yna_W_H = malloc1d(nSH * 2 * sizeof(float_complex));
485 B = malloc1d(nSH * 2 * sizeof(float_complex));
486 hrtfs_mod = malloc1d(2*N_dirs*sizeof(float_complex));
487 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nSH, N_dirs, N_dirs, &calpha,
488 Y_na, N_dirs,
489 W, N_dirs, &cbeta,
490 Yna_W, N_dirs);
491 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasTrans, nSH, nSH, N_dirs, &calpha,
492 Yna_W, N_dirs,
493 Y_na, N_dirs, &cbeta,
494 Yna_W_Yna, nSH);
495 for(band=0; band < N_bands; band++){
496 /* Remove itd from high frequency HRTFs */
497 if(band>=band_cutoff){
498 for(j=0; j<N_dirs; j++){
499 hrtfs_mod[0*N_dirs+j] = ccmulf(hrtfs[band_cutoff*2*N_dirs + 0*N_dirs + j],
500 cexpf( crmulf(cmplxf(0.0f, 0.0f), (itd_s[j]/2.0f))));
501 hrtfs_mod[1*N_dirs+j] = ccmulf(hrtfs[band_cutoff*2*N_dirs + 1*N_dirs + j],
502 cexpf( crmulf(cmplxf(0.0f, 0.0f), (-itd_s[j]/2.0f))));
503 }
504 }
505 else
506 memcpy(hrtfs_mod, &hrtfs[band*2*N_dirs], 2*N_dirs*sizeof(float_complex));
507 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasConjTrans, nSH, 2, N_dirs, &calpha,
508 Yna_W, N_dirs,
509 hrtfs_mod, N_dirs, &cbeta,
510 Yna_W_H, 2);
511 utility_cglslv(NULL, Yna_W_Yna, nSH, Yna_W_H, 2, B);
512 for(i=0; i<nSH; i++)
513 for(j=0; j<2; j++)
514 decMtx[band*2*nSH + j*nSH + i] = conjf(B[i*2+j]); /* ^H */
515 }
516
517 free(Y_na);
518 free(W);
519 free(Yna_W);
520 free(Yna_W_Yna);
521 free(Yna_W_H);
522 free(B);
523 free(hrtfs_mod);
524}
525
527(
528 float_complex* hrtfs, /* the HRTFs; FLAT: N_bands x 2 x N_dirs */
529 float* hrtf_dirs_deg,
530 int N_dirs,
531 int N_bands,
532 int order,
533 float* freqVector,
534 float* weights,
535 float_complex* decMtx /* N_bands x 2 x (order+1)^2 */
536)
537{
538 int i, j, nSH, band, band_cutoff;
539 float cutoff, minVal;
540 float* Y_tmp;
541 float_complex* W, *Y_na, *hrtfs_ls, *Yna_W, *Yna_W_Yna, *Yna_W_H, *H_mod, *B_magls;
542 const float_complex calpha = cmplxf(1.0f, 0.0f), cbeta = cmplxf(0.0f, 0.0f);
543
544 nSH = ORDER2NSH(order);
545
546 /* integration weights */
547 W = calloc1d(N_dirs*N_dirs, sizeof(float_complex));
548 if(weights!=NULL)
549 for(i=0; i<N_dirs; i++)
550 W[i*N_dirs+i] = cmplxf(weights[i], 0.0f);
551 else
552 for(i=0; i<N_dirs; i++)
553 W[i*N_dirs+i] = cmplxf(1.0f/(float)N_dirs, 0.0f);
554
555 /* SH */
556 Y_tmp = malloc1d(nSH*N_dirs*sizeof(float));
557 Y_na = malloc1d(nSH*N_dirs*sizeof(float_complex));
558 getRSH(order, hrtf_dirs_deg, N_dirs, Y_tmp);
559 for(i=0; i<nSH*N_dirs; i++)
560 Y_na[i] = cmplxf(Y_tmp[i], 0.0f);
561 free(Y_tmp);
562
563 /* find band index for cutoff frequency */
564 cutoff = 1.5e3f;
565 minVal = 2.23e10f;
566 for(band=0, band_cutoff=0; band<N_bands; band++){
567 if(minVal>fabsf(freqVector[band]-cutoff)){
568 minVal = fabsf(freqVector[band]-cutoff);
569 band_cutoff = band;
570 }
571 }
572
573 /* calculate decoding matrix per band */
574 Yna_W = malloc1d(nSH * N_dirs*sizeof(float_complex));
575 Yna_W_Yna = malloc1d(nSH * nSH * sizeof(float_complex));
576 Yna_W_H = malloc1d(nSH * 2 * sizeof(float_complex));
577 B_magls = malloc1d(nSH * 2 * sizeof(float_complex));
578 hrtfs_ls = malloc1d(2*N_dirs*sizeof(float_complex));
579 H_mod = malloc1d(2*N_dirs*sizeof(float_complex));
580 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nSH, N_dirs, N_dirs, &calpha,
581 Y_na, N_dirs,
582 W, N_dirs, &cbeta,
583 Yna_W, N_dirs);
584 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasTrans, nSH, nSH, N_dirs, &calpha,
585 Yna_W, N_dirs,
586 Y_na, N_dirs, &cbeta,
587 Yna_W_Yna, nSH);
588 for (band=0; band<N_bands; band++){
589 if(band<=band_cutoff){
590 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasConjTrans, nSH, 2, N_dirs, &calpha,
591 Yna_W, N_dirs,
592 &hrtfs[band*2*N_dirs], N_dirs, &cbeta,
593 Yna_W_H, 2);
594 utility_cglslv(NULL, Yna_W_Yna, nSH, Yna_W_H, 2, B_magls);
595 }
596 else{
597 /* Remove itd from high frequency HRTFs */
598 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, 2, N_dirs, nSH, &calpha,
599 &decMtx[(band-1)*2*nSH] , nSH,
600 Y_na, N_dirs, &cbeta,
601 H_mod, N_dirs);
602 for(i=0; i<2*N_dirs; i++)
603 H_mod[i] = ccmulf(cmplxf(cabsf(hrtfs[band*2*N_dirs + i]), 0.0f), cexpf(cmplxf(0.0f, atan2f(cimagf(H_mod[i]), crealf(H_mod[i])))));
604 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasConjTrans, nSH, 2, N_dirs, &calpha,
605 Yna_W, N_dirs,
606 H_mod, N_dirs, &cbeta,
607 Yna_W_H, 2);
608 utility_cglslv(NULL, Yna_W_Yna, nSH, Yna_W_H, 2, B_magls);
609 }
610
611 for(i=0; i<nSH; i++)
612 for(j=0; j<2; j++)
613 decMtx[band*2*nSH + j*nSH + i] = conjf(B_magls[i*2+j]); /* ^H */
614 }
615
616 free(W);
617 free(Y_na);
618 free(Yna_W);
619 free(Yna_W_Yna);
620 free(Yna_W_H);
621 free(B_magls);
622 free(hrtfs_ls);
623 free(H_mod);
624}
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
#define ORDER2NSH(order)
Converts spherical harmonic order to number of spherical harmonic components i.e: (order+1)^2.
Definition saf_sh.h:51
void checkCondNumberSHTReal(int order, float *dirs_rad, int nDirs, float *w, float *cond_N)
Computes the condition numbers for a least-squares SHT.
Definition saf_sh.c:991
#define saf_assert(x, message)
Macro to make an assertion, along with a string explaining its purpose.
#define SAF_PI
pi constant (single precision)
void utility_cglslv(void *const hWork, const float_complex *A, const int dim, float_complex *B, int nCol, float_complex *X)
General linear solver: single precision complex, i.e.
const float * __HANDLES_Tdesign_dirs_deg[21]
minimum T-design HANDLES (up to degree 21 only).
void utility_ssvd(void *const hWork, const float *A, const int dim1, const int dim2, float *U, float *S, float *V, float *sing)
Singular value decomposition: single precision, i.e.
const float __Tdesign_degree_30_dirs_deg[480][2]
Directions [azimuth, Elevation] in degrees, for minimum Tdesign degree: 30.
const float __Tdesign_degree_100_dirs_deg[5100][2]
Directions [azimuth, Elevation] in degrees, for minimum Tdesign degree: 100.
#define SAF_MIN(a, b)
Returns the minimum of the two values.
const int __Tdesign_nPoints_per_degree[21]
Number of points in each t-design (up to degree 21 only).
#define SQRT4PI
sqrt(4pi) (single precision)
void utility_svsmul(float *a, const float *s, const int len, float *c)
Single-precision, multiplies each element in vector 'a' with a scalar 's', i.e.
void generateVBAPgainTable3D_srcs(float *src_dirs_deg, int S, float *ls_dirs_deg, int L, int omitLargeTriangles, int enableDummies, float spread, float **gtable, int *N_gtable, int *nTriangles)
Generates a 3-D VBAP [1] gain table based on specified source and loudspeaker directions,...
Definition saf_vbap.c:53
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
Main header for the higher-order Ambisonics module (SAF_HOA_MODULE)
void getBinDecoder_TA(float_complex *hrtfs, float *hrtf_dirs_deg, int N_dirs, int N_bands, int order, float *freqVector, float *itd_s, float *weights, float_complex *decMtx)
Computes a binaural ambisonic decoder based on the time-alignment (TA) method described in [1].
void getEPAD(int order, float *ls_dirs_deg, int nLS, float *decMtx)
Computes the Energy preserving Ambisonic decoder (EPAD), as detailed in [1].
void getBinDecoder_LSDIFFEQ(float_complex *hrtfs, float *hrtf_dirs_deg, int N_dirs, int N_bands, int order, float *weights, float_complex *decMtx)
Computes a least-squares (LS) binaural ambisonic decoder with diffuse-field equalisation [1].
void getBinDecoder_SPR(float_complex *hrtfs, float *hrtf_dirs_deg, int N_dirs, int N_bands, int order, float *weights, float_complex *decMtx)
Computes a binaural ambisonic decoder based on spatial resampling (i.e, virtual loudspeaker decoding)...
void getBinDecoder_LS(float_complex *hrtfs, float *hrtf_dirs_deg, int N_dirs, int N_bands, int order, float *weights, float_complex *decMtx)
Computes a standard least-squares (LS) binaural ambisonic decoder.
void getBinDecoder_MAGLS(float_complex *hrtfs, float *hrtf_dirs_deg, int N_dirs, int N_bands, int order, float *freqVector, float *weights, float_complex *decMtx)
Computes a binaural ambisonic decoder based on the magnitude least-squares (MagLS) method,...
void getAllRAD(int order, float *ls_dirs_deg, int nLS, float *decMtx)
Computes the All-round Ambisonics decoder (AllRAD), as detailed in [1], which is essentially a spheri...
Internal header for the higher-order Ambisonics module (SAF_HOA_MODULE)