SAF
Loading...
Searching...
No Matches
spreader.c
Go to the documentation of this file.
1/*
2 * Copyright 2021 Leo McCormack
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10 * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
13 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14 * PERFORMANCE OF THIS SOFTWARE.
15 */
16
31
32#include "spreader_internal.h"
33
35(
36 void ** const phSpr
37)
38{
40 *phSpr = (void*)pData;
41 int band, t, src;
42
43 /* user parameters */
44 pData->sofa_filepath = NULL;
45 pData->nSources = 1;
47 pData->useDefaultHRIRsFLAG = 1;
48 pData->covAvgCoeff = 0.85f;
49 memset(pData->src_spread, 0, SPREADER_MAX_NUM_SOURCES*sizeof(float));
50 memset(pData->src_dirs_deg, 0, SPREADER_MAX_NUM_SOURCES*2*sizeof(float));
51
52 /* time-frequency transform + buffers */
53 pData->fs = 48000.0f;
54 pData->firstInit = 1;
55 pData->hSTFT = NULL;
56 pData->inputFrameTD = (float**)malloc2d(MAX_NUM_INPUTS, SPREADER_FRAME_SIZE, sizeof(float));
57 pData->outframeTD = (float**)malloc2d(MAX_NUM_OUTPUTS, SPREADER_FRAME_SIZE, sizeof(float));
58 pData->inputframeTF = (float_complex***)malloc3d(HYBRID_BANDS, MAX_NUM_INPUTS, TIME_SLOTS, sizeof(float_complex));
59 pData->protoframeTF = (float_complex***)malloc3d(HYBRID_BANDS, MAX_NUM_OUTPUTS, TIME_SLOTS, sizeof(float_complex));
60 pData->decorframeTF = (float_complex***)malloc3d(HYBRID_BANDS, MAX_NUM_OUTPUTS, TIME_SLOTS, sizeof(float_complex));
61 pData->spreadframeTF = (float_complex***)malloc3d(HYBRID_BANDS, MAX_NUM_OUTPUTS, TIME_SLOTS, sizeof(float_complex));
62 pData->outputframeTF = (float_complex***)malloc3d(HYBRID_BANDS, MAX_NUM_OUTPUTS, TIME_SLOTS, sizeof(float_complex));
63
64 /* Internal */
65 pData->Q = pData->nGrid = pData->h_len = 0;
67 pData->h_fs = 0.0f;
68 pData->h_grid = NULL;
69 pData->H_grid = NULL;
70 for(band=0; band<HYBRID_BANDS; band++)
71 pData->HHH[band] = NULL;
72 pData->grid_dirs_deg = NULL;
73 pData->grid_dirs_xyz = NULL;
74 pData->weights = NULL;
75 pData->angles = NULL;
76 for(src=0; src<SPREADER_MAX_NUM_SOURCES; src++){
77 pData->hDecor[src] = NULL;
78 pData->Cy[src] = NULL;
79 pData->Cproto[src] = NULL;
80 pData->prev_M[src] = NULL;
81 pData->prev_Mr[src] = NULL;
82 pData->dirActive[src] = NULL;
83 }
84 pData->new_M = NULL;
85 pData->new_Mr = NULL;
86 pData->interp_M = NULL;
87 pData->interp_Mr = NULL;
88 pData->interp_Mr_cmplx = NULL;
89 for(t=0; t<TIME_SLOTS; t++){
90 pData->interpolatorFadeIn[t] = ((float)t+1.0f)/(float)TIME_SLOTS;
91 pData->interpolatorFadeOut[t] = 1.0f - ((float)t+1.0f)/(float)TIME_SLOTS;
92 }
93
94 // Hotfix:
95 pData->_tmpFrame = malloc1d(MAX_NUM_CHANNELS*TIME_SLOTS *sizeof(float_complex));
96 pData->_H_tmp = malloc1d(MAX_NUM_CHANNELS *sizeof(float_complex));
97 pData->_Cy = malloc1d(MAX_NUM_CHANNELS*MAX_NUM_CHANNELS *sizeof(float_complex));
98 pData->_E_dir = malloc1d(MAX_NUM_CHANNELS*MAX_NUM_CHANNELS *sizeof(float_complex));
99 pData->_V = malloc1d(MAX_NUM_OUTPUTS*MAX_NUM_OUTPUTS *sizeof(float_complex));
100 pData->_D = malloc1d(MAX_NUM_OUTPUTS*MAX_NUM_OUTPUTS *sizeof(float_complex));
101 pData->_Cproto = malloc1d(MAX_NUM_OUTPUTS*MAX_NUM_OUTPUTS *sizeof(float_complex));
102
103 /* Optimal mixing */
104 pData->hCdf = NULL;
105 pData->hCdf_res = NULL;
106 pData->Qmix = NULL;
107 pData->Qmix_cmplx = NULL;
108 pData->Cr = NULL;
109 pData->Cr_cmplx = NULL;
110
111 /* flags/status */
112 pData->new_procMode = pData->procMode;
113 pData->new_nSources = pData->nSources;
114 pData->progressBar0_1 = 0.0f;
116 strcpy(pData->progressBarText,"");
119}
120
122(
123 void ** const phSpr
124)
125{
126 spreader_data *pData = (spreader_data*)(*phSpr);
127 int band, src;
128
129 if (pData != NULL) {
130 /* not safe to free memory during intialisation/processing loop */
131 while (pData->codecStatus == CODEC_STATUS_INITIALISING ||
133 SAF_SLEEP(10);
134 }
135
136 free(pData->sofa_filepath);
137
138 /* free afSTFT and buffers */
139 if(pData->hSTFT !=NULL)
140 afSTFT_destroy(&(pData->hSTFT));
141 free(pData->inputFrameTD);
142 free(pData->outframeTD);
143 free(pData->inputframeTF);
144 free(pData->decorframeTF);
145 free(pData->spreadframeTF);
146 free(pData->outputframeTF);
147
148 /* internal */
149 free(pData->h_grid);
150 free(pData->H_grid);
151 for(band=0; band<HYBRID_BANDS; band++)
152 free(pData->HHH[band]);
153 free(pData->grid_dirs_deg);
154 free(pData->grid_dirs_xyz);
155 free(pData->weights);
156 free(pData->angles);
157 for(src=0; src<SPREADER_MAX_NUM_SOURCES; src++){
158 latticeDecorrelator_destroy(&(pData->hDecor[src]));
159 free(pData->Cy[src]);
160 free(pData->Cproto[src]);
161 free(pData->prev_M[src]);
162 free(pData->prev_Mr[src]);
163 free(pData->dirActive[src]);
164 }
165 free(pData->new_M);
166 free(pData->new_Mr);
167 free(pData->interp_M);
168 free(pData->interp_Mr);
169 free(pData->interp_Mr_cmplx);
170
171 // Hotfix:
172 free(pData->_tmpFrame);
173 free(pData->_H_tmp);
174 free(pData->_Cy);
175 free(pData->_E_dir);
176 free(pData->_V);
177 free(pData->_D);
178 free(pData->_Cproto);
179
180 /* Optimal mixing */
181 cdf4sap_cmplx_destroy(&(pData->hCdf));
182 cdf4sap_destroy(&(pData->hCdf_res));
183 free(pData->Qmix);
184 free(pData->Qmix_cmplx);
185 free(pData->Cr);
186 free(pData->Cr_cmplx);
187
188 free(pData->progressBarText);
189
190 free(pData);
191 pData = NULL;
192 *phSpr = NULL;
193 }
194}
195
197(
198 void * const hSpr,
199 int sampleRate
200)
201{
202 spreader_data *pData = (spreader_data*)(hSpr);
203
204 /* define frequency vector */
205 if(pData->fs != sampleRate || pData->firstInit){
206 pData->fs = sampleRate;
208 pData->firstInit = 0;
209 }
210 afSTFT_getCentreFreqs(pData->hSTFT, (float)sampleRate, HYBRID_BANDS, pData->freqVector);
211}
212
214(
215 void* const hSpr
216)
217{
218 spreader_data *pData = (spreader_data*)(hSpr);
219 int q, band, ng, nSources, src;
220 float_complex scaleC;
221#ifdef SAF_ENABLE_SOFA_READER_MODULE
224#endif
225 SPREADER_PROC_MODES procMode;
226 float_complex H_tmp[MAX_NUM_CHANNELS];
227 const float_complex calpha = cmplxf(1.0f, 0.0f), cbeta = cmplxf(0.0f, 0.0f);
228
230 return; /* re-init not required, or already happening */
231 while (pData->procStatus == PROC_STATUS_ONGOING){
232 /* re-init required, but we need to wait for the current processing loop to end */
233 pData->codecStatus = CODEC_STATUS_INITIALISING; /* indicate that we want to init */
234 SAF_SLEEP(10);
235 }
236
237 nSources = pData->new_nSources;
238 procMode = pData->new_procMode;
239
240 /* for progress bar */
242 strcpy(pData->progressBarText,"Initialising");
243 pData->progressBar0_1 = 0.0f;
244
245 /* Load measurements (e.g. HRIRs, Microphone array IRs etc.) */
246#ifndef SAF_ENABLE_SOFA_READER_MODULE
247 pData->useDefaultHRIRsFLAG = 1;
248#endif
249 if(pData->useDefaultHRIRsFLAG){
250 /* Load default HRIR data */
251 pData->Q = NUM_EARS;
253 pData->h_len = __default_hrir_len;
254 pData->h_fs = (float)__default_hrir_fs;
255 pData->h_grid = realloc1d(pData->h_grid, pData->nGrid * (pData->Q) * (pData->h_len) * sizeof(float));
256 memcpy(pData->h_grid, (float*)__default_hrirs, pData->nGrid * (pData->Q) * (pData->h_len) * sizeof(float));
257 pData->grid_dirs_deg = realloc1d(pData->grid_dirs_deg, pData->nGrid * 2 * sizeof(float));
258 memcpy(pData->grid_dirs_deg, (float*)__default_hrir_dirs_deg, pData->nGrid * 2 * sizeof(float));
259 }
260#ifdef SAF_ENABLE_SOFA_READER_MODULE
261 else{
262 /* Use sofa loader */
264 if(error!=SAF_SOFA_OK){
265 /* if failed, then load default data instead */
266 pData->useDefaultHRIRsFLAG = 1;
267 saf_print_warning("Unable to load the specified SOFA file. Using default HRIR data instead");
268 spreader_initCodec(hSpr);
269 }
270 pData->h_fs = sofa.DataSamplingRate;
271 pData->h_len = sofa.DataLengthIR;
272 pData->nGrid = sofa.nSources;
273 pData->h_grid = realloc1d(pData->h_grid, pData->nGrid*(pData->Q)*(pData->h_len)*sizeof(float));
274 memcpy(pData->h_grid, sofa.DataIR, pData->nGrid*(pData->Q)*(pData->h_len)*sizeof(float));
275 pData->grid_dirs_deg = realloc1d(pData->grid_dirs_deg, pData->nGrid*2*sizeof(float));
276 cblas_scopy(pData->nGrid, sofa.SourcePosition, 3, pData->grid_dirs_deg, 2); /* azi */
277 cblas_scopy(pData->nGrid, &sofa.SourcePosition[1], 3, &pData->grid_dirs_deg[1], 2); /* elev */
278 saf_sofa_close(&sofa);
279 }
280#endif
281
282 /* Resample HRIRs if needed */
283 pData->h_orig_fs = pData->h_fs;
284 if(pData->h_fs!=pData->fs){
285 strcpy(pData->progressBarText, "Resampling IRs");
286 pData->progressBar0_1 = 0.5f;
287 float* h_grid_resampled;
288 int h_length_resample;
289 resampleHRIRs(pData->h_grid, pData->nGrid, pData->h_len, pData->h_fs, pData->fs, 1, &h_grid_resampled, &h_length_resample);
290
291 /* Replace with resampled HRIRs */
292 pData->h_fs = pData->fs;
293 pData->h_len = h_length_resample;
294 pData->h_grid = realloc1d(pData->h_grid, pData->nGrid*NUM_EARS*(pData->h_len)*sizeof(float));
295 memcpy(pData->h_grid, h_grid_resampled, pData->nGrid*NUM_EARS*(pData->h_len)*sizeof(float));
296
297 /* Clean-up */
298 free(h_grid_resampled);
299 }
300
301 /* Convert from the 0..360 convention, to -180..180, and pre-compute unit Cartesian vectors */
303 pData->grid_dirs_xyz = realloc1d(pData->grid_dirs_xyz, pData->nGrid*3*sizeof(float));
304 unitSph2cart(pData->grid_dirs_deg, pData->nGrid, 1, pData->grid_dirs_xyz);
305
306 /* Initialise time-frequency transform and decorrelators */
307 afSTFT_destroy(&(pData->hSTFT));
308 afSTFT_create(&(pData->hSTFT), nSources, pData->Q, HOP_SIZE, 0, 1, AFSTFT_BANDS_CH_TIME);
309 int orders[4] = {20, 15, 6, 6}; /* 20th order up to 700Hz, 15th->2.4kHz, 6th->4kHz, 3rd->12kHz, NONE(only delays)->Nyquist */
310 //float freqCutoffs[4] = {600.0f, 2.6e3f, 4.5e3f, 12e3f};
311 float freqCutoffs[4] = {900.0f, 6.8e3f, 12e3f, 24e3f};
312 const int maxDelay = 12;
313 for(src=0; src<SPREADER_MAX_NUM_SOURCES; src++){
314 latticeDecorrelator_destroy(&(pData->hDecor[src]));
315 latticeDecorrelator_create(&(pData->hDecor[src]), (float)pData->fs, HOP_SIZE, pData->freqVector, HYBRID_BANDS, pData->Q, orders, freqCutoffs, 4, maxDelay, 0, 0.75f);
316 }
317
318 /* Convert to filterbank coefficients and pre-compute outer products */
319 pData->H_grid = realloc1d(pData->H_grid, HYBRID_BANDS*(pData->Q)*pData->nGrid*sizeof(float_complex));
320 afSTFT_FIRtoFilterbankCoeffs(pData->h_grid, pData->nGrid, pData->Q, pData->h_len, HOP_SIZE, 0, 1, pData->H_grid);
321 pData->weights = realloc1d(pData->weights, pData->nGrid*sizeof(float));
322 getVoronoiWeights(pData->grid_dirs_deg, pData->nGrid, 0, pData->weights);
323 cblas_sscal(pData->nGrid, 1.0f/FOURPI, pData->weights, 1);
324 for(band=0; band<HYBRID_BANDS; band++){
325 pData->HHH[band] = (float_complex**)realloc2d((void**)pData->HHH[band], pData->nGrid, pData->Q * (pData->Q), sizeof(float_complex));
326 for(ng=0; ng<pData->nGrid; ng++){
327 for(q=0; q<pData->Q; q++)
328 H_tmp[q] = pData->H_grid[band*(pData->Q)*pData->nGrid + q*pData->nGrid + ng];
329 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasConjTrans, pData->Q, pData->Q, 1, &calpha,
330 H_tmp, 1,
331 H_tmp, 1, &cbeta,
332 pData->HHH[band][ng], pData->Q);
333 scaleC = cmplxf(pData->weights[ng], 0.0f);
334 cblas_cscal(pData->Q * (pData->Q), &scaleC, pData->HHH[band][ng], 1);
335 }
336 }
337 pData->angles = realloc1d(pData->angles, pData->nGrid*sizeof(float));
338
339 /* OM structures */
340 cdf4sap_cmplx_destroy(&(pData->hCdf));
341 cdf4sap_cmplx_create(&(pData->hCdf), pData->Q, pData->Q);
342 cdf4sap_destroy(&(pData->hCdf_res));
343 cdf4sap_create(&(pData->hCdf_res), pData->Q, pData->Q);
344 pData->Qmix = realloc1d(pData->Qmix, pData->Q*(pData->Q)*sizeof(float));
345 memset(pData->Qmix, 0, pData->Q*(pData->Q)*sizeof(float));
346 pData->Qmix_cmplx = realloc1d(pData->Qmix_cmplx, pData->Q*(pData->Q)*sizeof(float_complex));
347 memset(pData->Qmix_cmplx, 0, pData->Q*(pData->Q)*sizeof(float_complex));
348 for(q=0; q<pData->Q; q++){
349 pData->Qmix[q*(pData->Q)+q] = 1.0f;
350 pData->Qmix_cmplx[q*(pData->Q)+q] = cmplxf(1.0f, 0.0f);
351 }
352 pData->Cr = realloc1d(pData->Cr, pData->Q*(pData->Q)*sizeof(float));
353 pData->Cr_cmplx = realloc1d(pData->Cr_cmplx, pData->Q*(pData->Q)*sizeof(float_complex));
354
355 /* mixing matrices and buffers */
356 for(src=0; src<SPREADER_MAX_NUM_SOURCES; src++){
357 pData->Cy[src] = (float_complex**)realloc2d((void**)pData->Cy[src], HYBRID_BANDS, (pData->Q)*(pData->Q), sizeof(float_complex));
358 memset(FLATTEN2D(pData->Cy[src]), 0, HYBRID_BANDS * (pData->Q)*(pData->Q) * sizeof(float_complex));
359 pData->Cproto[src] = (float_complex**)realloc2d((void**)pData->Cproto[src], HYBRID_BANDS, (pData->Q)*(pData->Q), sizeof(float_complex));
360 memset(FLATTEN2D(pData->Cproto[src]), 0, HYBRID_BANDS * (pData->Q)*(pData->Q) * sizeof(float_complex));
361 pData->prev_M[src] = (float_complex**)realloc2d((void**)pData->prev_M[src], HYBRID_BANDS, (pData->Q)*(pData->Q), sizeof(float_complex));
362 memset(FLATTEN2D(pData->prev_M[src]), 0, HYBRID_BANDS * (pData->Q)*(pData->Q) * sizeof(float_complex));
363 pData->prev_Mr[src] = (float**)realloc2d((void**)pData->prev_Mr[src], HYBRID_BANDS, (pData->Q)*(pData->Q), sizeof(float));
364 memset(FLATTEN2D(pData->prev_Mr[src]), 0, HYBRID_BANDS * (pData->Q)*(pData->Q) * sizeof(float));
365 pData->dirActive[src] = realloc1d(pData->dirActive[src], pData->nGrid * sizeof(int));
366 memset(pData->dirActive[src], 0, pData->nGrid*sizeof(int));
367 }
368 pData->new_M = (float_complex**)realloc2d((void**)pData->new_M, HYBRID_BANDS, (pData->Q)*(pData->Q), sizeof(float_complex));
369 pData->new_Mr = (float**)realloc2d((void**)pData->new_Mr, HYBRID_BANDS, (pData->Q)*(pData->Q), sizeof(float));
370 pData->interp_M = realloc1d(pData->interp_M, (pData->Q)*(pData->Q) * sizeof(float_complex));
371 pData->interp_Mr = realloc1d(pData->interp_Mr, (pData->Q)*(pData->Q) * sizeof(float));
372 pData->interp_Mr_cmplx = realloc1d(pData->interp_Mr_cmplx, (pData->Q)*(pData->Q) * sizeof(float_complex));
373 memset(pData->interp_Mr_cmplx, 0, (pData->Q)*(pData->Q) * sizeof(float_complex));
374
375 /* New config */
376 pData->nSources = nSources;
377 pData->procMode = procMode;
378
379 /* done! */
380 strcpy(pData->progressBarText,"Done!");
381 pData->progressBar0_1 = 1.0f;
383}
384
386(
387 void * const hSpr,
388 const float *const * inputs,
389 float* const* const outputs,
390 int nInputs,
391 int nOutputs,
392 int nSamples
393)
394{
395 spreader_data *pData = (spreader_data*)(hSpr);
396 int q, src, ng, ch, i, j, band, t, nSources, Q, centre_ind, nSpread;
397 float trace, Ey, Eproto, Gcomp;
398 float src_dirs_deg[SPREADER_MAX_NUM_SOURCES][2], src_dir_xyz[3], CprotoDiag[MAX_NUM_OUTPUTS*MAX_NUM_OUTPUTS], src_spread[MAX_NUM_OUTPUTS];
399 float_complex scaleC, tmp;
400#if 0
401 float_complex Cx[MAX_NUM_OUTPUTS*MAX_NUM_OUTPUTS];
402 float CxDiag[MAX_NUM_OUTPUTS*MAX_NUM_OUTPUTS];
403#endif
404 SPREADER_PROC_MODES procMode;
405 const float_complex calpha = cmplxf(1.0f, 0.0f), cbeta = cmplxf(0.0f, 0.0f);
406
407 /* copy user parameters to local variables */
408 procMode = pData->procMode;
409 nSources = pData->nSources;
410 Q = pData->Q;
411 for(i=0; i<nSources; i++){
412 src_dirs_deg[i][0] = pData->src_dirs_deg[i][0];
413 src_dirs_deg[i][1] = pData->src_dirs_deg[i][1];
414 src_spread[i] = pData->src_spread[i];
415 }
416
417 /* apply binaural panner */
418 if ((nSamples == SPREADER_FRAME_SIZE) && (pData->codecStatus==CODEC_STATUS_INITIALISED) ){
420
421 /* Load time-domain data */
422 for(i=0; i < SAF_MIN(nSources,nInputs); i++)
423 utility_svvcopy(inputs[i], SPREADER_FRAME_SIZE, pData->inputFrameTD[i]);
424 for(; i<nSources; i++)
425 memset(pData->inputFrameTD[i], 0, SPREADER_FRAME_SIZE * sizeof(float));
426
427 /* Apply time-frequency transform (TFT) */
429
430 /* Zero output buffer */
431 for(band=0; band<HYBRID_BANDS; band++)
432 memset(FLATTEN2D(pData->outputframeTF[band]), 0, Q*TIME_SLOTS*sizeof(float_complex));
433
434 /* Loop over sources */
435 for(src=0; src<nSources; src++){
436 /* Find the "spread" indices */
437 unitSph2cart(src_dirs_deg[src], 1, 1, src_dir_xyz);
438 cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, pData->nGrid, 1, 3, 1.0f,
439 pData->grid_dirs_xyz, 3,
440 src_dir_xyz, 1, 0.0f,
441 pData->angles, 1);
442 for(i=0; i<pData->nGrid; i++)
443 pData->angles[i] = acosf(SAF_MIN(pData->angles[i], 0.9999999f))*180.0f/SAF_PI;
444 utility_siminv(pData->angles, pData->nGrid, &centre_ind);
445
446 /* Define Prototype signals */
447 switch(procMode){
448 case SPREADER_MODE_NAIVE: /* fall through */
449 case SPREADER_MODE_OM:
450 for(band=0; band<HYBRID_BANDS; band++){
451 if(pData->freqVector[band]<MAX_SPREAD_FREQ){
452 /* Loop over all angles, and sum the H_grid's within the spreading area */
453 memset(pData->_H_tmp, 0, Q*sizeof(float_complex));
454 for(ng=0,nSpread=0; ng<pData->nGrid; ng++){
455 if(pData->angles[ng] <= (src_spread[src]/2.0f)){
456 for(q=0; q<Q; q++)
457 pData->_H_tmp[q] = ccaddf(pData->_H_tmp[q], pData->H_grid[band*Q*pData->nGrid + q*pData->nGrid + ng]);
458 nSpread++;
459 pData->dirActive[src][ng] = 1;
460 }
461 else
462 pData->dirActive[src][ng] = 0;
463 }
464 }
465 else
466 nSpread = 0;
467
468 /* If no directions found in the spread area, then just include the nearest one */
469 if(nSpread==0){
470 for(q=0; q<Q; q++)
471 pData->_H_tmp[q] = pData->H_grid[band*Q*pData->nGrid + q*pData->nGrid + centre_ind];
472 nSpread=1;
473 }
474
475 /* Apply */
476 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, Q, TIME_SLOTS, 1, &calpha,
477 pData->_H_tmp, 1,
478 pData->inputframeTF[band][src], TIME_SLOTS, &cbeta,
479 FLATTEN2D(pData->protoframeTF[band]), TIME_SLOTS);
480
481 /* Scale by number of spreading directions */
482 cblas_sscal(/*re+im*/2*Q*TIME_SLOTS, 1.0f/(float)nSpread, (float*)FLATTEN2D(pData->protoframeTF[band]), 1);
483 }
484 break;
485#if 0
486 case SPREADER_MODE_OM:
487 /* Use the centre direction as the prototype */
488 for(band=0; band<HYBRID_BANDS; band++){
489 for(q=0; q<Q; q++)
490 H_tmp[q] = pData->H_grid[band*Q*pData->nGrid + q*pData->nGrid + centre_ind];
491 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, Q, TIME_SLOTS, 1, &calpha,
492 H_tmp, 1,
493 pData->inputframeTF[band][src], TIME_SLOTS, &cbeta,
494 FLATTEN2D(pData->protoframeTF[band]), TIME_SLOTS);
495 }
496 break;
497#endif
498
500 /* Replicate the mono signal for all Q channels */
501 for(band=0; band<HYBRID_BANDS; band++)
502 for(q=0; q<Q; q++)
503 memcpy(pData->protoframeTF[band][q], pData->inputframeTF[band][src], TIME_SLOTS*sizeof(float_complex));
504 break;
505 }
506
507 /* Main processing */
508 if(procMode==SPREADER_MODE_NAIVE) {
509 /* If naive mode, then we're already done... */
510 for(band=0; band<HYBRID_BANDS; band++)
511 memcpy(FLATTEN2D(pData->spreadframeTF[band]), FLATTEN2D(pData->protoframeTF[band]), Q*TIME_SLOTS*sizeof(float_complex));
512 }
513 else{
514 /* Apply decorrelation of prototype signals */
516
517 /* Compute prototype covariance matrix and average over time */
518 for(band=0; band<HYBRID_BANDS; band++){
519 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasConjTrans, Q, Q, TIME_SLOTS, &calpha,
520 FLATTEN2D(pData->protoframeTF[band]), TIME_SLOTS,
521 FLATTEN2D(pData->protoframeTF[band]), TIME_SLOTS, &cbeta,
522 pData->_Cproto, Q);
523 cblas_sscal(/*re+im*/2*Q*Q, pData->covAvgCoeff, (float*)pData->Cproto[src][band], 1);
524 cblas_saxpy(/*re+im*/2*Q*Q, 1.0f-pData->covAvgCoeff, (float*)pData->_Cproto, 1, (float*)pData->Cproto[src][band], 1);
525 }
526
527 /* Define target covariance matrices */
528 for(band=0; band<HYBRID_BANDS; band++){
529 /* Sum the H_array outer product matrices for the whole spreading area */
530 if(pData->freqVector[band]<MAX_SPREAD_FREQ){
531 memset(pData->_Cy, 0, Q*Q*sizeof(float_complex));
532 memset(pData->_H_tmp, 0, Q*sizeof(float_complex));
533 for(ng=0, nSpread=0; ng<pData->nGrid; ng++){
534 if(pData->angles[ng] <= (src_spread[src]/2.0f)){
535 cblas_caxpy(Q*Q, &calpha, pData->HHH[band][ng], 1, pData->_Cy, 1);
536 for(q=0; q<Q; q++)
537 pData->_H_tmp[q] = ccaddf(pData->_H_tmp[q], pData->H_grid[band*Q*pData->nGrid + q*pData->nGrid + ng]);
538 nSpread++;
539 pData->dirActive[src][ng] = 1;
540 }
541 else
542 pData->dirActive[src][ng] = 0;
543 }
544 }
545 else
546 nSpread = 0;
547
548 /* If no directions found in the spread area, then just include the nearest one */
549 if(nSpread==0) {
550 cblas_caxpy(Q*Q, &calpha, pData->HHH[band][centre_ind], 1, pData->_Cy, 1);
551 for(q=0; q<Q; q++)
552 pData->_H_tmp[q] = pData->H_grid[band*Q*pData->nGrid + q*pData->nGrid + centre_ind];
553 nSpread++;
554 }
555#if 1
556 /* Impose target energies too */
557 if (procMode == SPREADER_MODE_OM && pData->freqVector[band]<MAX_SPREAD_FREQ){
558 /* Normalise Cy */
559 trace = 0.0f;
560 for(q=0; q<Q; q++)
561 trace += crealf(pData->_Cy[q*Q+q]);
562 cblas_sscal(/*re+im*/2*Q*Q, 1.0f/(trace+2.23e-9f), (float*)pData->_Cy, 1);
563
564 /* Compute signals for the centre of the spread */
565 for(q=0; q<Q; q++)
566 pData->_H_tmp[q] = pData->H_grid[band*Q*pData->nGrid + q*pData->nGrid + centre_ind];
567 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, Q, TIME_SLOTS, 1, &calpha,
568 pData->_H_tmp, 1,
569 pData->inputframeTF[band][src], TIME_SLOTS, &cbeta,
570 pData->_tmpFrame, TIME_SLOTS);
571
572 /* Introduce their channel energies into the target */
573 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasConjTrans, Q, Q, TIME_SLOTS, &calpha,
574 pData->_tmpFrame, TIME_SLOTS,
575 pData->_tmpFrame, TIME_SLOTS, &cbeta,
576 pData->_E_dir, Q);
577 trace = 0.0f;
578 for(q=0; q<Q; q++)
579 trace += crealf(pData->_E_dir[q*Q+q]);
580 cblas_sscal(/*re+im*/2*Q*Q, trace, (float*)pData->_Cy, 1);
581 }
582#endif
583 /* Average over time */
584 cblas_sscal(/*re+im*/2*Q*Q, pData->covAvgCoeff, (float*)pData->Cy[src][band], 1);
585 cblas_saxpy(/*re+im*/2*Q*Q, 1.0f-pData->covAvgCoeff, (float*)pData->_Cy, 1, (float*)pData->Cy[src][band], 1);
586 }
587
588 /* Formulate mixing matrices */
589 switch(procMode){
590 case SPREADER_MODE_NAIVE: saf_print_error("Shouldn't have gotten this far?"); break;
592 /* For normalising the level of Cy */
593 Ey = Eproto = 0.0f;
594 for(band=0; band<HYBRID_BANDS; band++){
595 for(i=0; i<Q; i++){
596 Ey += crealf(pData->Cy[src][band][i*Q+i]);
597 Eproto += crealf(pData->Cproto[src][band][i*Q+i])+0.000001f;
598 }
599 }
600 Gcomp = sqrtf(Eproto/(Ey+2.23e-9f));
601
602 /* Compute mixing matrix per band */
603 for(band=0; band<HYBRID_BANDS; band++){
604 memcpy(pData->_Cy, pData->Cy[src][band], Q*Q*sizeof(float_complex));
605 cblas_sscal(/*re+im*/2*Q*Q, Gcomp, (float*)pData->_Cy, 1);
606 utility_cseig(NULL, pData->_Cy, Q, 1, pData->_V, pData->_D, NULL);
607 for(i=0; i<Q; i++)
608 for(j=0; j<Q; j++)
609 pData->_D[i*Q+j] = i==j ? csqrtf(pData->_D[i*Q+j]) : cmplxf(0.0f, 0.0f);
610 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, Q, Q, Q, &calpha,
611 pData->_V, Q,
612 pData->_D, Q, &cbeta,
613 pData->new_M[band], Q);
614 }
615 break;
616
617 case SPREADER_MODE_OM:
618 for(band=0; band<HYBRID_BANDS; band++){
619 if(pData->freqVector[band]<MAX_SPREAD_FREQ){
620#if 1
621 /* Diagonalise and diagonally load the Cproto matrices */
622 cblas_ccopy(Q*Q, pData->Cproto[src][band], 1, pData->_Cproto, 1);
623 for(i=0; i<Q; i++){
624 for(j=0; j<Q; j++){
625 if(i==j)
626 pData->_Cproto[i*Q+i] = craddf(pData->_Cproto[i*Q+i], 0.00001f);
627 CprotoDiag[i*Q+j] = i==j ? crealf(pData->_Cproto[i*Q+i]) : 0.0f;
628 }
629 }
630
631 /* Compute mixing matrices */
632 formulate_M_and_Cr_cmplx(pData->hCdf, pData->_Cproto, pData->Cy[src][band], pData->Qmix_cmplx, 0, 0.2f, pData->new_M[band], pData->Cr_cmplx);
633 for(i=0; i<Q*Q; i++)
634 pData->Cr[i] = crealf(pData->Cr_cmplx[i]);
635 formulate_M_and_Cr(pData->hCdf_res, CprotoDiag, pData->Cr, pData->Qmix, 0, 0.2f, pData->new_Mr[band], NULL);
636#else
637 for(q=0; q<Q; q++)
638 H_tmp[q] = pData->H_grid[band*Q*pData->nGrid + q*pData->nGrid + centre_ind];
639 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasConjTrans, Q, Q, 1, &calpha,
640 H_tmp, 1,
641 H_tmp, 1, &cbeta,
642 Cx, Q);
643 memset(CxDiag, 0, Q*Q*sizeof(float));
644 for(i=0; i<Q; i++)
645 CxDiag[i*Q+i] = crealf(Cx[i*Q+i]);
646
647 /* Compute mixing matrices */
648 formulate_M_and_Cr_cmplx(pData->hCdf, Cx, pData->Cy[src][band], pData->Qmix_cmplx, 0, 0.2f, pData->new_M[band], pData->Cr_cmplx);
649 for(i=0; i<Q*Q; i++)
650 pData->Cr[i] = crealf(pData->Cr_cmplx[i]);
651 formulate_M_and_Cr(pData->hCdf_res, CxDiag, pData->Cr, pData->Qmix, 0, 0.2f, pData->new_Mr[band], NULL);
652#endif
653 }
654 else{
655 memcpy(pData->new_M[band], pData->Qmix_cmplx, Q*Q*sizeof(float_complex));
656 memset(pData->new_Mr[band], 0, Q*Q*sizeof(float));
657 }
658 }
659 break;
660 }
661
662 /* Apply mixing matrices */
663 for(band=0; band<HYBRID_BANDS; band++){
664 for(t=0; t<TIME_SLOTS; t++){
665 scaleC = cmplxf(pData->interpolatorFadeIn[t], 0.0f);
666 utility_cvsmul(pData->new_M[band], &scaleC, Q*Q, pData->interp_M);
667 cblas_saxpy(/*re+im*/2*Q*Q, pData->interpolatorFadeOut[t], (float*)pData->prev_M[src][band], 1, (float*)pData->interp_M, 1);
668 for(i=0; i<Q; i++) {
669 cblas_cdotu_sub(Q, (float_complex*)(&(pData->interp_M[i*Q])), 1,
670 FLATTEN2D((procMode == SPREADER_MODE_EVD ? pData->decorframeTF[band] : pData->protoframeTF[band])) + t,
671 TIME_SLOTS, &(pData->spreadframeTF[band][i][t]));
672 }
673 }
674
675 /* Also mix in the residual part */
676 if(procMode == SPREADER_MODE_OM){
677 if(pData->freqVector[band]<MAX_SPREAD_FREQ){
678 for(t=0; t<TIME_SLOTS; t++){
679 utility_svsmul(pData->new_Mr[band], &(pData->interpolatorFadeIn[t]), Q*Q, pData->interp_Mr);
680 cblas_saxpy(Q*Q, pData->interpolatorFadeOut[t], pData->prev_Mr[src][band], 1, pData->interp_Mr, 1);
681 cblas_scopy(Q*Q, pData->interp_Mr, 1, (float*)pData->interp_Mr_cmplx, 2);
682 for(i=0; i<Q; i++){
683 cblas_cdotu_sub(Q, (float_complex*)(&(pData->interp_Mr_cmplx[i*Q])), 1, FLATTEN2D(pData->decorframeTF[band]) + t, TIME_SLOTS, &tmp);
684 pData->spreadframeTF[band][i][t] = ccaddf(pData->spreadframeTF[band][i][t], tmp);
685 }
686 }
687 }
688 }
689 }
690 }
691
692 /* Add the spread frame to the output frame, then move onto the next source... */
693 for(band=0; band<HYBRID_BANDS; band++)
694 cblas_saxpy(/*re+im*/2*Q*TIME_SLOTS, 1.0f, (float*)FLATTEN2D(pData->spreadframeTF[band]), 1, (float*)FLATTEN2D(pData->outputframeTF[band]), 1);
695
696 /* For next frame */
697 cblas_ccopy(HYBRID_BANDS*Q*Q, FLATTEN2D(pData->new_M), 1, FLATTEN2D(pData->prev_M[src]), 1);
698 cblas_scopy(HYBRID_BANDS*Q*Q, FLATTEN2D(pData->new_Mr), 1, FLATTEN2D(pData->prev_Mr[src]), 1);
699 }
700
701 /* inverse-TFT */
703
704 /* Copy to output buffer */
705 for (ch = 0; ch < SAF_MIN(Q, nOutputs); ch++)
706 utility_svvcopy(pData->outframeTD[ch], SPREADER_FRAME_SIZE, outputs[ch]);
707 for (; ch < nOutputs; ch++)
708 memset(outputs[ch], 0, SPREADER_FRAME_SIZE*sizeof(float));
709 }
710 else{
711 for (ch=0; ch < nOutputs; ch++)
712 memset(outputs[ch],0, SPREADER_FRAME_SIZE*sizeof(float));
713 }
714
716}
717
718/* Set Functions */
719
724
725void spreader_setSpreadingMode(void* const hSpr, int newMode)
726{
727 spreader_data *pData = (spreader_data*)(hSpr);
728 pData->new_procMode = newMode;
730}
731
732void spreader_setAveragingCoeff(void* const hSpr, float newValue)
733{
734 spreader_data *pData = (spreader_data*)(hSpr);
735 pData->covAvgCoeff = newValue;
736}
737
738void spreader_setSourceAzi_deg(void* const hSpr, int index, float newAzi_deg)
739{
740 spreader_data *pData = (spreader_data*)(hSpr);
741 saf_assert(index<SPREADER_MAX_NUM_SOURCES, "index exceeds the maximum number of sources permitted");
742 if(newAzi_deg>180.0f)
743 newAzi_deg = -360.0f + newAzi_deg;
744 newAzi_deg = SAF_MAX(newAzi_deg, -180.0f);
745 newAzi_deg = SAF_MIN(newAzi_deg, 180.0f);
746 if(pData->src_dirs_deg[index][0]!=newAzi_deg)
747 pData->src_dirs_deg[index][0] = newAzi_deg;
748}
749
750void spreader_setSourceElev_deg(void* const hSpr, int index, float newElev_deg)
751{
752 spreader_data *pData = (spreader_data*)(hSpr);
753 saf_assert(index<SPREADER_MAX_NUM_SOURCES, "index exceeds the maximum number of sources permitted");
754 newElev_deg = SAF_MAX(newElev_deg, -90.0f);
755 newElev_deg = SAF_MIN(newElev_deg, 90.0f);
756 if(pData->src_dirs_deg[index][1] != newElev_deg)
757 pData->src_dirs_deg[index][1] = newElev_deg;
758}
759
760void spreader_setSourceSpread_deg(void* const hSpr, int index, float newSpread_deg)
761{
762 spreader_data *pData = (spreader_data*)(hSpr);
763 saf_assert(index<SPREADER_MAX_NUM_SOURCES, "index exceeds the maximum number of sources permitted");
764 newSpread_deg = SAF_MAX(newSpread_deg, 0.0f);
765 newSpread_deg = SAF_MIN(newSpread_deg, 360.0f);
766 if(pData->src_spread[index] != newSpread_deg)
767 pData->src_spread[index] = newSpread_deg;
768}
769
770void spreader_setNumSources(void* const hSpr, int new_nSources)
771{
772 spreader_data *pData = (spreader_data*)(hSpr);
773 pData->new_nSources = SAF_CLAMP(new_nSources, 1, SPREADER_MAX_NUM_SOURCES);
775}
776
777void spreader_setUseDefaultHRIRsflag(void* const hSpr, int newState)
778{
779 spreader_data *pData = (spreader_data*)(hSpr);
780 if((!pData->useDefaultHRIRsFLAG) && (newState)){
781 pData->useDefaultHRIRsFLAG = newState;
782 spreader_refreshSettings(hSpr); // re-init and re-calc
783 }
784}
785
786void spreader_setSofaFilePath(void* const hSpr, const char* path)
787{
788 spreader_data *pData = (spreader_data*)(hSpr);
789
790 pData->sofa_filepath = realloc1d(pData->sofa_filepath, strlen(path) + 1);
791 strcpy(pData->sofa_filepath, path);
792 pData->useDefaultHRIRsFLAG = 0;
793 spreader_refreshSettings(hSpr); // re-init and re-calc
794}
795
796
797/* Get Functions */
798
800{
801 return SPREADER_FRAME_SIZE;
802}
803
805{
806 spreader_data *pData = (spreader_data*)(hSpr);
807 return pData->codecStatus;
808}
809
810float spreader_getProgressBar0_1(void* const hSpr)
811{
812 spreader_data *pData = (spreader_data*)(hSpr);
813 return pData->progressBar0_1;
814}
815
816void spreader_getProgressBarText(void* const hSpr, char* text)
817{
818 spreader_data *pData = (spreader_data*)(hSpr);
819 memcpy(text, pData->progressBarText, PROGRESSBARTEXT_CHAR_LENGTH*sizeof(char));
820}
821
822int* spreader_getDirectionActivePtr(void* const hSpr, int index)
823{
824 spreader_data *pData = (spreader_data*)(hSpr);
825 return pData->dirActive[index];
826}
827
828int spreader_getSpreadingMode(void* const hSpr)
829{
830 spreader_data *pData = (spreader_data*)(hSpr);
831 return pData->new_procMode;
832}
833
834float spreader_getAveragingCoeff(void* const hSpr)
835{
836 spreader_data *pData = (spreader_data*)(hSpr);
837 return pData->covAvgCoeff;
838}
839
840float spreader_getSourceAzi_deg(void* const hSpr, int index)
841{
842 spreader_data *pData = (spreader_data*)(hSpr);
843 saf_assert(index<SPREADER_MAX_NUM_SOURCES, "index exceeds the maximum number of sources permitted");
844 return pData->src_dirs_deg[index][0];
845}
846
847float spreader_getSourceElev_deg(void* const hSpr, int index)
848{
849 spreader_data *pData = (spreader_data*)(hSpr);
850 saf_assert(index<SPREADER_MAX_NUM_SOURCES, "index exceeds the maximum number of sources permitted");
851 return pData->src_dirs_deg[index][1];
852}
853
854float spreader_getSourceSpread_deg(void* const hSpr, int index)
855{
856 spreader_data *pData = (spreader_data*)(hSpr);
857 saf_assert(index<SPREADER_MAX_NUM_SOURCES, "index exceeds the maximum number of sources permitted");
858 return pData->src_spread[index];
859}
860
861int spreader_getNumSources(void* const hSpr)
862{
863 spreader_data *pData = (spreader_data*)(hSpr);
864 return pData->new_nSources;
865}
866
871
872int spreader_getNumOutputs(void* const hSpr)
873{
874 spreader_data *pData = (spreader_data*)(hSpr);
875 return pData->Q;
876}
877
878int spreader_getNDirs(void* const hSpr)
879{
880 spreader_data *pData = (spreader_data*)(hSpr);
881 return pData->nGrid;
882}
883
884float spreader_getIRAzi_deg(void* const hSpr, int index)
885{
886 spreader_data *pData = (spreader_data*)(hSpr);
887 if(pData->codecStatus == CODEC_STATUS_INITIALISED && pData->grid_dirs_deg!=NULL)
888 return pData->grid_dirs_deg[index*2+0];
889 else
890 return 0.0f;
891}
892
893float spreader_getIRElev_deg(void* const hSpr, int index)
894{
895 spreader_data *pData = (spreader_data*)(hSpr);
896 if(pData->codecStatus == CODEC_STATUS_INITIALISED && pData->grid_dirs_deg!=NULL)
897 return pData->grid_dirs_deg[index*2+1];
898 else
899 return 0.0f;
900}
901
902int spreader_getIRlength(void* const hSpr)
903{
904 spreader_data *pData = (spreader_data*)(hSpr);
905 return pData->h_len;
906}
907
908int spreader_getIRsamplerate(void* const hSpr)
909{
910 spreader_data *pData = (spreader_data*)(hSpr);
911 return (int)pData->h_orig_fs;
912}
913
915{
916 spreader_data *pData = (spreader_data*)(hSpr);
917 return pData->useDefaultHRIRsFLAG;
918}
919
920char* spreader_getSofaFilePath(void* const hSpr)
921{
922 spreader_data *pData = (spreader_data*)(hSpr);
923 if(pData->sofa_filepath!=NULL)
924 return pData->sofa_filepath;
925 else
926 return "no_file";
927}
928
929
930int spreader_getDAWsamplerate(void* const hSpr)
931{
932 spreader_data *pData = (spreader_data*)(hSpr);
933 return pData->fs;
934}
935
937{
938 return 12*HOP_SIZE;
939}
#define MAX_NUM_CHANNELS
Maximum number of input/output channels supported.
Definition _common.h:258
#define MAX_NUM_INPUTS
Maximum number of input channels supported.
Definition _common.h:261
#define MAX_NUM_OUTPUTS
Maximum number of output channels supported.
Definition _common.h:264
#define PROGRESSBARTEXT_CHAR_LENGTH
Length of progress bar string.
Definition _common.h:255
@ PROC_STATUS_ONGOING
Codec is processing input audio, and should not be reinitialised at this time.
Definition _common.h:248
@ PROC_STATUS_NOT_ONGOING
Codec is not processing input audio, and may be reinitialised if needed.
Definition _common.h:250
CODEC_STATUS
Current status of the codec.
Definition _common.h:229
@ CODEC_STATUS_NOT_INITIALISED
Codec has not yet been initialised, or the codec configuration has changed.
Definition _common.h:232
@ CODEC_STATUS_INITIALISED
Codec is initialised and ready to process input audio.
Definition _common.h:230
@ CODEC_STATUS_INITIALISING
Codec is currently being initialised, input audio should not be processed.
Definition _common.h:235
void afSTFT_FIRtoFilterbankCoeffs(float *hIR, int N_dirs, int nCH, int ir_len, int hopSize, int LDmode, int hybridmode, float_complex *hFB)
Converts FIR filters into Filterbank Coefficients by passing them through the afSTFT filterbank.
Definition afSTFTlib.c:593
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_backward_knownDimensions(void *const hSTFT, float_complex ***dataFD, int framesize, int dataFD_nCH, int dataFD_nHops, float **dataTD)
Performs backward afSTFT transform (dataFD dimensions are known)
Definition afSTFTlib.c:391
void afSTFT_forward_knownDimensions(void *const hSTFT, float **dataTD, int framesize, int dataFD_nCH, int dataFD_nHops, float_complex ***dataFD)
Performs forward afSTFT transform (dataFD dimensions are known)
Definition afSTFTlib.c:268
void afSTFT_getCentreFreqs(void *const hSTFT, float fs, int nBands, float *freqVector)
Returns current frequency vector.
Definition afSTFTlib.c:546
void afSTFT_destroy(void **const phSTFT)
Destroys an instance of afSTFT.
Definition afSTFTlib.c:199
@ AFSTFT_BANDS_CH_TIME
nBands x nChannels x nTimeHops
Definition afSTFTlib.h:80
#define TIME_SLOTS
Number of STFT timeslots.
#define HOP_SIZE
STFT hop size.
#define HYBRID_BANDS
Number of frequency bands.
void formulate_M_and_Cr_cmplx(void *const hCdf, float_complex *Cx, float_complex *Cy, float_complex *Q, int useEnergyFLAG, float reg, float_complex *M, float_complex *Cr)
Computes the optimal mixing matrices.
void cdf4sap_create(void **const phCdf, int nXcols, int nYcols)
Creates an instance of the Covariance Domain Framework.
Definition saf_cdf4sap.c:85
void cdf4sap_cmplx_destroy(void **const phCdf)
Destroys an instance of the Covariance Domain Framework.
void cdf4sap_destroy(void **const phCdf)
Destroys an instance of the Covariance Domain Framework.
void cdf4sap_cmplx_create(void **const phCdf, int nXcols, int nYcols)
Creates an instance of the Covariance Domain Framework.
void formulate_M_and_Cr(void *const hCdf, float *Cx, float *Cy, float *Q, int useEnergyFLAG, float reg, float *M, float *Cr)
Computes the optimal mixing matrices.
const int __default_N_hrir_dirs
The number of directions/measurements in the default HRIR dataset.
const float __default_hrirs[836][2][256]
The default HRIR data for SAF.
const float __default_hrir_dirs_deg[836][2]
The measurement directions used for the default HRIR dataset.
const int __default_hrir_len
The length of the filters, in samples, for the default HRIR dataset.
const int __default_hrir_fs
The samplerate used to measure the default HRIR filters.
void resampleHRIRs(float *hrirs_in, int hrirs_N_dirs, int hrirs_in_len, int hrirs_in_fs, int hrirs_out_fs, int padToNextPow2, float **hrirs_out, int *hrirs_out_len)
Resamples a set of HRIRs from its original samplerate to a new samplerate.
Definition saf_hrir.c:366
void saf_sofa_close(saf_sofa_container *c)
Frees all SOFA data in a sofa_container.
SAF_SOFA_ERROR_CODES saf_sofa_open(saf_sofa_container *h, char *sofa_filepath, SAF_SOFA_READER_OPTIONS option)
Fills a 'sofa_container' with data found in a SOFA file (GeneralFIR or SimpleFreeFieldHRIR),...
SAF_SOFA_ERROR_CODES
SOFA loader error codes.
@ SAF_SOFA_READER_OPTION_DEFAULT
The default option is SAF_SOFA_READER_OPTION_LIBMYSOFA.
@ SAF_SOFA_OK
None of the error checks failed.
#define saf_print_error(message)
Macro to print a error message along with the filename and line number.
#define saf_assert(x, message)
Macro to make an assertion, along with a string explaining its purpose.
#define SAF_CLAMP(a, min, max)
Ensures value "a" is clamped between the "min" and "max" values.
#define SAF_PI
pi constant (single precision)
#define SAF_MAX(a, b)
Returns the maximum of the two values.
void convert_0_360To_m180_180(float *dirs_deg, int nDirs)
Wraps around any angles exeeding 180 degrees (e.g., 200-> -160)
#define NUM_EARS
2 (true for most humans)
void getVoronoiWeights(float *dirs_deg, int nDirs, int diagFLAG, float *weights)
Computes the integration weights, based on the areas of each face of the corresponding Voronoi diagra...
#define FOURPI
4pi (single precision)
void utility_svvcopy(const float *a, const int len, float *c)
Single-precision, vector-vector copy, i.e.
void utility_cseig(void *const hWork, const float_complex *A, const int dim, int sortDecFLAG, float_complex *V, float_complex *D, float *eig)
Eigenvalue decomposition of a SYMMETRIC/HERMITION matrix: single precision complex,...
void latticeDecorrelator_apply(void *hDecor, float_complex ***inFrame, int nTimeSlots, float_complex ***decorFrame)
Applies the lattice all-pass-filter-based multi-channel signal decorrelator.
#define SAF_MIN(a, b)
Returns the minimum of the two values.
void latticeDecorrelator_create(void **phDecor, float fs, int hopsize, float *freqVector, int nBands, int nCH, int *orders, float *freqCutoffs, int nCutoffs, int maxDelay, int lookupOffset, float enComp_coeff)
Creates an instance of the lattice all-pass-filter-based multi-channel signal decorrelator.
void unitSph2cart(float *dirs, int nDirs, int anglesInDegreesFLAG, float *dirs_xyz)
Converts spherical coordinates to Cartesian coordinates of unit length.
#define saf_print_warning(message)
Macro to print a warning message along with the filename and line number.
void utility_cvsmul(float_complex *a, const float_complex *s, const int len, float_complex *c)
Single-precision, complex, multiplies each element in vector 'a' with a scalar 's',...
void latticeDecorrelator_destroy(void **phDecor)
Destroys an instance of the lattice all-pass-filter-based multi-channel signal decorrelator.
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 utility_siminv(const float *a, const int len, int *index)
Single-precision, index of minimum absolute value in a vector, i.e.
void ** malloc2d(size_t dim1, size_t dim2, size_t data_size)
2-D malloc (contiguously allocated, so use free() as usual to deallocate)
Definition md_malloc.c:89
void ** realloc2d(void **ptr, size_t dim1, size_t dim2, size_t data_size)
2-D realloc which does NOT retain previous data order
Definition md_malloc.c:115
void * malloc1d(size_t dim1_data_size)
1-D malloc (same as malloc, but with error checking)
Definition md_malloc.c:59
void * realloc1d(void *ptr, size_t dim1_data_size)
1-D realloc (same as realloc, but with error checking)
Definition md_malloc.c:79
void *** malloc3d(size_t dim1, size_t dim2, size_t dim3, size_t data_size)
3-D malloc (contiguously allocated, so use free() as usual to deallocate)
Definition md_malloc.c:151
#define FLATTEN2D(A)
Use this macro when passing a 2-D dynamic multi-dimensional array to memset, memcpy or any other func...
Definition md_malloc.h:65
int spreader_getNumSources(void *const hSpr)
Returns the number of inputs/sources in the current config.
Definition spreader.c:861
int spreader_getProcessingDelay(void)
Returns the processing delay in samples (may be used for delay compensation purposes)
Definition spreader.c:936
int spreader_getIRsamplerate(void *const hSpr)
Returns the IR sample rate.
Definition spreader.c:908
int spreader_getMaxNumSources(void)
Returns the maximum number of input sources supported by spreader.
Definition spreader.c:867
int spreader_getNDirs(void *const hSpr)
Returns the number of directions in the currently used HRIR set.
Definition spreader.c:878
void spreader_process(void *const hSpr, const float *const *inputs, float *const *const outputs, int nInputs, int nOutputs, int nSamples)
Spatialises and spreads the input signals in the user specified directions.
Definition spreader.c:386
float spreader_getIRAzi_deg(void *const hSpr, int index)
Returns the IR/TF azimuth for a given index, in DEGREES.
Definition spreader.c:884
void spreader_setSourceSpread_deg(void *const hSpr, int index, float newSpread_deg)
Sets the source spread for a specific channel index, in DEGREES.
Definition spreader.c:760
int * spreader_getDirectionActivePtr(void *const hSpr, int index)
Returns the pointer to a vector describing which directions are currently being used for the spreadin...
Definition spreader.c:822
void spreader_setSpreadingMode(void *const hSpr, int newMode)
Sets the spreading mode (see SPREADER_PROC_MODES)
Definition spreader.c:725
float spreader_getSourceElev_deg(void *const hSpr, int index)
Returns the source elevation for a given source index, in DEGREES.
Definition spreader.c:847
void spreader_create(void **const phSpr)
Creates an instance of the spreader.
Definition spreader.c:35
int spreader_getFrameSize(void)
Returns the processing framesize (i.e., number of samples processed with every _process() call )
Definition spreader.c:799
int spreader_getIRlength(void *const hSpr)
Returns the length of IRs in time-domain samples.
Definition spreader.c:902
int spreader_getDAWsamplerate(void *const hSpr)
Returns the DAW/Host sample rate.
Definition spreader.c:930
void spreader_initCodec(void *const hSpr)
Intialises the codec variables, based on current global/user parameters.
Definition spreader.c:214
float spreader_getAveragingCoeff(void *const hSpr)
Returns the averaging coefficient [0..1].
Definition spreader.c:834
float spreader_getProgressBar0_1(void *const hSpr)
(Optional) Returns current intialisation/processing progress, between 0..1
Definition spreader.c:810
float spreader_getIRElev_deg(void *const hSpr, int index)
Returns the IR/TF elevation for a given index, in DEGREES.
Definition spreader.c:893
void spreader_setSofaFilePath(void *const hSpr, const char *path)
Sets the file path for a .sofa file, in order to employ a custom HRIR set for the decoding.
Definition spreader.c:786
float spreader_getSourceSpread_deg(void *const hSpr, int index)
Returns the source spread for a given source index, in DEGREES.
Definition spreader.c:854
void spreader_getProgressBarText(void *const hSpr, char *text)
(Optional) Returns current intialisation/processing progress text
Definition spreader.c:816
int spreader_getNumOutputs(void *const hSpr)
Returns the number of ears possessed by the average homo sapien.
Definition spreader.c:872
void spreader_setSourceElev_deg(void *const hSpr, int index, float newElev_deg)
Sets the panning elevation for a specific channel index, in DEGREES.
Definition spreader.c:750
void spreader_setNumSources(void *const hSpr, int new_nSources)
Sets the number of input channels/sources to binauralise.
Definition spreader.c:770
void spreader_refreshSettings(void *const hSpr)
Sets all intialisation flags to 1; re-initialising all settings/variables as spreader is currently co...
Definition spreader.c:720
int spreader_getUseDefaultHRIRsflag(void *const hSpr)
Returns the value of a flag used to dictate whether the default HRIRs in the Spatial_Audio_Framework ...
Definition spreader.c:914
void spreader_setSourceAzi_deg(void *const hSpr, int index, float newAzi_deg)
Sets the panning azimuth for a specific channel index, in DEGREES.
Definition spreader.c:738
float spreader_getSourceAzi_deg(void *const hSpr, int index)
Returns the source azimuth for a given source index, in DEGREES.
Definition spreader.c:840
void spreader_destroy(void **const phSpr)
Destroys an instance of the spreader.
Definition spreader.c:122
int spreader_getSpreadingMode(void *const hSpr)
Returns the spreading mode (see SPREADER_PROC_MODES)
Definition spreader.c:828
void spreader_setAveragingCoeff(void *const hSpr, float newValue)
Sets the averaging coefficient [0..1].
Definition spreader.c:732
char * spreader_getSofaFilePath(void *const hSpr)
Returns the file path for a .sofa file.
Definition spreader.c:920
void spreader_setUseDefaultHRIRsflag(void *const hSpr, int newState)
Sets flag to dictate whether the default HRIRs in the Spatial_Audio_Framework should be used (1),...
Definition spreader.c:777
void spreader_init(void *const hSpr, int sampleRate)
Initialises an instance of spreader with default settings.
Definition spreader.c:197
CODEC_STATUS spreader_getCodecStatus(void *const hSpr)
Returns current codec status codec status (see CODEC_STATUS enum)
Definition spreader.c:804
SPREADER_PROC_MODES
Available processing modes.
Definition spreader.h:55
@ SPREADER_MODE_NAIVE
Simple coherent copies of the input signal(s) areassigned to the spreading areas.
Definition spreader.h:56
@ SPREADER_MODE_EVD
Basic solution based on an Eigenvalue decomposition.
Definition spreader.h:59
@ SPREADER_MODE_OM
Optimal mixing solution.
Definition spreader.h:58
#define SPREADER_MAX_NUM_SOURCES
Maximum number of sources supported by the spreader example.
Definition spreader.h:52
void spreader_setCodecStatus(void *const hSpr, CODEC_STATUS newStatus)
Sets codec status (see CODEC_STATUS enum)
An arbitrary array panner (HRIRs, microphone array IRs, etc.) with coherent and incoherent spreading ...
#define SPREADER_FRAME_SIZE
Framesize, in time-domain samples.
#define MAX_SPREAD_FREQ
Maximum spread frequency, above which no spreading occurs.
SOFA container struct comprising all possible data that can be extracted from SOFA 1....
int DataLengthIR
Length of the IRs, in samples.
int nSources
Number of source/measurement positions.
float * SourcePosition
Source positions (refer to SourcePositionType & SourcePositionUnits for the convention and units); FL...
float DataSamplingRate
Sampling rate used to measure the IRs.
float * DataIR
The impulse response (IR) Data; FLAT:nSources x nReceivers x DataLengthIR.
Main structure for spreader.
_Atomic_FLOAT32 covAvgCoeff
Covariance matrix averaging coefficient, [0..1].
float interpolatorFadeOut[TIME_SLOTS]
Linear Interpolator - Fade out.
_Atomic_INT32 h_len
Length of time-domain filters, in samples.
void * hCdf_res
covariance domain framework handle for the residual
int fs
Host sampling rate, in Hz.
float_complex *** spreadframeTF
time-frequency domain spread frame; HYBRID_BANDS x MAX_NUM_OUTPUTS x TIME_SLOTS
_Atomic_CODEC_STATUS codecStatus
see CODEC_STATUS
int firstInit
flag, 1: _init() function has never been called, 0: _init() function has been called
_Atomic_INT32 h_orig_fs
Can be different from h_fs if IRs were resampled.
float ** inputFrameTD
time-domain input frame; MAX_NUM_INPUTS x SPREADER_FRAME_SIZE
_Atomic_FLOAT32 h_fs
Sample rate used to measure the filters.
void * hSTFT
afSTFT handle
float_complex ** prev_M[SPREADER_MAX_NUM_SOURCES]
previous mixing matrices; HYBRID_BANDS x FLAT:(Q x Q)
float_complex *** inputframeTF
time-frequency domain input frame; HYBRID_BANDS x MAX_NUM_INPUTS x TIME_SLOTS
float_complex * interp_Mr_cmplx
Complex variant of interp_Mr.
float * h_grid
FLAT: nGrid x Q x h_len.
float * grid_dirs_xyz
Grid directions as unit-length Cartesian coordinates; FLAT: nGrid x 3.
float_complex ** Cy[SPREADER_MAX_NUM_SOURCES]
Target covariance matrices; HYBRID_BANDS x FLAT:(Q x Q)
float * Qmix
Identity; FLAT: Q x Q.
float_complex * Qmix_cmplx
Identity; FLAT: Q x Q.
float_complex ** HHH[HYBRID_BANDS]
Pre-computed array outer-products; HYBRID_BANDS x nGrid x FLAT: (Q x Q)
float ** prev_Mr[SPREADER_MAX_NUM_SOURCES]
previous residual mixing matrices; HYBRID_BANDS x FLAT:(Q x Q)
float_complex *** outputframeTF
time-frequency domain output frame; HYBRID_BANDS x MAX_NUM_OUTPUTS x TIME_SLOTS
void * hDecor[SPREADER_MAX_NUM_SOURCES]
handles for decorrelators
_Atomic_INT32 Q
Number of channels in the target playback setup; for example: 2 for binaural.
float_complex ** new_M
mixing matrices; HYBRID_BANDS x FLAT:(Q x Q)
float ** new_Mr
residual mixing matrices; HYBRID_BANDS x FLAT:(Q x Q)
int * dirActive[SPREADER_MAX_NUM_SOURCES]
1: IR direction currently used for spreading, 0: not
_Atomic_FLOAT32 src_spread[SPREADER_MAX_NUM_SOURCES]
Source spreading, in degrees.
float ** outframeTD
time-domain output frame; MAX_NUM_OUTPUTS x SPREADER_FRAME_SIZE
float_complex *** decorframeTF
time-frequency domain decorrelated frame; HYBRID_BANDS x MAX_NUM_OUTPUTS x TIME_SLOTS
char * progressBarText
Current (re)initialisation step, string.
_Atomic_FLOAT32 src_dirs_deg[SPREADER_MAX_NUM_SOURCES][2]
Source directions, in degrees.
char * sofa_filepath
SOFA file path.
float * Cr
Residual covariance; FLAT: Q x Q.
_Atomic_SPREADER_PROC_MODES new_procMode
See SPREADER_PROC_MODES (current value will be replaced by this after next re-init)
float_complex ** Cproto[SPREADER_MAX_NUM_SOURCES]
Current prototype covariance matrices; HYBRID_BANDS x FLAT:(Q x Q)
float freqVector[HYBRID_BANDS]
Frequency vector (filterbank centre frequencies)
float * angles
angles; nGrid x 1
_Atomic_INT32 nSources
Current number of input signals.
float * interp_Mr
Interpolated residual mixing matrix; FLAT:(Q x Q)
_Atomic_INT32 new_nSources
New number of input signals (current value will be replaced by this after next re-init)
_Atomic_INT32 useDefaultHRIRsFLAG
1: use default HRIRs in database, 0: use the measurements from SOFA file (can be anything,...
_Atomic_FLOAT32 progressBar0_1
Current (re)initialisation progress, between [0..1].
float_complex * H_grid
FLAT: HYBRID_BANDS x Q x nGrid.
void * hCdf
covariance domain framework handle
float * grid_dirs_deg
Grid directions, in degrees; FLAT: nGrid x 2.
_Atomic_PROC_STATUS procStatus
see PROC_STATUS
float interpolatorFadeIn[TIME_SLOTS]
Linear Interpolator - Fade in.
float * weights
Integration weights; nGrid x 1.
_Atomic_SPREADER_PROC_MODES procMode
See SPREADER_PROC_MODES.
_Atomic_INT32 nGrid
Number of directions/measurements/HRTFs etc.
float_complex * interp_M
Interpolated mixing matrix; FLAT:(Q x Q)
float_complex *** protoframeTF
time-frequency domain prototype frame; HYBRID_BANDS x MAX_NUM_OUTPUTS x TIME_SLOTS
float_complex * Cr_cmplx
Residual covariance; FLAT: Q x Q.