SAF
Loading...
Searching...
No Matches
saf_reverb.c
Go to the documentation of this file.
1/*
2 * Copyright 2020 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
29#include "saf_reverb.h"
30#include "saf_reverb_internal.h"
31
32/* ========================================================================== */
33/* IMS Shoebox Room Simulator */
34/* ========================================================================== */
35
37(
38 void** phIms,
39 float roomDimensions[3],
40 float* abs_wall,
41 float lowestOctaveBand,
42 int nOctBands,
43 float c_ms,
44 float fs
45)
46{
47 *phIms = malloc1d(sizeof(ims_scene_data));
48 ims_scene_data *sc = (ims_scene_data*)(*phIms);
49 int i,j,band,wall;
50
51 /* Shoebox dimensions */
52 sc->room_dims[0] = roomDimensions[0];
53 sc->room_dims[1] = roomDimensions[1];
54 sc->room_dims[2] = roomDimensions[2];
55 sc->c_ms = c_ms;
56
57 /* Octave band centre frequencies */
58 if(nOctBands>1){
59 sc->nBands = nOctBands;
60 sc->band_centerfreqs = malloc1d(nOctBands*sizeof(float));
61 sc->band_centerfreqs[0] = lowestOctaveBand;
62 for(band=1; band<nOctBands; band++)
63 sc->band_centerfreqs[band] = sc->band_centerfreqs[band-1]*2.0f;
64 sc->band_cutofffreqs = malloc1d((sc->nBands-1)*sizeof(float));
66 }
67 else { /* Broad-band operation */
68 sc->nBands = 1;
69 sc->band_centerfreqs = NULL;
70 sc->band_cutofffreqs = NULL;
71 }
72
73 /* Samplerate */
74 sc->fs = fs;
75
76 /* Absorption coeffients per wall and octave band */
77 sc->abs_wall = (float**)malloc2d(sc->nBands, IMS_NUM_WALLS_SHOEBOX, sizeof(float));
78 for(band=0; band<sc->nBands; band++)
79 for(wall=0; wall<IMS_NUM_WALLS_SHOEBOX; wall++)
80 sc->abs_wall[band][wall] = abs_wall[band*IMS_NUM_WALLS_SHOEBOX+wall];
81
82 /* Default are no sources or receivers in the room */
83 for(i=0; i<IMS_MAX_NUM_SOURCES; i++)
84 sc->srcs[i].ID = IMS_UNASSIGNED; /* -1 indicates not in use */
85 for(i=0; i<IMS_MAX_NUM_RECEIVERS; i++)
86 sc->recs[i].ID = IMS_UNASSIGNED;
87 sc->nSources = 0;
88 sc->nReceivers = 0;
89
90 /* ims_core_workspace per source / receiver combination */
92 for(i=0; i<IMS_MAX_NUM_RECEIVERS; i++)
93 for(j=0; j<IMS_MAX_NUM_SOURCES; j++)
94 sc->hCoreWrkSpc[i][j] = NULL;
95
96 /* FIR Fiterbank */
97 sc->H_filt = NULL;
98
99 /* RIRs per source / receiver combination */
101 for(i=0; i<IMS_MAX_NUM_RECEIVERS; i++){
102 for(j=0; j<IMS_MAX_NUM_SOURCES; j++){
103 sc->rirs[i][j].data = NULL;
104 sc->rirs[i][j].length = sc->rirs[i][j].nChannels = 0;
105 }
106 }
107
108 /* Circular buffers (only used/allocated when applyEchogramTD() function is called for the first time) */
109 memset(sc->wIdx, 0, IMS_MAX_NUM_RECEIVERS*IMS_MAX_NUM_SOURCES*2*sizeof(unsigned long));
110 sc->circ_buffer[0] = NULL;
111 sc->circ_buffer[1] = NULL;
112
113 /* IIR Filterbank per source (only used/allocated when applyEchogramTD() function is called for the first time) */
115 sc->src_sigs_bands = malloc1d(IMS_MAX_NUM_SOURCES*sizeof(float**));
116 for(j=0; j<IMS_MAX_NUM_SOURCES; j++){
117 sc->hFaFbank[j] = NULL;
118 sc->src_sigs_bands[j] = NULL;
119 }
120
121 /* Temp buffers for cross-fading (only used/allocated when applyEchogramTD() function is called for the first time) */
124 for(j=0; j<IMS_MAX_NUM_RECEIVERS; j++){
125 sc->rec_sig_tmp[IMS_EG_CURRENT][j] = NULL;
126 sc->rec_sig_tmp[IMS_EG_PREV][j] = NULL;
127 }
128 memset(sc->applyCrossFadeFLAG, 0, IMS_MAX_NUM_RECEIVERS*IMS_MAX_NUM_SOURCES*sizeof(int));
129 sc->interpolator_fIn = NULL;
130 sc->interpolator_fOut = NULL;
131 sc->tmp_frame = NULL;
132 sc->framesize = -1;
133
134 /* Lagrange interpolator look-up table */
135 for(i=0; i<IMS_LAGRANGE_LOOKUP_TABLE_SIZE; i++)
136 sc->lookup_fractions[i] = 1.0f/IMS_LAGRANGE_LOOKUP_TABLE_SIZE * (float)i;
137 lagrangeWeights(IMS_LAGRANGE_ORDER, sc->lookup_fractions, IMS_LAGRANGE_LOOKUP_TABLE_SIZE, (float*)sc->lookup_H_frac);
138}
139
141(
142 void** phIms
143)
144{
145 ims_scene_data *sc = (ims_scene_data*)(*phIms);
146 int i,j;
147
148 if(sc!=NULL){
149 free(sc->band_centerfreqs);
150 free(sc->band_cutofffreqs);
151 free(sc->abs_wall);
152 for(i=0; i<IMS_MAX_NUM_RECEIVERS; i++)
153 for(j=0; j<IMS_MAX_NUM_SOURCES; j++)
155 free(sc->hCoreWrkSpc);
156 free(sc->H_filt);
157 for(i=0; i<IMS_MAX_NUM_RECEIVERS; i++)
158 for(j=0; j<IMS_MAX_NUM_SOURCES; j++)
159 free(sc->rirs[i][j].data);
160 free(sc->rirs);
161 free(sc->circ_buffer[IMS_EG_CURRENT]);
162 free(sc->circ_buffer[IMS_EG_PREV]);
163 for(j=0; j<IMS_MAX_NUM_SOURCES; j++){
165 free(sc->src_sigs_bands[j]);
166 }
167 free(sc->hFaFbank);
168 free(sc->src_sigs_bands);
169 for(i=0; i<IMS_MAX_NUM_RECEIVERS; i++){
170 free(sc->rec_sig_tmp[IMS_EG_CURRENT][i]);
171 free(sc->rec_sig_tmp[IMS_EG_PREV][i]);
172 }
173 free(sc->rec_sig_tmp[IMS_EG_CURRENT]);
174 free(sc->rec_sig_tmp[IMS_EG_PREV]);
175 free(sc->interpolator_fIn);
176 free(sc->interpolator_fOut);
177 free(sc->tmp_frame);
178 free(sc);
179 sc=NULL;
180 *phIms = NULL;
181 }
182}
183
185(
186 void* hIms,
187 int maxN,
188 float maxTime_ms
189)
190{
191 ims_scene_data *sc = (ims_scene_data*)(hIms);
192 ims_core_workspace* workspace;
193 ims_pos_xyz src2, rec2;
194 int src_idx, rec_idx, band;
195
196 saf_assert(maxN<0 || maxTime_ms<0.0f, "one of these input arguments must be the same or greater than 0, and the other one must be less than 0.");
197 saf_assert(maxN>=0 || maxTime_ms>0.0f, "one of these input arguments must be the same or greater than 0, and the other one must be less than 0.");
198
199 /* Compute echograms for active source/receiver combinations */
200 for(rec_idx = 0; rec_idx < IMS_MAX_NUM_RECEIVERS; rec_idx++){
201 for(src_idx = 0; src_idx < IMS_MAX_NUM_SOURCES; src_idx++){
202 if( (sc->srcs[src_idx].ID != IMS_UNASSIGNED) && (sc->recs[rec_idx].ID != IMS_UNASSIGNED) ){
203 /* Change y coord for Receiver and Source to match convention
204 * used inside the coreInit function */
205 rec2.x = sc->recs[rec_idx].pos.x;
206 rec2.y = sc->room_dims[1] - sc->recs[rec_idx].pos.y;
207 rec2.z = sc->recs[rec_idx].pos.z;
208 src2.x = sc->srcs[src_idx].pos.x;
209 src2.y = sc->room_dims[1] - sc->srcs[src_idx].pos.y;
210 src2.z = sc->srcs[src_idx].pos.z;
211
212 /* Workspace handle for this source/receiver combination */
213 workspace = sc->hCoreWrkSpc[rec_idx][src_idx];
214
215 /* Copy previous echograms */
216 for(band=0; band<workspace->nBands; band++)
217 ims_shoebox_echogramCopy(workspace->hEchogram_abs[band], workspace->hPrevEchogram_abs[band]);
218
219 /* Force refresh if target RIR length or max reflection order has changed */
220 if(maxTime_ms>0.0f){
221 if(workspace->d_max != maxTime_ms)
222 workspace->refreshEchogramFLAG = 1;
223 }
224 else{
225 if(workspace->N_max != maxN)
226 workspace->refreshEchogramFLAG = 1;
227 }
228
229 /* Only update if it is required */
230 if(workspace->refreshEchogramFLAG){
231 /* Compute echogram due to pure propagation (frequency-independent, omni-directional) */
232 if(maxTime_ms>0.0f)
233 ims_shoebox_coreInitT(workspace, sc->room_dims, src2, rec2, maxTime_ms, sc->c_ms);
234 else
235 ims_shoebox_coreInitN(workspace, sc->room_dims, src2, rec2, maxN, sc->c_ms);
236
237 /* Apply receiver directivities */
238 switch(sc->recs[rec_idx].type){
239 case RECEIVER_SH:
240 ims_shoebox_coreRecModuleSH(workspace, NSH2ORDER(sc->recs[rec_idx].nChannels));
241 break;
242 }
243
244 /* Apply boundary absorption per frequency band */
246
247 /* Indicate that the echogram is now up to date, and that the RIR should be updated */
248 workspace->refreshEchogramFLAG = 0;
249 workspace->refreshRIRFLAG = 1;
250
251 /* Also indicate that applyTD() should cross-fade the next frame to void clicks */
252 sc->applyCrossFadeFLAG[rec_idx][src_idx] = 1;
253 }
254 }
255 }
256 }
257}
258
260(
261 void* hIms,
262 int fractionalDelayFLAG
263)
264{
265 ims_scene_data *sc = (ims_scene_data*)(hIms);
267 int src_idx, rec_idx;
268
269 /* Compute FIR Filterbank coefficients (if this is the first time this
270 * function is being called) */
271 if(sc->H_filt==NULL){
272 sc->H_filt = (float**)realloc2d((void**)sc->H_filt, sc->nBands, (IMS_FIR_FILTERBANK_ORDER+1), sizeof(float));
275 }
276
277 /* Render RIRs for all active source/receiver combinations */
278 for(rec_idx = 0; rec_idx < IMS_MAX_NUM_RECEIVERS; rec_idx++){
279 for(src_idx = 0; src_idx < IMS_MAX_NUM_SOURCES; src_idx++){
280 if( (sc->srcs[src_idx].ID!=IMS_UNASSIGNED) && (sc->recs[rec_idx].ID!=IMS_UNASSIGNED) ){
281
282 /* Workspace handle for this source/receiver combination */
283 wrk = sc->hCoreWrkSpc[rec_idx][src_idx];
284
285 /* Only update if it is required */
286 if(wrk->refreshRIRFLAG){
287 /* Render the RIRs for each band */
288 ims_shoebox_renderRIR(wrk, fractionalDelayFLAG, sc->fs, sc->H_filt, &(sc->rirs[rec_idx][src_idx]));
289
290 wrk->refreshRIRFLAG = 0;
291 }
292 }
293 }
294 }
295}
296
298(
299 void* hIms,
300 long receiverID,
301 int nSamples,
302 int fractionalDelaysFLAG
303)
304{
305 ims_scene_data *sc = (ims_scene_data*)(hIms);
307 echogram_data *echogram_abs, *echogram_abs_0;
308 int k, i, n, im, band, ch, rec_idx, src_idx, time_samples, wIdx_n;
309 unsigned long rIdx;
310
311 saf_assert(fractionalDelaysFLAG==0, "Untested!");
312 saf_assert(nSamples <= IMS_MAX_NSAMPLES_PER_FRAME, "nSamples exceeds the maximum number that ims_shoebox_applyEchogramTD() can process at a time");
313
314 /* Allocate circular buffers (if this is the first time this function is being called) */
315 if(sc->circ_buffer[0] == NULL)
316 sc->circ_buffer[0] = (float***)calloc3d(IMS_MAX_NUM_SOURCES, sc->nBands, IMS_CIRC_BUFFER_LENGTH, sizeof(float));
317 if(sc->circ_buffer[1] == NULL)
318 sc->circ_buffer[1] = (float***)calloc3d(IMS_MAX_NUM_SOURCES, sc->nBands, IMS_CIRC_BUFFER_LENGTH, sizeof(float));
319
320 /* Also allocate signal buffers and filterbank handles (if this is the first time this function is being called) */
321 for(src_idx = 0; src_idx < IMS_MAX_NUM_SOURCES; src_idx++){
322 /* only allocate if source is active... */
323 if( (sc->srcs[src_idx].ID != IMS_UNASSIGNED) && (sc->src_sigs_bands[src_idx] == NULL) ){
324 if(sc->nBands>1){ /* ... and only create the filterbank if there is more than one band: */
327 }
328 sc->src_sigs_bands[src_idx] = (float**)malloc2d(sc->nBands, IMS_MAX_NSAMPLES_PER_FRAME, sizeof(float));
329 }
330 }
331
332 /* Find index corresponding to this receiver ID */
333 rec_idx = IMS_UNASSIGNED;
334 for(i=0; i<IMS_MAX_NUM_RECEIVERS; i++){
335 if(sc->recs[i].ID == receiverID){
336 rec_idx = i;
337 break;
338 }
339 }
340 saf_assert(rec_idx != IMS_UNASSIGNED, "Invalid receiverID");
341
342 /* Allocate temporary buffer (if this is the first time this function is being called) */
343 if( (sc->recs[rec_idx].ID != IMS_UNASSIGNED) && (sc->rec_sig_tmp[IMS_EG_CURRENT][rec_idx] == NULL) ){
344 sc->rec_sig_tmp[IMS_EG_CURRENT][rec_idx] = (float**)malloc2d(sc->recs[rec_idx].nChannels, IMS_MAX_NSAMPLES_PER_FRAME, sizeof(float));
345 sc->rec_sig_tmp[IMS_EG_PREV][rec_idx] = (float**)malloc2d(sc->recs[rec_idx].nChannels, IMS_MAX_NSAMPLES_PER_FRAME, sizeof(float));
346 }
347 if(sc->framesize!=nSamples){
348 sc->framesize = nSamples;
349 sc->interpolator_fIn = realloc1d(sc->interpolator_fIn, nSamples*sizeof(float));
350 sc->interpolator_fOut = realloc1d(sc->interpolator_fOut, nSamples*sizeof(float));
351 sc->tmp_frame = realloc1d(sc->tmp_frame, nSamples*sizeof(float));
352 for(i=0; i<nSamples; i++){
353 sc->interpolator_fIn[i] = (i+1)*1.0f/(float)nSamples;
354 sc->interpolator_fOut[i] = 1.0f-sc->interpolator_fIn[i];
355 }
356 }
357
358 /* Initialise all receiver channels with zeros */
359 for(ch=0; ch<sc->recs[rec_idx].nChannels; ch++) /* (looping over channels since this array is not guaranteed to be contiguous) */
360 memset(sc->recs[rec_idx].sigs[ch], 0, nSamples * sizeof(float));
361
362 /* Process all active sources for this specific receiver, directly in the time-domain */
363 for(src_idx = 0; src_idx < IMS_MAX_NUM_SOURCES; src_idx++){
364 if( (sc->srcs[src_idx].ID!=IMS_UNASSIGNED) && (sc->recs[rec_idx].ID!=IMS_UNASSIGNED) ){
365
366 /* Broad-band operation */
367 if(sc->nBands==1)
368 memcpy(sc->src_sigs_bands[src_idx][0], sc->srcs[src_idx].sig, nSamples*sizeof(float));
369 else /* OR: Pass source signal through the Favrot & Faller (power-complementary) IIR filterbank */
370 faf_IIRFilterbank_apply(sc->hFaFbank[src_idx], sc->srcs[src_idx].sig, sc->src_sigs_bands[src_idx], nSamples);
371
372 /* Workspace handle for this source/receiver combination */
373 wrk = sc->hCoreWrkSpc[rec_idx][src_idx];
374
375 /* k=0 is for the current echogram,
376 * k=1 (when applyCrossFadeFLAG is enabled) is for the previous echogram */
377 for(k=sc->applyCrossFadeFLAG[rec_idx][src_idx]; k>=0; k--){
378
379 /* Loop over samples */
380 for(n=0; n<nSamples; n++){
381
382 /* Determine write index */
383 wIdx_n = sc->wIdx[k][rec_idx][src_idx] & IMS_CIRC_BUFFER_LENGTH_MASK;
384
385 /* Since the time vector is the same across bands, it makes sense to determine the read-indices only once... */
386 if(k==1)
387 echogram_abs_0 = (echogram_data*)wrk->hPrevEchogram_abs[0];
388 else
389 echogram_abs_0 = (echogram_data*)wrk->hEchogram_abs[0];
390
391 /* Handle the special case of an empty echogram */
392 if(echogram_abs_0->numImageSources==0){
393 /* Set output to 0... */
394 for(ch=0; ch<sc->recs[rec_idx].nChannels; ch++)
395 sc->rec_sig_tmp[k][rec_idx][ch][n] = 0.0f;
396
397 /* Store current sample (per band) into the circular buffer */
398 for(band=0; band < sc->nBands; band++){
399 sc->circ_buffer[k][src_idx][band][wIdx_n] = sc->src_sigs_bands[src_idx][band][n];
400 if(sc->applyCrossFadeFLAG[rec_idx][src_idx]==0)
401 sc->circ_buffer[IMS_EG_PREV][src_idx][band][wIdx_n] = sc->src_sigs_bands[src_idx][band][n];
402 }
403
404 /* Increment write index */
405 sc->wIdx[k][rec_idx][src_idx]++;
406 if(sc->applyCrossFadeFLAG[rec_idx][src_idx]==0)
407 sc->wIdx[IMS_EG_PREV][rec_idx][src_idx]++;
408
409 continue; /* to next sample... */
410 }
411
412 /* Convert time from seconds to number of samples */
413 memset(echogram_abs_0->tmp1, 0, echogram_abs_0->numImageSources*sizeof(float));
414 cblas_saxpy(echogram_abs_0->numImageSources, (sc->fs), echogram_abs_0->time, 1, echogram_abs_0->tmp1, 1);
415
416 /* Determine read-indices and (optionally) also the interpolation weights */
417 if(fractionalDelaysFLAG){
418 /* Loop over all image sources, and determine the circular buffer read indices */
419 for(im=0; im <echogram_abs_0->numImageSources; im++){
420 /* Base read-index */
421 time_samples = (int)(echogram_abs_0->tmp1[im]); /* FLOOR */
422 rIdx = IMS_CIRC_BUFFER_LENGTH-time_samples + sc->wIdx[k][rec_idx][src_idx] /* read index for this image source */
423 + (IMS_LAGRANGE_ORDER/2); /* in order to correctly centre the filter */
424 echogram_abs_0->rIdx[im] = rIdx & IMS_CIRC_BUFFER_LENGTH_MASK; /* wrap-around if needed */
425 }
426
427 /* Find fractional parts */
428 utility_svmod(echogram_abs_0->tmp1, echogram_abs_0->ones_dummy, echogram_abs_0->numImageSources, echogram_abs_0->tmp2);
429
430 /* Find read-indices for interpolator */
431 for(im=0; im <echogram_abs_0->numImageSources; im++){
432 /* Centre the filter */
433 echogram_abs_0->tmp2[im] += (float)(IMS_LAGRANGE_ORDER/2);
434
435 /* Read-indices for lagrange interpolation */
436 for(i=1; i<IMS_LAGRANGE_ORDER; i++){
437 echogram_abs_0->rIdx_frac[i-1][im] = echogram_abs_0->rIdx[im] - i;
438 /* Wrap around if needed */
439 if(echogram_abs_0->rIdx_frac[i-1][im]<0)
440 echogram_abs_0->rIdx_frac[i-1][im] += IMS_CIRC_BUFFER_LENGTH;
441 }
442 }
443
444 /* Compute interpolation weights */ // TODO: This line is around 50% CPU usage of the whole function, a look-up table would be faster...
445 lagrangeWeights(IMS_LAGRANGE_ORDER, echogram_abs_0->tmp2, echogram_abs_0->numImageSources, FLATTEN2D(echogram_abs_0->h_frac));
446 }
447 else{
448 /* Loop over all image sources, and determine the circular buffer read indices based on the nearest sample */
449 for(im=0; im <echogram_abs_0->numImageSources; im++){
450 time_samples = (int)(echogram_abs_0->tmp1[im] + 0.5f); /* ROUND to nearest sample */
451 rIdx = IMS_CIRC_BUFFER_LENGTH-time_samples + sc->wIdx[k][rec_idx][src_idx]; /* read index for this image source */
452 echogram_abs_0->rIdx[im] = rIdx & IMS_CIRC_BUFFER_LENGTH_MASK; /* wrap-around if needed */
453 }
454 }
455
456 /* Loop over octave bands */
457 for(band=0; band < sc->nBands; band++){
458 /* Echogram for this source/receiver at this band */
459 if(k==1)
460 echogram_abs = (echogram_data*)wrk->hPrevEchogram_abs[band];
461 else
462 echogram_abs = (echogram_data*)wrk->hEchogram_abs[band];
463 saf_assert(echogram_abs_0->numImageSources == echogram_abs->numImageSources, "The below code is assuming that the number of image sources should be the same across octave bands!");
464
465 /* Pull values from the circular buffer corresponding to these read indices, and store this sparse vector as a "compressed" vector */
466 utility_ssv2cv_inds(sc->circ_buffer[k][src_idx][band], echogram_abs_0->rIdx, echogram_abs->numImageSources, echogram_abs->cb_vals[0]);
467
468 /* Apply interpolation if enabled */
469 if(fractionalDelaysFLAG){
470 /* Apply the weights corresponding to the first tap in the filter */
471 utility_svvmul(echogram_abs->cb_vals[0], echogram_abs_0->h_frac[0], echogram_abs->numImageSources, echogram_abs->tmp1);
472
473 /* Step through the rest of the filter */
474 for(i=1; i<IMS_LAGRANGE_ORDER; i++){
475 /* Pull values from the circular buffer corresponding to the read-indices for the current tap in the filter */
476 utility_ssv2cv_inds(sc->circ_buffer[k][src_idx][band], echogram_abs_0->rIdx_frac[i-1], echogram_abs->numImageSources, echogram_abs->cb_vals[0]);
477
478 /* Apply interpolation weights */
479 utility_svvmul(echogram_abs->cb_vals[0], echogram_abs_0->h_frac[i], echogram_abs->numImageSources, echogram_abs->tmp2);
480
481 /* Sum */
482 cblas_saxpy(echogram_abs->numImageSources, 1.0f, echogram_abs->tmp2, 1, echogram_abs->tmp1, 1);
483 }
484
485 /* Copy result */
486 cblas_scopy(echogram_abs->numImageSources, echogram_abs->tmp1, 1, echogram_abs->cb_vals[0], 1);
487 }
488
489 /* Replicate these circular buffer values for all output channels */
490 for(ch=1; ch<sc->recs[rec_idx].nChannels; ch++)
491 cblas_scopy(echogram_abs->numImageSources, echogram_abs->cb_vals[0], 1, echogram_abs->cb_vals[ch], 1);
492
493 /* Apply the echogram scalings to each image source - for all channels */
494 utility_svvmul(FLATTEN2D(echogram_abs->value), FLATTEN2D(echogram_abs->cb_vals),
495 (sc->recs[rec_idx].nChannels)*echogram_abs->numImageSources, FLATTEN2D(echogram_abs->contrib));
496
497 /* Render frame */
498 for(ch=0; ch<sc->recs[rec_idx].nChannels; ch++)
499 sc->rec_sig_tmp[k][rec_idx][ch][n] = cblas_sdot(echogram_abs->numImageSources, echogram_abs->ones_dummy,
500 1, echogram_abs->contrib[ch], 1);
501
502 /* Store current sample into the circular buffer */
503 sc->circ_buffer[k][src_idx][band][wIdx_n] = sc->src_sigs_bands[src_idx][band][n];
504 if(sc->applyCrossFadeFLAG[rec_idx][src_idx]==0)
505 sc->circ_buffer[IMS_EG_PREV][src_idx][band][wIdx_n] = sc->src_sigs_bands[src_idx][band][n];
506 }
507
508 /* Increment write index */
509 sc->wIdx[k][rec_idx][src_idx]++;
510 if(sc->applyCrossFadeFLAG[rec_idx][src_idx]==0)
511 sc->wIdx[IMS_EG_PREV][rec_idx][src_idx]++;
512
513 } /* Loop over samples */
514 } /* Loop over slots */
515
516 /* Cross-fade between the buffers rendered using the previous and current echograms */
517 if(sc->applyCrossFadeFLAG[rec_idx][src_idx]){
518 for(ch=0; ch<sc->recs[rec_idx].nChannels; ch++){
519 /* Apply linear interpolator to fade in with the new echogram and fade out with the previous echogram */
520 utility_svvmul(sc->rec_sig_tmp[IMS_EG_CURRENT][rec_idx][ch], sc->interpolator_fIn, nSamples, sc->tmp_frame);
521 cblas_saxpy(nSamples, 1.0f, sc->tmp_frame, 1, sc->recs[rec_idx].sigs[ch], 1);
522 utility_svvmul(sc->rec_sig_tmp[IMS_EG_PREV][rec_idx][ch], sc->interpolator_fOut, nSamples, sc->tmp_frame);
523 cblas_saxpy(nSamples, 1.0f, sc->tmp_frame, 1, sc->recs[rec_idx].sigs[ch], 1);
524 }
525
526 /* No longer need to cross-fade for future frames (unless the echograms change again that is...) */
527 sc->applyCrossFadeFLAG[rec_idx][src_idx] = 0;
528 }
529 else
530 for(ch=0; ch<sc->recs[rec_idx].nChannels; ch++)
531 cblas_saxpy(nSamples, 1.0f, sc->rec_sig_tmp[IMS_EG_CURRENT][rec_idx][ch], 1, sc->recs[rec_idx].sigs[ch], 1);
532
533 } /* If source active */
534
535 } /* Loop over sources */
536}
537
538
539/* set/get functions: */
540
542(
543 void* hIms,
544 float new_roomDimensions[3]
545)
546{
547 ims_scene_data *sc = (ims_scene_data*)(hIms);
548 int rec_idx, src_idx;
549
550 /* Only update if room dimensions are different */
551 if( (sc->room_dims[0]!=new_roomDimensions[0]) ||
552 (sc->room_dims[1]!=new_roomDimensions[1]) ||
553 (sc->room_dims[2]!=new_roomDimensions[2]) )
554 {
555 sc->room_dims[0] = new_roomDimensions[0];
556 sc->room_dims[1] = new_roomDimensions[1];
557 sc->room_dims[2] = new_roomDimensions[2];
558
559 /* Echograms must be re-initialised */
560 for(rec_idx = 0; rec_idx < IMS_MAX_NUM_RECEIVERS; rec_idx++)
561 for(src_idx = 0; src_idx < IMS_MAX_NUM_SOURCES; src_idx++)
562 if( (sc->srcs[src_idx].ID != IMS_UNASSIGNED) && (sc->recs[rec_idx].ID != IMS_UNASSIGNED) )
563 ((ims_core_workspace*)(sc->hCoreWrkSpc[rec_idx][src_idx]))->refreshEchogramFLAG = 1;
564 }
565}
566
568(
569 void* hIms,
570 float* abs_wall
571)
572{
573 ims_scene_data *sc = (ims_scene_data*)(hIms);
574 int band, i, updateRequired, rec_idx, src_idx;
575 updateRequired = 0;
576
577 /* Only update if wall absorption coefficients are different */
578 for(band=0; band<sc->nBands; band++){
579 for(i=0; i<6; i++){
580 if(sc->abs_wall[band][i] != abs_wall[band*6 + i]){
581 sc->abs_wall[band][i] = abs_wall[band*6 + i];
582 updateRequired = 1;
583 }
584 }
585 }
586 if(updateRequired){
587 /* Echograms must be re-initialised */
588 for(rec_idx = 0; rec_idx < IMS_MAX_NUM_RECEIVERS; rec_idx++)
589 for(src_idx = 0; src_idx < IMS_MAX_NUM_SOURCES; src_idx++)
590 if( (sc->srcs[src_idx].ID != IMS_UNASSIGNED) && (sc->recs[rec_idx].ID != IMS_UNASSIGNED) )
591 ((ims_core_workspace*)(sc->hCoreWrkSpc[rec_idx][src_idx]))->refreshEchogramFLAG = 1;
592 }
593}
594
595
596/* add/remove/update functions: */
597
599(
600 void* hIms,
601 float src_xyz[3],
602 float** pSrc_sig
603)
604{
605 ims_scene_data *sc = (ims_scene_data*)(hIms);
606 int i, rec, obj_idx;
607
608 /* Increment number of sources */
609 sc->nSources++;
610 saf_assert(sc->nSources <= IMS_MAX_NUM_SOURCES, "Exceeded the maximum supported number of sources");
611
612 /* Find an unoccupied object */
613 obj_idx = IMS_UNASSIGNED;
614 for(i=0; i<IMS_MAX_NUM_SOURCES; i++){
615 /* an ID of '-1' indicates that it is free to use */
616 if(sc->srcs[i].ID == IMS_UNASSIGNED){
617 obj_idx = i;
618 break;
619 }
620 }
621 saf_assert(obj_idx != IMS_UNASSIGNED, "Ugly error");
622
623 /* Assign unique ID */
624 sc->srcs[obj_idx].ID = 0;
625 for(i=0; i<IMS_MAX_NUM_SOURCES; i++)
626 if(i!=obj_idx)
627 if(sc->srcs[i].ID == sc->srcs[obj_idx].ID)
628 sc->srcs[obj_idx].ID++; /* increment if ID is in use */
629
630 //CHECK
631 for(i=0; i<IMS_MAX_NUM_SOURCES; i++)
632 if(i!=obj_idx)
633 saf_assert(sc->srcs[obj_idx].ID != sc->srcs[i].ID, "Ugly error");
634
635 /* Set source starting position and signal pointer */
636 sc->srcs[obj_idx].pos.x = src_xyz[0];
637 sc->srcs[obj_idx].pos.y = src_xyz[1];
638 sc->srcs[obj_idx].pos.z = src_xyz[2];
639 sc->srcs[obj_idx].sig = pSrc_sig == NULL ? NULL : *pSrc_sig;
640
641 /* Create workspace for all receiver/source combinations, for this new source object */
642 for(rec=0; rec<IMS_MAX_NUM_RECEIVERS; rec++)
643 if(sc->recs[rec].ID!=IMS_UNASSIGNED)
644 ims_shoebox_coreWorkspaceCreate(&(sc->hCoreWrkSpc[rec][obj_idx]), sc->nBands);
645
646 return sc->srcs[obj_idx].ID;
647}
648
650(
651 void* hIms,
652 int sh_order,
653 float rec_xyz[3],
654 float*** pSH_sigs
655)
656{
657 ims_scene_data *sc = (ims_scene_data*)(hIms);
658 int i, src, obj_idx;
659
660 /* Increment number of receivers */
661 sc->nReceivers++;
662 saf_assert(sc->nReceivers <= IMS_MAX_NUM_RECEIVERS, "Exceeded the maximum supported number of receivers");
663
664 /* Find an unoccupied object */
665 obj_idx = IMS_UNASSIGNED;
666 for(i=0; i<IMS_MAX_NUM_RECEIVERS; i++){
667 /* an ID of '-1' indicates that it is free to use */
668 if(sc->recs[i].ID == IMS_UNASSIGNED){
669 obj_idx = i;
670 break;
671 }
672 }
673 saf_assert(obj_idx != IMS_UNASSIGNED, "Ugly error");
674
675 /* Assign unique ID */
676 sc->recs[obj_idx].ID = 0;
677 for(i=0; i<IMS_MAX_NUM_RECEIVERS; i++)
678 if(i!=obj_idx)
679 if(sc->recs[i].ID == sc->recs[obj_idx].ID)
680 sc->recs[obj_idx].ID++; /* increment if ID is in use */
681
682 //CHECK
683 for(i=0; i<IMS_MAX_NUM_RECEIVERS; i++)
684 if(i!=obj_idx)
685 saf_assert(sc->recs[obj_idx].ID != sc->recs[i].ID, "Ugly error");
686
687 /* Set starting position, signal pointers, and indicate that this object is
688 * a spherical harmonic receiver of order: "sh_order" */
689 sc->recs[obj_idx].pos.x = rec_xyz[0];
690 sc->recs[obj_idx].pos.y = rec_xyz[1];
691 sc->recs[obj_idx].pos.z = rec_xyz[2];
692 sc->recs[obj_idx].sigs = pSH_sigs == NULL ? NULL : *pSH_sigs;
693 sc->recs[obj_idx].type = RECEIVER_SH;
694 sc->recs[obj_idx].nChannels = ORDER2NSH(sh_order);
695
696 /* Create workspace for all receiver/source combinations, for this new receiver object */
697 for(src=0; src<IMS_MAX_NUM_SOURCES; src++)
698 if(sc->srcs[src].ID!=IMS_UNASSIGNED)
699 ims_shoebox_coreWorkspaceCreate(&(sc->hCoreWrkSpc[obj_idx][src]), sc->nBands);
700
701 return sc->recs[obj_idx].ID;
702}
703
705(
706 void* hIms,
707 int sourceID,
708 float new_position_xyz[3]
709)
710{
711 ims_scene_data *sc = (ims_scene_data*)(hIms);
712 ims_core_workspace* work;
713 int i, rec, src_idx;
714
715 saf_assert(sourceID >= 0, "Invalid sourceID");
716
717 /* Find index corresponding to this source ID */
718 src_idx = IMS_UNASSIGNED;
719 for(i=0; i<IMS_MAX_NUM_SOURCES; i++){
720 if(sc->srcs[i].ID == sourceID){
721 src_idx = i;
722 break;
723 }
724 }
725 saf_assert(src_idx != IMS_UNASSIGNED, "Invalid sourceID");
726
727 /* Check if source has actually moved */
728 if( (new_position_xyz[0] != sc->srcs[src_idx].pos.x) ||
729 (new_position_xyz[1] != sc->srcs[src_idx].pos.y) ||
730 (new_position_xyz[2] != sc->srcs[src_idx].pos.z))
731 {
732 /* update source position */
733 sc->srcs[src_idx].pos.x = new_position_xyz[0];
734 sc->srcs[src_idx].pos.y = new_position_xyz[1];
735 sc->srcs[src_idx].pos.z = new_position_xyz[2];
736
737 /* All source/receiver combinations for this source index will need to
738 * be refreshed */
739 for(rec=0; rec<IMS_MAX_NUM_RECEIVERS; rec++){
740 if(sc->recs[rec].ID!=IMS_UNASSIGNED){
741 work = (ims_core_workspace*)(sc->hCoreWrkSpc[rec][src_idx]);
742 work->refreshEchogramFLAG = 1;
743 }
744 }
745 }
746}
747
749(
750 void* hIms,
751 int receiverID,
752 float new_position_xyz[3]
753)
754{
755 ims_scene_data *sc = (ims_scene_data*)(hIms);
756 ims_core_workspace* work;
757 int i, src, rec_idx;
758
759 saf_assert(receiverID >= 0, "Invalid receiverID");
760
761 /* Find index corresponding to this receiver ID */
762 rec_idx = IMS_UNASSIGNED;
763 for(i=0; i<IMS_MAX_NUM_RECEIVERS; i++){
764 if(sc->recs[i].ID == receiverID){
765 rec_idx = i;
766 break;
767 }
768 }
769 saf_assert(rec_idx != IMS_UNASSIGNED, "Invalid receiverID");
770
771 /* Check if Receiver has actually moved */
772 if( (new_position_xyz[0] != sc->recs[rec_idx].pos.x) ||
773 (new_position_xyz[1] != sc->recs[rec_idx].pos.y) ||
774 (new_position_xyz[2] != sc->recs[rec_idx].pos.z))
775 {
776 /* update receiver position */
777 sc->recs[rec_idx].pos.x = new_position_xyz[0];
778 sc->recs[rec_idx].pos.y = new_position_xyz[1];
779 sc->recs[rec_idx].pos.z = new_position_xyz[2];
780
781 /* All source/receiver combinations for this receiver index will need to
782 * be refreshed */
783 for(src=0; src<IMS_MAX_NUM_SOURCES; src++){
784 if(sc->srcs[src].ID != IMS_UNASSIGNED){
785 work = (ims_core_workspace*)(sc->hCoreWrkSpc[rec_idx][src]);
786 work->refreshEchogramFLAG = 1;
787 }
788 }
789 }
790}
791
793(
794 void* hIms,
795 int sourceID
796)
797{
798 ims_scene_data *sc = (ims_scene_data*)(hIms);
799 int i, obj_idx, rec;
800
801 saf_assert(sourceID >= 0, "Invalid sourceID");
802
803 /* Find index corresponding to this source ID */
804 obj_idx = IMS_UNASSIGNED;
805 for(i=0; i<IMS_MAX_NUM_SOURCES; i++){
806 if(sc->srcs[i].ID == sourceID){
807 obj_idx = i;
808 break;
809 }
810 }
811 saf_assert(obj_idx != IMS_UNASSIGNED, "Invalid sourceID");
812
813 /* Set ID to -1 (invalid, so no longer rendered) */
814 sc->srcs[obj_idx].ID = IMS_UNASSIGNED;
815
816 /* Destroy workspace for all receiver/source combinations, for this dead source */
817 for(rec=0; rec<IMS_MAX_NUM_RECEIVERS; rec++)
818 if(sc->recs[rec].ID != IMS_UNASSIGNED)
819 ims_shoebox_coreWorkspaceDestroy(&(sc->hCoreWrkSpc[rec][obj_idx]));
820
821 /* De-increment number of sources */
822 sc->nSources--;
823}
824
826(
827 void* hIms,
828 int receiverID
829)
830{
831 ims_scene_data *sc = (ims_scene_data*)(hIms);
832 int i, obj_idx, src;
833
834 saf_assert(receiverID >= 0, "Invalid receiverID");
835
836 /* Find index corresponding to this source ID */
837 obj_idx = IMS_UNASSIGNED;
838 for(i=0; i<IMS_MAX_NUM_RECEIVERS; i++){
839 if(sc->recs[i].ID == receiverID){
840 obj_idx = i;
841 break;
842 }
843 }
844 saf_assert(obj_idx != IMS_UNASSIGNED, "Invalid receiverID");
845
846 /* Set ID to -1 (invalid, so no longer active) */
847 sc->recs[obj_idx].ID = IMS_UNASSIGNED;
848
849 /* Destroy workspace for all receiver/source combinations, for this dead receiver */
850 for(src=0; src<IMS_MAX_NUM_SOURCES; src++)
851 if(sc->srcs[src].ID != IMS_UNASSIGNED)
852 ims_shoebox_coreWorkspaceDestroy(&(sc->hCoreWrkSpc[obj_idx][src]));
853
854 /* De-increment number of receivers */
855 sc->nReceivers--;
856}
void ims_shoebox_setRoomDimensions(void *hIms, float new_roomDimensions[3])
Sets new room dimensions.
Definition saf_reverb.c:542
void ims_shoebox_removeSource(void *hIms, int sourceID)
Removes a specific source from the simulation.
Definition saf_reverb.c:793
int ims_shoebox_addReceiverSH(void *hIms, int sh_order, float rec_xyz[3], float ***pSH_sigs)
Adds a spherical harmonic (SH) receiver object to the simulator of a given order, and returns a uniqu...
Definition saf_reverb.c:650
int ims_shoebox_addSource(void *hIms, float src_xyz[3], float **pSrc_sig)
Adds a source object to the simulator, and returns a unique ID corresponding to it.
Definition saf_reverb.c:599
void ims_shoebox_applyEchogramTD(void *hIms, long receiverID, int nSamples, int fractionalDelaysFLAG)
Applies the currently computed echograms in the time-domain, for all sources, for one specified recei...
Definition saf_reverb.c:298
void ims_shoebox_updateSource(void *hIms, int sourceID, float new_position_xyz[3])
Updates the position of a specific source in the simulation.
Definition saf_reverb.c:705
void ims_shoebox_computeEchograms(void *hIms, int maxN, float maxTime_ms)
Computes echograms for all active source/receiver combinations.
Definition saf_reverb.c:185
void ims_shoebox_updateReceiver(void *hIms, int receiverID, float new_position_xyz[3])
Updates the position of a specific receiver in the simulation.
Definition saf_reverb.c:749
void ims_shoebox_destroy(void **phIms)
Destroys an instance of ims_shoebox room simulator.
Definition saf_reverb.c:141
void ims_shoebox_renderRIRs(void *hIms, int fractionalDelayFLAG)
Renders room impulse responses for all active source/receiver combinations.
Definition saf_reverb.c:260
void ims_shoebox_removeReceiver(void *hIms, int receiverID)
Removes a specific receiver from the simulation.
Definition saf_reverb.c:826
void ims_shoebox_create(void **phIms, float roomDimensions[3], float *abs_wall, float lowestOctaveBand, int nOctBands, float c_ms, float fs)
Creates an instance of ims_shoebox room simulator.
Definition saf_reverb.c:37
#define IMS_MAX_NUM_SOURCES
TODO: Arbitrary array receiver option; Directional source option; finish ims_shoebox_renderRIRs() fun...
Definition saf_reverb.h:52
#define IMS_MAX_NUM_RECEIVERS
Maximum number of receivers supported by an instance of the IMS simulator.
Definition saf_reverb.h:55
void ims_shoebox_setWallAbsCoeffs(void *hIms, float *abs_wall)
Sets new wall absorption coefficients per wall and per band.
Definition saf_reverb.c:568
#define NSH2ORDER(nSH)
Converts number of spherical harmonic components to spherical harmonic order i.e: sqrt(nSH)-1.
Definition saf_sh.h:55
#define ORDER2NSH(order)
Converts spherical harmonic order to number of spherical harmonic components i.e: (order+1)^2.
Definition saf_sh.h:51
void faf_IIRFilterbank_destroy(void **phFaF)
Destroys an instance of the Favrot & Faller filterbank.
#define saf_assert(x, message)
Macro to make an assertion, along with a string explaining its purpose.
void utility_svvmul(const float *a, const float *b, const int len, float *c)
Single-precision, element-wise vector-vector multiplication i.e.
void utility_svmod(const float *a, const float *b, const int len, float *c)
Single-precision, modulus of vector elements, i.e.
void FIRFilterbank(int order, float *fc, int nCutoffFreq, float sampleRate, WINDOWING_FUNCTION_TYPES windowType, int scalingFLAG, float *filterbank)
Computes a bank of FIR filter coefficients required to divide a signal into frequency bands.
void faf_IIRFilterbank_create(void **phFaF, int order, float *fc, int nCutoffFreq, float sampleRate, int maxNumSamples)
Computes a bank of IIR filter coefficients required to divide a signal into frequency bands,...
void lagrangeWeights(int N, float *x, int len_x, float *weights)
Computes Lagrange interpolation weights of order 'N' for value 'x'.
void getOctaveBandCutoffFreqs(float *centreFreqs, int nCentreFreqs, float *cutoffFreqs)
Converts octave band CENTRE frequencies into CUTOFF frequencies.
void utility_ssv2cv_inds(const float *sv, const int *inds, const int len, float *cv)
Single-precision, sparse-vector to compressed vector given known indices i.e.
void faf_IIRFilterbank_apply(void *hFaF, float *inSig, float **outBands, int nSamples)
Applies the Favrot & Faller filterbank.
@ WINDOWING_FUNCTION_HAMMING
Hamming.
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 *** calloc3d(size_t dim1, size_t dim2, size_t dim3, size_t data_size)
3-D calloc (contiguously allocated, so use free() as usual to deallocate)
Definition md_malloc.c:170
#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
Main header for the reverb processing module (SAF_REVERB_MODULE)
void ims_shoebox_coreWorkspaceCreate(void **phWork, int nBands)
Creates an instance of the core workspace.
void ims_shoebox_coreInitT(void *hWork, float room[3], ims_pos_xyz src, ims_pos_xyz rec, float maxTime_s, float c_ms)
Calculates an echogram of a rectangular space using the image source method, for a specific source/re...
void ims_shoebox_coreWorkspaceDestroy(void **phWork)
Destroys an instance of the core workspace.
void ims_shoebox_coreRecModuleSH(void *hWork, int sh_order)
Imposes spherical harmonic directivies onto the echogram computed with ims_shoebox_coreInit() for a s...
void ims_shoebox_coreAbsorptionModule(void *hWork, float **abs_wall)
Applies boundary absoption per frequency band, onto the echogram computed with ims_shoebox_coreRecMod...
void ims_shoebox_renderRIR(void *hWork, int fractionalDelayFLAG, float fs, float **H_filt, ims_rir *rir)
Renders a room impulse response for a specific source/reciever combination.
void ims_shoebox_echogramCopy(void *hEchoX, void *hEchoY)
Copies echogram data from container 'X' into container 'Y' (also resizing 'Y' as needed)
void ims_shoebox_coreInitN(void *hWork, float room[3], ims_pos_xyz src, ims_pos_xyz rec, int maxN, float c_ms)
Calculates an echogram of a rectangular space using the image source method, for a specific source/re...
Internal header for the reverb processing module (SAF_REVERB_MODULE)
@ RECEIVER_SH
Spherical harmonic receiver.
#define IMS_LAGRANGE_ORDER
Order of lagrange interpolation filters.
#define IMS_UNASSIGNED
While a source or receiver ID is yet to be active, it is "IMS_UNASSIGNED".
#define IMS_MAX_NSAMPLES_PER_FRAME
Maximum number of samples that ims should expect to process at a time.
#define IMS_LAGRANGE_LOOKUP_TABLE_SIZE
Lagrange interpolator look-up table size.
#define IMS_EG_CURRENT
Index for the current echogram slot.
#define IMS_CIRC_BUFFER_LENGTH
Circular buffer length.
#define IMS_CIRC_BUFFER_LENGTH_MASK
Circular buffer length, minus 1.
#define IMS_IIR_FILTERBANK_ORDER
IIR filter order (1st or 3rd)
#define IMS_NUM_WALLS_SHOEBOX
Number of wall for a shoebox room.
void * voidPtr
Void pointer (just to improve code readability when working with arrays of handles)
#define IMS_FIR_FILTERBANK_ORDER
FIR filter order (must be even)
#define IMS_EG_PREV
Index for the previous echogram slot.
Echogram structure.
float ** value
Echogram magnitudes per image source and channel; nChannels x numImageSources.
float ** contrib
Total contribution (i.e.
int numImageSources
Number of image sources in current echogram.
int * rIdx_frac[IMS_LAGRANGE_ORDER]
Current circular buffer read indices for fractional buffers; IMS_LAGRANGE_ORDER x numImageSources.
int * rIdx
Current circular buffer read indices; numImageSources x 1.
float ** h_frac
Current fractional delay coeffs; (IMS_LAGRANGE_ORDER+1) x numImageSources x.
float ** cb_vals
Current circular buffer values (per channel & image source); nChannels x numImageSources.
float * time
Propagation time (in seconds) for each image source; numImageSources x 1.
float * tmp1
1st temporary vector; numImageSources x 1
float * ones_dummy
Just a vector of ones, for the cblas_sdot sum hack, and fmodf; numImageSources x 1.
float * tmp2
2nd temporary vector; numImageSources x 1
Helper structure, comprising variables used when computing echograms and rendering RIRs.
voidPtr * hPrevEchogram_abs
Previous echograms (hEchogram_abs), one per band, which can be used for cross- fading.
voidPtr * hEchogram_abs
Echograms with the receiver directivities and also wall absorption applied (multi- channel); one echo...
int refreshEchogramFLAG
1: Refresh needed, 0: refresh not needed
int N_max
Maximum reflection order.
int nBands
Number of bands.
float d_max
Maximum distance, in meters.
Union struct for Cartesian coordinates (access as .x,.y,.z, or .v[3])
float y
y Cartesian coordinate, in metres
float z
z Cartesian coordinate, in metres
float x
x Cartesian coordinate, in metres
RECEIVER_TYPES type
Receiver type (see RECEIVER_TYPES enum)
int nChannels
Number of channels for receiver.
ims_pos_xyz pos
Receiver position.
int ID
Unique receiver ID.
float ** sigs
Receiver signal pointers (one per channel)
Output format of the rendered room impulse responses (RIR)
Definition saf_reverb.h:58
Main structure for IMS.
float * band_cutofffreqs
Octave band CUTOFF frequencies; (nBands-1) x 1.
long nSources
Current number of sources.
float *** circ_buffer[IMS_EG_NUM_SLOTS]
[IMS_EG_NUM_SLOTS] x (nChannels x nBands x IMS_CIRC_BUFFER_LENGTH)
float c_ms
Speed of sound, in ms^1.
float * interpolator_fIn
framesize x 1
int nBands
Number of frequency bands.
float ** abs_wall
Wall aborption coeffs per wall; nBands x 6.
float *** src_sigs_bands
nSources x nBands x nSamples
float room_dims[3]
Room dimensions, in meters.
float * band_centerfreqs
Octave band CENTRE frequencies; nBands x 1.
float * tmp_frame
framesize x 1
int framesize
Curent framesize in samples.
long nReceivers
Current number of receivers.
ims_rec_obj recs[IMS_MAX_NUM_RECEIVERS]
Receiver positions.
voidPtr ** hCoreWrkSpc
One per source/receiver combination.
ims_src_obj srcs[IMS_MAX_NUM_SOURCES]
Source positions.
float *** rec_sig_tmp[IMS_EG_NUM_SLOTS]
[IMS_EG_NUM_SLOTS] x (nReceivers x nChannels x nSamples)
ims_rir ** rirs
One per source/receiver combination.
unsigned long wIdx[IMS_EG_NUM_SLOTS][IMS_MAX_NUM_RECEIVERS][IMS_MAX_NUM_SOURCES]
current write indices for circular buffers
float ** H_filt
nBands x (IMS_FIR_FILTERBANK_ORDER+1)
float * interpolator_fOut
framesize x 1
float fs
Sampling rate.
voidPtr * hFaFbank
One per source.
ims_pos_xyz pos
Source position.
int ID
Unique source ID.
float * sig
Source signal pointer.