Line data Source code
1 : /******************************************************************************************************
2 :
3 : (C) 2022-2025 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB,
4 : Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
5 : Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
6 : Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
7 : contributors to this repository. All Rights Reserved.
8 :
9 : This software is protected by copyright law and by international treaties.
10 : The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB,
11 : Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
12 : Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
13 : Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
14 : contributors to this repository retain full ownership rights in their respective contributions in
15 : the software. This notice grants no license of any kind, including but not limited to patent
16 : license, nor is any license granted by implication, estoppel or otherwise.
17 :
18 : Contributors are required to enter into the IVAS codec Public Collaboration agreement before making
19 : contributions.
20 :
21 : This software is provided "AS IS", without any express or implied warranties. The software is in the
22 : development stage. It is intended exclusively for experts who have experience with such software and
23 : solely for the purpose of inspection. All implied warranties of non-infringement, merchantability
24 : and fitness for a particular purpose are hereby disclaimed and excluded.
25 :
26 : Any dispute, controversy or claim arising under or in relation to providing this software shall be
27 : submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in
28 : accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and
29 : the United Nations Convention on Contracts on the International Sales of Goods.
30 :
31 : *******************************************************************************************************/
32 :
33 : #include "options.h"
34 : #include <stdint.h>
35 : #include <math.h>
36 : #include "ivas_prot.h"
37 : #include "ivas_prot_rend.h"
38 : #include "ivas_stat_rend.h"
39 : #include "ivas_cnst.h"
40 : #include "prot.h"
41 : #include "wmc_auto.h"
42 :
43 :
44 : /*-------------------------------------------------------------------------
45 : * Local constants
46 : *------------------------------------------------------------------------*/
47 :
48 : #define ER_MAX_SOURCES 25
49 : #define ER_REF_ORDER 1
50 : #define ER_AIR_COEFF ( 0.00137f )
51 : #define ER_SOUND_SPEED ( 343.0f )
52 : #define ER_MIN_WALL_DIST ( 0.1f )
53 : #define ER_EUCLIDEAN_SCALE ( 1.29246971E-26f )
54 :
55 :
56 : /*-----------------------------------------------------------------------------------------*
57 : * Function ivas_shoebox_config_init
58 : *
59 : * Function transfer the parameters from the reverb config handle to the shoebox
60 : * calibration data structure.
61 : *-----------------------------------------------------------------------------------------*/
62 :
63 12 : void ivas_shoebox_config_init(
64 : shoebox_config_t *cal,
65 : RENDER_CONFIG_HANDLE hRenderConfig /* i : Renderer configuration handle */
66 : )
67 : {
68 : int16_t wall_idx;
69 :
70 12 : cal->room_L = hRenderConfig->roomAcoustics.dimensions.x;
71 12 : cal->room_W = hRenderConfig->roomAcoustics.dimensions.y;
72 12 : cal->room_H = hRenderConfig->roomAcoustics.dimensions.z;
73 :
74 : /* Absorption Coefficients */
75 : /* Convention: [Front wall, Back wall, Left wall, Right wall, Ceiling, Floor] */
76 84 : for ( wall_idx = 0; wall_idx < 6; wall_idx++ )
77 : {
78 72 : cal->abs_coeff[wall_idx] = hRenderConfig->roomAcoustics.AbsCoeff[wall_idx];
79 : }
80 :
81 : /* Listener position (only X and Y can be pos. or neg. ) */
82 12 : cal->list_orig[0] = hRenderConfig->roomAcoustics.ListenerOrigin.x;
83 12 : cal->list_orig[1] = hRenderConfig->roomAcoustics.ListenerOrigin.y;
84 12 : cal->list_orig[2] = hRenderConfig->roomAcoustics.ListenerOrigin.z;
85 :
86 12 : return;
87 : }
88 :
89 :
90 : /*-----------------------------------------------------------------------------------------*
91 : * Function ivas_shoebox_init()
92 : *
93 : * Function initializes the shoebox operating parameters by setting limits and defaults,
94 : * also contains the calibration structure.
95 : *-----------------------------------------------------------------------------------------*/
96 :
97 12 : void ivas_shoebox_init(
98 : shoebox_obj_t *obj,
99 : shoebox_config_t *cal )
100 : {
101 : uint16_t i;
102 :
103 : /* Add cal to obj struct */
104 12 : obj->cal = *cal;
105 : /* Add defaults */
106 12 : obj->max_bands = 1;
107 12 : obj->MAX_SOURCES = ER_MAX_SOURCES;
108 12 : obj->REF_ORDER = ER_REF_ORDER;
109 :
110 : /* Positions */
111 12 : set_f( &obj->src_pos[0], 0.0f, 75U );
112 12 : set_f( &obj->src_dist[0], 0.0f, 25U );
113 :
114 48 : for ( i = 0; i < 3; i++ )
115 : {
116 36 : obj->list_pos[i] = cal->list_orig[i];
117 : }
118 :
119 : /* Pointer */
120 12 : obj->nSrc = 0;
121 :
122 : /* Flags */
123 12 : obj->isCartesian = 1;
124 12 : obj->isRelative = 1;
125 12 : obj->isZHeight = 1;
126 12 : obj->isRadians = 1;
127 :
128 : /* Params */
129 12 : obj->radius = ER_RADIUS;
130 12 : obj->min_wall_dist = ER_MIN_WALL_DIST;
131 12 : obj->soundspeed = ER_SOUND_SPEED;
132 12 : obj->air_coeff = ER_AIR_COEFF;
133 :
134 12 : return;
135 : }
136 :
137 :
138 : /*-----------------------------------------------------------------------------------------*
139 : * Function shoebox_bound()
140 : *
141 : * SHOEBOX_BOUND takes in CARTESIAN coordinates of either a receiver or
142 : * source and checks if it is within the virtual room boundaries established
143 : * by the surface parameters. If object is out of bounds, then new cartesian
144 : * coordinates are established to collapse the object position.
145 : *-----------------------------------------------------------------------------------------*/
146 :
147 126 : static void shoebox_bound(
148 : shoebox_obj_t *obj,
149 : float *out_pos )
150 : {
151 : float out_tmp;
152 : int32_t i;
153 :
154 126 : out_tmp = ( obj->cal.room_L / 2.0f ) - obj->min_wall_dist;
155 :
156 126 : if ( ( out_pos[0] > out_tmp ) || ( out_pos[0] < ( ( ( -obj->cal.room_L ) / 2.0f ) + obj->min_wall_dist ) ) )
157 : {
158 3 : if ( out_pos[0] < 0.0f )
159 : {
160 0 : i = -1;
161 : }
162 : else
163 : {
164 3 : i = ( out_pos[0] > 0.0f ) ? ( (int32_t) 1 ) : ( (int32_t) 0 );
165 : }
166 3 : out_pos[0] = out_tmp * ( (float) i );
167 : }
168 :
169 126 : out_tmp = ( obj->cal.room_W / 2.0f ) - obj->min_wall_dist;
170 :
171 126 : if ( ( out_pos[1] > out_tmp ) || ( out_pos[1] < ( ( ( -obj->cal.room_W ) / 2.0f ) + obj->min_wall_dist ) ) )
172 : {
173 3 : if ( out_pos[1] < 0.0f )
174 : {
175 3 : i = -1;
176 : }
177 : else
178 : {
179 0 : i = ( out_pos[1] > 0.0f ) ? ( (int32_t) 1 ) : ( (int32_t) 0 );
180 : }
181 3 : out_pos[1] = out_tmp * ( (float) i );
182 : }
183 :
184 126 : out_tmp = ( obj->cal.room_H / 2.0f ) - obj->min_wall_dist;
185 :
186 126 : if ( ( out_pos[2] > out_tmp ) || ( out_pos[2] < ( ( ( -obj->cal.room_H ) / 2.0f ) + obj->min_wall_dist ) ) )
187 : {
188 0 : if ( out_pos[2] < 0.0f )
189 : {
190 0 : i = -1;
191 : }
192 : else
193 : {
194 0 : i = ( out_pos[2] > 0.0f ) ? ( (int32_t) 1 ) : ( (int32_t) 0 );
195 : }
196 0 : out_pos[2] = out_tmp * ( (float) i );
197 : }
198 :
199 126 : return;
200 : }
201 :
202 :
203 : /*-----------------------------------------------------------------------------------------*
204 : * Function shoebox_get_coord()
205 : *
206 : * Transform relative spherical coordinate to 3D cartesian point
207 : *-----------------------------------------------------------------------------------------*/
208 :
209 114 : static void shoebox_get_coord(
210 : shoebox_obj_t *obj,
211 : float *fcnOutput_data,
212 : const float src_pos_data[],
213 : float *tmp_pos,
214 : float out_tmp,
215 : int32_t coord,
216 : int32_t loop_ub,
217 : int32_t k,
218 : uint16_t isRelative )
219 : {
220 : float tmp_data[75];
221 : float rcoselev;
222 : int32_t tmp_size_idx_1;
223 : int32_t n;
224 :
225 114 : tmp_size_idx_1 = 3;
226 114 : if ( obj->isCartesian == 0 )
227 : {
228 : /* Convert Spherical to Cartesian */
229 114 : if ( obj->isRadians == 0 )
230 : {
231 0 : for ( n = 0; n < loop_ub; n++ )
232 : {
233 0 : fcnOutput_data[n] = deg2rad( src_pos_data[k + n] );
234 : }
235 : }
236 114 : tmp_data[2] = fcnOutput_data[2] * sinf( fcnOutput_data[1] );
237 114 : rcoselev = fcnOutput_data[2] * cosf( fcnOutput_data[1] );
238 114 : tmp_data[0] = rcoselev * cosf( fcnOutput_data[0] );
239 114 : tmp_data[1] = rcoselev * sinf( fcnOutput_data[0] );
240 : }
241 : else
242 : {
243 : /* CARTESIAN CASE */
244 0 : tmp_size_idx_1 = loop_ub;
245 0 : for ( n = 0; n < loop_ub; n++ )
246 : {
247 0 : tmp_data[n] = src_pos_data[k + n];
248 : }
249 0 : if ( obj->isZHeight != 0.0f )
250 : {
251 : /* FIX Z COORDINATE */
252 0 : tmp_data[2] = src_pos_data[k + 2] - ( obj->cal.room_H / 2.0f );
253 : }
254 : }
255 :
256 456 : for ( k = 0; k < tmp_size_idx_1; k++ )
257 : {
258 342 : obj->src_pos[( coord + k ) - 1] = tmp_data[k];
259 : }
260 :
261 : /* CENTER TO LISTENER */
262 114 : if ( ( out_tmp + 1.0f ) > ( ( out_tmp + 1.0f ) + 2.0f ) )
263 : {
264 0 : k = 1;
265 : }
266 : else
267 : {
268 114 : k = (int32_t) ( (float) ( out_tmp + 1.0f ) );
269 : }
270 :
271 114 : tmp_pos[0] = obj->src_pos[k - 1];
272 114 : tmp_pos[1] = obj->src_pos[k];
273 114 : tmp_pos[2] = obj->src_pos[k + 1];
274 :
275 114 : if ( isRelative != 0.0f )
276 : {
277 114 : tmp_pos[0] += obj->list_pos[0];
278 114 : tmp_pos[1] += obj->list_pos[1];
279 114 : tmp_pos[2] += obj->list_pos[2];
280 : }
281 :
282 114 : return;
283 : }
284 :
285 :
286 : /*-----------------------------------------------------------------------------------------*
287 : * Function shoebox_get_euclidian_distance_internal()
288 : *
289 : * Get 3D source distance from receiver
290 : *-----------------------------------------------------------------------------------------*/
291 :
292 798 : static float shoebox_get_euclidian_distance_internal(
293 : shoebox_obj_t *obj,
294 : float *tmp_pos,
295 : float *scale )
296 : {
297 : float absxk, out_tmp, t;
298 :
299 798 : absxk = fabsf( obj->list_pos[0] - tmp_pos[0] );
300 :
301 798 : if ( absxk > ER_EUCLIDEAN_SCALE )
302 : {
303 768 : out_tmp = 1.0f;
304 768 : *scale = absxk;
305 : }
306 : else
307 : {
308 30 : t = absxk / ER_EUCLIDEAN_SCALE;
309 30 : out_tmp = t * t;
310 : }
311 :
312 798 : absxk = fabsf( obj->list_pos[1] - tmp_pos[1] );
313 :
314 798 : if ( absxk > *scale )
315 : {
316 318 : t = *scale / absxk;
317 318 : out_tmp = ( ( out_tmp * t ) * t ) + 1.0f;
318 318 : *scale = absxk;
319 : }
320 : else
321 : {
322 480 : t = absxk / *scale;
323 480 : out_tmp += t * t;
324 : }
325 :
326 798 : absxk = fabsf( obj->list_pos[2] - tmp_pos[2] );
327 :
328 798 : if ( absxk > *scale )
329 : {
330 228 : t = *scale / absxk;
331 228 : out_tmp = ( ( out_tmp * t ) * t ) + 1.0f;
332 228 : *scale = absxk;
333 : }
334 : else
335 : {
336 570 : t = absxk / *scale;
337 570 : out_tmp += t * t;
338 : }
339 :
340 798 : return out_tmp;
341 : }
342 :
343 :
344 : /*-----------------------------------------------------------------------------------------*
345 : * Function ivas_shoebox_set_scene()
346 : *
347 : * Initial scene setup returning computed reflection (arrival times, DOA and gain).
348 : *-----------------------------------------------------------------------------------------*/
349 :
350 12 : void ivas_shoebox_set_scene(
351 : shoebox_obj_t *obj,
352 : shoebox_output_t *ER_PARAMS,
353 : const float list_pos[3],
354 : const float src_pos_data[],
355 : const uint16_t isCartesian,
356 : const uint16_t isRelative )
357 : {
358 : float tmp_pos[3];
359 : float out_tmp;
360 : int32_t i, j, k, n;
361 : int32_t loop_ub;
362 : /* ------------- SET FLAGS ------------- */
363 12 : obj->isCartesian = isCartesian;
364 12 : obj->isRelative = isRelative;
365 : /* ------------- CHECK DIMENSIONS ------------- */
366 12 : if ( ER_PARAMS->n_sources > obj->MAX_SOURCES )
367 : {
368 0 : obj->nSrc = obj->MAX_SOURCES;
369 : }
370 : else
371 : {
372 12 : obj->nSrc = ER_PARAMS->n_sources;
373 : }
374 : /* ---------- RESET DATA HOLDERS ---------- */
375 12 : set_f( &obj->src_pos[0], 0.0f, 75U );
376 12 : obj->list_pos[0] = list_pos[0];
377 12 : obj->list_pos[1] = list_pos[1];
378 12 : obj->list_pos[2] = list_pos[2];
379 : /* ---------- ADJUST LISTENER ------------- */
380 12 : if ( obj->isZHeight != 0 )
381 : {
382 12 : obj->list_pos[2] = list_pos[2] - ( obj->cal.room_H / 2.0f );
383 : }
384 12 : tmp_pos[1] = obj->list_pos[1];
385 12 : tmp_pos[2] = obj->list_pos[2];
386 :
387 12 : shoebox_bound( obj, obj->list_pos );
388 :
389 : /* ---------- SOURCE LOOP ------------- */
390 12 : i = (int32_t) obj->nSrc;
391 126 : for ( j = 0; j < i; j++ )
392 : {
393 : float fcnOutput_data[75];
394 : float rcoselev;
395 : float scale;
396 : int32_t coord;
397 : /* idx = single(i); */
398 114 : out_tmp = 3.0f * ( ( ( (float) j ) + 1.0f ) - 1.0f );
399 : /* GET COORDINATE IN CARTESIAN ABSOLUTE FORMAT */
400 114 : if ( ( out_tmp + 1.0f ) > ( ( out_tmp + 1.0f ) + 2.0f ) )
401 : {
402 0 : k = 0;
403 0 : n = 0;
404 0 : coord = 1;
405 : }
406 : else
407 : {
408 114 : k = ( (int32_t) ( (float) ( out_tmp + 1.0f ) ) ) - 1;
409 114 : n = (int32_t) ( (float) ( ( out_tmp + 1.0f ) + 2.0f ) );
410 114 : coord = (int32_t) ( (float) ( out_tmp + 1.0f ) );
411 : }
412 114 : loop_ub = n - k;
413 456 : for ( n = 0; n < loop_ub; n++ )
414 : {
415 342 : fcnOutput_data[n] = src_pos_data[k + n];
416 : }
417 :
418 114 : shoebox_get_coord( obj, fcnOutput_data, src_pos_data, tmp_pos, out_tmp, coord, loop_ub, k, isRelative );
419 :
420 114 : shoebox_bound( obj, tmp_pos );
421 :
422 114 : scale = ER_EUCLIDEAN_SCALE;
423 :
424 114 : out_tmp = shoebox_get_euclidian_distance_internal( obj, tmp_pos, &scale );
425 :
426 114 : obj->src_dist[( (int32_t) ( (float) ( ( (float) j ) + 1.0f ) ) ) - 1] = scale * sqrtf( out_tmp );
427 :
428 : /* COMPUTE PATTERNS */
429 :
430 : /* SHOEBOX_COMPUTE: fills an input structure (4 array fields of length NxR ) with the */
431 : /* Early reflection metadata (time of arrival, gain, az, el). */
432 : /* */
433 : /* Input: */
434 : /* 1. obj : Module data holder */
435 : /* 2. ER_struct : Early reflection structure */
436 : /* 3. src_num : Index of source to compute patterns for */
437 : /* ------ */
438 114 : out_tmp = obj->src_dist[( (int32_t) ( (float) ( ( (float) j ) + 1.0f ) ) ) - 1];
439 798 : for ( loop_ub = 0; loop_ub < 6; loop_ub++ )
440 : {
441 : float im_pos[3];
442 : float path_dist;
443 : /* Retrieve coordinate and surface sign */
444 684 : coord = ( (int32_t) ceilf( ( ( (float) loop_ub ) + 1.0f ) / 2.0f ) ) - 1;
445 684 : rcoselev = ( ( (float) loop_ub ) + 1.0f ) + ( ER_PARAMS->n_ref * ( ( ( (float) j ) + 1.0f ) - 1.0f ) );
446 : /* Initialize image position coordinates */
447 684 : im_pos[0] = tmp_pos[0];
448 684 : im_pos[1] = tmp_pos[1];
449 684 : im_pos[2] = tmp_pos[2];
450 : /* Calculate image projection coordinate based on current surface axis */
451 684 : if ( ( loop_ub + 1 ) < 3 )
452 : {
453 228 : scale = obj->cal.room_L;
454 : }
455 456 : else if ( ( loop_ub + 1 ) < 5 )
456 : {
457 228 : scale = obj->cal.room_W;
458 : }
459 : else
460 : {
461 228 : scale = obj->cal.room_H;
462 : }
463 684 : im_pos[coord] =
464 684 : tmp_pos[coord] +
465 684 : ( 2.0f * ( ( ( ( -( 1.0f - ( fmodf( ( (float) loop_ub ) + 1.0f, 2.0f ) * 2.0f ) ) ) * scale ) / 2.0f ) - tmp_pos[coord] ) );
466 : /* 0. Get euclidean distance from IMAGE SOURCE [N,W] to LIST */
467 684 : scale = ER_EUCLIDEAN_SCALE;
468 684 : path_dist = shoebox_get_euclidian_distance_internal( obj, im_pos, &scale );
469 :
470 684 : path_dist = scale * sqrtf( path_dist );
471 : /* 1. Compute time-of arrival (TOA) */
472 684 : ER_PARAMS->times.data[( (int32_t) rcoselev ) - 1] = path_dist / obj->soundspeed;
473 : /* 2./3. DOA */
474 1368 : ER_PARAMS->az_angle.data[( (int32_t) rcoselev ) - 1] =
475 684 : rad2deg(
476 684 : atan2f( im_pos[1] - obj->list_pos[1], im_pos[0] - obj->list_pos[0] ) );
477 1368 : ER_PARAMS->el_angle.data[( (int32_t) rcoselev ) - 1] =
478 684 : rad2deg(
479 684 : asinf( ( im_pos[2] - obj->list_pos[2] ) / path_dist ) );
480 : /* 4. Compute gain taking into account air and surface absorption */
481 : /* and propagation loss */
482 684 : if ( path_dist < out_tmp )
483 : {
484 0 : path_dist = out_tmp;
485 : }
486 684 : ER_PARAMS->gains.data[( (int32_t) rcoselev ) - 1] =
487 684 : ( ( 1.0f - obj->cal.abs_coeff[loop_ub] ) * ( out_tmp / path_dist ) ) - ( path_dist * obj->air_coeff );
488 : }
489 : }
490 :
491 12 : return;
492 : }
|