From e66ee8333e9e0dd8afef62d6ecea7476611f9e2c Mon Sep 17 00:00:00 2001 From: Sandesh Venkatesh Date: Tue, 16 Jan 2024 20:04:13 +0530 Subject: [PATCH] efap_init_data converted to fixed [x] efap_init_data and related function calls updated for fixed point. --- lib_com/ivas_prot.h | 8 +- lib_com/ivas_tools.c | 40 + lib_com/prot.h | 5 + lib_com/prot_fx2.h | 12 +- lib_com/tools.c | 49 + lib_dec/ivas_binRenderer_internal.c | 4 + lib_dec/ivas_init_dec.c | 21 + lib_dec/ivas_ism_param_dec.c | 4 + lib_dec/ivas_ism_renderer.c | 4 + lib_rend/ivas_allrad_dec.c | 2 +- lib_rend/ivas_efap.c | 1321 ++++++++++++++++++++++++--- lib_rend/ivas_prot_rend.h | 10 +- lib_rend/ivas_stat_rend.h | 30 +- lib_rend/lib_rend.c | 8 + 14 files changed, 1368 insertions(+), 150 deletions(-) diff --git a/lib_com/ivas_prot.h b/lib_com/ivas_prot.h index abb2ca97c..ab01dda53 100644 --- a/lib_com/ivas_prot.h +++ b/lib_com/ivas_prot.h @@ -5400,7 +5400,13 @@ void v_sort_ind( int16_t *idx, /* o : Original index positions */ const int16_t len /* i : vector length */ ); - +#ifdef IVAS_FLOAT_FIXED +void v_sort_ind_fixed( + Word32 *x, /* i/o: Vector to be sorted */ + Word16 *idx, /* o : Original index positions */ + const Word16 len /* i : vector length */ +); +#endif /*----------------------------------------------------------------------------------* * LS Renderer prototypes diff --git a/lib_com/ivas_tools.c b/lib_com/ivas_tools.c index e1c2e6e76..efc9e6440 100644 --- a/lib_com/ivas_tools.c +++ b/lib_com/ivas_tools.c @@ -1304,6 +1304,7 @@ void panning_wrap_angles_fixed( } } #endif + /*-------------------------------------------------------------------------* * v_sort_ind() * @@ -1342,6 +1343,45 @@ void v_sort_ind( return; } +#ifdef IVAS_FLOAT_FIXED +/*-------------------------------------------------------------------------* + * v_sort_ind_fixed() + * + * Sort a float array + * (modified version of v_sort() to return an index array) + *-------------------------------------------------------------------------*/ + +void v_sort_ind_fixed( + Word32 *x, /* i/o: Vector to be sorted */ + Word16 *idx, /* o : Original index positions */ + const Word16 len /* i : vector length */ +) +{ + Word16 i, j; + Word32 tempr; + Word16 tempi; + + FOR ( i = 0; i < len; i++ ) + { + idx[i] = i; + } + + FOR ( i = len - 2; i >= 0; i-- ) + { + tempr = x[i]; + tempi = idx[i]; + FOR ( j = i + 1; ( j < len ) && ( tempr > x[j] ); j++ ) + { + x[j - 1] = x[j]; + idx[j - 1] = idx[j]; + } + x[j - 1] = tempr; + idx[j - 1] = tempi; + } + + return; +} +#endif /*-------------------------------------------------------------------* * is_IVAS_bitrate() diff --git a/lib_com/prot.h b/lib_com/prot.h index ff46aa029..eb6702340 100644 --- a/lib_com/prot.h +++ b/lib_com/prot.h @@ -402,6 +402,11 @@ void sort( uint16_t len /* i/o: vector length */ ); +void sort_l( + Word32 *x, /* i/o: Vector to be sorted */ + Word16 len /* i/o: vector length */ +); + /*! r: variance of vector */ float var( const float *x, /* i : input vector */ diff --git a/lib_com/prot_fx2.h b/lib_com/prot_fx2.h index 8085cb209..f76c37783 100644 --- a/lib_com/prot_fx2.h +++ b/lib_com/prot_fx2.h @@ -7931,6 +7931,16 @@ void delay_signal_fx( const Word16 delay /* i : delay in samples */ ); +Word32 anint_fixed( + Word32 x, /* i: Round to the nearest integer */ + Word16 exp /* i: Exponent for round step */ +); + +Word32 ceil_fixed( + Word32 x, /* i: number to ceil */ + Word16 exp /* i: Exponent for ceil step */ +); + void v_add_fx( const Word32 x1[], /* i : Input vector 1 */ const Word32 x2[], /* i : Input vector 2 */ @@ -7945,4 +7955,4 @@ void cldfbAnalysis_ts_fx( const Word16 samplesToProcess, /* i : samples to process */ HANDLE_CLDFB_FILTER_BANK h_cldfb ); -#endif \ No newline at end of file +#endif diff --git a/lib_com/tools.c b/lib_com/tools.c index 21e238090..b00ea6b21 100644 --- a/lib_com/tools.c +++ b/lib_com/tools.c @@ -1433,6 +1433,26 @@ void sort( return; } +void sort_l( + Word32 *x, /* i/o: Vector to be sorted */ + Word16 len /* i/o: vector length */ +) +{ + Word16 i, j; + Word32 tempr; + + FOR ( i = len - 2; i >= 0; i-- ) + { + tempr = x[i]; + FOR ( j = i + 1; ( j < len ) && ( tempr > x[j] ); j++ ) + { + x[j - 1] = x[j]; + } + x[j - 1] = tempr; + } + + return; +} /*---------------------------------------------------------------------* * var() @@ -1813,6 +1833,35 @@ double anint( return ( x ) >= 0 ? (int32_t) ( ( x ) + 0.5 ) : (int32_t) ( (x) -0.5 ); } +/*-------------------------------------------------------------------* + * anint_fixed() + * + * Round to the nearest integer. + *-------------------------------------------------------------------*/ +Word32 anint_fixed( Word32 x, Word16 exp ) +{ + IF ( x == 0 ) + { + return 0; + } + return ( x ) >= 0 ? L_add( x, ( 1 << ( exp - 1 ) ) ) : L_sub( x, ( 1 << ( exp - 1 ) ) ); +} + +/*-------------------------------------------------------------------* + * ceil_fixed() + * + * Ceil to the next multiple of (1 << exp). + *-------------------------------------------------------------------*/ +Word32 ceil_fixed(Word32 x, Word16 exp) { + Word32 step; + step = x / ( 1 << exp ); + IF ( x % ( 1 << exp ) > 0 ) + { + step++; + } + return (step << exp); +} + /*-------------------------------------------------------------------* * is_numeric_float() * diff --git a/lib_dec/ivas_binRenderer_internal.c b/lib_dec/ivas_binRenderer_internal.c index 7b71eb328..3818e6f38 100644 --- a/lib_dec/ivas_binRenderer_internal.c +++ b/lib_dec/ivas_binRenderer_internal.c @@ -1064,7 +1064,11 @@ ivas_error ivas_binRenderer_open( } ELSE IF ( st_ivas->ivas_format == MC_FORMAT && ( st_ivas->hDecoderConfig->Opt_Headrotation || st_ivas->hDecoderConfig->Opt_ExternalOrientation ) ) { +#ifndef IVAS_FLOAT_FIXED IF ( ( error = efap_init_data( &( st_ivas->hEFAPdata ), st_ivas->hIntSetup.ls_azimuth, st_ivas->hIntSetup.ls_elevation, st_ivas->hIntSetup.nchan_out_woLFE, EFAP_MODE_EFAP ) ) != IVAS_ERR_OK ) +#else + IF( ( error = efap_init_data_fixed( &( st_ivas->hEFAPdata ), st_ivas->hIntSetup.ls_azimuth, st_ivas->hIntSetup.ls_elevation, st_ivas->hIntSetup.nchan_out_woLFE, EFAP_MODE_EFAP ) ) != IVAS_ERR_OK ) +#endif { return error; } diff --git a/lib_dec/ivas_init_dec.c b/lib_dec/ivas_init_dec.c index d88605eed..1094935ef 100644 --- a/lib_dec/ivas_init_dec.c +++ b/lib_dec/ivas_init_dec.c @@ -125,6 +125,7 @@ ivas_error ivas_dec_setup( { /* read Ambisonic (SBA) planar flag */ st_ivas->sba_planar = st_ivas->bit_stream[num_bits_read]; + num_bits_read = add(num_bits_read, SBA_PLANAR_BITS); /* read Ambisonic (SBA) order */ @@ -983,7 +984,11 @@ ivas_error ivas_init_decoder( /* init EFAP for custom LS output and set hTransSetup */ IF ( output_config == IVAS_AUDIO_CONFIG_LS_CUSTOM ) { +#ifndef IVAS_FLOAT_FIXED IF ( ( error = efap_init_data( &( st_ivas->hEFAPdata ), st_ivas->hOutSetup.ls_azimuth, st_ivas->hOutSetup.ls_elevation, st_ivas->hOutSetup.nchan_out_woLFE, EFAP_MODE_EFAP ) ) != IVAS_ERR_OK ) +#else + IF( ( error = efap_init_data_fixed( &( st_ivas->hEFAPdata ), st_ivas->hOutSetup.ls_azimuth, st_ivas->hOutSetup.ls_elevation, st_ivas->hOutSetup.nchan_out_woLFE, EFAP_MODE_EFAP ) ) != IVAS_ERR_OK ) +#endif { return error; } @@ -1405,7 +1410,11 @@ ivas_error ivas_init_decoder( /* init EFAP for custom LS setup */ IF ( output_config == IVAS_AUDIO_CONFIG_LS_CUSTOM ) { +#ifndef IVAS_FLOAT_FIXED IF ( ( error = efap_init_data( &( st_ivas->hEFAPdata ), st_ivas->hLsSetupCustom->ls_azimuth, st_ivas->hLsSetupCustom->ls_elevation, st_ivas->hLsSetupCustom->num_spk, EFAP_MODE_EFAP ) ) != IVAS_ERR_OK ) +#else + IF( ( error = efap_init_data_fixed( &( st_ivas->hEFAPdata ), st_ivas->hLsSetupCustom->ls_azimuth, st_ivas->hLsSetupCustom->ls_elevation, st_ivas->hLsSetupCustom->num_spk, EFAP_MODE_EFAP ) ) != IVAS_ERR_OK ) +#endif { return error; } @@ -1440,7 +1449,11 @@ ivas_error ivas_init_decoder( /* init EFAP for custom LS setup */ IF ( output_config == IVAS_AUDIO_CONFIG_LS_CUSTOM ) { +#ifndef IVAS_FLOAT_FIXED IF ( ( error = efap_init_data( &( st_ivas->hEFAPdata ), st_ivas->hLsSetupCustom->ls_azimuth, st_ivas->hLsSetupCustom->ls_elevation, st_ivas->hLsSetupCustom->num_spk, EFAP_MODE_EFAP ) ) != IVAS_ERR_OK ) +#else + IF( ( error = efap_init_data_fixed( &( st_ivas->hEFAPdata ), st_ivas->hLsSetupCustom->ls_azimuth, st_ivas->hLsSetupCustom->ls_elevation, st_ivas->hLsSetupCustom->num_spk, EFAP_MODE_EFAP ) ) != IVAS_ERR_OK ) +#endif { return error; } @@ -1480,7 +1493,11 @@ ivas_error ivas_init_decoder( /* init EFAP for custom LS setup */ IF ( output_config == IVAS_AUDIO_CONFIG_LS_CUSTOM ) { +#ifndef IVAS_FLOAT_FIXED IF ( ( error = efap_init_data( &( st_ivas->hEFAPdata ), st_ivas->hLsSetupCustom->ls_azimuth, st_ivas->hLsSetupCustom->ls_elevation, st_ivas->hLsSetupCustom->num_spk, EFAP_MODE_EFAP ) ) != IVAS_ERR_OK ) +#else + IF ( ( error = efap_init_data_fixed( &( st_ivas->hEFAPdata ), st_ivas->hLsSetupCustom->ls_azimuth, st_ivas->hLsSetupCustom->ls_elevation, st_ivas->hLsSetupCustom->num_spk, EFAP_MODE_EFAP ) ) != IVAS_ERR_OK ) +#endif { return error; } @@ -1700,7 +1717,11 @@ ivas_error ivas_init_decoder( { IF ( st_ivas->renderer_type == RENDERER_BINAURAL_MIXER_CONV_ROOM && st_ivas->ivas_format == MC_FORMAT && ( st_ivas->hDecoderConfig->Opt_Headrotation || st_ivas->hDecoderConfig->Opt_ExternalOrientation ) ) { +#ifndef IVAS_FLOAT_FIXED IF ( ( error = efap_init_data( &( st_ivas->hEFAPdata ), st_ivas->hIntSetup.ls_azimuth, st_ivas->hIntSetup.ls_elevation, st_ivas->hIntSetup.nchan_out_woLFE, EFAP_MODE_EFAP ) ) != IVAS_ERR_OK ) +#else + IF ( ( error = efap_init_data_fixed( &( st_ivas->hEFAPdata ), st_ivas->hIntSetup.ls_azimuth, st_ivas->hIntSetup.ls_elevation, st_ivas->hIntSetup.nchan_out_woLFE, EFAP_MODE_EFAP ) ) != IVAS_ERR_OK ) +#endif { return error; } diff --git a/lib_dec/ivas_ism_param_dec.c b/lib_dec/ivas_ism_param_dec.c index 05aca6651..dda7ee7f9 100644 --- a/lib_dec/ivas_ism_param_dec.c +++ b/lib_dec/ivas_ism_param_dec.c @@ -542,7 +542,11 @@ ivas_error ivas_param_ism_dec_open( output_config == IVAS_AUDIO_CONFIG_MONO || output_config == IVAS_AUDIO_CONFIG_STEREO ) ) { /* Initialize efap handle */ +#ifndef IVAS_FLOAT_FIXED if ( ( error = efap_init_data( &( st_ivas->hEFAPdata ), hOutSetup.ls_azimuth, hOutSetup.ls_elevation, hOutSetup.nchan_out_woLFE, EFAP_MODE_EFAP ) ) != IVAS_ERR_OK ) +#else + IF ( ( error = efap_init_data_fixed( &( st_ivas->hEFAPdata ), hOutSetup.ls_azimuth, hOutSetup.ls_elevation, hOutSetup.nchan_out_woLFE, EFAP_MODE_EFAP ) ) != IVAS_ERR_OK ) +#endif { return error; } diff --git a/lib_dec/ivas_ism_renderer.c b/lib_dec/ivas_ism_renderer.c index 1f51e9f19..a728110f7 100644 --- a/lib_dec/ivas_ism_renderer.c +++ b/lib_dec/ivas_ism_renderer.c @@ -65,7 +65,11 @@ ivas_error ivas_ism_renderer_open( if ( st_ivas->hIntSetup.is_loudspeaker_setup && st_ivas->hIntSetup.ls_azimuth != NULL && st_ivas->hIntSetup.ls_elevation != NULL && st_ivas->hEFAPdata == NULL ) { +#ifndef IVAS_FLOAT_FIXED if ( ( error = efap_init_data( &( st_ivas->hEFAPdata ), st_ivas->hIntSetup.ls_azimuth, st_ivas->hIntSetup.ls_elevation, st_ivas->hIntSetup.nchan_out_woLFE, EFAP_MODE_EFAP ) ) != IVAS_ERR_OK ) +#else + IF ( ( error = efap_init_data_fixed( &( st_ivas->hEFAPdata ), st_ivas->hIntSetup.ls_azimuth, st_ivas->hIntSetup.ls_elevation, st_ivas->hIntSetup.nchan_out_woLFE, EFAP_MODE_EFAP ) ) != IVAS_ERR_OK ) +#endif { return error; } diff --git a/lib_rend/ivas_allrad_dec.c b/lib_rend/ivas_allrad_dec.c index e91b2c306..b176e2d62 100644 --- a/lib_rend/ivas_allrad_dec.c +++ b/lib_rend/ivas_allrad_dec.c @@ -213,7 +213,7 @@ ivas_error ivas_sba_get_hoa_dec_matrix( ELSE IF ( hOutSetup.is_loudspeaker_setup ) { /* init EFIP */ - IF ( ( error = efap_init_data( &( hEFAP ), hOutSetup.ls_azimuth, hOutSetup.ls_elevation, num_spk, EFAP_MODE_EFIP ) ) != IVAS_ERR_OK ) + IF ( ( error = efap_init_data_fixed( &( hEFAP ), hOutSetup.ls_azimuth, hOutSetup.ls_elevation, num_spk, EFAP_MODE_EFIP ) ) != IVAS_ERR_OK ) { return error; } diff --git a/lib_rend/ivas_efap.c b/lib_rend/ivas_efap.c index 6d4ad09eb..7679cc213 100644 --- a/lib_rend/ivas_efap.c +++ b/lib_rend/ivas_efap.c @@ -48,8 +48,13 @@ #define EFAP_MAX_SIZE_TMP_BUFF 30 #define EFAP_MAX_GHOST_LS 5 /* Maximum number of ghost Loudspeakers, for memory allocation purpose */ -#define POLY_THRESH 1e-4f +#ifndef IVAS_FLOAT_FIXED +#define POLY_THRESH 1e-4f +#else +#define POLY_THRESH 53687 //Q29 +#endif #ifdef IVAS_FLOAT_FIXED +#define Q22_90_DEG 377487360 #define Q22_180_DEG 754974720 #define Q22_360_DEG 1509949440 #endif @@ -62,21 +67,45 @@ static ivas_error poly_init( EFAP *efap, const int16_t efip_flag ); static ivas_error sphere_triangulation( const int16_t numSpk, EFAP_VERTEX_DATA *vtxData, EFAP_POLYSET_DATA *polyData, float ***dmTranspose, int16_t *numTot, const int16_t efip_flag ); +#ifndef IVAS_FLOAT_FIXED static void initial_polyeder( EFAP_VERTEX_DATA *vtxData, EFAP_LS_TRIANGLE *triArray, int16_t *numTri, int16_t *vtxInHull ); +#else +static void initial_polyeder_fixed( EFAP_VERTEX_DATA *vtxData, EFAP_LS_TRIANGLE *triArray, Word16 *numTri, Word16 *vtxInHull ); +#endif +#ifndef IVAS_FLOAT_FIXED static void add_ghost_speakers( EFAP_VERTEX *vertexArray, int16_t *numVtx, const int16_t efip_flag ); +#else +static void add_ghost_speakers_fixed( EFAP_VERTEX *vertexArray, Word16 *numVtx, const Word16 efip_flag ); +#endif static void sort_vertices( const EFAP_VERTEX *vertexArray, const int16_t *numVtx, int16_t *order ); +#ifndef IVAS_FLOAT_FIXED static void add_vertex_to_convex_hull( const EFAP_VERTEX_DATA *vtxData, const int16_t vtxIdx, int16_t *vtxInHull, EFAP_LS_TRIANGLE *triArray, int16_t *szTri ); +#else +static void add_vertex_to_convex_hull_fixed( const EFAP_VERTEX_DATA *vtxData, const Word16 vtxIdx, Word16 *vtxInHull, EFAP_LS_TRIANGLE *triArray, Word16 *szTri ); +#endif -static void visible_edges( const EFAP_LS_TRIANGLE *triArray, const int16_t *visible, const int16_t numSurface, int16_t *numEdges, int16_t *edges ); +static void visible_edges( const EFAP_LS_TRIANGLE *triArray, const Word16 *visible, const Word16 numSurface, Word16 *numEdges, Word16 *edges ); +#ifndef IVAS_FLOAT_FIXED static void flip_plane( const EFAP_VERTEX *vtxArray, int16_t *surface, const float centroid[3] ); +#else +static void flip_plane_fixed( const EFAP_VERTEX *vtxArray, Word16 *surface, const Word32 centroid[3] ); +#endif +#ifndef IVAS_FLOAT_FIXED static void remap_ghosts( EFAP_VERTEX *vtxArray, EFAP_LS_TRIANGLE *triArray, int16_t numSpk, int16_t *numVertex, int16_t numTri, float **downmixMatrix ); +#else +static void remap_ghosts_fixed( EFAP_VERTEX *vtxArray, EFAP_LS_TRIANGLE *triArray, Word16 numSpk, Word16 *numVertex, Word16 numTri, float **downmixMatrix ); +#endif +#ifndef IVAS_FLOAT_FIXED static void vertex_init( const float *aziSpk, const float *eleSpk, EFAP_VERTEX_DATA *efapVtxData ); +#else +static void vertex_init_fixed( const Word32 *aziSpk, const Word32 *eleSpk, EFAP_VERTEX_DATA *efapVtxData ); +#endif static void efap_panning( const float azi, const float ele, const EFAP_POLYSET_DATA *polyData, float *bufferL ); #ifdef IVAS_FLOAT_FIXED @@ -95,11 +124,19 @@ static Word32 get_tri_gain_fixed( const Word32 A[2], const Word32 B[2], const Wo * EFAP Utils *-----------------------------------------------------------------------*/ +#ifndef IVAS_FLOAT_FIXED static void add_vertex( EFAP_VERTEX *vtxArray, const float azi, const float ele, const int16_t pos, const EFAP_VTX_DMX_TYPE ); +#else +static void add_vertex_fixed( EFAP_VERTEX *vtxArray, const Word32 azi, const Word32 ele, const Word16 pos, const EFAP_VTX_DMX_TYPE ); +#endif static void efap_sort_s( int16_t *x, int16_t *idx, const int16_t len ); +#ifndef IVAS_FLOAT_FIXED static float vertex_distance( const EFAP_VERTEX *vtxArray, const EFAP_LS_TRIANGLE tri, const int16_t vtxIdx ); +#else +static Word32 vertex_distance_fixed( const EFAP_VERTEX *vtxArray, const EFAP_LS_TRIANGLE tri, const Word16 vtxIdx ); +#endif static float point_plane_distance( const float P1[3], const float P2[3], const float P3[3], const float X[3] ); #ifdef IVAS_FLOAT_FIXED @@ -118,22 +155,28 @@ static int16_t find_int_in_tri( const EFAP_LS_TRIANGLE *tri, const int16_t n, co static void remove_vertex( EFAP_VERTEX *vtxArray, const int16_t idx, const int16_t L ); static int16_t get_neighbours( const EFAP_LS_TRIANGLE *triArray, const int16_t vtxIdx, const int16_t numTri, int16_t *neighbours ); - +#ifndef IVAS_FLOAT_FIXED static void matrix_times_row( float mat[EFAP_MAX_SIZE_TMP_BUFF][EFAP_MAX_SIZE_TMP_BUFF], const float *vec, const int16_t L, float *out ); - +#else +static void matrix_times_row_int( Word32 mat[EFAP_MAX_SIZE_TMP_BUFF][EFAP_MAX_SIZE_TMP_BUFF], const Word32 *vec, const Word16 L, Word32 *out ); +#endif static void tri_to_poly( const EFAP_VERTEX *vtxArray, const EFAP_LS_TRIANGLE *triArray, const int16_t numVtx, const int16_t numTri, int16_t sortedChan[EFAP_MAX_POLY_SET][EFAP_MAX_CHAN_NUM], int16_t *outLengthPS, int16_t outLengthSorted[EFAP_MAX_POLY_SET] ); static int16_t compare_poly( int16_t *old, int16_t lenOld, int16_t *new, int16_t lenNew ); +#ifndef IVAS_FLOAT_FIXED static void sort_channels_vertex( const EFAP_VERTEX *vtxArray, const EFAP_LS_TRIANGLE *triArray, int16_t channels[EFAP_MAX_CHAN_NUM], const int16_t lengthChannels, int16_t idxTri ); - +#else +static void sort_channels_vertex_fixed( const EFAP_VERTEX *vtxArray, const EFAP_LS_TRIANGLE *triArray, Word16 channels[EFAP_MAX_CHAN_NUM], const Word16 lengthChannels, Word16 idxTri ); +#endif +#ifndef IVAS_FLOAT_FIXED static float efap_fmodf( const float x, const float y ); - +#else static Word32 efap_lmodl( const Word32 x, const Word32 y ); - +#endif static int16_t get_poly_num( const float P[2], const EFAP_POLYSET_DATA *polyData ); #ifdef IVAS_FLOAT_FIXED -static int16_t get_poly_num_fixed( const Word32 P[2], const EFAP_POLYSET_DATA *polyData ); +static Word16 get_poly_num_fixed( const Word32 P[2], const EFAP_POLYSET_DATA *polyData ); #endif static int16_t in_poly( const float P[2], const EFAP_POLYSET poly ); #ifdef IVAS_FLOAT_FIXED @@ -157,6 +200,7 @@ static void sph2cart_fixed( const Word32 azi, const Word32 ele, Word32 *pos ); * Wrap the internal functions to initialize the EFAP data structure *-------------------------------------------------------------------------*/ +#ifndef IVAS_FLOAT_FIXED ivas_error efap_init_data( EFAP_HANDLE *hEFAPdata, /* i/o: handle for EFAP data structure that will be initialized */ const float *speaker_node_azi_deg, /* i : vector of speaker node azimuths (positive left) */ @@ -241,7 +285,103 @@ ivas_error efap_init_data( *hEFAPdata = efap; return error; } +#else +/*-------------------------------------------------------------------------* + * efap_init_data_fixed() + * + * Wrap the internal functions to initialize the EFAP data structure + *-------------------------------------------------------------------------*/ + +ivas_error efap_init_data_fixed( + EFAP_HANDLE *hEFAPdata, /* i/o: handle for EFAP data structure that will be initialized */ + const float *speaker_node_azi_deg, /* i : vector of speaker node azimuths (positive left) */ + const float *speaker_node_ele_deg, /* i : vector of speaker node elevations (positive up) */ + const Word16 num_speaker_nodes, /* i : number of speaker nodes in the set */ + const Word16 efap_mode /* i : indicates whether EFAP or EFIP is used */ +) +{ + /* Handle instance declaration */ + EFAP *efap; + ivas_error error; + + error = IVAS_ERR_OK; + + /* Basic init checks */ + IF( !speaker_node_azi_deg || !speaker_node_ele_deg ) + { + hEFAPdata = NULL; + return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "EFAP requires arrays of speaker azimuths and elevations" ); + } + /*-----------------------------------------------------------------* + * Allocate memory + *-----------------------------------------------------------------*/ + + /* Memory Allocations for efap */ + IF( ( efap = (EFAP *) malloc( sizeof( EFAP ) ) ) == NULL ) + { + return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for EFAP handle\n" ) ); + } + + /* Memory Allocation and update for aziSpk & eleSpk arrays*/ + IF( ( efap->aziSpk = (Word32 *) malloc( num_speaker_nodes * sizeof( Word32 ) ) ) == NULL ) + { + return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for EFAP speaker azimuths\n" ) ); + } + IF( ( efap->eleSpk = (Word32 *) malloc( num_speaker_nodes * sizeof( Word32 ) ) ) == NULL ) + { + return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for EFAP speaker elevations\n" ) ); + } + + /* Memory Allocation for vertexArray */ + IF( ( efap->vtxData.vertexArray = (EFAP_VERTEX *) malloc( ( num_speaker_nodes + EFAP_MAX_GHOST_LS ) * sizeof( EFAP_VERTEX ) ) ) == NULL ) + { + return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for EFAP Vertex Array\n" ) ); + } + + /* Memory allocation for the tmp buffer short */ + IF( ( efap->bufferShort = (float *) malloc( num_speaker_nodes * sizeof( float ) ) ) == NULL ) + { + return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for EFAP bufferS\n" ) ); + } + + /*-----------------------------------------------------------------* + * Initialize values + *-----------------------------------------------------------------*/ + + efap->numSpk = num_speaker_nodes; + + /* The number of vertex is first set to the number of LS but will evolve further */ + efap->vtxData.numVtx = num_speaker_nodes; + + /* Loudspeaker configuration */ + // mvr2r( speaker_node_azi_deg, efap->aziSpk, num_speaker_nodes ); + // mvr2r( speaker_node_ele_deg, efap->eleSpk, num_speaker_nodes ); + FOR ( int spk_idx = 0; spk_idx < num_speaker_nodes; spk_idx++ ) + { + efap->aziSpk[spk_idx] = (Word32) ( speaker_node_azi_deg[spk_idx] * ( 1 << Q22 ) ); + efap->eleSpk[spk_idx] = (Word32) ( speaker_node_ele_deg[spk_idx] * ( 1 << Q22 ) ); + } + + /* Initialization of the vertex */ + vertex_init_fixed( efap->aziSpk, efap->eleSpk, &efap->vtxData ); + + /* Initialization of polygons and ghost LS */ + IF( ( error = poly_init( efap, efap_mode ) ) != IVAS_ERR_OK ) + { + return error; + } + + /* Memory allocation for the tmp buffer long */ + IF( ( efap->bufferLong = (float *) malloc( efap->vtxData.numVtx * sizeof( float ) ) ) == NULL ) + { + return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for EFAP bufferL\n" ) ); + } + + *hEFAPdata = efap; + return error; +} +#endif /*-------------------------------------------------------------------------* * efap_determine_gains() @@ -394,7 +534,7 @@ void efap_determine_gains_fixed( /* Copy gains to output */ mvr2r( hEFAPdata->bufferShort, gain_temp, hEFAPdata->numSpk ); - FOR( i = 0; i < MAX_OUTPUT_CHANNELS; i++ ) + FOR( i = 0; i < hEFAPdata->numSpk; i++ ) { gains[i] = (Word32) ( gain_temp[i] * ( 1 << Q28 ) ); } @@ -489,22 +629,27 @@ static ivas_error poly_init( /* Safety Check */ assert( efap != NULL && "EFAP: efap == NULL" ); - for ( n = 0; n < EFAP_MAX_POLY_SET; n++ ) + FOR ( n = 0; n < EFAP_MAX_POLY_SET; n++ ) { set_s( sortedChan[n], 0, EFAP_MAX_CHAN_NUM ); } /* Computing the different ghost vertex, the downmix matrix and the triangle array */ - if ( ( error = sphere_triangulation( efap->numSpk, &efap->vtxData, &efap->polyData, &efap->dmTranspose, &efap->numTot, efip_flag ) ) != IVAS_ERR_OK ) + IF ( ( error = sphere_triangulation( efap->numSpk, &efap->vtxData, &efap->polyData, &efap->dmTranspose, &efap->numTot, efip_flag ) ) != IVAS_ERR_OK ) { return error; } /* set isNaN for ghost loudspeakers */ - for ( n = 0; n < efap->vtxData.numVtx; ++n ) + FOR ( n = 0; n < efap->vtxData.numVtx; ++n ) { - if ( efap->vtxData.vertexArray[n].ele > 90.0 - 1e-6 || +#ifndef IVAS_FLOAT_FIXED + IF ( efap->vtxData.vertexArray[n].ele > 90.0 - 1e-6 || efap->vtxData.vertexArray[n].ele < 1e-6 - 90.0 ) +#else + IF ( efap->vtxData.vertexArray[n].ele > Q22_90_DEG - 4 || + efap->vtxData.vertexArray[n].ele < 4 - Q22_90_DEG ) +#endif { efap->vtxData.vertexArray[n].isNaN = 1; } @@ -516,21 +661,16 @@ static ivas_error poly_init( /* Completing the polyData Structure */ finalLength = -1; - for ( n = 0; n < lengthTri2PolyPS; ++n ) + FOR ( n = 0; n < lengthTri2PolyPS; ++n ) { m = finalLength + 1; /* Complete the fields of the polygon */ - for ( j = 0; j < lengthTri2PolySorted[n]; ++j ) + FOR ( j = 0; j < lengthTri2PolySorted[n]; ++j ) { efap->polyData.polysetArray[m].chan[j] = sortedChan[n][j]; -#ifndef IVAS_FLOAT_FIXED efap->polyData.polysetArray[m].polyAzi[j] = efap->vtxData.vertexArray[sortedChan[n][j]].azi; efap->polyData.polysetArray[m].polyEle[j] = efap->vtxData.vertexArray[sortedChan[n][j]].ele; -#else - efap->polyData.polysetArray[m].polyAzi[j] = (Word32) ( efap->vtxData.vertexArray[sortedChan[n][j]].azi * ( 1 << Q22 ) ); - efap->polyData.polysetArray[m].polyEle[j] = (Word32) ( efap->vtxData.vertexArray[sortedChan[n][j]].ele * ( 1 << Q22 ) ); -#endif efap->polyData.polysetArray[m].isNaN[j] = efap->vtxData.vertexArray[sortedChan[n][j]].isNaN; } @@ -548,10 +688,10 @@ static ivas_error poly_init( #ifndef IVAS_FLOAT_FIXED if ( ( tmpMax - tmpMin ) > 180 ) #else - if ( ( tmpMax - tmpMin ) > Q22_180_DEG /*180 in Q22*/ ) + IF ( ( tmpMax - tmpMin ) > Q22_180_DEG /*180 in Q22*/ ) #endif { - for ( j = 0; j < lengthTri2PolySorted[n]; ++j ) + FOR ( j = 0; j < lengthTri2PolySorted[n]; ++j ) { assert( ( m + 2 < EFAP_MAX_POLY_SET ) && "EFAP: maximum polygons exceeded!" ); @@ -611,10 +751,14 @@ static ivas_error sphere_triangulation( set_s( vtxInHull, 0, EFAP_MAX_SIZE_TMP_BUFF ); /* Add Imaginary Speakers */ +#ifndef IVAS_FLOAT_FIXED add_ghost_speakers( vtxData->vertexArray, &vtxData->numVtx, efip_flag ); +#else + add_ghost_speakers_fixed( vtxData->vertexArray, &vtxData->numVtx, efip_flag ); +#endif /* Sort the vertices according to their index */ - if ( ( vtxData->vtxOrder = (int16_t *) malloc( vtxData->numVtx * sizeof( int16_t ) ) ) == NULL ) + IF ( ( vtxData->vtxOrder = (int16_t *) malloc( vtxData->numVtx * sizeof( int16_t ) ) ) == NULL ) { return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for EFAP Vertex Order\n" ) ); } @@ -622,18 +766,26 @@ static ivas_error sphere_triangulation( sort_vertices( vtxData->vertexArray, &vtxData->numVtx, vtxData->vtxOrder ); /* Computing the initial Polyeder */ +#ifndef IVAS_FLOAT_FIXED initial_polyeder( vtxData, polyData->triArray, &polyData->numTri, &vtxInHull[0] ); +#else + initial_polyeder_fixed( vtxData, polyData->triArray, &polyData->numTri, &vtxInHull[0] ); +#endif /* Add the vertex to the convex hull */ - for ( i = 0; i < vtxData->numVtx; ++i ) + FOR ( i = 0; i < vtxData->numVtx; ++i ) { +#ifndef IVAS_FLOAT_FIXED add_vertex_to_convex_hull( vtxData, vtxData->vtxOrder[i], &vtxInHull[0], polyData->triArray, &polyData->numTri ); +#else + add_vertex_to_convex_hull_fixed( vtxData, vtxData->vtxOrder[i], &vtxInHull[0], polyData->triArray, &polyData->numTri ); +#endif } assert( polyData->numTri != 0 && "EFAP: failed to construct convex hull!" ); /* Allocate the DM matrix transpose */ - if ( ( p_dmTranspose = malloc( vtxData->numVtx * sizeof( float * ) ) ) == NULL ) + IF ( ( p_dmTranspose = malloc( vtxData->numVtx * sizeof( float * ) ) ) == NULL ) { return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "EFAP: can not allocate memory for dmTranspose\n" ) ); } @@ -641,9 +793,9 @@ static ivas_error sphere_triangulation( /* Store the value of numVtx to be used for freeing later (numVtx will change after remap_ghosts() ) */ *numTot = vtxData->numVtx; - for ( i = 0; i < vtxData->numVtx; i++ ) + FOR ( i = 0; i < vtxData->numVtx; i++ ) { - if ( ( p_dmTranspose[i] = malloc( numSpk * sizeof( float ) ) ) == NULL ) + IF ( ( p_dmTranspose[i] = malloc( numSpk * sizeof( float ) ) ) == NULL ) { return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "EFAP: can not allocate memory for dmTranspose\n" ) ); } @@ -651,12 +803,16 @@ static ivas_error sphere_triangulation( *dmTranspose = (float **) p_dmTranspose; /* Remap Ghosts */ +#ifndef IVAS_FLOAT_FIXED remap_ghosts( vtxData->vertexArray, polyData->triArray, numSpk, &vtxData->numVtx, polyData->numTri, *dmTranspose ); +#else + remap_ghosts_fixed( vtxData->vertexArray, polyData->triArray, numSpk, &vtxData->numVtx, polyData->numTri, *dmTranspose ); +#endif return IVAS_ERR_OK; } - +#ifndef IVAS_FLOAT_FIXED /*-------------------------------------------------------------------------* * initial_polyeder() * @@ -782,8 +938,136 @@ static void initial_polyeder( return; } +#else +/*-------------------------------------------------------------------------* + * initial_polyeder() + * + * + *-------------------------------------------------------------------------*/ + +static void initial_polyeder_fixed( + EFAP_VERTEX_DATA *vtxData, /* i : Vertex data structure */ + EFAP_LS_TRIANGLE *triArray, /* o : Triangle array structure */ + Word16 *numTri, /* o : Size of triangle array */ + Word16 *vtxInHull /* o : Flag if the vertex was added to the inital simplex */ +) +{ + Word16 i; + Word16 numVtx; + Word16 tmpSurface[3]; + Word16 tetrahedron[4]; + Word32 tmp; + Word32 centroid[3]; + Word32 tmpCross[3]; + Word32 tmp1[3], tmp2[3], tmp3[3]; + + /* Safety check */ + assert( triArray != NULL && "EFAP: triArray==NULL" ); + + /* initialize variables */ + set_s( tmpSurface, -1, 3 ); + set_l( centroid, 0, 3 ); + set_l( tmp1, 0, 3 ); + set_l( tmp2, 0, 3 ); + set_l( tmp3, 0, 3 ); + set_l( tmpCross, 0, 3 ); + + numVtx = vtxData->numVtx; + + /* seed vertices */ + FOR ( i = 0; i < 4; i++ ) + { + tetrahedron[i] = i; + } + + /* 1. attempt to create an edge with nonzero length */ + WHILE ( ( vtxData->vertexArray[tetrahedron[0]].azi == vtxData->vertexArray[tetrahedron[1]].azi ) && + ( vtxData->vertexArray[tetrahedron[0]].ele == vtxData->vertexArray[tetrahedron[1]].ele ) ) + { + tetrahedron[1]++; + assert( tetrahedron[1] < numVtx && "EFAP: convex hull construction failed, vertices are coincident!" ); + } + + /* 2. attempt to create a triangle with nonzero area */ + tmp = 0; + v_sub_fixed( vtxData->vertexArray[tetrahedron[1]].pos, vtxData->vertexArray[tetrahedron[0]].pos, tmp1, 3, 1 ); + WHILE ( tetrahedron[2] < numVtx ) + { + v_sub_fixed( vtxData->vertexArray[tetrahedron[2]].pos, vtxData->vertexArray[tetrahedron[0]].pos, tmp2, 3, 1 ); + efap_crossp_fixed( tmp1, tmp2, tmpCross ); //tmpCross Q29 + FOR ( i = 0; i < 3; i++ ) + { + tmp += Mpy_32_32( tmpCross[i], tmpCross[i] ); //tmp Q27 + } + IF ( L_abs( tmp << 2 ) > Mpy_32_32( ( POLY_THRESH << 1 ), ( POLY_THRESH << 1 ) ) ) /* compare tmp against POLY_THRESH^2 instead of sqrtf(tmp) */ + { + break; + } + tetrahedron[2]++; + } + assert( tetrahedron[2] < numVtx && "EFAP: convex hull construction failed, vertices are colinear!" ); + + /* 3. attempt to create a tetrahedron with nonzero volume */ + tmp = 0; + WHILE ( tetrahedron[3] < numVtx ) + { + v_sub_fixed( vtxData->vertexArray[tetrahedron[3]].pos, vtxData->vertexArray[tetrahedron[0]].pos, tmp3, 3, 1 ); //tmp3 Q30 + tmp = dotp_fixed( tmp3, tmpCross, 3 ); //tmp Q28 + IF ( L_abs( tmp ) > ( POLY_THRESH >> Q1 ) ) + { + break; + } + tetrahedron[3]++; + } + assert( tetrahedron[3] < numVtx && "EFAP: convex hull construction failed, vertices are coplanar!" ); + + /* 4. compute the centroid of the initial tetrahedron */ + FOR ( i = 0; i < 4; i++ ) + { + vtxInHull[tetrahedron[i]] = 1; /* set vertex as added to hull*/ + centroid[0] += vtxData->vertexArray[tetrahedron[i]].pos[0] >> 2; + centroid[1] += vtxData->vertexArray[tetrahedron[i]].pos[1] >> 2; + centroid[2] += vtxData->vertexArray[tetrahedron[i]].pos[2] >> 2; + } + /* Executed below float operation + centroid[0] /= 4; + centroid[1] /= 4; + centroid[2] /= 4; + */ + + /* 5. create and orient planes */ + tmpSurface[0] = tetrahedron[0]; + tmpSurface[1] = tetrahedron[1]; + tmpSurface[2] = tetrahedron[2]; + flip_plane_fixed( vtxData->vertexArray, tmpSurface, centroid ); + mvs2s( tmpSurface, triArray[0].LS, 3 ); + + tmpSurface[0] = tetrahedron[0]; + tmpSurface[1] = tetrahedron[1]; + tmpSurface[2] = tetrahedron[3]; + flip_plane_fixed( vtxData->vertexArray, tmpSurface, centroid ); + mvs2s( tmpSurface, triArray[1].LS, 3 ); + + tmpSurface[0] = tetrahedron[0]; + tmpSurface[1] = tetrahedron[2]; + tmpSurface[2] = tetrahedron[3]; + flip_plane_fixed( vtxData->vertexArray, tmpSurface, centroid ); + mvs2s( tmpSurface, triArray[2].LS, 3 ); + + tmpSurface[0] = tetrahedron[1]; + tmpSurface[1] = tetrahedron[2]; + tmpSurface[2] = tetrahedron[3]; + flip_plane_fixed( vtxData->vertexArray, tmpSurface, centroid ); + mvs2s( tmpSurface, triArray[3].LS, 3 ); + + /* set numTri */ + *numTri = 4; + return; +} +#endif +#ifndef IVAS_FLOAT_FIXED /*-------------------------------------------------------------------------* * add_ghost_speakers() * @@ -839,9 +1123,7 @@ static void add_ghost_speakers( vtxDmxType = EFAP_DMX_AMPLITUDE; } } - add_vertex( vertexArray, 0, 90, numVertex + a, vtxDmxType ); - ++lengthVertGhst; ++a; } @@ -948,36 +1230,199 @@ static void add_ghost_speakers( return; } - - +#else /*-------------------------------------------------------------------------* - * sort_vertices() + * add_ghost_speakers_fixed() * * *-------------------------------------------------------------------------*/ -static void sort_vertices( - const EFAP_VERTEX *vertexArray, /* i : Vertex array */ - const int16_t *numVtx, /* i : Size of vertex array */ - int16_t *order /* o : Original index positions */ +static void add_ghost_speakers_fixed( + EFAP_VERTEX *vertexArray, /* i/o: Vertex array */ + Word16 *numVtx, /* i/o: Size of vertex array */ + const Word16 efip_flag /* i : flag to indicate whether initialization is for EFIP (used for ALLRAD) */ ) { - int16_t tmpIdx[EFAP_MAX_SIZE_TMP_BUFF]; - int16_t i; + Word16 numVertex; + Word16 lengthVertGhst; /* Nb of vertical ghost added */ + Word16 lengthHorGhst; /* Nb of Horizontal Ghost */ + Word16 i, j, k, a; /* Integer for loops */ + Word16 num_new; /* Number of new vertices to add */ + Word32 maxAngle; /* Max azimuth tolerance for extend the LS setup horizontaly */ + Word32 newDiff; /* Angle differences that will help us set the extended LS setup */ + Word32 newAzi; /* New azimuth for the new horizontal LS */ + Word32 ele[EFAP_MAX_SIZE_TMP_BUFF]; + Word32 tmpEle; + Word32 tmpAzi[EFAP_MAX_SIZE_TMP_BUFF]; + Word32 tmpAngleDiff[EFAP_MAX_SIZE_TMP_BUFF]; /* tmp array of angles differences */ + Word32 sectors[EFAP_MAX_SIZE_TMP_BUFF]; /* Help us determine the zone where we should extend LS */ + EFAP_VTX_DMX_TYPE vtxDmxType; - /* Initializing tmpIdx */ - for ( i = 0; i < *numVtx; ++i ) + vtxDmxType = EFAP_DMX_INTENSITY; + numVertex = *numVtx; + maxAngle = 13421773; //(1.f / 160.0f) in Q31 + + /* Extracting Azi and Ele for computation purposes */ + FOR ( i = 0; i < numVertex; ++i ) { - tmpIdx[i] = vertexArray[i].idx; + ele[i] = vertexArray[i].ele; } - /* Sorting indexes */ - efap_sort_s( tmpIdx, order, *numVtx ); - - return; -} - + /* ADD VOG IF NECESSERAY (i.e. if the elevation of the highest LS is < 90 deg) */ + a = 0; + maximum_l( ele, numVertex, &tmpEle ); + lengthVertGhst = 0; + IF ( tmpEle < Q22_90_DEG ) + { + IF ( efip_flag ) + { + IF ( tmpEle > ( Q22_90_DEG >> Q1 ) ) + { + vtxDmxType = EFAP_DMX_NONE; + } + ELSE + { + vtxDmxType = EFAP_DMX_AMPLITUDE; + } + } + add_vertex_fixed( vertexArray, 0, Q22_90_DEG, numVertex + a, vtxDmxType ); + ++lengthVertGhst; + ++a; + } + + /* ADD VOH IF NECESSERAY (i.e. if the elevation of the lowest LS is > -90 deg) */ + minimum_l( ele, numVertex, &tmpEle ); + IF ( tmpEle > -Q22_90_DEG ) + { + IF ( efip_flag ) + { + IF ( tmpEle < -( Q22_90_DEG >> Q1 ) ) + { + vtxDmxType = EFAP_DMX_NONE; + } + ELSE + { + vtxDmxType = EFAP_DMX_AMPLITUDE; + } + } + + add_vertex_fixed( vertexArray, 0, -Q22_90_DEG, numVertex + a, vtxDmxType ); + + ++lengthVertGhst; + ++a; + } + + /* LIST ALL SURROUNDING loudspeakers */ + k = 0; + + FOR ( i = 0; i < numVertex; ++i ) + { + IF ( L_abs( vertexArray[i].ele ) < ( Q22_90_DEG >> Q1 ) ) + { + tmpAzi[k] = vertexArray[i].azi; + ++k; + } + } + + lengthHorGhst = 0; + IF ( k == 0 ) /* no speakers found: add a triangle of ghost speakers */ + { + add_vertex_fixed( vertexArray, 0, 0, numVertex + a, EFAP_DMX_INTENSITY ); + add_vertex_fixed( vertexArray, 120 << Q22, 0, numVertex + a + 1, EFAP_DMX_INTENSITY ); + add_vertex_fixed( vertexArray, 240 << Q22, 0, numVertex + a + 2, EFAP_DMX_INTENSITY ); + a += 3; + lengthHorGhst += 3; + } + ELSE IF ( k == 1 ) /* only one speaker found: add two ghost speakers to complete a triangle */ + { + add_vertex_fixed( vertexArray, tmpAzi[0] + ( 120 << Q22 ), 0, numVertex + a, EFAP_DMX_INTENSITY ); + add_vertex_fixed( vertexArray, tmpAzi[0] + ( 240 << Q22 ), 0, numVertex + a + 1, EFAP_DMX_INTENSITY ); + a += 2; + lengthHorGhst += 2; + } + ELSE /* fill gaps greater than maxAngle */ + { + /* Here, k correspond to the number of LS whose ele is < 45 deg, should be = numVertex */ + sort_l( tmpAzi, k ); + + /* The next lines correspond to angle_diff = [azi(2:end), azi(1) + 360] - azi; in Matlab */ + FOR ( i = 0; i < k - 1; ++i ) + { + tmpAngleDiff[i] = tmpAzi[i + 1] - tmpAzi[i]; + sectors[i] = ceil_fixed( Mpy_32_32( tmpAngleDiff[i], maxAngle ), Q22 ); + + IF ( sectors[i] > ( 1 << Q22 ) ) + { + ++lengthHorGhst; + } + } + tmpAngleDiff[k - 1] = tmpAzi[0] + Q22_360_DEG - tmpAzi[k - 1]; + + sectors[k - 1] = ceil_fixed( Mpy_32_32( tmpAngleDiff[k - 1], maxAngle ), Q22 ); + + IF ( sectors[k - 1] > ( 1 << Q22 ) ) + { + ++lengthHorGhst; + } + + /* Adding new virtual speakers */ + FOR ( i = 0; i < k; ++i ) + { + IF ( sectors[i] > ( 1 << Q22 ) ) + { + newDiff = tmpAngleDiff[i] / sectors[i]; + num_new = (Word16) ( L_sub( sectors[i], ( 1 << Q22 ) ) >> Q22 ); + + FOR ( j = 0; j < num_new; ++j ) + { + newAzi = tmpAzi[i] + ( ( ( j + 1 ) * newDiff ) << Q22 ); + + add_vertex_fixed( vertexArray, newAzi, 0, numVertex + a, EFAP_DMX_INTENSITY ); + ++a; + + IF ( j > 0 ) + { + ++lengthHorGhst; + } + } + } + } + } + *numVtx = numVertex + lengthHorGhst + lengthVertGhst; + + return; +} +#endif + +/*-------------------------------------------------------------------------* + * sort_vertices() + * + * + *-------------------------------------------------------------------------*/ + +static void sort_vertices( + const EFAP_VERTEX *vertexArray, /* i : Vertex array */ + const int16_t *numVtx, /* i : Size of vertex array */ + int16_t *order /* o : Original index positions */ +) +{ + int16_t tmpIdx[EFAP_MAX_SIZE_TMP_BUFF]; + int16_t i; + + /* Initializing tmpIdx */ + for ( i = 0; i < *numVtx; ++i ) + { + tmpIdx[i] = vertexArray[i].idx; + } + + /* Sorting indexes */ + efap_sort_s( tmpIdx, order, *numVtx ); + + return; +} + +#ifndef IVAS_FLOAT_FIXED /*-------------------------------------------------------------------------* * add_vertex_to_convex_hull() * @@ -1072,7 +1517,106 @@ static void add_vertex_to_convex_hull( vtxInHull[vtxIdx] = 1; return; } +#else +/*-------------------------------------------------------------------------* + * add_vertex_to_convex_hull_fixed() + * + * + *-------------------------------------------------------------------------*/ + +static void add_vertex_to_convex_hull_fixed( + const EFAP_VERTEX_DATA *vtxData, /* i : Vertex data structure */ + const Word16 vtxIdx, /* i : Vertex to be added to the hull */ + Word16 *vtxInHull, /* i/o: Array indicating whether the vertex is part of the hull */ + EFAP_LS_TRIANGLE *triArray, /* i/o: Triangle array */ + Word16 *szTri /* i/o: Size of Triangle array */ +) +{ + Word16 i, k, l; + Word16 visible[EFAP_MAX_SIZE_TMP_BUFF]; + Word16 edges[EFAP_MAX_SIZE_TMP_BUFF]; + Word16 numEdges[1]; + Word16 surface[3]; + Word32 numHullVtx; + Word32 centroid[3]; + const Word32 threshold = -67; + Word32 tmpDist; + EFAP_LS_TRIANGLE triArrayNew[EFAP_MAX_POLY_SET]; + + /* If the vertex is already part of the hull, nothing must be done */ + IF ( vtxInHull[vtxIdx] ) + { + return; + } + + /* Compute the centroid of the current convex hull */ + numHullVtx = 0; + set_l( centroid, 0, 3 ); + FOR ( i = 0; i < vtxData->numVtx; i++ ) + { + IF ( vtxInHull[i] ) + { + numHullVtx++; + centroid[0] += vtxData->vertexArray[i].pos[0] >> 4; + centroid[1] += vtxData->vertexArray[i].pos[1] >> 4; + centroid[2] += vtxData->vertexArray[i].pos[2] >> 4; + } + } + // Multiplying with 1 / numHullVtx; + + centroid[0] /= numHullVtx; + centroid[1] /= numHullVtx; + centroid[2] /= numHullVtx; + + centroid[0] <<= 4; + centroid[1] <<= 4; + centroid[2] <<= 4; + + /* Processing */ + k = 0; + l = 0; + + FOR ( i = 0; i < *szTri; ++i ) + { + tmpDist = vertex_distance_fixed( vtxData->vertexArray, triArray[i], vtxIdx ); // Q26 + IF ( tmpDist > threshold ) + { + visible[k] = i; + ++k; + } + ELSE + { + mvs2s( triArray[i].LS, triArrayNew[l].LS, 3 ); + ++l; + } + } + + visible_edges( triArray, visible, k, numEdges, edges ); + + FOR ( i = 0; i < numEdges[0]; i += 2 ) + { + surface[0] = edges[i]; + surface[1] = edges[i + 1]; + surface[2] = vtxIdx; + + flip_plane_fixed( vtxData->vertexArray, surface, centroid ); + + mvs2s( surface, triArrayNew[l].LS, 3 ); + ++l; + } + /* Outputs */ + FOR ( i = 0; i < l; i++ ) + { + mvs2s( triArrayNew[i].LS, triArray[i].LS, 3 ); + } + *szTri = l; + + /* Flag the vertex as added to the hull */ + vtxInHull[vtxIdx] = 1; + return; +} +#endif /*-------------------------------------------------------------------------* * visible_edges() @@ -1082,10 +1626,10 @@ static void add_vertex_to_convex_hull( static void visible_edges( const EFAP_LS_TRIANGLE *triArray, /* i : Triangle array */ - const int16_t *visible, /* i : Visible surface flag */ - const int16_t numSurface, /* i : Number of surfaces */ - int16_t *numEdges, /* i/o: Number of edges */ - int16_t *edges /* i/o: Array of edges */ + const Word16 *visible, /* i : Visible surface flag */ + const Word16 numSurface, /* i : Number of surfaces */ + Word16 *numEdges, /* i/o: Number of edges */ + Word16 *edges /* i/o: Array of edges */ ) { int16_t maxVertex; @@ -1094,37 +1638,52 @@ static void visible_edges( int16_t tmpSurface[4]; int16_t counter[EFAP_MAX_SIZE_TMP_BUFF][EFAP_MAX_SIZE_TMP_BUFF]; int16_t counterTranspose[EFAP_MAX_SIZE_TMP_BUFF][EFAP_MAX_SIZE_TMP_BUFF]; +#ifndef IVAS_FLOAT_FIXED float tmpMax[EFAP_MAX_SIZE_TMP_BUFF]; +#else + Word16 tmpMax[EFAP_MAX_SIZE_TMP_BUFF]; +#endif /* Set counter and counterTranspose to 0 */ - for ( i = 0; i < EFAP_MAX_SIZE_TMP_BUFF; i++ ) + FOR ( i = 0; i < EFAP_MAX_SIZE_TMP_BUFF; i++ ) { set_s( counter[i], 0, EFAP_MAX_SIZE_TMP_BUFF ); set_s( counterTranspose[i], 0, EFAP_MAX_SIZE_TMP_BUFF ); } /* Finding the max vertex */ - for ( i = 0; i < numSurface; ++i ) + FOR ( i = 0; i < numSurface; ++i ) { +#ifndef IVAS_FLOAT_FIXED tmpMax[i] = (float) triArray[visible[i]].LS[0]; - for ( j = 1; j < 3; ++j ) +#else + tmpMax[i] = triArray[visible[i]].LS[0]; +#endif + FOR ( j = 1; j < 3; ++j ) { - if ( tmpMax[i] < triArray[visible[i]].LS[j] ) + IF( tmpMax[i] < triArray[visible[i]].LS[j] ) { +#ifndef IVAS_FLOAT_FIXED tmpMax[i] = (float) triArray[visible[i]].LS[j]; +#else + tmpMax[i] = triArray[visible[i]].LS[j]; +#endif } } } +#ifndef IVAS_FLOAT_FIXED maxVertex = (int16_t) tmpMax[maximum( tmpMax, numSurface, NULL )]; - - for ( i = 0; i < numSurface; ++i ) +#else + maxVertex = tmpMax[maximum_s( tmpMax, numSurface, NULL )]; +#endif + FOR ( i = 0; i < numSurface; ++i ) { tmpSurface[0] = triArray[visible[i]].LS[0]; tmpSurface[1] = triArray[visible[i]].LS[1]; tmpSurface[2] = triArray[visible[i]].LS[2]; tmpSurface[3] = triArray[visible[i]].LS[0]; - for ( j = 0; j < 3; ++j ) + FOR ( j = 0; j < 3; ++j ) { a = tmpSurface[j]; b = tmpSurface[j + 1]; @@ -1133,9 +1692,9 @@ static void visible_edges( } } - for ( i = 0; i < maxVertex + 1; ++i ) + FOR ( i = 0; i < maxVertex + 1; ++i ) { - for ( j = 0; j < maxVertex + 1; ++j ) + FOR ( j = 0; j < maxVertex + 1; ++j ) { counter[i][j] = counterTranspose[i][j] + counterTranspose[j][i]; } @@ -1144,11 +1703,11 @@ static void visible_edges( /* Finding the edges */ k = 0; - for ( a = 0; a < maxVertex; ++a ) + FOR ( a = 0; a < maxVertex; ++a ) { - for ( b = a + 1; b < maxVertex + 1; ++b ) + FOR ( b = a + 1; b < maxVertex + 1; ++b ) { - if ( counter[a][b] == 1 ) + IF ( counter[a][b] == 1 ) { edges[k] = a; edges[k + 1] = b; @@ -1163,7 +1722,7 @@ static void visible_edges( return; } - +#ifndef IVAS_FLOAT_FIXED /*-------------------------------------------------------------------------* * flip_plane() * @@ -1195,8 +1754,40 @@ static void flip_plane( return; } +#else /*-------------------------------------------------------------------------* \ + * flip_plane() \ + * \ + * \ + *-------------------------------------------------------------------------*/ +static void flip_plane_fixed( + const EFAP_VERTEX *vtxArray, /* i : Vertex array */ + Word16 *surface, /* i/o: Surface/vertices to be flipped */ + const Word32 centroid[3] /* i : Centroid of convex hull from which to orient the planes outward */ +) +{ + int16_t tmp; + Word32 dist; + dist = point_plane_distance_fixed( + vtxArray[surface[0]].pos, + vtxArray[surface[1]].pos, + vtxArray[surface[2]].pos, + centroid ); + + IF ( dist > 0 ) + { + /*efap_flipLeftRight( surface, 3 );*/ + tmp = surface[0]; + surface[0] = surface[2]; + surface[2] = tmp; + } + + return; +} +#endif + +#ifndef IVAS_FLOAT_FIXED /*-------------------------------------------------------------------------* * remap_ghosts() * @@ -1228,29 +1819,179 @@ static void remap_ghosts( float tmpDist; const float thresh = 1e-4f; - set_f( tmpVec, 0.0f, EFAP_MAX_SIZE_TMP_BUFF ); - set_f( tmpVec2, 0.0f, EFAP_MAX_SIZE_TMP_BUFF ); + set_f( tmpVec, 0.0f, EFAP_MAX_SIZE_TMP_BUFF ); + set_f( tmpVec2, 0.0f, EFAP_MAX_SIZE_TMP_BUFF ); + + /* Finding unused ghosts (i.e. ghost speakers that aren't used for triangulation */ + for ( g = numVtx - 1; g > numSpk - 1; --g ) + { + /* find(triangle_mat == ghost, 1, 'first') */ + if ( find_int_in_tri( triArray, g, numTri, posFound ) == 0 ) + { + remove_vertex( vtxArray, g, numVtx ); + --numVtx; + for ( i = 0; i < numTri; ++i ) + { + for ( j = 0; j < 3; ++j ) + { + if ( triArray[i].LS[j] > g ) + { + triArray[i].LS[j] = g - 1; + } + } + } + } + else + { + ++numGhst; + } + } + + /* Final number of LS (real + ghosts) */ + numTot = numSpk + numGhst; + + /* Initializing tmpMat as the identity matrix */ + for ( i = 0; i < numTot; ++i ) + { + set_f( tmpMat[i], 0.0f, numTot ); + set_f( tmpNewMat[i], 0.0f, numTot ); + + tmpMat[i][i] = 1; + tmpNewMat[i][i] = 1; + } + + /* Generate initial sound energy distribution matrix */ + for ( i = numSpk; i < numTot; ++i ) + { + tmpL = get_neighbours( triArray, i, numTri, neighbours ); + + /* Initializing the column to 0 */ + for ( j = 0; j < numTot; ++j ) + { + tmpMat[j][i] = 0; + tmpNewMat[j][i] = 0; + } + + /* The neighbours are set to 1.0/tmpL */ + inv_tmpL = 1.f / tmpL; + for ( j = 0; j < tmpL; ++j ) + { + tmpMat[neighbours[j]][i] = inv_tmpL; + tmpNewMat[neighbours[j]][i] = inv_tmpL; + } + } + + /* Redistributing sound energy */ + for ( i = 0; i < numTot; ++i ) + { + for ( j = 0; j < numTot; ++j ) + { + tmpNewMat[i][j] = tmpMat[j][i]; + } + } + + for ( i = numSpk; i < numTot; ++i ) + { + mvr2r( tmpNewMat[i], tmpVec, numTot ); + + tmpDist = sum_f( &tmpVec[numSpk], numTot - numSpk ); + + while ( tmpDist > thresh ) + { + matrix_times_row( tmpMat, tmpVec, numTot, tmpVec2 ); + mvr2r( tmpVec2, tmpVec, numTot ); + set_zero( tmpVec2, numTot ); + tmpDist = sum_f( &tmpVec[numSpk], numTot - numSpk ); + } + mvr2r( tmpVec, tmpNewMat[i], numTot ); + } + + for ( i = 0; i < numSpk; ++i ) + { + /* Applying a sqrt(2) coeff and obtaining the dmMatrix*/ + for ( j = 0; j < numSpk; ++j ) + { + downmixMatrixTranspose[j][i] = sqrtf( tmpNewMat[j][i] ); + } + /* Downmix ghost loudspeakers according to dmxType */ + for ( ; j < numTot; ++j ) + { + switch ( vtxArray[j].dmxType ) + { + case EFAP_DMX_NONE: + downmixMatrixTranspose[j][i] = 0.f; + break; + case EFAP_DMX_AMPLITUDE: + downmixMatrixTranspose[j][i] = tmpNewMat[j][i]; + break; + case EFAP_DMX_INTENSITY: + default: + downmixMatrixTranspose[j][i] = sqrtf( tmpNewMat[j][i] ); + break; + } + } + } + + /* Output */ + *numVertex = numTot; + + return; +} +#else +/*-------------------------------------------------------------------------* + * remap_ghosts_fixed() + * + * + *-------------------------------------------------------------------------*/ + +static void remap_ghosts_fixed( + EFAP_VERTEX *vtxArray, /* i/o: Vertex array */ + EFAP_LS_TRIANGLE *triArray, /* i/o: Triangle array */ + Word16 numSpk, /* i : Number of speakers */ + Word16 *numVertex, /* i/o: Size of vertex array */ + Word16 numTri, /* i : Size of triangle array */ + float **downmixMatrixTranspose /* o : Transpose of downmix matrix */ +) +{ + Word16 numGhst = 0; + Word16 numVtx = *numVertex; + Word16 numTot; + Word16 g; + Word16 i, j; + Word16 tmpL; + Word32 inv_tmpL; + Word16 posFound[2]; + Word16 neighbours[EFAP_MAX_SIZE_TMP_BUFF]; + Word32 tmpVec[EFAP_MAX_SIZE_TMP_BUFF]; + Word32 tmpVec2[EFAP_MAX_SIZE_TMP_BUFF]; + Word32 tmpMat[EFAP_MAX_SIZE_TMP_BUFF][EFAP_MAX_SIZE_TMP_BUFF]; + Word32 tmpNewMat[EFAP_MAX_SIZE_TMP_BUFF][EFAP_MAX_SIZE_TMP_BUFF]; + Word32 tmpDist; + const Word32 thresh = 214748; // 1e-4f in Q31 + + set_l( tmpVec, 0, EFAP_MAX_SIZE_TMP_BUFF ); + set_l( tmpVec2, 0, EFAP_MAX_SIZE_TMP_BUFF ); /* Finding unused ghosts (i.e. ghost speakers that aren't used for triangulation */ - for ( g = numVtx - 1; g > numSpk - 1; --g ) + FOR ( g = numVtx - 1; g > numSpk - 1; --g ) { /* find(triangle_mat == ghost, 1, 'first') */ - if ( find_int_in_tri( triArray, g, numTri, posFound ) == 0 ) + IF ( find_int_in_tri( triArray, g, numTri, posFound ) == 0 ) { remove_vertex( vtxArray, g, numVtx ); --numVtx; - for ( i = 0; i < numTri; ++i ) + FOR ( i = 0; i < numTri; ++i ) { - for ( j = 0; j < 3; ++j ) + FOR ( j = 0; j < 3; ++j ) { - if ( triArray[i].LS[j] > g ) + IF ( triArray[i].LS[j] > g ) { triArray[i].LS[j] = g - 1; } } } } - else + ELSE { ++numGhst; } @@ -1260,30 +2001,30 @@ static void remap_ghosts( numTot = numSpk + numGhst; /* Initializing tmpMat as the identity matrix */ - for ( i = 0; i < numTot; ++i ) + FOR ( i = 0; i < numTot; ++i ) { - set_f( tmpMat[i], 0.0f, numTot ); - set_f( tmpNewMat[i], 0.0f, numTot ); + set_l( tmpMat[i], 0, numTot ); + set_l( tmpNewMat[i], 0, numTot ); - tmpMat[i][i] = 1; - tmpNewMat[i][i] = 1; + tmpMat[i][i] = 0x7fffffff; + tmpNewMat[i][i] = 0x7fffffff; } /* Generate initial sound energy distribution matrix */ - for ( i = numSpk; i < numTot; ++i ) + FOR ( i = numSpk; i < numTot; ++i ) { tmpL = get_neighbours( triArray, i, numTri, neighbours ); /* Initializing the column to 0 */ - for ( j = 0; j < numTot; ++j ) + FOR ( j = 0; j < numTot; ++j ) { tmpMat[j][i] = 0; tmpNewMat[j][i] = 0; } /* The neighbours are set to 1.0/tmpL */ - inv_tmpL = 1.f / tmpL; - for ( j = 0; j < tmpL; ++j ) + inv_tmpL = 0x7fffffff / tmpL; + FOR ( j = 0; j < tmpL; ++j ) { tmpMat[neighbours[j]][i] = inv_tmpL; tmpNewMat[neighbours[j]][i] = inv_tmpL; @@ -1291,51 +2032,90 @@ static void remap_ghosts( } /* Redistributing sound energy */ - for ( i = 0; i < numTot; ++i ) + FOR ( i = 0; i < numTot; ++i ) { - for ( j = 0; j < numTot; ++j ) + FOR ( j = 0; j < numTot; ++j ) { tmpNewMat[i][j] = tmpMat[j][i]; } } - for ( i = numSpk; i < numTot; ++i ) + FOR ( i = numSpk; i < numTot; ++i ) { - mvr2r( tmpNewMat[i], tmpVec, numTot ); + mvl2l( tmpNewMat[i], tmpVec, numTot ); - tmpDist = sum_f( &tmpVec[numSpk], numTot - numSpk ); + tmpDist = sum_l( &tmpVec[numSpk], numTot - numSpk ); - while ( tmpDist > thresh ) + WHILE ( tmpDist > thresh ) { - matrix_times_row( tmpMat, tmpVec, numTot, tmpVec2 ); - mvr2r( tmpVec2, tmpVec, numTot ); - set_zero( tmpVec2, numTot ); - tmpDist = sum_f( &tmpVec[numSpk], numTot - numSpk ); + matrix_times_row_int( tmpMat, tmpVec, numTot, tmpVec2 ); + mvl2l( tmpVec2, tmpVec, numTot ); + set_l( tmpVec2, 0, numTot ); + tmpDist = sum_l( &tmpVec[numSpk], numTot - numSpk ); } - mvr2r( tmpVec, tmpNewMat[i], numTot ); + mvl2l( tmpVec, tmpNewMat[i], numTot ); } - for ( i = 0; i < numSpk; ++i ) + FOR ( i = 0; i < numSpk; ++i ) { /* Applying a sqrt(2) coeff and obtaining the dmMatrix*/ - for ( j = 0; j < numSpk; ++j ) + FOR ( j = 0; j < numSpk; ++j ) { - downmixMatrixTranspose[j][i] = sqrtf( tmpNewMat[j][i] ); + IF ( tmpNewMat[j][i] == 0 ) + { + downmixMatrixTranspose[j][i] = 0; + } + ELSE IF ( tmpNewMat[j][i] == 0x7fffffff ) + { + downmixMatrixTranspose[j][i] = 1; + } + ELSE + { + Word16 exp = 0; + Word32 tmp_sqrt = Sqrt32( tmpNewMat[j][i], &exp ); + tmp_sqrt = L_shl( tmp_sqrt, exp ); + downmixMatrixTranspose[j][i] = ( (float) tmp_sqrt ) / 0x7fffffff; + } } /* Downmix ghost loudspeakers according to dmxType */ - for ( ; j < numTot; ++j ) + FOR ( ; j < numTot; ++j ) { - switch ( vtxArray[j].dmxType ) + SWITCH ( vtxArray[j].dmxType ) { case EFAP_DMX_NONE: downmixMatrixTranspose[j][i] = 0.f; break; case EFAP_DMX_AMPLITUDE: - downmixMatrixTranspose[j][i] = tmpNewMat[j][i]; + IF ( tmpNewMat[j][i] == 0 ) + { + downmixMatrixTranspose[j][i] = 0; + } + ELSE IF ( tmpNewMat[j][i] == 0x7fffffff ) + { + downmixMatrixTranspose[j][i] = 1; + } + ELSE + { + downmixMatrixTranspose[j][i] = ( (float) tmpNewMat[j][i] ) / 0x7fffffff; + } break; case EFAP_DMX_INTENSITY: default: - downmixMatrixTranspose[j][i] = sqrtf( tmpNewMat[j][i] ); + IF ( tmpNewMat[j][i] == 0 ) + { + downmixMatrixTranspose[j][i] = 0; + } + ELSE IF ( tmpNewMat[j][i] == 0x7fffffff ) + { + downmixMatrixTranspose[j][i] = 1; + } + ELSE + { + Word16 exp = 0; + Word32 tmp_sqrt = Sqrt32( tmpNewMat[j][i], &exp ); + tmp_sqrt = L_shl( tmp_sqrt, exp ); + downmixMatrixTranspose[j][i] = ( (float) tmp_sqrt ) / 0x7fffffff; + } break; } } @@ -1346,8 +2126,9 @@ static void remap_ghosts( return; } +#endif - +#ifndef IVAS_FLOAT_FIXED /*-------------------------------------------------------------------------* * vertex_init() * @@ -1370,7 +2151,30 @@ static void vertex_init( return; } +#else +/*-------------------------------------------------------------------------* + * vertex_init_fixed() + * + * Initialize the vertex structures + *-------------------------------------------------------------------------*/ + +static void vertex_init_fixed( + const Word32 *aziSpk, /* i : Azimuths of the LS setup */ + const Word32 *eleSpk, /* i : Elevations of the LS setup */ + EFAP_VERTEX_DATA *efapVtxData /* i/o: Vertex data structure that will be updated */ +) +{ + Word16 i; + + /* Main Processing */ + FOR ( i = 0; i < efapVtxData->numVtx; i++ ) + { + add_vertex_fixed( efapVtxData->vertexArray, aziSpk[i], eleSpk[i], i, EFAP_DMX_INTENSITY ); + } + return; +} +#endif /*-------------------------------------------------------------------------* * efap_panning() @@ -1700,6 +2504,7 @@ static Word32 get_tri_gain_fixed( return gain; // Q18 } #endif +#ifndef IVAS_FLOAT_FIXED /*-------------------------------------------------------------------------* * add_vertex() * @@ -1752,7 +2557,60 @@ static void add_vertex( return; } +#else +/*-------------------------------------------------------------------------* + * add_vertex_fixed() + * + * Add a vertex to the vertex array + *-------------------------------------------------------------------------*/ + +static void add_vertex_fixed( + EFAP_VERTEX *vtxArray, /* i/o: Handle to the vertex array that will be updated */ + const Word32 azi, /* i : Azimuth of the vertex */ + const Word32 ele, /* i : Elevation of the vertex */ + const Word16 pos, /* i : Index in the vtxArray where we want to add the vertex */ + const EFAP_VTX_DMX_TYPE dmxType /* i : downmix type for the vertex */ +) +{ + Word32 idxAziTmp, idxEleTmp; + Word32 tmp; + assert( vtxArray != NULL && "EFAP: vtxArray == NULL" ); + + /* Updating the vertex array */ + + tmp = efap_lmodl( Q22_180_DEG - azi, Q22_360_DEG ); + vtxArray[pos].azi = L_sub( Q22_180_DEG, tmp ); + + tmp = ( ( Q22_180_DEG < ele ) ? Q22_180_DEG : ele ); + vtxArray[pos].ele = ( ( -Q22_180_DEG > tmp ) ? -Q22_180_DEG : tmp ); + + /* Converting spherical coordinates to cartesians, assuming radius = 1 */ + sph2cart_fixed( vtxArray[pos].azi, vtxArray[pos].ele, &vtxArray[pos].pos[0] ); + + /* Computing the index defined by idx = idxAziTmp + 181 * idxEleTmp */ + + /* IdxAziTmp */ + tmp = L_abs( L_sub( Q22_90_DEG, L_abs( vtxArray[pos].azi ) ) ); // Q22 + idxAziTmp = anint_fixed( tmp, Q22 ) >> Q22; + + /* IdxEleTmp */ + tmp = L_abs( vtxArray[pos].ele ); + idxEleTmp = tmp; + idxEleTmp = L_sub( Q22_90_DEG, idxEleTmp ); + + /* Final Idx */ + vtxArray[pos].idx = (Word16) idxAziTmp + 181 * (Word16) ( idxEleTmp >> Q22 ); + + /* Setting the nan flag to 0 */ + vtxArray[pos].isNaN = 0; + + /* Set the default downmix type */ + vtxArray[pos].dmxType = dmxType; + + return; +} +#endif /*-------------------------------------------------------------------------* * efap_sort_s() @@ -1791,7 +2649,7 @@ static void efap_sort_s( return; } - +#ifndef IVAS_FLOAT_FIXED /*-------------------------------------------------------------------------* * vertex_distance() * @@ -1819,6 +2677,35 @@ static float vertex_distance( return point_plane_distance( A, B, C, P ); } +#else +/*-------------------------------------------------------------------------* + * vertex_distance() + * + * Compute the signed distance between a vertex and a hull surface + *-------------------------------------------------------------------------*/ + +static Word32 vertex_distance_fixed( + const EFAP_VERTEX *vtxArray, /* i : The considered vertex */ + const EFAP_LS_TRIANGLE tri, /* i : The considered triangle */ + const Word16 vtxIdx /* i : Index of the considered vertex */ +) +{ + Word32 A[3], B[3], C[3], P[3]; + Word16 i; + + /* Assigning the coordinates to the vector */ + FOR ( i = 0; i < 3; ++i ) + { + A[i] = vtxArray[tri.LS[0]].pos[i]; + B[i] = vtxArray[tri.LS[1]].pos[i]; + C[i] = vtxArray[tri.LS[2]].pos[i]; + + P[i] = vtxArray[vtxIdx].pos[i]; + } + + return point_plane_distance_fixed( A, B, C, P ); +} +#endif /*-------------------------------------------------------------------------* * point_poly_distance() @@ -1858,9 +2745,9 @@ static Word32 point_poly_distance_fixed( { Word32 P1[3], P2[3], P3[3]; - sph2cart_fixed( poly.polyAzi[0] << 1, poly.polyEle[0] << 1, &P1[0] ); - sph2cart_fixed( poly.polyAzi[1] << 1, poly.polyEle[1] << 1, &P2[0] ); - sph2cart_fixed( poly.polyAzi[2] << 1, poly.polyEle[2] << 1, &P3[0] ); + sph2cart_fixed( poly.polyAzi[0], poly.polyEle[0], &P1[0] ); + sph2cart_fixed( poly.polyAzi[1], poly.polyEle[1], &P2[0] ); + sph2cart_fixed( poly.polyAzi[2], poly.polyEle[2], &P3[0] ); return point_plane_distance_fixed( P1, P2, P3, X ); } @@ -1915,11 +2802,11 @@ static float point_plane_distance( * Compute the signed distance between a point and a given plane *-------------------------------------------------------------------------*/ -static Word32 point_plane_distance_fixed( - const Word32 P1[3], /* i : First point of the triangle that defines the planes */ - const Word32 P2[3], /* i : Second point of the triangle */ - const Word32 P3[3], /* i : Third point of the triangle */ - const Word32 X[3] /* i : The point of interest */ +static Word32 point_plane_distance_fixed( // returns output in Q26 + const Word32 P1[3], /* i : First point of the triangle that defines the planes */ + const Word32 P2[3], /* i : Second point of the triangle */ + const Word32 P3[3], /* i : Third point of the triangle */ + const Word32 X[3] /* i : The point of interest */ ) { Word32 tmpCross1[3], tmpCross2[3]; @@ -1941,17 +2828,18 @@ static Word32 point_plane_distance_fixed( v_sub_fixed( P1, P3, tmpCross2, 3, 1 ); /* resultCross = cross(P1-P2,P1-P3) */ - efap_crossp_fixed( tmpCross1, tmpCross2, resultCross ); + efap_crossp_fixed( tmpCross1, tmpCross2, resultCross ); //Q29 /* Dot Product */ - tmpNorm = dotp_fixed( resultCross, resultCross, 3 ); - Word16 exp = 31; - tmpNorm = ISqrt32( tmpNorm << 1, &exp ); // Q29 - v_sub_fixed( X, P1, tmpDot1, 3, 1 ); - v_multc_fixed( resultCross, tmpNorm, tmpDot2, 3 ); - dist = L_shl( dotp_fixed( tmpDot1, tmpDot2, 3 ), 12 + exp ); - - return dist; + tmpNorm = dotp_fixed( resultCross, resultCross, 3 ); //Q27 + Word16 exp = 4; + tmpNorm = ISqrt32( tmpNorm, &exp ); // Q29 + //tmpNorm = L_shl( tmpNorm, exp ); //Q31 + v_sub_fixed( X, P1, tmpDot1, 3, 1 ); //Q30 + v_multc_fixed( resultCross, tmpNorm, tmpDot2, 3 ); //Q29 - exp + dist = dotp_fixed( tmpDot1, tmpDot2, 3 ); //Q28 - exp + + return L_shr( dist, ( 2 - exp ) ); } #endif /*-------------------------------------------------------------------------* @@ -2045,11 +2933,19 @@ static void remove_vertex( /* Shift all vertex of one position, so vtxArray[i] will be vtxArray[i+1] and so on */ for ( i = idx; i < L - 1; ++i ) { +#ifndef IVAS_FLOAT_FIXED add_vertex( vtxArray, vtxArray[i + 1].azi, vtxArray[i + 1].ele, i, EFAP_DMX_INTENSITY ); +#else + add_vertex_fixed( vtxArray, vtxArray[i + 1].azi, vtxArray[i + 1].ele, i, EFAP_DMX_INTENSITY ); +#endif } /* The last vertex is set to 0 */ +#ifndef IVAS_FLOAT_FIXED add_vertex( vtxArray, 0, 0, L - 1, EFAP_DMX_INTENSITY ); +#else + add_vertex_fixed( vtxArray, 0, 0, L - 1, EFAP_DMX_INTENSITY ); +#endif return; } @@ -2123,7 +3019,7 @@ static int16_t get_neighbours( return j; } - +#ifndef IVAS_FLOAT_FIXED /*-------------------------------------------------------------------------* * matrix_times_row() * @@ -2149,7 +3045,33 @@ static void matrix_times_row( return; } +#else +/*-------------------------------------------------------------------------* + * matrix_times_row_int() + * + * Computes the product of a matrix and a row vector + *-------------------------------------------------------------------------*/ + +static void matrix_times_row_int( + Word32 mat[EFAP_MAX_SIZE_TMP_BUFF][EFAP_MAX_SIZE_TMP_BUFF], /* i : The input matrix */ + const Word32 *vec, /* i : The input row vector */ + const Word16 L, /* i : Row length */ + Word32 *out /* o : Output vector */ +) +{ + int16_t i, j; + + FOR ( i = 0; i < L; ++i ) + { + FOR ( j = 0; j < L; ++j ) + { + out[i] += Mpy_32_32( mat[i][j], vec[j] ); + } + } + return; +} +#endif /*-------------------------------------------------------------------------* * tri_to_poly() @@ -2178,23 +3100,39 @@ static void tri_to_poly( int16_t sortedLengths[EFAP_MAX_POLY_SET]; int16_t sortedTri[EFAP_MAX_POLY_SET]; +#ifndef IVAS_FLOAT_FIXED float dist; +#else + Word32 dist; +#endif lenPolySet = 0; /* Sorting the polygons */ - for ( i = 0; i < numTri; ++i ) + FOR ( i = 0; i < numTri; ++i ) { /* search for coplanar vertices and add them to the polygon */ lenPoly = 0; - for ( j = 0; j < numVtx; ++j ) + FOR ( j = 0; j < numVtx; ++j ) { +#ifndef IVAS_FLOAT_FIXED dist = fabsf( point_plane_distance( vtxArray[triArray[i].LS[0]].pos, vtxArray[triArray[i].LS[1]].pos, vtxArray[triArray[i].LS[2]].pos, vtxArray[j].pos ) ); +#else + dist = L_abs( point_plane_distance_fixed( + vtxArray[triArray[i].LS[0]].pos, + vtxArray[triArray[i].LS[1]].pos, + vtxArray[triArray[i].LS[2]].pos, + vtxArray[j].pos ) ); // Q26 +#endif - if ( dist < 1e-3f ) +#ifndef IVAS_FLOAT_FIXED + IF( dist < 1e-3f ) +#else + IF ( dist < 67109 /* 1e-3f in Q26 */ ) +#endif { assert( lenPoly < EFAP_MAX_CHAN_NUM && "EFAP: exceeded max polygon vertices!" ); poly[lenPoly] = j; @@ -2205,21 +3143,21 @@ static void tri_to_poly( /* search existing polygons to determine whether the new one already exists/is a subset or is a superset */ found = 0; replaceIdx = -1; - for ( j = 0; j < lenPolySet; ++j ) + FOR ( j = 0; j < lenPolySet; ++j ) { found = compare_poly( sortedChan[j], sortedLengths[j], poly, lenPoly ); - if ( found > 0 ) + IF ( found > 0 ) { break; } - else if ( found < 0 ) + ELSE IF ( found < 0 ) { replaceIdx = j; } } - if ( found == 0 ) + IF ( found == 0 ) { /* append new poly */ mvs2s( poly, sortedChan[lenPolySet], lenPoly ); @@ -2227,7 +3165,7 @@ static void tri_to_poly( sortedLengths[lenPolySet] = lenPoly; ++lenPolySet; } - else if ( found == -1 ) + ELSE IF ( found == -1 ) { /* replace with superset */ mvs2s( poly, sortedChan[replaceIdx], lenPoly ); @@ -2237,15 +3175,18 @@ static void tri_to_poly( } /* Sorting the vertex */ - for ( i = 0; i < lenPolySet; ++i ) + FOR ( i = 0; i < lenPolySet; ++i ) { +#ifndef IVAS_FLOAT_FIXED sort_channels_vertex( vtxArray, triArray, sortedChan[i], sortedLengths[i], sortedTri[i] ); +#else + sort_channels_vertex_fixed( vtxArray, triArray, sortedChan[i], sortedLengths[i], sortedTri[i] ); +#endif } /* Output */ *outLengthPS = lenPolySet; mvs2s( sortedLengths, outLengthSorted, EFAP_MAX_POLY_SET ); - return; } @@ -2297,7 +3238,7 @@ static int16_t compare_poly( } } - +#ifndef IVAS_FLOAT_FIXED /*-------------------------------------------------------------------------* * sort_channels_vertex() * @@ -2388,8 +3329,109 @@ static void sort_channels_vertex( return; } +#else +/*-------------------------------------------------------------------------* + * sort_channels_vertex() + * + * Sort the channels of a polygon set according to the vertex azimuth + *-------------------------------------------------------------------------*/ + +static void sort_channels_vertex_fixed( + const EFAP_VERTEX *vtxArray, /* i : Vertex array */ + const EFAP_LS_TRIANGLE *triArray, /* i : Triangle array */ + Word16 channels[EFAP_MAX_CHAN_NUM], /* o : Channels array to be modified */ + const Word16 lengthChannels, /* i : Length of the channels array */ + Word16 idxTri /* i : Index of the considered triangle */ +) +{ + Word16 i, j; + + Word32 P1[3], P2[3], P3[3]; + Word32 tmpU[3]; + Word32 U[3], V[3]; + Word32 tmpV1[3], tmpV2[3], tmpV3[3]; + Word32 normU, normV; + Word32 MC[3]; + Word32 tmpP[3], P[3]; + + Word32 x, y; + + Word32 azi[EFAP_MAX_CHAN_NUM]; + Word16 order[EFAP_MAX_CHAN_NUM]; + + Word16 newChannels[EFAP_MAX_CHAN_NUM]; + + + /* Initializing coordinates with the vertices of the considered triangle */ + FOR ( i = 0; i < 3; ++i ) + { + P1[i] = vtxArray[triArray[idxTri].LS[0]].pos[i]; //Q31 + P2[i] = vtxArray[triArray[idxTri].LS[1]].pos[i]; //Q31 + P3[i] = vtxArray[triArray[idxTri].LS[2]].pos[i]; //Q31 + } + + /* First Base Vector */ + v_sub_fixed( P2, P1, tmpU, 3, 1 ); //tmpU Q30 + Word16 exp1 = 2; + normU = ISqrt32( dotp_fixed( tmpU, tmpU, 3 ), &exp1 ); + //normU = L_shl_sat( normU, exp ); //normU Q31 + v_multc_fixed( tmpU, normU, U, 3 ); //U Q30 - exp1 + + /* Second Base Vector */ + v_sub_fixed( P3, P2, tmpV1, 3, 1 ); //tmpV1 Q30 + v_multc_fixed( U, dotp_fixed( U, tmpV1, 3 ), tmpV2, 3 ); // tmpV2 Q28 - exp1 + + FOR ( i = 0; i < 3; i++ ) + { + tmpV2[i] <<= 2; + } + + v_sub_fixed( tmpV1, tmpV2, tmpV3, 3, 0 ); // tmpV3 Q30 + Word16 exp2 = 2; + normV = ISqrt32( dotp_fixed( tmpV3, tmpV3, 3 ), &exp2 ); + v_multc_fixed( tmpV3, normV, V, 3 ); //V Q30 - exp2 + /* Center of the first Triangle */ + FOR ( i = 0; i < 3; ++i ) + { + MC[i] = ( ( ( P1[i] >> 2 ) + ( P2[i] >> 2 ) + ( P3[i] >> 2 ) ) / 3 ) << 2; + } + + /* Sort Vertices */ + FOR ( i = 0; i < lengthChannels; ++i ) + { + FOR ( j = 0; j < 3; ++j ) + { + tmpP[j] = vtxArray[channels[i]].pos[j]; + } + + v_sub_fixed( tmpP, MC, P, 3, 1 ); // P Q30 + + x = dotp_fixed( P, U, 3 ); //x Q29 - exp1 + y = dotp_fixed( P, V, 3 ); //y Q29 - exp2 + + // Executing azi[i] = atan2f( y, x ); + azi[i] = ( (Word32) BASOP_util_atan2( y, x, exp2 - exp1 ) ) << 16; // azi 2Q29 + } + + /* Sorting the azi vec */ + v_sort_ind_fixed( azi, order, lengthChannels ); + + /* Updating the channel array */ + FOR ( i = 0; i < lengthChannels; ++i ) + { + newChannels[i] = channels[order[i]]; + } + + /* return Success */ + mvs2s( newChannels, channels, lengthChannels ); + + return; +} +#endif + +#ifndef IVAS_FLOAT_FIXED /*-------------------------------------------------------------------------* * efap_fmodf() * @@ -2404,7 +3446,7 @@ static float efap_fmodf( float result = fmodf( x, y ); return result >= 0 ? result : result + y; } - +#else /*-------------------------------------------------------------------------* * efap_lmodl() * @@ -2419,6 +3461,7 @@ static Word32 efap_lmodl( Word32 result = x % y; return result >= 0 ? result : result + y; } +#endif /*-------------------------------------------------------------------------* * get_poly_num() @@ -2503,7 +3546,7 @@ static Word16 get_poly_num_fixed( num_poly = 0; - sph2cart_fixed( P[0] << 1, P[1] << 1, &pos[0] ); + sph2cart_fixed( P[0], P[1], &pos[0] ); /* Filter the polygon list with a fast 2d check */ FOR( i = 0; i < polyData->numPoly; ++i ) @@ -2854,28 +3897,28 @@ static void sph2cart( *-------------------------------------------------------------------------*/ static void sph2cart_fixed( - const Word32 azi, /* i : Azimuth in degrees (Q23) */ - const Word32 ele, /* i : Elevation in degrees (Q23) */ + const Word32 azi, /* i : Azimuth in degrees (Q22) */ + const Word32 ele, /* i : Elevation in degrees (Q22) */ Word32 *pos /* o : Cartesian coordinates vector (x, y, z) */ ) { Word16 azi_temp, ele_temp; IF( azi >= 0 ) { - azi_temp = div_l( ( azi >> Q7 ), 360 ); + azi_temp = div_l( ( azi >> Q6 ), 360 ); } ELSE { - azi_temp = div_l( L_negate( azi >> Q7 ), 360 ); + azi_temp = div_l( L_negate( azi >> Q6 ), 360 ); azi_temp = negate( azi_temp ); } IF( ele >= 0 ) { - ele_temp = div_l( ( ele >> Q7 ), 360 ); + ele_temp = div_l( ( ele >> Q6 ), 360 ); } ELSE { - ele_temp = div_l( L_negate( ele >> Q7 ), 360 ); + ele_temp = div_l( L_negate( ele >> Q6 ), 360 ); ele_temp = negate( ele_temp ); } diff --git a/lib_rend/ivas_prot_rend.h b/lib_rend/ivas_prot_rend.h index 62d52cd91..ba6a4ec47 100644 --- a/lib_rend/ivas_prot_rend.h +++ b/lib_rend/ivas_prot_rend.h @@ -181,7 +181,15 @@ ivas_error efap_init_data( const int16_t num_speaker_nodes, /* i : number of speaker nodes in the set */ const int16_t efap_mode /* i : indicates whether EFAP or EFIP is used */ ); - +#ifdef IVAS_FLOAT_FIXED +ivas_error efap_init_data_fixed( + EFAP_HANDLE *hEFAPdata, /* i/o: handle for EFAP data structure that will be initialized */ + const float *speaker_node_azi_deg, /* i : vector of speaker node azimuths (positive left) */ + const float *speaker_node_ele_deg, /* i : vector of speaker node elevations (positive up) */ + const Word16 num_speaker_nodes, /* i : number of speaker nodes in the set */ + const Word16 efap_mode /* i : indicates whether EFAP or EFIP is used */ +); +#endif void efap_free_data( EFAP_HANDLE *hEFAPdata /* i/o: EFAP handle to be freed */ ); diff --git a/lib_rend/ivas_stat_rend.h b/lib_rend/ivas_stat_rend.h index 457564945..5ab433cf8 100644 --- a/lib_rend/ivas_stat_rend.h +++ b/lib_rend/ivas_stat_rend.h @@ -585,6 +585,7 @@ typedef struct ivas_binaural_rendering_conv_module_struct * EFAP structures *----------------------------------------------------------------------------------*/ +#ifndef IVAS_FLOAT_FIXED typedef struct EFAP_VERTEX { float azi; /* azimuth of the loudspeaker */ @@ -595,6 +596,18 @@ typedef struct EFAP_VERTEX EFAP_VTX_DMX_TYPE dmxType; /* virtual speaker downmix type */ } EFAP_VERTEX; +#else +typedef struct EFAP_VERTEX +{ + Word32 azi; /* azimuth of the loudspeaker */ + Word32 ele; /* elevation of the loudspeaker */ + Word32 pos[3]; /* [x y z] cartesian coordinate vector */ + Word16 idx; /* integer, that corresponds to the first index for the LS in the 1D output */ + Word16 isNaN; /* used to indicate if the vertex is a virtual speaker */ + EFAP_VTX_DMX_TYPE dmxType; /* virtual speaker downmix type */ + +} EFAP_VERTEX; +#endif typedef struct EFAP_VERTEX_DATA { @@ -616,9 +629,9 @@ typedef struct EFAP_POLYSET #else typedef struct EFAP_POLYSET { - Word16 chan[EFAP_MAX_CHAN_NUM]; /* An array indicating the loudspeaker index of the polygon vertices */ + Word16 chan[EFAP_MAX_CHAN_NUM]; /* An array indicating the loudspeaker index of the polygon vertices */ Word16 isNaN[EFAP_MAX_CHAN_NUM]; /* Indicates if one of the vertices isNaN */ - Word16 numChan; /* An integer between 0 and EFAP_MAX_CHAN_NUM corresponding to the number of vertices of the polygon */ + Word16 numChan; /* An integer between 0 and EFAP_MAX_CHAN_NUM corresponding to the number of vertices of the polygon */ Word32 polyAzi[EFAP_MAX_CHAN_NUM]; /* An array (same length as "chan"), with the azimuth of the channels */ Word32 polyEle[EFAP_MAX_CHAN_NUM]; /* An array (same length as "chan"), with the elevation of the channels */ @@ -641,9 +654,14 @@ typedef struct EFAP_POLYSET_DATA typedef struct EFAP { - int16_t numSpk; /* Number of loudspeakers */ - float *aziSpk; /* Loudspeaker azimuths */ - float *eleSpk; /* Loudspeaker elevations */ + int16_t numSpk; /* Number of loudspeakers */ +#ifndef IVAS_FLOAT_FIXED + float *aziSpk; /* Loudspeaker azimuths */ + float *eleSpk; /* Loudspeaker elevations */ +#else + Word32 *aziSpk; /* Loudspeaker azimuths */ + Word32 *eleSpk; /* Loudspeaker elevations */ +#endif EFAP_VERTEX_DATA vtxData; /* Vertex Data, contains all the data concerning the vertex */ EFAP_POLYSET_DATA polyData; /* Polygon data */ float **dmTranspose; /* Downmix Matrix used for redistributing the energy of ghosts LS and its transpose */ @@ -1528,8 +1546,6 @@ typedef struct ivas_hrtfs_parambin_struct } HRTFS_PARAMBIN, *HRTFS_PARAMBIN_HANDLE; - - /*----------------------------------------------------------------------------------* * Limiter structure *----------------------------------------------------------------------------------*/ diff --git a/lib_rend/lib_rend.c b/lib_rend/lib_rend.c index aa1a4f638..2871022ff 100644 --- a/lib_rend/lib_rend.c +++ b/lib_rend/lib_rend.c @@ -953,7 +953,11 @@ static ivas_error initEfap( if ( outConfig == IVAS_AUDIO_CONFIG_LS_CUSTOM ) { +#ifndef IVAS_FLOAT_FIXED if ( ( error = efap_init_data( &pEfapWrapper->hEfap, pCustomLsOut->ls_azimuth, pCustomLsOut->ls_elevation, pCustomLsOut->num_spk, EFAP_MODE_EFAP ) ) != IVAS_ERR_OK ) +#else + if ( ( error = efap_init_data_fixed( &pEfapWrapper->hEfap, pCustomLsOut->ls_azimuth, pCustomLsOut->ls_elevation, pCustomLsOut->num_spk, EFAP_MODE_EFAP ) ) != IVAS_ERR_OK ) +#endif { return error; } @@ -975,7 +979,11 @@ static ivas_error initEfap( return error; } +#ifndef IVAS_FLOAT_FIXED if ( ( error = efap_init_data( &pEfapWrapper->hEfap, azimuths, elevations, numNonLfeChannels, EFAP_MODE_EFAP ) ) != IVAS_ERR_OK ) +#else + if ( ( error = efap_init_data_fixed( &pEfapWrapper->hEfap, azimuths, elevations, numNonLfeChannels, EFAP_MODE_EFAP ) ) != IVAS_ERR_OK ) +#endif { return error; } -- GitLab