LCOV - code coverage report
Current view: top level - lib_rend - ivas_rotation_fx.c (source / functions) Hit Total Coverage
Test: Coverage on main enc/dec/rend @ 574a190e3c6896c6c4ed10d7f23649709a0c4347 Lines: 1057 1168 90.5 %
Date: 2025-06-27 02:59:36 Functions: 29 30 96.7 %

          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 <assert.h>
      34             : #include <stdint.h>
      35             : #include "basop_util.h"
      36             : #include "ivas_cnst.h"
      37             : #include "options.h"
      38             : #include <math.h>
      39             : #include "cnst.h"
      40             : #include "prot_fx.h"
      41             : #include "ivas_prot_rend_fx.h"
      42             : #include "wmc_auto.h"
      43             : #include <stdio.h>
      44             : #include "ivas_prot_fx.h"
      45             : #include "debug.h"
      46             : #include "ivas_rom_binaural_crend_head.h"
      47             : 
      48             : Word16 square_root16_table[] = { 0, 0x4000, 0x5A82 }; // Q14
      49             : Word16 square_root30_q12[31] = {
      50             :     // Q12
      51             :     0,
      52             :     4096,
      53             :     5793,
      54             :     7094,
      55             :     8192,
      56             :     9159,
      57             :     10033,
      58             :     10837,
      59             :     11585,
      60             :     12288,
      61             :     12953,
      62             :     13585,
      63             :     14189,
      64             :     14768,
      65             :     15326,
      66             :     15864,
      67             :     16384,
      68             :     16888,
      69             :     17378,
      70             :     17854,
      71             :     18318,
      72             :     18770,
      73             :     19212,
      74             :     19644,
      75             :     20066,
      76             :     20480,
      77             :     20886,
      78             :     21283,
      79             :     21674,
      80             :     22058,
      81             :     22435,
      82             : };
      83             : /*-----------------------------------------------------------------------*
      84             :  * Local funtion declarations
      85             :  *-----------------------------------------------------------------------*/
      86             : 
      87             : static ivas_error combine_external_and_head_orientations(
      88             :     IVAS_QUATERNION *headRotQuaternions,
      89             :     IVAS_VECTOR3 *listenerPos,
      90             :     EXTERNAL_ORIENTATION_HANDLE hExtOrientationData,
      91             :     COMBINED_ORIENTATION_HANDLE hCombinedOrientationData );
      92             : static void external_target_interpolation_fx(
      93             :     EXTERNAL_ORIENTATION_HANDLE hExtOrientationData,
      94             :     COMBINED_ORIENTATION_HANDLE hCombinedOrientationData,
      95             :     const Word16 i );
      96             : 
      97             : static bool are_orientations_same_fx( const IVAS_QUATERNION *orientation1, const IVAS_QUATERNION *orientation2 );
      98             : 
      99             : /*-----------------------------------------------------------------------*
     100             :  * ivas_headTrack_open()
     101             :  *
     102             :  * Allocate and initialize Head-Tracking handle
     103             :  *-----------------------------------------------------------------------*/
     104             : 
     105          81 : ivas_error ivas_headTrack_open_fx(
     106             :     HEAD_TRACK_DATA_HANDLE *hHeadTrackData /* o  : head track handle    */
     107             : )
     108             : {
     109             :     Word16 i;
     110             :     ivas_error error;
     111             : 
     112             :     /* Allocate Head-Tracking handle */
     113          81 :     IF( ( *hHeadTrackData = (HEAD_TRACK_DATA_HANDLE) malloc( sizeof( HEAD_TRACK_DATA ) ) ) == NULL )
     114             :     {
     115           0 :         return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for head-tracking memory\n" ) );
     116             :     }
     117             : 
     118             :     /* Initialization */
     119          81 :     ( *hHeadTrackData )->lrSwitchInterpVal_fx = 0; // Q30
     120          81 :     move32();
     121          81 :     ( *hHeadTrackData )->lrSwitchedCurrent = 0;
     122          81 :     move32();
     123          81 :     ( *hHeadTrackData )->lrSwitchedNext = 0;
     124          81 :     move32();
     125          81 :     IF( ( ( *hHeadTrackData )->OrientationTracker = (ivas_orient_trk_state_t *) malloc( sizeof( ivas_orient_trk_state_t ) ) ) == NULL )
     126             :     {
     127           0 :         return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for Orientation tracking" );
     128             :     }
     129             : 
     130          81 :     IF( NE_32( ( error = ivas_orient_trk_Init_fx( ( *hHeadTrackData )->OrientationTracker ) ), IVAS_ERR_OK ) )
     131             :     {
     132           0 :         return error;
     133             :     }
     134             : 
     135             :     /* Initialise Rmat_prev to I, Rmat will be computed later */
     136         324 :     FOR( i = 0; i < 3; i++ )
     137             :     {
     138         243 :         set32_fx( ( *hHeadTrackData )->Rmat_prev_fx[i], 0, 3 );
     139         243 :         ( *hHeadTrackData )->Rmat_prev_fx[i][i] = ONE_IN_Q31; // Q31
     140         243 :         move32();
     141             :     }
     142             : 
     143             : 
     144          81 :     set32_fx( ( *hHeadTrackData )->chEneIIR_fx[0], 0, MASA_FREQUENCY_BANDS );
     145          81 :     set32_fx( ( *hHeadTrackData )->chEneIIR_fx[1], 0, MASA_FREQUENCY_BANDS );
     146          81 :     set32_fx( ( *hHeadTrackData )->procChEneIIR_fx[0], 0, MASA_FREQUENCY_BANDS );
     147          81 :     set32_fx( ( *hHeadTrackData )->procChEneIIR_fx[1], 0, MASA_FREQUENCY_BANDS );
     148             : 
     149          81 :     return IVAS_ERR_OK;
     150             : }
     151             : 
     152             : 
     153             : /*-----------------------------------------------------------------------*
     154             :  * ivas_headTrack_close()
     155             :  *
     156             :  * Deallocate Head-Tracking handle
     157             :  *-----------------------------------------------------------------------*/
     158         604 : void ivas_headTrack_close_fx(
     159             :     HEAD_TRACK_DATA_HANDLE *hHeadTrackData /* i/o: head track handle    */
     160             : )
     161             : {
     162         604 :     test();
     163         604 :     IF( hHeadTrackData == NULL || *hHeadTrackData == NULL )
     164             :     {
     165         523 :         return;
     166             :     }
     167             : 
     168          81 :     IF( ( *hHeadTrackData )->OrientationTracker != NULL )
     169             :     {
     170          81 :         free( ( *hHeadTrackData )->OrientationTracker );
     171          81 :         ( *hHeadTrackData )->OrientationTracker = NULL;
     172             :     }
     173             : 
     174          81 :     free( ( *hHeadTrackData ) );
     175          81 :     *hHeadTrackData = NULL;
     176             : 
     177          81 :     return;
     178             : }
     179             : 
     180             : /*----------------------------------------------------------------------------------
     181             :  * QuatToRotMat()
     182             :  *
     183             :  * Quaternion handling: calculate rotation matrices in real-space and SHD
     184             :  *---------------------------------------------------------------------------------*/
     185             : 
     186     2549413 : void QuatToRotMat_fx(
     187             :     const IVAS_QUATERNION quat, /* i  : quaternion describing the rotation            Qx      */
     188             :     Word32 Rmat[3][3]           /* o  : real-space rotation matrix for this rotation  2*Qx-32 */
     189             : )
     190             : {
     191     2549413 :     Word32 w = quat.w_fx; // Qx
     192     2549413 :     move32();
     193     2549413 :     Word32 x = quat.x_fx;
     194     2549413 :     move32();
     195     2549413 :     Word32 y = quat.y_fx;
     196     2549413 :     move32();
     197     2549413 :     Word32 z = quat.z_fx;
     198     2549413 :     move32();
     199             : 
     200             :     // Adding a guard bit to squared terms since 2*x is not being done in those
     201             :     // statements (R[0][0], R[1][1], R[2][2]). This is done to avoid L_shl.
     202     2549413 :     Word32 ww = L_shr( Mpy_32_32( w, w ), 1 ); // 2 * Qx - 31 - 1 = 2*Qx-32
     203     2549413 :     Word32 xx = L_shr( Mpy_32_32( x, x ), 1 );
     204     2549413 :     Word32 yy = L_shr( Mpy_32_32( y, y ), 1 );
     205     2549413 :     Word32 zz = L_shr( Mpy_32_32( z, z ), 1 );
     206             : 
     207     2549413 :     Word32 wx = Mpy_32_32( w, x ); // 2 * Qx - 31
     208     2549413 :     Word32 wz = Mpy_32_32( w, z );
     209     2549413 :     Word32 wy = Mpy_32_32( w, y );
     210             : 
     211     2549413 :     Word32 xy = Mpy_32_32( x, y );
     212     2549413 :     Word32 xz = Mpy_32_32( x, z );
     213             : 
     214     2549413 :     Word32 yz = Mpy_32_32( y, z );
     215             : 
     216     2549413 :     Rmat[0][0] = L_sub( L_sub( L_add( ww, xx ), yy ), zz ); // 2 * Qx - 31 - 1 = 2*Qx-32
     217     2549413 :     move32();
     218     2549413 :     Rmat[0][1] = L_sub( xy, wz );
     219     2549413 :     move32();
     220     2549413 :     Rmat[0][2] = L_add( xz, wy );
     221     2549413 :     move32();
     222             : 
     223     2549413 :     Rmat[1][0] = L_add( xy, wz ); // 2 * Qx - 32
     224     2549413 :     move32();
     225     2549413 :     Rmat[1][1] = L_sub( L_add( L_sub( ww, xx ), yy ), zz );
     226     2549413 :     move32();
     227     2549413 :     Rmat[1][2] = L_sub( yz, wx );
     228     2549413 :     move32();
     229             : 
     230     2549413 :     Rmat[2][0] = L_sub( xz, wy ); // 2 * Qx - 32
     231     2549413 :     move32();
     232     2549413 :     Rmat[2][1] = L_add( yz, wx );
     233     2549413 :     move32();
     234     2549413 :     Rmat[2][2] = L_add( L_sub( L_sub( ww, xx ), yy ), zz );
     235     2549413 :     move32();
     236             : 
     237     2549413 :     return;
     238             : }
     239             : 
     240             : 
     241           0 : void Euler2Quat_fx(
     242             :     const Word32 yaw,     /* i  : yaw   (x)  Q22                    */
     243             :     const Word32 pitch,   /* i  : pitch (y)  Q22                     */
     244             :     const Word32 roll,    /* i  : roll  (z)  Q22                    */
     245             :     IVAS_QUATERNION *quat /* o  : quaternion describing the rotation Q19 */
     246             : )
     247             : {
     248           0 :     Word16 cr = getCosWord16( extract_l( L_shr_r( roll, 10 ) ) ); // Q14
     249           0 :     Word16 sr = getSinWord16( extract_l( L_shr_r( roll, 10 ) ) );
     250           0 :     Word16 cp = getCosWord16( extract_l( L_shr_r( pitch, 10 ) ) );
     251           0 :     Word16 sp = getSinWord16( extract_l( L_shr_r( pitch, 10 ) ) );
     252           0 :     Word16 cy = getCosWord16( extract_l( L_shr_r( yaw, 10 ) ) );
     253           0 :     Word16 sy = getSinWord16( extract_l( L_shr_r( yaw, 10 ) ) );
     254           0 :     quat->w_fx = L_shr_r( L_add( Mpy_32_16_1( L_mult0( cr, cp ), cy ), Mpy_32_16_1( L_mult0( sr, sp ), sy ) ), 5 ); // Q19
     255           0 :     move32();
     256           0 :     quat->x_fx = L_shr_r( L_sub( Mpy_32_16_1( L_mult0( sr, cp ), cy ), Mpy_32_16_1( L_mult0( cr, sp ), sy ) ), 5 ); // Q19
     257           0 :     move32();
     258           0 :     quat->y_fx = L_shr_r( L_add( Mpy_32_16_1( L_mult0( sr, cp ), sy ), Mpy_32_16_1( L_mult0( cr, sp ), cy ) ), 5 ); // Q19
     259           0 :     move32();
     260           0 :     quat->z_fx = L_shr_r( L_sub( Mpy_32_16_1( L_mult0( cr, cp ), sy ), Mpy_32_16_1( L_mult0( sr, sp ), cy ) ), 5 ); // Q19
     261           0 :     move32();
     262             : 
     263           0 :     return;
     264             : }
     265             : 
     266             : 
     267             : /*-------------------------------------------------------------------------
     268             :  * deg2rad()
     269             :  *
     270             :  * Converts degrees to normalized radians
     271             :  *------------------------------------------------------------------------*/
     272         396 : Word32 deg2rad_fx(
     273             :     Word32 degrees // Q23
     274             : )
     275             : {
     276         396 :     WHILE( GE_32( degrees, DEGREE_180 ) )
     277             :     {
     278           0 :         degrees = L_sub( degrees, DEGREE_360 );
     279             :     }
     280         396 :     WHILE( LE_32( degrees, -DEGREE_180 ) )
     281             :     {
     282           0 :         degrees = L_add( degrees, DEGREE_360 );
     283             :     }
     284             : 
     285         396 :     return Mpy_32_32( PI_OVER_180_FX, degrees ); // Q23
     286             : }
     287             : 
     288             : /*-------------------------------------------------------------------------
     289             :  * rad2deg()
     290             :  *
     291             :  * Converts normalized radians to degrees
     292             :  *------------------------------------------------------------------------*/
     293         396 : Word32 rad2deg_fx(
     294             :     Word32 radians // Q13
     295             : )
     296             : {
     297             : 
     298         399 :     WHILE( GE_32( radians, EVS_PI_FX ) )
     299             :     {
     300           3 :         radians = L_sub( radians, EVS_PI_FX );
     301             :     }
     302         396 :     WHILE( LE_32( radians, -EVS_PI_FX ) )
     303             :     {
     304           0 :         radians = L_add( radians, EVS_PI_FX );
     305             :     }
     306             : 
     307         396 :     return W_extract_l( W_mult0_32_32( radians, _180_OVER_PI_FX ) ); // Q23
     308             : }
     309             : /*-------------------------------------------------------------------------
     310             :  * rotateAziEle()
     311             :  *
     312             :  * Apply rotation to direction parameters azimuth and elevation
     313             :  *------------------------------------------------------------------------*/
     314             : 
     315     3472585 : void rotateAziEle_fx(
     316             :     Word16 azi_in,        /* i  : output elevation                           Q0 */
     317             :     Word16 ele_in,        /* i  : input elevation                            Q0 */
     318             :     Word16 *azi,          /* o  : rotated azimuth                            Q0 */
     319             :     Word16 *ele,          /* o  : rotated elevation                          Q0 */
     320             :     Word32 Rmat_fx[3][3], /* i  : real-space rotation matrix                 Q30*/
     321             :     const Word16 isPlanar /* i  : is rotation planar and elevation meaningless? Q0*/
     322             : )
     323             : {
     324             :     Word16 n;
     325             :     Word32 dv_fx[3], dv_r_fx[3];
     326             :     Word32 w_fx;
     327             :     Word32 temp;
     328             :     Word16 temp_16;
     329             :     Word32 y, x, sqrt_fx;
     330             :     Word16 radian, angle;
     331             :     /*Conversion spherical to cartesian coordinates*/
     332     3472585 :     IF( GT_16( abs_s( azi_in ), 180 ) )
     333             :     {
     334     1061852 :         IF( azi_in > 0 )
     335             :         {
     336     1061852 :             azi_in = sub( azi_in, 360 );
     337             :         }
     338             :         ELSE
     339             :         {
     340           0 :             azi_in = add( azi_in, 360 );
     341             :         }
     342             :     }
     343     3472585 :     temp_16 = ele_in;
     344     3472585 :     move16();
     345     3472585 :     w_fx = cosine_table_Q31[abs_s( temp_16 )]; // Q31
     346     3472585 :     move32();
     347     3472585 :     temp_16 = azi_in;
     348     3472585 :     move16();
     349     3472585 :     dv_fx[0] = Mpy_32_32( w_fx, cosine_table_Q31[abs_s( temp_16 )] ); // Q31
     350     3472585 :     move32();
     351     3472585 :     temp_16 = azi_in;
     352     3472585 :     move16();
     353     3472585 :     dv_fx[1] = Mpy_32_32( w_fx, sine_table_Q31[add( temp_16, 180 )] ); // Q31
     354     3472585 :     move32();
     355     3472585 :     temp_16 = ele_in;
     356     3472585 :     move16();
     357     3472585 :     dv_fx[2] = sine_table_Q31[add( temp_16, 180 )]; // Q31
     358     3472585 :     move32();
     359             :     /*Rotation mtx multiplication*/
     360    13890340 :     FOR( n = 0; n < 3; n++ )
     361             :     {
     362    10417755 :         temp = L_add( Mpy_32_32( dv_fx[0], Rmat_fx[n][0] ), Mpy_32_32( dv_fx[1], Rmat_fx[n][1] ) ); // Q30
     363    10417755 :         dv_r_fx[n] = L_add( Mpy_32_32( dv_fx[2], Rmat_fx[n][2] ), temp );                           // Q30
     364    10417755 :         move32();
     365             :     }
     366             : 
     367             :     /*Conversion cartesian to spherical coordinates*/
     368     3472585 :     y = dv_r_fx[1];
     369     3472585 :     move32();
     370     3472585 :     x = dv_r_fx[0];
     371     3472585 :     move32();
     372     3472585 :     radian = atan2_fx( L_abs( L_shr( y, 15 ) ), L_abs( L_shr( x, 15 ) ) ); // Q14
     373             : 
     374     3472585 :     if ( y <= 0 )
     375             :     {
     376     1735529 :         radian = negate( radian );
     377             :     }
     378             : 
     379     3472585 :     IF( x < 0 )
     380             :     {
     381     1256302 :         IF( radian < 0 )
     382             :         {
     383      592703 :             angle = negate( add( 180, extract_l( L_shr( Mpy_32_16_1( _180_OVER_PI_Q25, radian ), ( 24 ) ) ) ) ); // Q0
     384             :         }
     385             :         ELSE
     386             :         {
     387      663599 :             angle = sub( 180, extract_l( L_shr( Mpy_32_16_1( _180_OVER_PI_Q25, radian ), ( 24 ) ) ) ); // Q0
     388             :         }
     389     1256302 :         IF( radian == 0 )
     390             :         {
     391         844 :             if ( y < 0 )
     392             :             {
     393           3 :                 angle = negate( angle );
     394             :             }
     395             :         }
     396             :     }
     397             :     ELSE
     398             :     {
     399     2216283 :         angle = extract_l( L_shr( Mpy_32_16_1( _180_OVER_PI_Q25, radian ), 24 ) ); // Q0
     400             :     }
     401             : 
     402     3472585 :     *azi = s_max( -180, s_min( 180, angle ) );
     403     3472585 :     move16();
     404             : 
     405     3472585 :     IF( isPlanar == 0 )
     406             :     {
     407     3272585 :         sqrt_fx = L_Frac_sqrtQ31( L_add( Mpy_32_32( dv_r_fx[0], dv_r_fx[0] ), Mpy_32_32( dv_r_fx[1], dv_r_fx[1] ) ) );
     408     3272585 :         y = dv_r_fx[2];
     409     3272585 :         move32();
     410     3272585 :         x = sqrt_fx;
     411     3272585 :         move32();
     412     3272585 :         radian = atan2_fx( L_abs( L_shr( y, 15 ) ), L_abs( L_shr( x, 15 ) ) ); // Q14
     413     3272585 :         if ( y <= 0 )
     414             :         {
     415     1908914 :             radian = negate( radian );
     416             :         }
     417             : 
     418     3272585 :         IF( x < 0 )
     419             :         {
     420           0 :             IF( radian < 0 )
     421             :             {
     422           0 :                 angle = negate( add( 180, extract_l( L_shr( Mpy_32_16_r( _180_OVER_PI_Q25, radian ), ( 24 ) ) ) ) ); // Q0
     423             :             }
     424             :             ELSE
     425             :             {
     426           0 :                 angle = sub( 180, extract_l( L_shr( Mpy_32_16_r( _180_OVER_PI_Q25, radian ), ( 24 ) ) ) ); // Q0
     427             :             }
     428           0 :             IF( radian == 0 )
     429             :             {
     430           0 :                 if ( y < 0 )
     431             :                 {
     432           0 :                     angle = negate( angle );
     433             :                 }
     434             :             }
     435             :         }
     436             :         ELSE
     437             :         {
     438     3272585 :             angle = extract_l( L_shr( Mpy_32_16_r( _180_OVER_PI_Q25, radian ), ( 24 ) ) ); // Q0
     439             :         }
     440             : 
     441     3272585 :         *ele = s_max( -90, s_min( 90, angle ) ); // Q0
     442     3272585 :         move16();
     443             :     }
     444             :     ELSE
     445             :     {
     446      200000 :         *ele = 0;
     447      200000 :         move16();
     448             :     }
     449             : 
     450     3472585 :     return;
     451             : }
     452             : 
     453             : /*-------------------------------------------------------------------------
     454             :  * rotateAziEle_fx_frac_az_el()
     455             :  *
     456             :  * Apply rotation to direction parameters azimuth and elevation
     457             :  *------------------------------------------------------------------------*/
     458      150000 : void rotateAziEle_fx_frac_az_el(
     459             :     Word32 azi_in,        /* i  : output elevation                          Q22 */
     460             :     Word32 ele_in,        /* i  : input elevation                           Q22 */
     461             :     Word32 *azi,          /* o  : rotated azimuth                           Q22 */
     462             :     Word32 *ele,          /* o  : rotated elevation                         Q22 */
     463             :     Word32 Rmat_fx[3][3], /* i  : real-space rotation matrix                Q30 */
     464             :     const Word16 isPlanar /* i  : is rotation planar and elevation meaningless? Q0*/
     465             : )
     466             : {
     467             :     Word16 n, radian; // temp_16;
     468             :     Word32 dv_fx[3], dv_r_fx[3];
     469             :     Word16 w_fx;
     470             :     Word32 temp;
     471             :     Word32 y, x, sqrt_fx;
     472             :     Word32 angle;
     473             :     Word16 azi_in_q13, ele_in_q13;
     474             :     /*Conversion spherical to cartesian coordinates*/
     475      150000 :     IF( GT_32( L_abs( azi_in ), _180_IN_Q22 ) )
     476             :     {
     477       71744 :         IF( azi_in > 0 )
     478             :         {
     479       71744 :             azi_in = L_sub( azi_in, _360_IN_Q22 );
     480             :         }
     481             :         ELSE
     482             :         {
     483           0 :             azi_in = L_add( azi_in, _360_IN_Q22 );
     484             :         }
     485             :     }
     486      150000 :     azi_in_q13 = extract_l( L_shr( Mpy_32_32( azi_in, PI_OVER_180_FX ), Q9 ) );
     487      150000 :     ele_in_q13 = extract_l( L_shr( Mpy_32_32( ele_in, PI_OVER_180_FX ), Q9 ) );
     488      150000 :     w_fx = getCosWord16( ele_in_q13 );                     // Q14
     489      150000 :     dv_fx[0] = L_mult( w_fx, getCosWord16( azi_in_q13 ) ); // Q28
     490      150000 :     move32();
     491      150000 :     IF( EQ_32( dv_fx[0], ONE_IN_Q29 ) )
     492             :     {
     493        2414 :         move32();
     494        2414 :         dv_fx[0] = ONE_IN_Q31;
     495             :     }
     496             :     ELSE
     497             :     {
     498      147586 :         dv_fx[0] = L_shl( dv_fx[0], 2 );
     499      147586 :         move32();
     500             :     }
     501      150000 :     dv_fx[1] = L_mult( w_fx, getSinWord16( azi_in_q13 ) ); // Q28
     502      150000 :     move32();
     503      150000 :     IF( EQ_32( dv_fx[1], ONE_IN_Q30 ) )
     504             :     {
     505           0 :         move32();
     506           0 :         dv_fx[1] = ONE_IN_Q31;
     507             :     }
     508             :     ELSE
     509             :     {
     510      150000 :         dv_fx[1] = L_shl( dv_fx[1], 1 );
     511      150000 :         move32();
     512             :     }
     513      150000 :     dv_fx[2] = L_deposit_l( getSinWord16( ele_in_q13 ) ); // Q14
     514      150000 :     move32();
     515      150000 :     IF( EQ_32( dv_fx[2], ONE_IN_Q15 ) )
     516             :     {
     517           0 :         move32();
     518           0 :         dv_fx[2] = ONE_IN_Q31;
     519             :     }
     520             :     ELSE
     521             :     {
     522      150000 :         dv_fx[2] = L_shl( dv_fx[2], 16 );
     523      150000 :         move32();
     524             :     }
     525             : 
     526             :     /*Rotation mtx multiplication*/
     527      600000 :     FOR( n = 0; n < 3; n++ )
     528             :     {
     529      450000 :         temp = L_add( Mpy_32_32( dv_fx[0], Rmat_fx[n][0] ), Mpy_32_32( dv_fx[1], Rmat_fx[n][1] ) );
     530      450000 :         dv_r_fx[n] = L_add( Mpy_32_32( dv_fx[2], Rmat_fx[n][2] ), temp ); // Q30
     531      450000 :         move32();
     532             :     }
     533             : 
     534             :     /*Conversion cartesian to spherical coordinates*/
     535      150000 :     move32();
     536      150000 :     y = dv_r_fx[1];
     537      150000 :     move32();
     538      150000 :     x = dv_r_fx[0];
     539      150000 :     radian = BASOP_util_atan2( y, x, 0 ); // Q13
     540             : 
     541      150000 :     angle = L_shr( Mpy_32_16_1( _180_OVER_PI_Q25, radian ), 1 ); // Q22
     542             : 
     543             : 
     544      150000 :     *azi = L_max( L_shl( -180, 22 ), L_min( L_shl( 180, 22 ), angle ) ); // Q22
     545      150000 :     move32();
     546      150000 :     *azi = L_shl( L_shr( L_add( *azi, ONE_IN_Q21 ), Q22 ), Q22 );
     547      150000 :     move32();
     548      150000 :     if ( LT_32( L_abs( *azi ), ONE_IN_Q22 ) )
     549             :     {
     550         532 :         move32();
     551         532 :         *azi = 0;
     552             :     }
     553      150000 :     IF( isPlanar == 0 )
     554             :     {
     555      150000 :         sqrt_fx = L_Frac_sqrtQ31( L_add( Mpy_32_32( dv_r_fx[0], dv_r_fx[0] ), Mpy_32_32( dv_r_fx[1], dv_r_fx[1] ) ) );
     556      150000 :         y = dv_r_fx[2];
     557      150000 :         move32();
     558      150000 :         x = sqrt_fx;
     559      150000 :         move32();
     560      150000 :         radian = BASOP_util_atan2( y, x, 0 ); // Q13
     561             : 
     562      150000 :         angle = L_shr( Mpy_32_16_1( _180_OVER_PI_Q25, radian ), 1 ); // Q22
     563             : 
     564             : 
     565      150000 :         *ele = L_max( L_shl( -90, 22 ), L_min( L_shl( 90, 22 ), angle ) ); // Q22
     566      150000 :         move32();
     567      150000 :         *ele = L_shl( L_shr( L_add( *ele, ONE_IN_Q21 ), Q22 ), Q22 );
     568      150000 :         move32();
     569      150000 :         if ( LT_32( L_abs( *ele ), ONE_IN_Q22 ) )
     570             :         {
     571       18942 :             *ele = 0;
     572       18942 :             move32();
     573             :         }
     574             :     }
     575             :     ELSE
     576             :     {
     577           0 :         *ele = 0;
     578           0 :         move32();
     579             :     }
     580             : 
     581      150000 :     return;
     582             : }
     583             : 
     584             : /*-------------------------------------------------------------------------
     585             :  * rotateAziEle()
     586             :  *
     587             :  * Apply rotation to direction parameters azimuth and elevation
     588             :  *------------------------------------------------------------------------*/
     589             : 
     590      449760 : void rotateAziEle_fixed(
     591             :     Word16 azi_in,        /* i  : output elevation                         Q0   */
     592             :     Word16 ele_in,        /* i  : input elevation                          Q0   */
     593             :     Word32 *azi,          /* o  : rotated azimuth                          Q22  */
     594             :     Word32 *ele,          /* o  : rotated elevation                        Q22  */
     595             :     Word32 Rmat_fx[3][3], /* i  : real-space rotation matrix               Q30  */
     596             :     const Word16 isPlanar /* i  : is rotation planar and elevation meaningless? Q0*/
     597             : )
     598             : {
     599             :     Word16 n, radian, temp_16;
     600             :     Word32 dv_fx[3], dv_r_fx[3];
     601             :     Word32 w_fx;
     602             :     Word32 temp;
     603             :     Word32 y, x, sqrt_fx;
     604             :     Word32 angle;
     605             :     /*Conversion spherical to cartesian coordinates*/
     606      449760 :     IF( GT_16( abs_s( azi_in ), 180 ) )
     607             :     {
     608           0 :         IF( azi_in > 0 )
     609             :         {
     610           0 :             azi_in = sub( azi_in, 360 );
     611             :         }
     612             :         ELSE
     613             :         {
     614           0 :             azi_in = add( azi_in, 360 );
     615             :         }
     616             :     }
     617      449760 :     temp_16 = ele_in;
     618      449760 :     move16();
     619      449760 :     w_fx = cosine_table_Q31[abs( temp_16 )];
     620      449760 :     move32();
     621      449760 :     temp_16 = azi_in;
     622      449760 :     move16();
     623      449760 :     dv_fx[0] = Mpy_32_32( w_fx, cosine_table_Q31[abs( temp_16 )] );
     624      449760 :     move32();
     625      449760 :     temp_16 = azi_in;
     626      449760 :     move16();
     627      449760 :     dv_fx[1] = Mpy_32_32( w_fx, sine_table_Q31[temp_16 + 180] );
     628      449760 :     move32();
     629      449760 :     temp_16 = ele_in;
     630      449760 :     move16();
     631      449760 :     dv_fx[2] = sine_table_Q31[temp_16 + 180];
     632      449760 :     move32();
     633             :     /*Rotation mtx multiplication*/
     634     1799040 :     FOR( n = 0; n < 3; n++ )
     635             :     {
     636     1349280 :         temp = L_add( Mpy_32_32( dv_fx[0], Rmat_fx[n][0] ), Mpy_32_32( dv_fx[1], Rmat_fx[n][1] ) );
     637     1349280 :         dv_r_fx[n] = L_add( Mpy_32_32( dv_fx[2], Rmat_fx[n][2] ), temp ); // Q30
     638     1349280 :         move32();
     639             :     }
     640             : 
     641             :     /*Conversion cartesian to spherical coordinates*/
     642      449760 :     y = dv_r_fx[1];
     643      449760 :     move32();
     644      449760 :     x = dv_r_fx[0];
     645      449760 :     move32();
     646      449760 :     radian = BASOP_util_atan2( y, x, 0 ); // Q13
     647             : 
     648      449760 :     angle = L_shr( Mpy_32_16_1( _180_OVER_PI_Q25, radian ), 1 ); // Q22
     649             :     /* Handeled roudning off operation*/
     650      449760 :     IF( angle > 0 )
     651             :     {
     652      242804 :         temp = L_add( angle, ONE_IN_Q21 /* 0.5 in Q22*/ );
     653      242804 :         temp_16 = extract_l( L_shr( temp, Q22 ) );
     654      242804 :         angle = L_shl( temp_16, Q22 ); // Q22
     655             :     }
     656             :     ELSE
     657             :     {
     658      206956 :         temp = L_sub( angle, ONE_IN_Q21 /* 0.5 in Q22*/ );
     659      206956 :         temp_16 = add( extract_l( L_shr( temp, Q22 ) ), 1 );
     660      206956 :         angle = L_shl( temp_16, Q22 ); // Q22
     661             :     }
     662      449760 :     *azi = L_max( L_shl( -180, 22 ), L_min( L_shl( 180, 22 ), angle ) ); // Q22
     663      449760 :     move32();
     664      449760 :     if ( LE_32( L_abs( *azi ), ONE_IN_Q22 ) )
     665             :     {
     666        4142 :         *azi = 0;
     667        4142 :         move32();
     668             :     }
     669      449760 :     IF( isPlanar == 0 )
     670             :     {
     671      401360 :         sqrt_fx = L_Frac_sqrtQ31( L_add( Mpy_32_32( dv_r_fx[0], dv_r_fx[0] ), Mpy_32_32( dv_r_fx[1], dv_r_fx[1] ) ) );
     672      401360 :         y = dv_r_fx[2];
     673      401360 :         move32();
     674      401360 :         x = sqrt_fx;
     675      401360 :         move32();
     676      401360 :         radian = BASOP_util_atan2( y, x, 0 ); // Q13
     677             : 
     678      401360 :         angle = L_shr( Mpy_32_16_1( _180_OVER_PI_Q25, radian ), 1 ); // Q22
     679             : 
     680      401360 :         *ele = L_max( L_shl( -90, 22 ), L_min( L_shl( 90, 22 ), angle ) ); // Q22
     681      401360 :         move32();
     682      401360 :         if ( LT_32( L_abs( *ele ), ONE_IN_Q22 ) )
     683             :         {
     684      208480 :             *ele = 0;
     685      208480 :             move32();
     686             :         }
     687             :     }
     688             :     ELSE
     689             :     {
     690       48400 :         *ele = 0;
     691       48400 :         move32();
     692             :     }
     693             : 
     694      449760 :     return;
     695             : }
     696             : 
     697             : /*-------------------------------------------------------------------------
     698             :  * rotateFrame_shd()
     699             :  *
     700             :  * Apply rotation to signals in Spherical Harmonic Domain
     701             :  *------------------------------------------------------------------------*/
     702             : 
     703         600 : void rotateFrame_shd(
     704             :     COMBINED_ORIENTATION_HANDLE hCombinedOrientationData, /* i  : head and external orientation combined handle */
     705             :     Word32 *output[],                                     /* i/o: unrotated HOA3 signal buffer in TD        Q11 */
     706             :     const Word16 subframe_len,                            /* i  : subframe length per channel               Q0   */
     707             :     const IVAS_OUTPUT_SETUP hTransSetup,                  /* i  : format for rotation                           */
     708             :     const Word16 subframe_idx                             /* i  : subframe index                            Q0    */
     709             : )
     710             : {
     711             :     // Not yet tested, no test cases entering the function.
     712             :     Word16 i, l, n, m, offset;
     713             :     Word16 m1, m2;
     714             :     Word16 shd_rot_max_order;
     715             : 
     716         600 :     Word32 tmp = Q31_BY_SUB_FRAME_240;
     717         600 :     move32();
     718             :     Word32 tmpRot[2 * HEADROT_ORDER + 1];
     719             :     Word16 SHrotmat_prev[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM];
     720             :     Word16 SHrotmat[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM];
     721             :     Word32 cross_fade[L_FRAME48k / MAX_PARAM_SPATIAL_SUBFRAMES];
     722         600 :     shd_rot_max_order = hTransSetup.ambisonics_order;
     723         600 :     move16();
     724         600 :     offset = imult1616( subframe_idx, subframe_len );
     725             : 
     726         600 :     SWITCH( subframe_len )
     727             :     {
     728         600 :         case L_SUBFRAME_48k:
     729         600 :             tmp = Q31_BY_SUB_FRAME_240; // Q31
     730         600 :             move32();
     731         600 :             BREAK;
     732           0 :         case L_SUBFRAME_32k:
     733           0 :             tmp = Q31_BY_SUB_FRAME_180;
     734           0 :             move32();
     735           0 :             BREAK;
     736           0 :         case L_SUBFRAME_16k:
     737           0 :             tmp = Q31_BY_SUB_FRAME_80;
     738           0 :             move32();
     739           0 :             BREAK;
     740           0 :         case L_SUBFRAME_8k:
     741           0 :             tmp = Q31_BY_SUB_FRAME_40;
     742           0 :             move32();
     743           0 :             BREAK;
     744           0 :         default:
     745           0 :             BREAK;
     746             :     }
     747      144600 :     FOR( i = 0; i < subframe_len; i++ )
     748             :     {
     749      144000 :         cross_fade[i] = UL_Mpy_32_32( i, tmp ); // Q31
     750      144000 :         move32();
     751             :     }
     752             :     /* initialize rotation matrices with zeros */
     753       10200 :     FOR( i = 0; i < HEADROT_SHMAT_DIM; i++ )
     754             :     {
     755        9600 :         set16_fx( SHrotmat_prev[i], 0, HEADROT_SHMAT_DIM );
     756        9600 :         set16_fx( SHrotmat[i], 0, HEADROT_SHMAT_DIM );
     757             :     }
     758             : 
     759             :     /* calculate ambisonics rotation matrices for the previous and current frames */
     760         600 :     SHrotmatgen_fx( SHrotmat_prev, hCombinedOrientationData->Rmat_prev_fx, shd_rot_max_order );
     761             : 
     762         600 :     SHrotmatgen_fx( SHrotmat /*Q14*/, hCombinedOrientationData->Rmat_fx[hCombinedOrientationData->subframe_idx], shd_rot_max_order );
     763             : 
     764      144600 :     FOR( i = 0; i < subframe_len; i++ )
     765             :     {
     766             :         /*As the rotation matrix becomes block diagonal in a SH basis, we can
     767             :           apply each angular-momentum block individually to save complexity. */
     768             : 
     769             :         /* loop over l blocks */
     770      144000 :         m1 = 1;
     771      144000 :         move16();
     772      144000 :         m2 = 4;
     773      144000 :         move16();
     774      576000 :         FOR( l = 1; l <= shd_rot_max_order; l++ )
     775             :         {
     776             :             /* compute mtx-vector product for this l */
     777     2592000 :             FOR( n = m1; n < m2; n++ )
     778             :             {
     779     2160000 :                 tmpRot[n - m1] = 0;
     780     2160000 :                 move32();
     781    14112000 :                 FOR( m = m1; m < m2; m++ )
     782             :                 {
     783             :                     /* crossfade with previous rotation gains */
     784    11952000 :                     tmp = L_add( Mpy_32_32( Mpy_32_16_r( cross_fade[i], SHrotmat[n][m] ) /*Q30*/, output[m][offset + i] /*Q11*/ ), Mpy_32_32( Mpy_32_16_r( L_sub( ONE_IN_Q31, cross_fade[i] ), SHrotmat_prev[n][m] ) /*Q30*/, output[m][offset + i] /*Q11*/ ) );
     785    11952000 :                     tmpRot[n - m1] = L_add( L_shl( tmp, 1 ), tmpRot[n - m1] ); // Q11
     786             :                 }
     787             :             }
     788             : 
     789             :             /* write back the result */
     790     2592000 :             FOR( n = m1; n < m2; n++ )
     791             :             {
     792     2160000 :                 output[n][offset + i] = tmpRot[n - m1]; // Q11
     793     2160000 :                 move32();
     794             :             }
     795      432000 :             m1 = m2;
     796      432000 :             move16();
     797      432000 :             m2 = add( m2, add( shl( add( l, 1 ), 1 ), 1 ) );
     798             :         }
     799             : 
     800             :         /* unoptimized code for reference (full matrix multiplication)
     801             :         for ( n = 0; n < nchan; n++ )
     802             :         {
     803             :             tmpRot[n] = 0.f;
     804             : 
     805             :             for ( m = 0; m < nchan; m++ )
     806             :             {
     807             :                 tmpRot[n] += SHrotmat[n][m] * output[m][i];
     808             :             }
     809             :         }
     810             :         for ( n = 0; n < nchan; n++ )
     811             :         {
     812             :             output[n][i] = tmpRot[n];
     813             :         }
     814             :         */
     815             :     }
     816             : 
     817             :     /* move Rmat to Rmat_prev */
     818        2400 :     FOR( i = 0; i < 3; i++ )
     819             :     {
     820        1800 :         MVR2R_WORD32(
     821             :             hCombinedOrientationData->Rmat_fx[subframe_idx][i],
     822             :             hCombinedOrientationData->Rmat_prev_fx[i],
     823             :             3 ); // Q14
     824             :     }
     825             : 
     826         600 :     return;
     827             : }
     828             : 
     829             : /*-------------------------------------------------------------------------
     830             :  * rotateFrame_sd()
     831             :  *
     832             :  * Apply rotation to signals in Spatial Domain
     833             :  *------------------------------------------------------------------------*/
     834       16000 : void rotateFrame_sd(
     835             :     COMBINED_ORIENTATION_HANDLE hCombinedOrientationData, /* i  : head and external orientation combined handle */
     836             :     Word32 *output[],                                     /* i/o: unrotated SD signal buffer in TD          Q11 */
     837             :     const Word16 subframe_len,                            /* i  : subframe length per channel                   */
     838             :     const IVAS_OUTPUT_SETUP hTransSetup,                  /* i  : format for rotation                           */
     839             :     const EFAP_HANDLE hEFAPdata,                          /* i  : EFAP structure                                */
     840             :     const Word16 subframe_idx                             /* i  : subframe index                                */
     841             : )
     842             : {
     843             :     Word16 i, j, offset;
     844             :     Word16 nchan, index_lfe;
     845             :     Word16 ch_in, ch_in_woLFE, ch_out, ch_out_woLFE;
     846             :     Word16 azimuth, elevation;
     847             :     Word32 azimuth_fx, elevation_fx;
     848       16000 :     Word32 tmp = Q31_BY_SUB_FRAME_240; // Q31
     849             :     Word32 out_temp;
     850             :     Word32 tmp_gains_fx[MAX_CICP_CHANNELS - 1];
     851             :     Word32 gains_fx[MAX_CICP_CHANNELS][MAX_CICP_CHANNELS];
     852             :     Word32 gains_prev_fx[MAX_CICP_CHANNELS][MAX_CICP_CHANNELS];
     853             :     Word32 output_tmp_fx[MAX_CICP_CHANNELS][L_FRAME48k];
     854             :     Word32 cross_fade_fx[L_FRAME48k / MAX_PARAM_SPATIAL_SUBFRAMES];
     855             : 
     856       16000 :     push_wmops( "rotateFrame_sd" );
     857             : 
     858       16000 :     move32();
     859             : 
     860       16000 :     nchan = add( hTransSetup.nchan_out_woLFE, hTransSetup.num_lfe );
     861       16000 :     index_lfe = hTransSetup.index_lfe[0];
     862       16000 :     move16();
     863       16000 :     offset = imult1616( subframe_idx, subframe_len );
     864       16000 :     SWITCH( subframe_len )
     865             :     {
     866       16000 :         case L_SUBFRAME_48k:
     867       16000 :             tmp = Q31_BY_SUB_FRAME_240;
     868       16000 :             move32();
     869       16000 :             BREAK;
     870           0 :         case L_SUBFRAME_32k:
     871           0 :             tmp = Q31_BY_SUB_FRAME_180;
     872           0 :             move32();
     873           0 :             BREAK;
     874           0 :         case L_SUBFRAME_16k:
     875           0 :             tmp = Q31_BY_SUB_FRAME_80;
     876           0 :             move32();
     877           0 :             BREAK;
     878           0 :         case L_SUBFRAME_8k:
     879           0 :             tmp = Q31_BY_SUB_FRAME_40;
     880           0 :             move32();
     881           0 :             BREAK;
     882           0 :         default:
     883           0 :             BREAK;
     884             :     }
     885             : 
     886     3856000 :     FOR( i = 0; i < subframe_len; i++ )
     887             :     {
     888     3840000 :         cross_fade_fx[i] = UL_Mpy_32_32( i, tmp ); // Q31
     889     3840000 :         move32();
     890             :     }
     891             : 
     892      112000 :     FOR( ch_in = 0; ch_in < nchan; ch_in++ )
     893             :     {
     894             :         /* zero output and gain buffers */
     895       96000 :         set32_fx( &output_tmp_fx[ch_in][offset], 0, subframe_len );
     896       96000 :         set32_fx( gains_prev_fx[ch_in], 0, nchan );
     897       96000 :         set32_fx( gains_fx[ch_in], 0, nchan );
     898             :         /* set gains to passthrough by default */
     899       96000 :         gains_prev_fx[ch_in][ch_in] = ONE_IN_Q30;
     900       96000 :         move32();
     901       96000 :         gains_fx[ch_in][ch_in] = ONE_IN_Q30;
     902       96000 :         move32();
     903             :         /* skip LFE */
     904       96000 :         IF( EQ_16( ch_in, index_lfe ) )
     905             :         {
     906       16000 :             CONTINUE;
     907             :         }
     908             : 
     909             :         /* input channel index without LFE */
     910       80000 :         IF( GE_16( ch_in, index_lfe ) )
     911             :         {
     912       32000 :             ch_in_woLFE = sub( ch_in, 1 );
     913             :         }
     914             :         ELSE
     915             :         {
     916       48000 :             ch_in_woLFE = ch_in;
     917       48000 :             move16();
     918             :         }
     919             : 
     920             :         /* gains for previous subframe rotation */
     921       80000 :         rotateAziEle_fx( extract_l( L_shr( hTransSetup.ls_azimuth_fx[ch_in_woLFE], Q22 ) ), extract_l( L_shr( hTransSetup.ls_elevation_fx[ch_in_woLFE], Q22 ) ), &azimuth, &elevation, hCombinedOrientationData->Rmat_prev_fx, hTransSetup.is_planar_setup );
     922             : 
     923       80000 :         test();
     924       80000 :         test();
     925       80000 :         IF( hEFAPdata != NULL && ( NE_16( extract_l( L_shr( hTransSetup.ls_azimuth_fx[ch_in_woLFE], Q22 ) ), azimuth ) || NE_16( extract_l( L_shr( hTransSetup.ls_elevation_fx[ch_in_woLFE], Q22 ) ), elevation ) ) )
     926             :         {
     927       76747 :             azimuth_fx = L_shl( L_deposit_l( azimuth ), Q22 );     // Q0->Q22
     928       76747 :             elevation_fx = L_shl( L_deposit_l( elevation ), Q22 ); // Q0->Q22
     929       76747 :             efap_determine_gains_fx( hEFAPdata, tmp_gains_fx, azimuth_fx, elevation_fx, EFAP_MODE_EFAP );
     930             : 
     931             : 
     932      537229 :             FOR( ch_out = 0; ch_out < nchan; ch_out++ )
     933             :             {
     934             :                 /* skip LFE */
     935      460482 :                 IF( EQ_16( ch_out, index_lfe ) )
     936             :                 {
     937       76747 :                     CONTINUE;
     938             :                 }
     939             : 
     940             :                 /* output channel index without LFE */
     941      383735 :                 IF( GE_16( ch_out, index_lfe ) )
     942             :                 {
     943      153494 :                     ch_out_woLFE = sub( ch_out, 1 );
     944             :                 }
     945             :                 ELSE
     946             :                 {
     947      230241 :                     ch_out_woLFE = ch_out;
     948      230241 :                     move16();
     949             :                 }
     950      383735 :                 gains_prev_fx[ch_in][ch_out] = tmp_gains_fx[ch_out_woLFE]; // Q30
     951      383735 :                 move32();
     952             :             }
     953             :         }
     954             : 
     955             :         /* gains for current subframe rotation */
     956       80000 :         rotateAziEle_fx( extract_l( L_shr( hTransSetup.ls_azimuth_fx[ch_in_woLFE], Q22 ) ), extract_l( L_shr( hTransSetup.ls_elevation_fx[ch_in_woLFE], Q22 ) ), &azimuth, &elevation, hCombinedOrientationData->Rmat_fx[hCombinedOrientationData->subframe_idx], hTransSetup.is_planar_setup );
     957       80000 :         test();
     958       80000 :         test();
     959       80000 :         IF( hEFAPdata != NULL && ( NE_16( extract_l( L_shr( hTransSetup.ls_azimuth_fx[ch_in_woLFE], Q22 ) ), azimuth ) || NE_16( extract_l( L_shr( hTransSetup.ls_elevation_fx[ch_in_woLFE], Q22 ) ), elevation ) ) )
     960             :         {
     961             : 
     962       76763 :             azimuth_fx = L_shl( L_deposit_l( azimuth ), Q22 );     // Q0->Q22
     963       76763 :             elevation_fx = L_shl( L_deposit_l( elevation ), Q22 ); // Q0->Q22
     964       76763 :             efap_determine_gains_fx( hEFAPdata, tmp_gains_fx, azimuth_fx, elevation_fx, EFAP_MODE_EFAP );
     965             : 
     966      537341 :             FOR( ch_out = 0; ch_out < nchan; ch_out++ )
     967             :             {
     968             :                 /* skip LFE */
     969      460578 :                 IF( EQ_16( ch_out, index_lfe ) )
     970             :                 {
     971       76763 :                     CONTINUE;
     972             :                 }
     973             : 
     974             :                 /* output channel index without LFE */
     975      383815 :                 IF( GE_16( ch_out, index_lfe ) )
     976             :                 {
     977      153526 :                     ch_out_woLFE = sub( ch_out, 1 );
     978             :                 }
     979             :                 ELSE
     980             :                 {
     981      230289 :                     ch_out_woLFE = ch_out;
     982      230289 :                     move16();
     983             :                 }
     984             : 
     985      383815 :                 gains_fx[ch_in][ch_out] = tmp_gains_fx[ch_out_woLFE]; // Q30
     986      383815 :                 move32();
     987             :             }
     988             :         }
     989             :     }
     990             : 
     991             :     /* apply panning gains by mtx multiplication */
     992      112000 :     FOR( ch_out = 0; ch_out < nchan; ch_out++ )
     993             :     {
     994      672000 :         FOR( ch_in = 0; ch_in < nchan; ch_in++ )
     995             :         {
     996      576000 :             j = 0;
     997             :             /* crossfade with previous rotation gains */
     998   138816000 :             FOR( i = subframe_idx * subframe_len; j < subframe_len; i++ )
     999             :             {
    1000   138240000 :                 out_temp = output[ch_in][i];
    1001   138240000 :                 move32();
    1002   138240000 :                 Word32 temp = Mpy_32_32( Mpy_32_32( ( cross_fade_fx[j] ), gains_fx[ch_in][ch_out] ), out_temp );                        // Q10
    1003   138240000 :                 Word32 temp1 = Mpy_32_32( Mpy_32_32( L_sub( ONE_IN_Q31, cross_fade_fx[j] ), gains_prev_fx[ch_in][ch_out] ), out_temp ); // Q10
    1004   138240000 :                 output_tmp_fx[ch_out][i] = L_add( L_shl( L_add( temp, temp1 ), 1 ), output_tmp_fx[ch_out][i] );                         // Q11
    1005   138240000 :                 move32();
    1006   138240000 :                 j = add( j, 1 );
    1007             :             }
    1008             :         }
    1009             :     }
    1010             : 
    1011             :     /* move Rmat to Rmat_prev */
    1012       64000 :     FOR( i = 0; i < 3; i++ )
    1013             :     {
    1014       48000 :         MVR2R_WORD32(
    1015             :             hCombinedOrientationData->Rmat_fx[hCombinedOrientationData->subframe_idx][i], // Q30
    1016             :             hCombinedOrientationData->Rmat_prev_fx[i],
    1017             :             3 );
    1018             :     }
    1019             :     /* copy to output */
    1020      112000 :     FOR( ch_out = 0; ch_out < nchan; ch_out++ )
    1021             :     {
    1022       96000 :         MVR2R_WORD32( &output_tmp_fx[ch_out][offset], &output[ch_out][offset], subframe_len ); // Q11
    1023             :     }
    1024             : 
    1025       16000 :     pop_wmops();
    1026       16000 :     return;
    1027             : }
    1028             : /*-------------------------------------------------------------------------
    1029             :  * rotateFrame_shd_cldfb()
    1030             :  *
    1031             :  * Apply rotation to signals in Spherical Harmonic Domain and in CLDFB
    1032             :  *------------------------------------------------------------------------*/
    1033       18520 : void rotateFrame_shd_cldfb(
    1034             :     Word32 Cldfb_RealBuffer[][MAX_PARAM_SPATIAL_SUBFRAMES][CLDFB_NO_CHANNELS_MAX], /* i/o: unrotated HOA3 signal buffer in cldfb domain real part Qx -> Qx-1  */
    1035             :     Word32 Cldfb_ImagBuffer[][MAX_PARAM_SPATIAL_SUBFRAMES][CLDFB_NO_CHANNELS_MAX], /* i/o: unrotated HOA3 signal buffer in cldfb domain imag part Qx -> Qx-1  */
    1036             :     Word32 Rmat[3][3],                                                             /* i  : real-space rotation matrix                             Q30         */
    1037             :     const Word16 nInChannels,                                                      /* i  : number of channels                                                 */
    1038             :     const Word16 numTimeSlots,                                                     /* i  : number of time slots to process                                    */
    1039             :     const Word16 shd_rot_max_order                                                 /* i  : split-order rotation method                                        */
    1040             : )
    1041             : {
    1042       18520 :     Word16 n = 0;
    1043       18520 :     Word16 m = 0;
    1044       18520 :     Word16 i = 0, j = 0, k = 0;
    1045       18520 :     Word16 iBand = 0;
    1046       18520 :     Word16 l = 0, m1 = 0, m2 = 0;
    1047             :     Word32 realRot[2 * HEADROT_ORDER + 1], imagRot[2 * HEADROT_ORDER + 1];
    1048             :     Word16 SHrotmat[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM];
    1049             : #ifndef OPT_HEAD_ROT_REND_V1_BE
    1050             :     Word32 temp1, temp2;
    1051             : #endif /* OPT_HEAD_ROT_REND_V1_BE */
    1052       18520 :     move16();
    1053       18520 :     move16();
    1054       18520 :     move16();
    1055       18520 :     move16();
    1056       18520 :     move16();
    1057       18520 :     move16();
    1058       18520 :     move16();
    1059       18520 :     move16();
    1060       18520 :     move16();
    1061       18520 :     assert( nInChannels == HEADROT_SHMAT_DIM && "Number of channels must be 16!" );
    1062             : 
    1063             :     /* initialize rotation matrices with zeros */
    1064      314840 :     FOR( i = 0; i < HEADROT_SHMAT_DIM; i++ )
    1065             :     {
    1066      296320 :         set16_fx( SHrotmat[i], 0, HEADROT_SHMAT_DIM );
    1067             :     }
    1068             : 
    1069             :     /* calculate Ambisonics rotation matrix from the quaternion */
    1070       18520 :     SHrotmatgen_fx( SHrotmat, Rmat, shd_rot_max_order );
    1071             : 
    1072             : #ifdef DEBUGGING
    1073             :     dbgwrite_txt( (const float *) SHrotmat[0], HEADROT_SHMAT_DIM * HEADROT_SHMAT_DIM, "Fixed_SHrotmat.txt", NULL );
    1074             : #endif
    1075             : 
    1076             :     /* rotation by mtx multiplication */
    1077       92600 :     FOR( i = 0; i < numTimeSlots; i++ )
    1078             :     {
    1079     4518880 :         FOR( iBand = 0; iBand < CLDFB_NO_CHANNELS_MAX; iBand++ )
    1080             :         {
    1081             :             /*As the rotation matrix becomes block diagonal in a SH basis, we can
    1082             :             apply each angular-momentum block individually to save complexity. */
    1083             : 
    1084             :             /* loop over l blocks */
    1085     4444800 :             m1 = 1;
    1086     4444800 :             move16();
    1087     4444800 :             m2 = 4;
    1088     4444800 :             move16();
    1089    17779200 :             FOR( l = 1; l <= shd_rot_max_order; l++ )
    1090             :             {
    1091             :                 /* compute mtx-vector product for this l */
    1092    80006400 :                 FOR( n = m1; n < m2; n++ )
    1093             :                 {
    1094    66672000 :                     realRot[n - m1] = 0;
    1095    66672000 :                     move32();
    1096    66672000 :                     imagRot[n - m1] = 0;
    1097    66672000 :                     move32();
    1098   435590400 :                     FOR( m = m1; m < m2; m++ )
    1099             :                     {
    1100             : #ifdef OPT_HEAD_ROT_REND_V1_BE
    1101   368918400 :                         realRot[n - m1] = Madd_32_16_r( realRot[n - m1], Cldfb_RealBuffer[m][i][iBand], SHrotmat[n][m] ); // Q(x + 14 - 15)
    1102   368918400 :                         move32();
    1103   368918400 :                         imagRot[n - m1] = Madd_32_16_r( imagRot[n - m1], Cldfb_ImagBuffer[m][i][iBand], SHrotmat[n][m] ); // Q(x + 14 - 15)
    1104   368918400 :                         move32();
    1105             : #else  /* OPT_HEAD_ROT_REND_V1_BE */
    1106             :                         temp1 = Mpy_32_16_r( Cldfb_RealBuffer[m][i][iBand], SHrotmat[n][m] ); // Q(x + 14 - 15)
    1107             :                         temp2 = Mpy_32_16_r( Cldfb_ImagBuffer[m][i][iBand], SHrotmat[n][m] ); // Q(x + 14 - 15)
    1108             :                         realRot[n - m1] = L_add( temp1, realRot[n - m1] );                    // Q(x + 14 - 15)
    1109             :                         move32();
    1110             :                         imagRot[n - m1] = L_add( temp2, imagRot[n - m1] ); // Q(x + 14 - 15)
    1111             :                         move32();
    1112             : #endif /* OPT_HEAD_ROT_REND_V1_BE */
    1113             :                     }
    1114             :                 }
    1115             :                 /* write back the result */
    1116    80006400 :                 FOR( n = m1; n < m2; n++ )
    1117             :                 {
    1118    66672000 :                     Cldfb_RealBuffer[n][i][iBand] = realRot[n - m1]; // Qx - 1
    1119    66672000 :                     move32();
    1120    66672000 :                     Cldfb_ImagBuffer[n][i][iBand] = imagRot[n - m1]; // Qx - 1
    1121    66672000 :                     move32();
    1122             :                 }
    1123    13334400 :                 m1 = m2;
    1124    13334400 :                 move16();
    1125    13334400 :                 m2 = add( m2, add( shl( add( l, 1 ), 1 ), 1 ) );
    1126             :             }
    1127             : 
    1128             :             /* unoptimized code for reference (full matrix multiplication)
    1129             :             for (n = 0; n < nInChannels; n++)
    1130             :             {
    1131             :                 realRot[n] = 0.f;
    1132             :                 imagRot[n] = 0.f;
    1133             : 
    1134             :                 for (m = 0; m < nInChannels; m++)
    1135             :                 {
    1136             :                     realRot[n] += SHrotmat[n][m] * Cldfb_RealBuffer[m][i][iBand];
    1137             :                     imagRot[n] += SHrotmat[n][m] * Cldfb_ImagBuffer[m][i][iBand];
    1138             :                 }
    1139             :             }
    1140             :             for (n = 0; n < nInChannels; n++)
    1141             :             {
    1142             :                 Cldfb_RealBuffer[n][i][iBand] = realRot[n];
    1143             :                 Cldfb_ImagBuffer[n][i][iBand] = imagRot[n];
    1144             :             }
    1145             :             */
    1146             :         }
    1147             :     }
    1148             :     // To make the Q factor same as tha of the remaining vector values
    1149       92600 :     FOR( j = 0; j < MAX_PARAM_SPATIAL_SUBFRAMES; j++ )
    1150             :     {
    1151     4518880 :         FOR( k = 0; k < CLDFB_NO_CHANNELS_MAX; k++ )
    1152             :         {
    1153     4444800 :             Cldfb_RealBuffer[0][j][k] = L_shr( Cldfb_RealBuffer[0][j][k], 1 ); // Qx - 1
    1154     4444800 :             move32();
    1155     4444800 :             Cldfb_ImagBuffer[0][j][k] = L_shr( Cldfb_ImagBuffer[0][j][k], 1 ); // Qx - 1
    1156     4444800 :             move32();
    1157             :         }
    1158             :     }
    1159       18520 :     return;
    1160             : }
    1161             : /*-------------------------------------------------------------------------
    1162             :  * rotateFrame_sd_cldfb()
    1163             :  *
    1164             :  * Apply rotation to signals in Spatial Domain and in CLDFB
    1165             :  *------------------------------------------------------------------------*/
    1166             : 
    1167        8000 : void rotateFrame_sd_cldfb_fixed(
    1168             :     Word32 Rmat_fx[3][3],                                                          /* i  : real-space rotation matrix (Q30)                          */
    1169             :     Word32 Cldfb_RealBuffer[][MAX_PARAM_SPATIAL_SUBFRAMES][CLDFB_NO_CHANNELS_MAX], /* i/o: unrotated HOA3 signal buffer in cldfb domain real part Qx */
    1170             :     Word32 Cldfb_ImagBuffer[][MAX_PARAM_SPATIAL_SUBFRAMES][CLDFB_NO_CHANNELS_MAX], /* i/o: unrotated HOA3 signal buffer in cldfb domain imag part Qx */
    1171             :     const IVAS_OUTPUT_SETUP_HANDLE hOutputSetup,                                   /* i  : output format setup number of channels                    */
    1172             :     const EFAP_HANDLE hEFAPdata,                                                   /* i  : EFAP structure                                            */
    1173             :     const Word16 numTimeSlots,                                                     /* i  : number of time slots to process                           */
    1174             :     const Word16 nb_band                                                           /* i  : number of CLDFB bands to process                          */
    1175             : )
    1176             : {
    1177             :     Word16 iBlock, iBand, m, n;
    1178             :     Word32 gains_fx[MAX_CICP_CHANNELS - 1][MAX_CICP_CHANNELS - 1];
    1179             :     Word16 azimuth, elevation;
    1180             :     Word32 g1_fx;
    1181             :     Word32 realRot_fx[MAX_CICP_CHANNELS - 1][MAX_PARAM_SPATIAL_SUBFRAMES * CLDFB_NO_CHANNELS_MAX];
    1182             :     Word32 imagRot_fx[MAX_CICP_CHANNELS - 1][MAX_PARAM_SPATIAL_SUBFRAMES * CLDFB_NO_CHANNELS_MAX];
    1183             :     Word32 *p_realRot_fx, *p_imagRot_fx;
    1184             :     Word32 *p_real_fx, *p_imag_fx;
    1185             :     Word16 nInChannels;
    1186             :     Word16 isPlanar;
    1187        8000 :     push_wmops( "rotateFrame_sd_cldfb" );
    1188             : 
    1189        8000 :     nInChannels = hOutputSetup->nchan_out_woLFE;
    1190        8000 :     move16();
    1191        8000 :     isPlanar = 1;
    1192        8000 :     move16();
    1193       48000 :     FOR( n = 0; n < nInChannels; n++ )
    1194             :     {
    1195       40000 :         IF( hOutputSetup->ls_elevation_fx[n] != 0 )
    1196             :         {
    1197           0 :             isPlanar = 0;
    1198           0 :             move16();
    1199           0 :             BREAK;
    1200             :         }
    1201             :     }
    1202             : 
    1203             :     /* rotation of Euler angles */
    1204       48000 :     FOR( n = 0; n < nInChannels; n++ )
    1205             :     {
    1206       40000 :         rotateAziEle_fx( extract_l( L_shr( hOutputSetup->ls_azimuth_fx[n], Q22 ) ), extract_l( L_shr( hOutputSetup->ls_elevation_fx[n], Q22 ) ), &azimuth, &elevation, Rmat_fx, isPlanar );
    1207       40000 :         test();
    1208       40000 :         test();
    1209       40000 :         IF( hEFAPdata != NULL && ( NE_16( extract_l( L_shr( hOutputSetup->ls_azimuth_fx[n], Q22 ) ), azimuth ) || NE_16( extract_l( L_shr( hOutputSetup->ls_elevation_fx[n], Q22 ) ), elevation ) ) )
    1210             :         {
    1211           0 :             efap_determine_gains_fx( hEFAPdata, gains_fx[n], L_shl( azimuth, Q22 ), L_shl( elevation, Q22 ), EFAP_MODE_EFAP );
    1212             :         }
    1213             :         ELSE
    1214             :         {
    1215       40000 :             set32_fx( gains_fx[n], 0, nInChannels );
    1216       40000 :             gains_fx[n][n] = 0x7fffffff; // 1 in Q31
    1217       40000 :             move32();
    1218             :         }
    1219             :     }
    1220             : 
    1221             :     /* Apply panning gains by mtx multiplication*/
    1222       48000 :     FOR( n = 0; n < nInChannels; n++ )
    1223             :     {
    1224       40000 :         set32_fx( realRot_fx[n], 0, shl( nb_band, 2 ) );
    1225       40000 :         set32_fx( imagRot_fx[n], 0, shl( nb_band, 2 ) );
    1226      240000 :         FOR( m = 0; m < nInChannels; m++ )
    1227             :         {
    1228      200000 :             g1_fx = gains_fx[m][n]; // Q31
    1229      200000 :             move32();
    1230      200000 :             p_realRot_fx = realRot_fx[n];
    1231      200000 :             p_imagRot_fx = imagRot_fx[n];
    1232             : 
    1233      200000 :             IF( g1_fx > 0 )
    1234             :             {
    1235      200000 :                 FOR( iBlock = 0; iBlock < numTimeSlots; iBlock++ )
    1236             :                 {
    1237      160000 :                     p_real_fx = Cldfb_RealBuffer[m][iBlock];
    1238      160000 :                     p_imag_fx = Cldfb_ImagBuffer[m][iBlock];
    1239             : 
    1240     8160000 :                     FOR( iBand = 0; iBand < nb_band; iBand++ )
    1241             :                     {
    1242     8000000 :                         *( p_realRot_fx ) = L_add( *p_realRot_fx, Mpy_32_32( g1_fx, *( p_real_fx++ ) ) ); // Qx
    1243     8000000 :                         move32();
    1244     8000000 :                         *( p_imagRot_fx ) = L_add( *p_imagRot_fx, Mpy_32_32( g1_fx, *( p_imag_fx++ ) ) ); // Qx
    1245     8000000 :                         move32();
    1246     8000000 :                         p_realRot_fx++;
    1247     8000000 :                         p_imagRot_fx++;
    1248             :                     }
    1249             :                 }
    1250             :             }
    1251             :         }
    1252             :     }
    1253             : 
    1254       48000 :     FOR( n = 0; n < nInChannels; n++ )
    1255             :     {
    1256       40000 :         p_realRot_fx = realRot_fx[n];
    1257       40000 :         p_imagRot_fx = imagRot_fx[n];
    1258             : 
    1259      200000 :         FOR( iBlock = 0; iBlock < numTimeSlots; iBlock++ )
    1260             :         {
    1261      160000 :             p_real_fx = Cldfb_RealBuffer[n][iBlock];
    1262      160000 :             p_imag_fx = Cldfb_ImagBuffer[n][iBlock];
    1263             : 
    1264     8160000 :             FOR( iBand = 0; iBand < nb_band; iBand++ )
    1265             :             {
    1266     8000000 :                 *( p_real_fx++ ) = *( p_realRot_fx++ );
    1267     8000000 :                 move32();
    1268     8000000 :                 *( p_imag_fx++ ) = *( p_imagRot_fx++ );
    1269     8000000 :                 move32();
    1270             :             }
    1271     1760000 :             FOR( ; iBand < CLDFB_NO_CHANNELS_MAX; iBand++ )
    1272             :             {
    1273     1600000 :                 *( p_real_fx++ ) = 0;
    1274     1600000 :                 move32();
    1275     1600000 :                 *( p_imag_fx++ ) = 0;
    1276     1600000 :                 move32();
    1277             :             }
    1278             :         }
    1279             :     }
    1280        8000 :     pop_wmops();
    1281             : 
    1282        8000 :     return;
    1283             : }
    1284             : 
    1285             : /*-----------------------------------------------------------------------*
    1286             :  * ivas_external_orientation_open()
    1287             :  *
    1288             :  * Allocate and initialize external orientation handle
    1289             :  *-----------------------------------------------------------------------*/
    1290         697 : ivas_error ivas_external_orientation_open(
    1291             :     EXTERNAL_ORIENTATION_HANDLE *hExtOrientationData, /* o  : external orientation handle    */
    1292             :     const Word16 num_subframes                        /* i  : number of subframes            */
    1293             : )
    1294             : {
    1295             : 
    1296             :     Word16 i;
    1297             :     IVAS_QUATERNION identity;
    1298         697 :     identity.w_fx = ONE_IN_Q31;
    1299         697 :     move32();
    1300         697 :     identity.x_fx = 0;
    1301         697 :     move32();
    1302         697 :     identity.y_fx = 0;
    1303         697 :     move32();
    1304         697 :     identity.z_fx = 0;
    1305         697 :     move32();
    1306         697 :     identity.q_fact = 31;
    1307         697 :     move16();
    1308             :     /* Allocate handle */
    1309         697 :     IF( ( *hExtOrientationData = (EXTERNAL_ORIENTATION_HANDLE) malloc( sizeof( EXTERNAL_ORIENTATION_DATA ) ) ) == NULL )
    1310             :     {
    1311           0 :         return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for external orientation memory\n" ) );
    1312             :     }
    1313         697 :     ( *hExtOrientationData )->num_subframes = num_subframes;
    1314         697 :     move16();
    1315             :     /* Enable head rotation and disable external orientation as default */
    1316        3485 :     FOR( i = 0; i < MAX_PARAM_SPATIAL_SUBFRAMES; i++ )
    1317             :     {
    1318        2788 :         ( *hExtOrientationData )->enableHeadRotation[i] = 1;
    1319        2788 :         move16();
    1320        2788 :         ( *hExtOrientationData )->enableExternalOrientation[i] = 0;
    1321        2788 :         move16();
    1322        2788 :         ( *hExtOrientationData )->enableRotationInterpolation[i] = 0;
    1323        2788 :         move16();
    1324        2788 :         ( *hExtOrientationData )->numFramesToTargetOrientation[i] = 0;
    1325        2788 :         move16();
    1326             : 
    1327        2788 :         ( *hExtOrientationData )->Quaternions[i] = identity;
    1328             :     }
    1329         697 :     return IVAS_ERR_OK;
    1330             : }
    1331             : 
    1332             : /*-----------------------------------------------------------------------*
    1333             :  * ivas_external_orientation_close()
    1334             :  *
    1335             :  * Deallocate external orientation handle
    1336             :  *-----------------------------------------------------------------------*/
    1337        1270 : void ivas_external_orientation_close_fx(
    1338             :     EXTERNAL_ORIENTATION_HANDLE *hExtOrientationData /* i/o: external orientation handle    */
    1339             : )
    1340             : {
    1341        1270 :     test();
    1342        1270 :     IF( hExtOrientationData == NULL || *hExtOrientationData == NULL )
    1343             :     {
    1344         573 :         return;
    1345             :     }
    1346             : 
    1347         697 :     free( ( *hExtOrientationData ) );
    1348         697 :     *hExtOrientationData = NULL;
    1349             : 
    1350         697 :     return;
    1351             : }
    1352             : 
    1353             : 
    1354             : /*-----------------------------------------------------------------------*
    1355             :  * ivas_combined_orientation_open()
    1356             :  *
    1357             :  * Allocate and initialize combined orientation handle
    1358             :  *-----------------------------------------------------------------------*/
    1359         747 : ivas_error ivas_combined_orientation_open(
    1360             :     COMBINED_ORIENTATION_HANDLE *hCombinedOrientationData, /* o  : combined orientation handle   */
    1361             :     const Word32 fs,                                       /* i  : sampling rate                 */
    1362             :     const Word16 num_subframes                             /* i  : number of subframes           */
    1363             : )
    1364             : {
    1365             :     Word16 i;
    1366             :     Word16 j;
    1367             :     IVAS_QUATERNION identity;
    1368             :     IVAS_VECTOR3 origo;
    1369         747 :     identity.w_fx = ONE_IN_Q31;
    1370         747 :     move32();
    1371         747 :     identity.x_fx = 0;
    1372         747 :     move32();
    1373         747 :     identity.y_fx = 0;
    1374         747 :     move32();
    1375         747 :     identity.z_fx = 0;
    1376         747 :     move32();
    1377         747 :     identity.q_fact = 31;
    1378         747 :     move16();
    1379         747 :     origo.x_fx = 0;
    1380         747 :     move32();
    1381         747 :     origo.y_fx = 0;
    1382         747 :     move32();
    1383         747 :     origo.z_fx = 0;
    1384         747 :     move32();
    1385         747 :     origo.q_fact = 31;
    1386         747 :     move16();
    1387             :     /* Allocate handle */
    1388         747 :     IF( ( *hCombinedOrientationData = (COMBINED_ORIENTATION_HANDLE) malloc( sizeof( COMBINED_ORIENTATION_DATA ) ) ) == NULL )
    1389             :     {
    1390           0 :         return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for combined orientation memory\n" ) );
    1391             :     }
    1392             : 
    1393             :     /* Initialization */
    1394         747 :     ( *hCombinedOrientationData )->num_subframes = num_subframes;
    1395         747 :     move16();
    1396         747 :     ( *hCombinedOrientationData )->interpolationCoefficient_fx = ONE_IN_Q30; // Q30
    1397         747 :     move32();
    1398         747 :     ( *hCombinedOrientationData )->interpolationIncrement_fx = ONE_IN_Q30; // Q30
    1399         747 :     move32();
    1400         747 :     IF( EQ_16( num_subframes, 1 ) )
    1401             :     {
    1402         304 :         ( *hCombinedOrientationData )->maximumFramesToTargetOrientation = 2000;
    1403         304 :         move16();
    1404             :     }
    1405             :     ELSE
    1406             :     {
    1407         443 :         ( *hCombinedOrientationData )->maximumFramesToTargetOrientation = 500;
    1408         443 :         move16();
    1409             :     }
    1410         747 :     ( *hCombinedOrientationData )->lrSwitchedNext = 0;
    1411         747 :     move16();
    1412         747 :     ( *hCombinedOrientationData )->lrSwitchedCurrent = 0;
    1413         747 :     move16();
    1414             : 
    1415         747 :     ( *hCombinedOrientationData )->lrSwitchInterpVal_fx = 0;
    1416         747 :     move32();
    1417         747 :     ( *hCombinedOrientationData )->isInterpolationOngoing = FALSE;
    1418         747 :     move16();
    1419         747 :     ( *hCombinedOrientationData )->Quaternions_ext_interpolation_start = identity;
    1420         747 :     ( *hCombinedOrientationData )->Quaternions_ext_interpolation_target = identity;
    1421             : 
    1422             :     /* Initialise orientations to identity */
    1423        3735 :     FOR( i = 0; i < MAX_PARAM_SPATIAL_SUBFRAMES; i++ )
    1424             :     {
    1425        2988 :         ( *hCombinedOrientationData )->enableCombinedOrientation[i] = 0;
    1426        2988 :         move16();
    1427        2988 :         ( *hCombinedOrientationData )->Quaternions[i] = identity;
    1428        2988 :         ( *hCombinedOrientationData )->listenerPos[i] = origo;
    1429             : 
    1430       11952 :         FOR( j = 0; j < 3; j++ )
    1431             :         {
    1432        8964 :             set32_fx( ( *hCombinedOrientationData )->Rmat_fx[i][j], 0, 3 );
    1433        8964 :             ( *hCombinedOrientationData )->Rmat_fx[i][j][j] = ONE_IN_Q30; // Q30
    1434        8964 :             move32();
    1435             :         }
    1436             :     }
    1437             : 
    1438        2988 :     FOR( j = 0; j < 3; j++ )
    1439             :     {
    1440        2241 :         set32_fx( ( *hCombinedOrientationData )->Rmat_prev_fx[j], 0, 3 );
    1441        2241 :         ( *hCombinedOrientationData )->Rmat_prev_fx[j][j] = ONE_IN_Q30;
    1442        2241 :         move32();
    1443             :     }
    1444         747 :     ( *hCombinedOrientationData )->Quaternion_prev_extOrientation = identity;
    1445         747 :     ( *hCombinedOrientationData )->Quaternion_frozen_ext = identity;
    1446         747 :     ( *hCombinedOrientationData )->Quaternion_frozen_head = identity;
    1447         747 :     set_zero_fx( ( *hCombinedOrientationData )->chEneIIR_fx[0], MASA_FREQUENCY_BANDS );
    1448         747 :     set_zero_fx( ( *hCombinedOrientationData )->chEneIIR_fx[1], MASA_FREQUENCY_BANDS );
    1449         747 :     set_zero_fx( ( *hCombinedOrientationData )->procChEneIIR_fx[0], MASA_FREQUENCY_BANDS );
    1450         747 :     set_zero_fx( ( *hCombinedOrientationData )->procChEneIIR_fx[1], MASA_FREQUENCY_BANDS );
    1451         747 :     set16_fx( ( *hCombinedOrientationData )->q_chEneIIR[0], Q31, MASA_FREQUENCY_BANDS );
    1452         747 :     set16_fx( ( *hCombinedOrientationData )->q_chEneIIR[1], Q31, MASA_FREQUENCY_BANDS );
    1453         747 :     set16_fx( ( *hCombinedOrientationData )->q_procChEneIIR[0], Q31, MASA_FREQUENCY_BANDS );
    1454         747 :     set16_fx( ( *hCombinedOrientationData )->q_procChEneIIR[1], Q31, MASA_FREQUENCY_BANDS );
    1455             : 
    1456         747 :     ( *hCombinedOrientationData )->isExtOrientationFrozen = 0;
    1457         747 :     move16();
    1458         747 :     ( *hCombinedOrientationData )->isHeadRotationFrozen = 0;
    1459         747 :     move16();
    1460             : 
    1461         747 :     ( *hCombinedOrientationData )->subframe_idx = 0;
    1462         747 :     move16();
    1463             :     /* ( *hCombinedOrientationData )->subframe_size = (int16_t) ( fs / ( FRAMES_PER_SEC * MAX_PARAM_SPATIAL_SUBFRAMES ) ); */
    1464         747 :     ( *hCombinedOrientationData )->subframe_size = extract_l( Mpy_32_32_r( fs, 10737418 /* 1 / ( FRAMES_PER_SEC * MAX_PARAM_SPATIAL_SUBFRAMES ) in Q31 */ ) );
    1465         747 :     move16();
    1466         747 :     ( *hCombinedOrientationData )->cur_subframe_samples_rendered = 0;
    1467         747 :     move16();
    1468             : 
    1469         747 :     return IVAS_ERR_OK;
    1470             : }
    1471             : /*-----------------------------------------------------------------------*
    1472             :  * ivas_combined_orientation_close()
    1473             :  *
    1474             :  * Deallocate combined orientation handle
    1475             :  *-----------------------------------------------------------------------*/
    1476             : 
    1477        1270 : void ivas_combined_orientation_close_fx(
    1478             :     COMBINED_ORIENTATION_HANDLE *hCombinedOrientationData /* i/o: combined orientation handle   */
    1479             : )
    1480             : {
    1481        1270 :     test();
    1482        1270 :     IF( hCombinedOrientationData == NULL || *hCombinedOrientationData == NULL )
    1483             :     {
    1484         523 :         return;
    1485             :     }
    1486             : 
    1487         747 :     free( ( *hCombinedOrientationData ) );
    1488         747 :     *hCombinedOrientationData = NULL;
    1489             : 
    1490         747 :     return;
    1491             : }
    1492             : 
    1493             : 
    1494             : /*-------------------------------------------------------------------------
    1495             :  * combine_external_and_head_orientations_dec()
    1496             :  *
    1497             :  *
    1498             :  *------------------------------------------------------------------------*/
    1499             : 
    1500       69350 : ivas_error combine_external_and_head_orientations_dec(
    1501             :     HEAD_TRACK_DATA_HANDLE hHeadTrackData,               /* i  : head track handle              */
    1502             :     EXTERNAL_ORIENTATION_HANDLE hExtOrientationData,     /* i  : external orientation handle    */
    1503             :     COMBINED_ORIENTATION_HANDLE hCombinedOrientationData /* i/o: combined orientation handle    */
    1504             : )
    1505             : {
    1506       69350 :     IVAS_QUATERNION *pHeadRotQuaternion = NULL;
    1507       69350 :     IVAS_VECTOR3 *listenerPos = NULL;
    1508             : 
    1509       69350 :     IF( hHeadTrackData != NULL )
    1510             :     {
    1511       69350 :         pHeadRotQuaternion = hHeadTrackData->Quaternions;
    1512       69350 :         listenerPos = hHeadTrackData->Pos;
    1513             :     }
    1514       69350 :     return combine_external_and_head_orientations( pHeadRotQuaternion, listenerPos,
    1515             :                                                    hExtOrientationData, hCombinedOrientationData );
    1516             : }
    1517             : 
    1518             : 
    1519             : /*-------------------------------------------------------------------------
    1520             :  * combine_external_and_head_orientations_rend()
    1521             :  *
    1522             :  *
    1523             :  *------------------------------------------------------------------------*/
    1524             : 
    1525     1126140 : ivas_error combine_external_and_head_orientations_rend(
    1526             :     IVAS_REND_HeadRotData *hHeadTrackData,               /* i  : head track handle              */
    1527             :     EXTERNAL_ORIENTATION_HANDLE hExtOrientationData,     /* i  : external orientation handle    */
    1528             :     COMBINED_ORIENTATION_HANDLE hCombinedOrientationData /* i/o: combined orientation handle    */
    1529             : )
    1530             : {
    1531     1126140 :     IVAS_QUATERNION *headRotQuaternions = NULL;
    1532     1126140 :     IVAS_VECTOR3 *listenerPos = NULL;
    1533             :     Word16 i;
    1534             : 
    1535     1126140 :     IF( hHeadTrackData != NULL )
    1536             :     {
    1537     1126140 :         IF( hHeadTrackData->headRotEnabled )
    1538             :         {
    1539      180024 :             headRotQuaternions = hHeadTrackData->headPositions;
    1540      180024 :             listenerPos = hHeadTrackData->Pos;
    1541             :         }
    1542             :     }
    1543           0 :     ELSE IF( hExtOrientationData != NULL )
    1544             :     {
    1545             :         /* Head rotation data not available, use the freezed value or disable */
    1546           0 :         FOR( i = 0; i < hExtOrientationData->num_subframes; i++ )
    1547             :         {
    1548           0 :             IF( NE_16( hExtOrientationData->enableHeadRotation[i], 2 ) )
    1549             :             {
    1550           0 :                 hExtOrientationData->enableHeadRotation[i] = 0;
    1551           0 :                 move16();
    1552             :             }
    1553             :         }
    1554             :     }
    1555             : 
    1556     1126140 :     return combine_external_and_head_orientations( headRotQuaternions, listenerPos,
    1557             :                                                    hExtOrientationData, hCombinedOrientationData );
    1558             : }
    1559             : 
    1560             : 
    1561             : /*-------------------------------------------------------------------------
    1562             :  * combine_external_and_head_orientations()
    1563             :  *
    1564             :  * Combine the external orientations and the head orientation.
    1565             :  * NOTE that the external orientations are inversed.
    1566             :  *------------------------------------------------------------------------*/
    1567     1195490 : ivas_error combine_external_and_head_orientations(
    1568             :     IVAS_QUATERNION *headRotQuaternions,                 /* i  : quaternions for head rotation                            */
    1569             :     IVAS_VECTOR3 *listenerPos,                           /* i  : listener position                                        */
    1570             :     EXTERNAL_ORIENTATION_HANDLE hExtOrientationData,     /* i  : external orientation handle                              */
    1571             :     COMBINED_ORIENTATION_HANDLE hCombinedOrientationData /* i/o: combined orientation handle                              */
    1572             : )
    1573             : {
    1574             :     Word16 i;
    1575             :     Word16 j;
    1576             :     IVAS_QUATERNION identity;
    1577             :     IVAS_VECTOR3 origo;
    1578     1195490 :     identity.w_fx = ONE_IN_Q31;
    1579     1195490 :     move32();
    1580     1195490 :     identity.x_fx = 0;
    1581     1195490 :     move32();
    1582     1195490 :     identity.y_fx = 0;
    1583     1195490 :     move32();
    1584     1195490 :     identity.z_fx = 0;
    1585     1195490 :     move32();
    1586     1195490 :     identity.q_fact = 31;
    1587     1195490 :     move16();
    1588     1195490 :     origo.x_fx = 0;
    1589     1195490 :     move32();
    1590     1195490 :     origo.y_fx = 0;
    1591     1195490 :     move32();
    1592     1195490 :     origo.z_fx = 0;
    1593     1195490 :     move32();
    1594     1195490 :     origo.q_fact = 31;
    1595     1195490 :     move16();
    1596             : 
    1597             :     /* Form combined orientations or return if no data available */
    1598     1195490 :     test();
    1599     1195490 :     test();
    1600     1195490 :     IF( hCombinedOrientationData == NULL )
    1601             :     {
    1602           0 :         test();
    1603           0 :         IF( headRotQuaternions != NULL || hExtOrientationData != NULL )
    1604             :         {
    1605           0 :             return IVAS_ERR_UNEXPECTED_NULL_POINTER;
    1606             :         }
    1607             :         ELSE
    1608             :         {
    1609           0 :             return IVAS_ERR_OK;
    1610             :         }
    1611             :     }
    1612     1195490 :     ELSE IF( headRotQuaternions == NULL && hExtOrientationData == NULL )
    1613             :     {
    1614             :         /* Reset the combined orientations and rotations */
    1615           0 :         hCombinedOrientationData->isInterpolationOngoing = FALSE;
    1616           0 :         move16();
    1617           0 :         hCombinedOrientationData->interpolationCoefficient_fx = ONE_IN_Q30;
    1618           0 :         move32();
    1619           0 :         hCombinedOrientationData->interpolationIncrement_fx = ONE_IN_Q30;
    1620           0 :         move32();
    1621           0 :         hCombinedOrientationData->Quaternions_ext_interpolation_start = identity;
    1622           0 :         hCombinedOrientationData->Quaternions_ext_interpolation_target = identity;
    1623           0 :         FOR( i = 0; i < hCombinedOrientationData->num_subframes; i++ )
    1624             :         {
    1625           0 :             hCombinedOrientationData->enableCombinedOrientation[i] = 0;
    1626           0 :             move16();
    1627           0 :             hCombinedOrientationData->Quaternions[i] = identity;
    1628           0 :             hCombinedOrientationData->listenerPos[i] = origo;
    1629             : 
    1630           0 :             FOR( j = 0; j < 3; j++ )
    1631             :             {
    1632           0 :                 set_zero_fx( hCombinedOrientationData->Rmat_fx[i][j], 3 );
    1633           0 :                 hCombinedOrientationData->Rmat_fx[i][j][j] = ONE_IN_Q30;
    1634           0 :                 move32();
    1635             :             }
    1636             :         }
    1637             :     }
    1638     1195490 :     ELSE IF( hExtOrientationData == NULL && headRotQuaternions != NULL )
    1639             :     {
    1640             :         /* Head rotation only */
    1641      200750 :         FOR( i = 0; i < hCombinedOrientationData->num_subframes; i++ )
    1642             :         {
    1643      160600 :             hCombinedOrientationData->Quaternions[i] = headRotQuaternions[i];
    1644             :         }
    1645             :     }
    1646             : 
    1647     1195490 :     IF( hExtOrientationData != NULL )
    1648             :     {
    1649             :         /* External orientations */
    1650     3190508 :         FOR( i = 0; i < hCombinedOrientationData->num_subframes; i++ )
    1651             :         {
    1652             :             /* Check for frozen external orientation */
    1653     2035168 :             IF( EQ_16( hExtOrientationData->enableExternalOrientation[i], 2 ) )
    1654             :             {
    1655       17391 :                 IF( NE_16( hCombinedOrientationData->isExtOrientationFrozen, 1 ) )
    1656             :                 {
    1657          31 :                     hCombinedOrientationData->Quaternion_frozen_ext = hExtOrientationData->Quaternions[i];
    1658          31 :                     hCombinedOrientationData->isExtOrientationFrozen = 1;
    1659          31 :                     move16();
    1660             :                 }
    1661             :             }
    1662             :             ELSE
    1663             :             {
    1664     2017777 :                 hCombinedOrientationData->Quaternion_frozen_ext = identity;
    1665     2017777 :                 hCombinedOrientationData->isExtOrientationFrozen = 0;
    1666     2017777 :                 move16();
    1667             :             }
    1668     2035168 :             test();
    1669     2035168 :             IF( EQ_16( hExtOrientationData->enableRotationInterpolation[i], 1 ) && hExtOrientationData->enableExternalOrientation[i] > 0 )
    1670             :             {
    1671       60970 :                 test();
    1672       60970 :                 test();
    1673       60970 :                 IF( EQ_16( hCombinedOrientationData->isInterpolationOngoing, true ) && LE_32( hCombinedOrientationData->interpolationCoefficient_fx, ONE_IN_Q30 ) && EQ_16( are_orientations_same_fx( &hCombinedOrientationData->Quaternions_ext_interpolation_target, &hExtOrientationData->Quaternions[i] ), true ) )
    1674             :                 {
    1675             :                     /* Continue interpolation */
    1676       50636 :                     hCombinedOrientationData->Quaternions_ext_interpolation_start.w_fx = L_shr( hCombinedOrientationData->Quaternions_ext_interpolation_start.w_fx, sub( hCombinedOrientationData->Quaternions_ext_interpolation_start.q_fact, Q29 ) ); // Q29
    1677       50636 :                     move32();
    1678       50636 :                     hCombinedOrientationData->Quaternions_ext_interpolation_start.x_fx = L_shr( hCombinedOrientationData->Quaternions_ext_interpolation_start.x_fx, sub( hCombinedOrientationData->Quaternions_ext_interpolation_start.q_fact, Q29 ) ); // Q29
    1679       50636 :                     move32();
    1680       50636 :                     hCombinedOrientationData->Quaternions_ext_interpolation_start.y_fx = L_shr( hCombinedOrientationData->Quaternions_ext_interpolation_start.y_fx, sub( hCombinedOrientationData->Quaternions_ext_interpolation_start.q_fact, Q29 ) ); // Q29
    1681       50636 :                     move32();
    1682       50636 :                     hCombinedOrientationData->Quaternions_ext_interpolation_start.z_fx = L_shr( hCombinedOrientationData->Quaternions_ext_interpolation_start.z_fx, sub( hCombinedOrientationData->Quaternions_ext_interpolation_start.q_fact, Q29 ) ); // Q29
    1683       50636 :                     move32();
    1684             : 
    1685       50636 :                     hCombinedOrientationData->Quaternions_ext_interpolation_target.w_fx = L_shr( hCombinedOrientationData->Quaternions_ext_interpolation_target.w_fx, sub( hCombinedOrientationData->Quaternions_ext_interpolation_target.q_fact, Q29 ) ); // Q29
    1686       50636 :                     move32();
    1687       50636 :                     hCombinedOrientationData->Quaternions_ext_interpolation_target.x_fx = L_shr( hCombinedOrientationData->Quaternions_ext_interpolation_target.x_fx, sub( hCombinedOrientationData->Quaternions_ext_interpolation_target.q_fact, Q29 ) ); // Q29
    1688       50636 :                     move32();
    1689       50636 :                     hCombinedOrientationData->Quaternions_ext_interpolation_target.y_fx = L_shr( hCombinedOrientationData->Quaternions_ext_interpolation_target.y_fx, sub( hCombinedOrientationData->Quaternions_ext_interpolation_target.q_fact, Q29 ) ); // Q29
    1690       50636 :                     move32();
    1691       50636 :                     hCombinedOrientationData->Quaternions_ext_interpolation_target.z_fx = L_shr( hCombinedOrientationData->Quaternions_ext_interpolation_target.z_fx, sub( hCombinedOrientationData->Quaternions_ext_interpolation_target.q_fact, Q29 ) ); // Q29
    1692       50636 :                     move32();
    1693             : 
    1694       50636 :                     hCombinedOrientationData->Quaternions_ext_interpolation_start.q_fact = Q29;
    1695       50636 :                     move16();
    1696       50636 :                     hCombinedOrientationData->Quaternions_ext_interpolation_target.q_fact = Q29;
    1697       50636 :                     move16();
    1698       50636 :                     QuaternionSlerp_fx( hCombinedOrientationData->Quaternions_ext_interpolation_start, hCombinedOrientationData->Quaternions_ext_interpolation_target, hCombinedOrientationData->interpolationCoefficient_fx, &hCombinedOrientationData->Quaternions[i] );
    1699       50636 :                     hCombinedOrientationData->interpolationCoefficient_fx = L_add( hCombinedOrientationData->interpolationIncrement_fx, hCombinedOrientationData->interpolationCoefficient_fx );
    1700       50636 :                     move32();
    1701             :                 }
    1702             :                 ELSE
    1703             :                 {
    1704             :                     /* Stop interpolation or check for new interpolation */
    1705       10334 :                     hCombinedOrientationData->isInterpolationOngoing = FALSE;
    1706       10334 :                     move16();
    1707       10334 :                     hCombinedOrientationData->interpolationCoefficient_fx = ONE_IN_Q30;
    1708       10334 :                     move32();
    1709       10334 :                     hCombinedOrientationData->interpolationIncrement_fx = ONE_IN_Q30;
    1710       10334 :                     move32();
    1711       10334 :                     external_target_interpolation_fx( hExtOrientationData, hCombinedOrientationData, i );
    1712       10334 :                     Word16 l_shift = 0;
    1713       10334 :                     move16();
    1714       10334 :                     l_shift = s_min( s_min( Q_factor_L_32( hCombinedOrientationData->Quaternions[i].w_fx ), Q_factor_L_32( hCombinedOrientationData->Quaternions[i].x_fx ) ), s_min( Q_factor_L_32( hCombinedOrientationData->Quaternions[i].y_fx ), Q_factor_L_32( hCombinedOrientationData->Quaternions[i].z_fx ) ) );
    1715       10334 :                     hCombinedOrientationData->Quaternions[i].w_fx = L_shl( hCombinedOrientationData->Quaternions[i].w_fx, l_shift ); // q_fact+l_shift
    1716       10334 :                     move32();
    1717       10334 :                     hCombinedOrientationData->Quaternions[i].x_fx = L_shl( hCombinedOrientationData->Quaternions[i].x_fx, l_shift ); // q_fact+l_shift
    1718       10334 :                     move32();
    1719       10334 :                     hCombinedOrientationData->Quaternions[i].y_fx = L_shl( hCombinedOrientationData->Quaternions[i].y_fx, l_shift ); // q_fact+l_shift
    1720       10334 :                     move32();
    1721       10334 :                     hCombinedOrientationData->Quaternions[i].z_fx = L_shl( hCombinedOrientationData->Quaternions[i].z_fx, l_shift ); // q_fact+l_shift
    1722       10334 :                     move32();
    1723       10334 :                     hCombinedOrientationData->Quaternions[i].q_fact = add( hCombinedOrientationData->Quaternions[i].q_fact, l_shift );
    1724       10334 :                     move16();
    1725             :                 }
    1726             :             }
    1727             :             ELSE
    1728             :             {
    1729             :                 /* Interpolation disabled, use the current orientation values */
    1730             : 
    1731             :                 /* Use the most recent external orientation */
    1732     1974198 :                 IF( EQ_16( hExtOrientationData->enableExternalOrientation[i], 1 ) )
    1733             :                 {
    1734       36393 :                     hCombinedOrientationData->Quaternions[i] = hExtOrientationData->Quaternions[i];
    1735             :                 }
    1736             :                 /* Use the freezed external orientation */
    1737     1937805 :                 ELSE IF( EQ_16( hExtOrientationData->enableExternalOrientation[i], 2 ) )
    1738             :                 {
    1739       12803 :                     hCombinedOrientationData->Quaternions[i] = hCombinedOrientationData->Quaternion_frozen_ext;
    1740             :                 }
    1741             :             }
    1742             :         }
    1743             :     }
    1744     1195490 :     test();
    1745     1195490 :     IF( hExtOrientationData != NULL && headRotQuaternions != NULL )
    1746             :     {
    1747             :         /* Combine head and external orientations */
    1748      614084 :         FOR( i = 0; i < hCombinedOrientationData->num_subframes; i++ )
    1749             :         {
    1750             :             /* Check for frozen head rotation */
    1751      404860 :             IF( EQ_16( hExtOrientationData->enableHeadRotation[i], 2 ) )
    1752             :             {
    1753       12400 :                 IF( NE_16( hCombinedOrientationData->isHeadRotationFrozen, 1 ) )
    1754             :                 {
    1755          62 :                     hCombinedOrientationData->Quaternion_frozen_head = headRotQuaternions[i];
    1756          62 :                     hCombinedOrientationData->isHeadRotationFrozen = 1;
    1757          62 :                     move16();
    1758             :                 }
    1759             :             }
    1760             :             ELSE
    1761             :             {
    1762      392460 :                 hCombinedOrientationData->Quaternion_frozen_head = identity;
    1763      392460 :                 hCombinedOrientationData->isHeadRotationFrozen = 0;
    1764      392460 :                 move16();
    1765             :             }
    1766             :             /* Use the most recent head rotation */
    1767      404860 :             IF( EQ_16( hExtOrientationData->enableHeadRotation[i], 1 ) )
    1768             :             {
    1769      371655 :                 IF( hExtOrientationData->enableExternalOrientation[i] > 0 )
    1770             :                 {
    1771       76961 :                     QuaternionProduct_fx( hCombinedOrientationData->Quaternions[i], headRotQuaternions[i], &hCombinedOrientationData->Quaternions[i] );
    1772             :                 }
    1773             :                 ELSE
    1774             :                 {
    1775      294694 :                     hCombinedOrientationData->Quaternions[i] = headRotQuaternions[i];
    1776             :                 }
    1777             :             }
    1778             :             /* Use the freezed head rotation */
    1779       33205 :             ELSE IF( EQ_16( hExtOrientationData->enableHeadRotation[i], 2 ) )
    1780             :             {
    1781       12400 :                 IF( hExtOrientationData->enableExternalOrientation[i] > 0 )
    1782             :                 {
    1783       12400 :                     QuaternionProduct_fx( hCombinedOrientationData->Quaternions[i], hCombinedOrientationData->Quaternion_frozen_head, &hCombinedOrientationData->Quaternions[i] );
    1784             :                 }
    1785             :                 ELSE
    1786             :                 {
    1787           0 :                     hCombinedOrientationData->Quaternions[i] = hCombinedOrientationData->Quaternion_frozen_head;
    1788             :                 }
    1789             :             }
    1790             :             /* Reset the combined orientations to identity */
    1791      404860 :             test();
    1792      404860 :             if ( hExtOrientationData->enableHeadRotation[i] == 0 && hExtOrientationData->enableExternalOrientation[i] == 0 )
    1793             :             {
    1794           0 :                 hCombinedOrientationData->Quaternions[i] = identity;
    1795             :             }
    1796             :         }
    1797             :     }
    1798     1195490 :     test();
    1799     1195490 :     IF( headRotQuaternions != NULL || hExtOrientationData != NULL )
    1800             :     {
    1801             :         /* Calculate the combined rotation matrix */
    1802     3391258 :         FOR( i = 0; i < hCombinedOrientationData->num_subframes; i++ )
    1803             :         {
    1804     2195768 :             QuatToRotMat_fx( hCombinedOrientationData->Quaternions[i], hCombinedOrientationData->Rmat_fx[i] );
    1805             :         }
    1806             :     }
    1807             : 
    1808             :     /* Save the current orientations */
    1809     1195490 :     IF( hExtOrientationData != NULL )
    1810             :     {
    1811     1155340 :         IF( hExtOrientationData->enableExternalOrientation[hExtOrientationData->num_subframes - 1] > 0 )
    1812             :         {
    1813       27557 :             hCombinedOrientationData->Quaternion_prev_extOrientation = hExtOrientationData->Quaternions[hExtOrientationData->num_subframes - 1];
    1814             :         }
    1815             :         ELSE
    1816             :         {
    1817     1127783 :             hCombinedOrientationData->Quaternion_prev_extOrientation = identity;
    1818             :         }
    1819             :     }
    1820     1195490 :     IF( headRotQuaternions != NULL )
    1821             :     {
    1822      814834 :         FOR( i = 0; i < hCombinedOrientationData->num_subframes; i++ )
    1823             :         {
    1824      565460 :             hCombinedOrientationData->listenerPos[i] = listenerPos[i];
    1825             :         }
    1826             :     }
    1827             :     /* Check if combined orientation is enabled */
    1828     1195490 :     test();
    1829     1195490 :     test();
    1830     1195490 :     test();
    1831     1195490 :     IF( headRotQuaternions != NULL && hExtOrientationData == NULL )
    1832             :     {
    1833      200750 :         FOR( i = 0; i < hCombinedOrientationData->num_subframes; i++ )
    1834             :         {
    1835      160600 :             hCombinedOrientationData->enableCombinedOrientation[i] = 1;
    1836      160600 :             move16();
    1837             :         }
    1838             :     }
    1839     1155340 :     ELSE IF( headRotQuaternions == NULL && hExtOrientationData != NULL )
    1840             :     {
    1841     2576424 :         FOR( i = 0; i < hCombinedOrientationData->num_subframes; i++ )
    1842             :         {
    1843     1630308 :             IF( hExtOrientationData->enableExternalOrientation[i] > 0 )
    1844             :             {
    1845           0 :                 hCombinedOrientationData->enableCombinedOrientation[i] = 1;
    1846           0 :                 move16();
    1847             :             }
    1848             :             ELSE
    1849             :             {
    1850     1630308 :                 hCombinedOrientationData->enableCombinedOrientation[i] = 0;
    1851     1630308 :                 move16();
    1852             :             }
    1853             :         }
    1854             :     }
    1855      209224 :     ELSE IF( headRotQuaternions != NULL && hExtOrientationData != NULL )
    1856             :     {
    1857      614084 :         FOR( i = 0; i < hCombinedOrientationData->num_subframes; i++ )
    1858             :         {
    1859      404860 :             test();
    1860      404860 :             IF( hExtOrientationData->enableExternalOrientation[i] > 0 || ( hExtOrientationData->enableHeadRotation[i] > 0 ) )
    1861             :             {
    1862      404860 :                 hCombinedOrientationData->enableCombinedOrientation[i] = 1;
    1863      404860 :                 move16();
    1864             :             }
    1865             :             ELSE
    1866             :             {
    1867           0 :                 hCombinedOrientationData->enableCombinedOrientation[i] = 0;
    1868           0 :                 move16();
    1869             :             }
    1870             :         }
    1871             :     }
    1872             :     ELSE
    1873             :     {
    1874           0 :         FOR( i = 0; i < hCombinedOrientationData->num_subframes; i++ )
    1875             :         {
    1876           0 :             hCombinedOrientationData->enableCombinedOrientation[i] = 0;
    1877           0 :             move16();
    1878             :         }
    1879             :     }
    1880             : 
    1881     1195490 :     hCombinedOrientationData->subframe_idx = 0;
    1882     1195490 :     move16();
    1883     1195490 :     hCombinedOrientationData->cur_subframe_samples_rendered = 0;
    1884     1195490 :     move16();
    1885     1195490 :     hCombinedOrientationData->subframe_idx_start = 0;
    1886     1195490 :     move16();
    1887     1195490 :     hCombinedOrientationData->cur_subframe_samples_rendered_start = 0;
    1888     1195490 :     move16();
    1889     3391258 :     FOR( i = 0; i < hCombinedOrientationData->num_subframes; i++ )
    1890             :     {
    1891     8783072 :         FOR( j = 0; j < 3; j++ )
    1892             :         {
    1893    26349216 :             FOR( Word16 k = 0; k < 3; k++ )
    1894             :             {
    1895    19761912 :                 hCombinedOrientationData->Rmat_fx[i][j][k] = L_shl( hCombinedOrientationData->Rmat_fx[i][j][k], sub( 62, shl( hCombinedOrientationData->Quaternions[i].q_fact, 1 ) ) ); // Q30
    1896    19761912 :                 move32();
    1897             :             }
    1898             :         }
    1899             :     }
    1900             : 
    1901     1195490 :     Word16 l_shift = 0;
    1902     1195490 :     move16();
    1903     3391258 :     FOR( i = 0; i < hCombinedOrientationData->num_subframes; i++ )
    1904             :     {
    1905     2195768 :         l_shift = s_min( s_min( Q_factor_L_32( hCombinedOrientationData->Quaternions[i].w_fx ), Q_factor_L_32( hCombinedOrientationData->Quaternions[i].x_fx ) ), s_min( Q_factor_L_32( hCombinedOrientationData->Quaternions[i].y_fx ), Q_factor_L_32( hCombinedOrientationData->Quaternions[i].z_fx ) ) );
    1906     2195768 :         hCombinedOrientationData->Quaternions[i].w_fx = L_shl( hCombinedOrientationData->Quaternions[i].w_fx, l_shift ); // q_fact+l_shift
    1907     2195768 :         move32();
    1908     2195768 :         hCombinedOrientationData->Quaternions[i].x_fx = L_shl( hCombinedOrientationData->Quaternions[i].x_fx, l_shift ); // q_fact+l_shift
    1909     2195768 :         move32();
    1910     2195768 :         hCombinedOrientationData->Quaternions[i].y_fx = L_shl( hCombinedOrientationData->Quaternions[i].y_fx, l_shift ); // q_fact+l_shift
    1911     2195768 :         move32();
    1912     2195768 :         hCombinedOrientationData->Quaternions[i].z_fx = L_shl( hCombinedOrientationData->Quaternions[i].z_fx, l_shift ); // q_fact+l_shift
    1913     2195768 :         move32();
    1914     2195768 :         hCombinedOrientationData->Quaternions[i].q_fact = add( hCombinedOrientationData->Quaternions[i].q_fact, l_shift );
    1915     2195768 :         move16();
    1916             :     }
    1917     1195490 :     return IVAS_ERR_OK;
    1918             : }
    1919             : 
    1920             : /*-------------------------------------------------------------------------
    1921             :  * external_target_interpolation()
    1922             :  *
    1923             :  *
    1924             :  *------------------------------------------------------------------------*/
    1925       10334 : static void external_target_interpolation_fx(
    1926             :     EXTERNAL_ORIENTATION_HANDLE hExtOrientationData,      /* i  : external orientation handle   */
    1927             :     COMBINED_ORIENTATION_HANDLE hCombinedOrientationData, /* i/o: combined orientation handle   */
    1928             :     const Word16 i                                        /* i  : subframe index                */
    1929             : )
    1930             : {
    1931             :     /* Sanity check for number of frames */
    1932       10334 :     hExtOrientationData->numFramesToTargetOrientation[i] = s_min( hExtOrientationData->numFramesToTargetOrientation[i], hCombinedOrientationData->maximumFramesToTargetOrientation );
    1933       10334 :     move16();
    1934       10334 :     hExtOrientationData->numFramesToTargetOrientation[i] = s_max( hExtOrientationData->numFramesToTargetOrientation[i], 0 );
    1935       10334 :     move16();
    1936             : 
    1937             :     /* Interpolate from the current orientation to the target orientation */
    1938       10334 :     IF( hExtOrientationData->numFramesToTargetOrientation[i] > 0 )
    1939             :     {
    1940        3349 :         IF( EQ_16( are_orientations_same_fx( &hCombinedOrientationData->Quaternions_ext_interpolation_target, &hExtOrientationData->Quaternions[i] ), false ) )
    1941             :         {
    1942             :             /* Target orientation is different from the previous target, update the values */
    1943             : 
    1944             :             /* Set the received orientation as the target */
    1945         249 :             hCombinedOrientationData->Quaternions_ext_interpolation_target = hExtOrientationData->Quaternions[i];
    1946             : 
    1947             :             /* Use the most recent external orientation as the starting orientation */
    1948         249 :             IF( EQ_16( hExtOrientationData->enableExternalOrientation[i], 1 ) )
    1949             :             {
    1950         218 :                 IF( i > 0 )
    1951             :                 {
    1952         141 :                     IF( hExtOrientationData->enableExternalOrientation[i - 1] == 0 )
    1953             :                     {
    1954             :                         IVAS_QUATERNION identity;
    1955          31 :                         identity.w_fx = ONE_IN_Q31;
    1956          31 :                         move32();
    1957          31 :                         identity.x_fx = identity.y_fx = identity.z_fx = 0;
    1958          31 :                         move32();
    1959          31 :                         move32();
    1960          31 :                         move32();
    1961          31 :                         identity.q_fact = 31;
    1962          31 :                         move16();
    1963          31 :                         hCombinedOrientationData->Quaternions_ext_interpolation_start = identity;
    1964             :                     }
    1965         110 :                     ELSE IF( EQ_16( hExtOrientationData->enableExternalOrientation[i - 1], 2 ) )
    1966             :                     {
    1967           0 :                         hCombinedOrientationData->Quaternions_ext_interpolation_start = hCombinedOrientationData->Quaternion_frozen_ext;
    1968             :                     }
    1969             :                     ELSE
    1970             :                     {
    1971         110 :                         hCombinedOrientationData->Quaternions_ext_interpolation_start = hExtOrientationData->Quaternions[i - 1];
    1972             :                     }
    1973             :                 }
    1974             :                 ELSE
    1975             :                 {
    1976          77 :                     hCombinedOrientationData->Quaternions_ext_interpolation_start = hCombinedOrientationData->Quaternion_prev_extOrientation;
    1977             :                 }
    1978             :             }
    1979          31 :             ELSE IF( EQ_16( hExtOrientationData->enableExternalOrientation[i], 2 ) )
    1980             :             {
    1981          31 :                 hCombinedOrientationData->Quaternions_ext_interpolation_start = hCombinedOrientationData->Quaternion_frozen_ext;
    1982             :             }
    1983         249 :             Word16 tmp_e = 0;
    1984         249 :             move16();
    1985             :             Word32 tmp;
    1986             :             /* Calculate the interpolation increment and coefficient */
    1987         249 :             tmp = BASOP_Util_Divide3232_Scale_newton( ONE_IN_Q30, L_shl( L_deposit_l( hExtOrientationData->numFramesToTargetOrientation[i] ), 2 ), &tmp_e );
    1988         249 :             hCombinedOrientationData->interpolationIncrement_fx = L_shl( tmp, sub( tmp_e, 31 ) ); /* Q30 */
    1989         249 :             move32();
    1990         249 :             hCombinedOrientationData->interpolationCoefficient_fx = hCombinedOrientationData->interpolationIncrement_fx;
    1991         249 :             move32();
    1992             :         }
    1993             : 
    1994             :         /* Interpolate */
    1995        3349 :         hCombinedOrientationData->isInterpolationOngoing = TRUE;
    1996        3349 :         move16();
    1997        3349 :         hCombinedOrientationData->Quaternions_ext_interpolation_start.w_fx = L_shr( hCombinedOrientationData->Quaternions_ext_interpolation_start.w_fx, sub( hCombinedOrientationData->Quaternions_ext_interpolation_start.q_fact, Q29 ) ); // Q29
    1998        3349 :         move32();
    1999        3349 :         hCombinedOrientationData->Quaternions_ext_interpolation_start.x_fx = L_shr( hCombinedOrientationData->Quaternions_ext_interpolation_start.x_fx, sub( hCombinedOrientationData->Quaternions_ext_interpolation_start.q_fact, Q29 ) ); // Q29
    2000        3349 :         move32();
    2001        3349 :         hCombinedOrientationData->Quaternions_ext_interpolation_start.y_fx = L_shr( hCombinedOrientationData->Quaternions_ext_interpolation_start.y_fx, sub( hCombinedOrientationData->Quaternions_ext_interpolation_start.q_fact, Q29 ) ); // Q29
    2002        3349 :         move32();
    2003        3349 :         hCombinedOrientationData->Quaternions_ext_interpolation_start.z_fx = L_shr( hCombinedOrientationData->Quaternions_ext_interpolation_start.z_fx, sub( hCombinedOrientationData->Quaternions_ext_interpolation_start.q_fact, Q29 ) ); // Q29
    2004        3349 :         move32();
    2005             : 
    2006        3349 :         hCombinedOrientationData->Quaternions_ext_interpolation_target.w_fx = L_shr( hCombinedOrientationData->Quaternions_ext_interpolation_target.w_fx, sub( hCombinedOrientationData->Quaternions_ext_interpolation_target.q_fact, Q29 ) ); // Q29
    2007        3349 :         move32();
    2008        3349 :         hCombinedOrientationData->Quaternions_ext_interpolation_target.x_fx = L_shr( hCombinedOrientationData->Quaternions_ext_interpolation_target.x_fx, sub( hCombinedOrientationData->Quaternions_ext_interpolation_target.q_fact, Q29 ) ); // Q29
    2009        3349 :         move32();
    2010        3349 :         hCombinedOrientationData->Quaternions_ext_interpolation_target.y_fx = L_shr( hCombinedOrientationData->Quaternions_ext_interpolation_target.y_fx, sub( hCombinedOrientationData->Quaternions_ext_interpolation_target.q_fact, Q29 ) ); // Q29
    2011        3349 :         move32();
    2012        3349 :         hCombinedOrientationData->Quaternions_ext_interpolation_target.z_fx = L_shr( hCombinedOrientationData->Quaternions_ext_interpolation_target.z_fx, sub( hCombinedOrientationData->Quaternions_ext_interpolation_target.q_fact, Q29 ) ); // Q29
    2013        3349 :         move32();
    2014             : 
    2015        3349 :         hCombinedOrientationData->Quaternions_ext_interpolation_start.q_fact = Q29;
    2016        3349 :         move16();
    2017        3349 :         hCombinedOrientationData->Quaternions_ext_interpolation_target.q_fact = Q29;
    2018        3349 :         move16();
    2019        3349 :         QuaternionSlerp_fx( hCombinedOrientationData->Quaternions_ext_interpolation_start, hCombinedOrientationData->Quaternions_ext_interpolation_target, hCombinedOrientationData->interpolationCoefficient_fx, &hCombinedOrientationData->Quaternions[i] );
    2020        3349 :         hCombinedOrientationData->interpolationCoefficient_fx = L_add_sat( hCombinedOrientationData->interpolationCoefficient_fx, hCombinedOrientationData->interpolationIncrement_fx );
    2021        3349 :         move32();
    2022             :     }
    2023             :     ELSE
    2024             :     {
    2025             :         /* Use the target orientation immediately */
    2026        6985 :         hCombinedOrientationData->isInterpolationOngoing = FALSE;
    2027        6985 :         move16();
    2028        6985 :         hCombinedOrientationData->interpolationCoefficient_fx = ONE_IN_Q30;
    2029        6985 :         move32();
    2030        6985 :         hCombinedOrientationData->interpolationIncrement_fx = ONE_IN_Q30;
    2031        6985 :         move32();
    2032        6985 :         hCombinedOrientationData->Quaternions[i] = hExtOrientationData->Quaternions[i];
    2033             :     }
    2034             : 
    2035       10334 :     return;
    2036             : }
    2037             : 
    2038             : /*-------------------------------------------------------------------------
    2039             :  * are_orientations_same()
    2040             :  *
    2041             :  *
    2042             :  *------------------------------------------------------------------------*/
    2043       54203 : static bool are_orientations_same_fx(
    2044             :     const IVAS_QUATERNION *orientation1,
    2045             :     const IVAS_QUATERNION *orientation2 )
    2046             : {
    2047       54203 :     bool orientationsAreSame = true;
    2048       54203 :     move16();
    2049       54203 :     Word32 error_margin_fx = 107374182; // 0.05f in Q31
    2050       54203 :     move32();
    2051       54203 :     Word16 error_margin_e = 0;
    2052       54203 :     move16();
    2053       54203 :     Word16 w_e = 0, x_e = 0, y_e = 0, z_e = 0;
    2054       54203 :     move16();
    2055       54203 :     move16();
    2056       54203 :     move16();
    2057       54203 :     move16();
    2058       54203 :     Word32 w_result = 0, x_result = 0, y_result = 0, z_result = 0;
    2059       54203 :     move32();
    2060       54203 :     move32();
    2061       54203 :     move32();
    2062       54203 :     move32();
    2063       54203 :     w_result = L_abs( BASOP_Util_Add_Mant32Exp( orientation1->w_fx, sub( 31, orientation1->q_fact ), L_negate( orientation2->w_fx ), sub( 31, orientation2->q_fact ), &w_e ) );
    2064       54203 :     x_result = L_abs( BASOP_Util_Add_Mant32Exp( orientation1->x_fx, sub( 31, orientation1->q_fact ), L_negate( orientation2->x_fx ), sub( 31, orientation2->q_fact ), &x_e ) );
    2065       54203 :     y_result = L_abs( BASOP_Util_Add_Mant32Exp( orientation1->y_fx, sub( 31, orientation1->q_fact ), L_negate( orientation2->y_fx ), sub( 31, orientation2->q_fact ), &y_e ) );
    2066       54203 :     z_result = L_abs( BASOP_Util_Add_Mant32Exp( orientation1->z_fx, sub( 31, orientation1->q_fact ), L_negate( orientation2->z_fx ), sub( 31, orientation2->q_fact ), &z_e ) );
    2067       54203 :     Word16 Flag_1 = BASOP_Util_Cmp_Mant32Exp( w_result, w_e, error_margin_fx, error_margin_e );
    2068       54203 :     Word16 Flag_2 = BASOP_Util_Cmp_Mant32Exp( x_result, x_e, error_margin_fx, error_margin_e );
    2069       54203 :     Word16 Flag_3 = BASOP_Util_Cmp_Mant32Exp( y_result, y_e, error_margin_fx, error_margin_e );
    2070       54203 :     Word16 Flag_4 = BASOP_Util_Cmp_Mant32Exp( z_result, z_e, error_margin_fx, error_margin_e );
    2071             : 
    2072       54203 :     test();
    2073       54203 :     test();
    2074       54203 :     test();
    2075      108073 :     if ( EQ_16( Flag_1, 1 ) ||
    2076      107740 :          EQ_16( Flag_2, 1 ) ||
    2077      107740 :          EQ_16( Flag_3, 1 ) ||
    2078       53870 :          EQ_16( Flag_4, 1 ) )
    2079             :     {
    2080         467 :         orientationsAreSame = false;
    2081         467 :         move16();
    2082             :     }
    2083             : 
    2084       54203 :     return orientationsAreSame;
    2085             : }
    2086             : 
    2087             : 
    2088             : /*-----------------------------------------------------------------------*
    2089             :  * Local Function definitions
    2090             :  *-----------------------------------------------------------------------*/
    2091             : /*-------------------------------------------------------------------------
    2092             :  * Helper functions used by SHrotmatgen,
    2093             :  * an implementation of the algorithm in
    2094             :  * Ivanic, J. & Ruedenberg, K., J. Phys. Chem. 100, 6342 (1996)
    2095             :  *------------------------------------------------------------------------*/
    2096    11445085 : static Word32 SHrot_p_fx(
    2097             :     const Word16 i,
    2098             :     const Word16 l,
    2099             :     const Word16 a,
    2100             :     const Word16 b,
    2101             :     Word16 SHrotmat[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM], // Q14
    2102             :     Word16 R_lm1[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM] )   // Q14
    2103             : {
    2104             : 
    2105    11445085 :     Word16 ri1 = 0, rim1 = 0, ri0 = 0, R_lm1_1 = 0, R_lm1_2 = 0;
    2106    11445085 :     move16();
    2107    11445085 :     move16();
    2108    11445085 :     move16();
    2109    11445085 :     move16();
    2110    11445085 :     move16();
    2111             : 
    2112    11445085 :     Word32 p = 0;
    2113    11445085 :     move32();
    2114             : 
    2115    11445085 :     ri1 = SHrotmat[i + 1 + 1][1 + 1 + 1]; // Q14
    2116    11445085 :     move16();
    2117    11445085 :     rim1 = SHrotmat[i + 1 + 1][-1 + 1 + 1]; // Q14
    2118    11445085 :     move16();
    2119    11445085 :     ri0 = SHrotmat[i + 1 + 1][0 + 1 + 1]; // Q14
    2120    11445085 :     move16();
    2121             : 
    2122    11445085 :     IF( EQ_16( b, -l ) )
    2123             :     {
    2124     1886655 :         R_lm1_1 = R_lm1[a + l - 1][0]; // Q14
    2125     1886655 :         move16();
    2126     1886655 :         R_lm1_2 = R_lm1[a + l - 1][2 * l - 2]; // Q14
    2127     1886655 :         move16();
    2128     1886655 :         p = L_mac0( L_mult0( ri1, R_lm1_1 ), rim1, R_lm1_2 ); // Q28
    2129             :     }
    2130             :     ELSE
    2131             :     {
    2132     9558430 :         IF( EQ_16( b, l ) )
    2133             :         {
    2134     1886655 :             R_lm1_1 = R_lm1[a + l - 1][2 * l - 2]; // Q14
    2135     1886655 :             move16();
    2136     1886655 :             R_lm1_2 = R_lm1[a + l - 1][0]; // Q14
    2137     1886655 :             move16();
    2138     1886655 :             p = L_msu0( L_mult0( ri1, R_lm1_1 ), rim1, R_lm1_2 ); // Q28
    2139             :         }
    2140             :         ELSE
    2141             :         {
    2142     7671775 :             R_lm1_1 = R_lm1[a + l - 1][b + l - 1];
    2143     7671775 :             move16();
    2144     7671775 :             p = L_mult0( ri0, R_lm1_1 ); // Q28
    2145             :         }
    2146             :     }
    2147             : 
    2148    11445085 :     return p; // Q28
    2149             : }
    2150             : 
    2151     2546975 : static Word32 SHrot_u_fx(
    2152             :     const Word16 l,
    2153             :     const Word16 m,
    2154             :     const Word16 n,
    2155             :     Word16 SHrotmat[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM], // Q14
    2156             :     Word16 R_lm1[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM]     // Q14
    2157             : )
    2158             : {
    2159     2546975 :     return SHrot_p_fx( 0, l, m, n, SHrotmat, R_lm1 );
    2160             : }
    2161             : 
    2162     3836765 : static Word32 SHrot_v_fx(
    2163             :     const Word16 l,
    2164             :     const Word16 m,
    2165             :     const Word16 n,
    2166             :     Word16 SHrotmat[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM], // Q14
    2167             :     Word16 R_lm1[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM]     // Q14
    2168             : )
    2169             : {
    2170             : 
    2171             :     Word32 result;
    2172             :     Word32 p0, p1;
    2173             :     Word16 d;
    2174             : 
    2175     3836765 :     IF( m == 0 )
    2176             :     {
    2177      644895 :         p0 = SHrot_p_fx( 1, l, 1, n, SHrotmat, R_lm1 );   // Q28
    2178      644895 :         p1 = SHrot_p_fx( -1, l, -1, n, SHrotmat, R_lm1 ); // Q28
    2179      644895 :         result = L_shr( L_add( p0, p1 ), 2 );             // converting to Q27
    2180             :     }
    2181             :     ELSE
    2182             :     {
    2183     3191870 :         IF( m > 0 )
    2184             :         {
    2185     1595935 :             d = 0;
    2186     1595935 :             move16();
    2187     1595935 :             if ( EQ_16( m, 1 ) )
    2188             :             {
    2189      644895 :                 d = 1;
    2190      644895 :                 move16();
    2191             :             }
    2192     1595935 :             p0 = SHrot_p_fx( 1, l, sub( m, 1 ), n, SHrotmat, R_lm1 );                                           // Q28
    2193     1595935 :             p1 = SHrot_p_fx( -1, l, add( negate( m ), 1 ), n, SHrotmat, R_lm1 );                                // Q28
    2194     1595935 :             result = Msub_32_16_r( Mpy_32_16_r( p0, square_root16_table[1 + d] ), p1, shl( sub( 1, d ), 14 ) ); // Q27
    2195             :         }
    2196             :         ELSE
    2197             :         {
    2198     1595935 :             d = 0;
    2199     1595935 :             move16();
    2200     1595935 :             if ( EQ_16( m, -1 ) )
    2201             :             {
    2202      644895 :                 d = 1;
    2203      644895 :                 move16();
    2204             :             }
    2205     1595935 :             p0 = SHrot_p_fx( 1, l, add( m, 1 ), n, SHrotmat, R_lm1 );
    2206     1595935 :             p1 = SHrot_p_fx( -1, l, negate( add( m, 1 ) ), n, SHrotmat, R_lm1 );
    2207     1595935 :             result = Madd_32_16_r( Mpy_32_16_r( p0, shl( sub( 1, d ), 14 ) ), p1, square_root16_table[1 + d] ); // Q27
    2208             :         }
    2209             :     }
    2210     3836765 :     return result; // Q27
    2211             : }
    2212             : 
    2213      612290 : static Word32 SHrot_w_fx(
    2214             :     const Word16 l,
    2215             :     const Word16 m,
    2216             :     const Word16 n,
    2217             :     Word16 SHrotmat[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM], // Q14
    2218             :     Word16 R_lm1[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM]     // Q14
    2219             : )
    2220             : {
    2221             :     Word32 result, p0, p1;
    2222             : 
    2223      612290 :     IF( m == 0 )
    2224             :     {
    2225           0 :         printf( "ERROR should not be called\n" );
    2226           0 :         return 0;
    2227             :     }
    2228             :     ELSE
    2229             :     {
    2230      612290 :         IF( m > 0 )
    2231             :         {
    2232      306145 :             p0 = SHrot_p_fx( 1, l, add( m, 1 ), n, SHrotmat, R_lm1 );            // Q28
    2233      306145 :             p1 = SHrot_p_fx( -1, l, negate( add( m, 1 ) ), n, SHrotmat, R_lm1 ); // Q28
    2234      306145 :             result = L_add( p0, p1 );                                            // Q28
    2235             :         }
    2236             :         ELSE
    2237             :         {
    2238      306145 :             p0 = SHrot_p_fx( 1, l, sub( m, 1 ), n, SHrotmat, R_lm1 );  // Q28
    2239      306145 :             p1 = SHrot_p_fx( -1, l, sub( 1, m ), n, SHrotmat, R_lm1 ); // Q28
    2240      306145 :             result = L_sub( p0, p1 );                                  // Q28
    2241             :         }
    2242             :     }
    2243             : 
    2244      612290 :     return result; // Q28
    2245             : }
    2246             : 
    2247             : 
    2248             : /*-------------------------------------------------------------------------
    2249             :  * SHrotmatgen_fx()
    2250             :  *
    2251             :  *
    2252             :  *------------------------------------------------------------------------*/
    2253             : 
    2254       91765 : void SHrotmatgen_fx(
    2255             :     Word16 SHrotmat[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM], /* o  : rotation matrix in SHD     Q14 */
    2256             :     Word32 Rmat[3][3],                                     /* i  : real-space rotation matrix Q30 */
    2257             :     const Word16 order                                     /* i  : ambisonics order               */
    2258             : )
    2259             : {
    2260       91765 :     Word16 d = 0;
    2261       91765 :     move16();
    2262       91765 :     Word16 band_idx = 0;
    2263       91765 :     move16();
    2264             :     Word16 i, j;
    2265             :     Word16 l, m, n;
    2266             :     Word16 absm;
    2267       91765 :     Word16 sqdenom = 0, sql2mm2 = 0, sqdabsm = 0, sqlabsm = 0;
    2268       91765 :     Word16 u = 0, v = 0, w = 0;
    2269       91765 :     move16();
    2270       91765 :     move16();
    2271       91765 :     move16();
    2272       91765 :     move16();
    2273       91765 :     move16();
    2274       91765 :     move16();
    2275       91765 :     move16();
    2276             : 
    2277       91765 :     Word32 u32_fx = 0, v32_fx = 0, w32_fx = 0;
    2278       91765 :     move32();
    2279       91765 :     move32();
    2280       91765 :     move32();
    2281             : 
    2282             :     Word16 R_lm1[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM];
    2283             : 
    2284     1560005 :     FOR( i = 0; i < HEADROT_SHMAT_DIM; i++ )
    2285             :     {
    2286     1468240 :         set16_fx( R_lm1[i], 0, HEADROT_SHMAT_DIM );
    2287             :     }
    2288             :     Word16 R_l[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM];
    2289             :     Word32 result;
    2290       91765 :     SHrotmat[0][0] = ONE_IN_Q14;
    2291       91765 :     move16();
    2292             : 
    2293       91765 :     SHrotmat[1][1] = extract_h( Rmat[1][1] ); // Q14
    2294       91765 :     move16();
    2295       91765 :     SHrotmat[1][2] = extract_h( Rmat[1][2] );
    2296       91765 :     move16();
    2297       91765 :     SHrotmat[1][3] = extract_h( Rmat[1][0] );
    2298       91765 :     move16();
    2299             : 
    2300       91765 :     SHrotmat[2][1] = extract_h( Rmat[2][1] ); // Q14
    2301       91765 :     move16();
    2302       91765 :     SHrotmat[2][2] = extract_h( Rmat[2][2] );
    2303       91765 :     move16();
    2304       91765 :     SHrotmat[2][3] = extract_h( Rmat[2][0] );
    2305       91765 :     move16();
    2306             : 
    2307       91765 :     SHrotmat[3][1] = extract_h( Rmat[0][1] ); // Q14
    2308       91765 :     move16();
    2309       91765 :     SHrotmat[3][2] = extract_h( Rmat[0][2] );
    2310       91765 :     move16();
    2311       91765 :     SHrotmat[3][3] = extract_h( Rmat[0][0] );
    2312       91765 :     move16();
    2313             : 
    2314      367060 :     FOR( i = 0; i < 2 * 1 + 1; i++ )
    2315             :     {
    2316     1101180 :         FOR( j = 0; j < 2 * 1 + 1; j++ )
    2317             :         {
    2318      825885 :             R_lm1[i][j] = SHrotmat[i + 1][j + 1]; // Q14
    2319      825885 :             move16();
    2320             :         }
    2321             :     }
    2322       91765 :     band_idx = 4;
    2323       91765 :     move16();
    2324      203250 :     FOR( l = 2; l <= order; l++ )
    2325             :     {
    2326      111485 :         set16_fx( &R_l[0][0], 0, HEADROT_SHMAT_DIM2 );
    2327      756380 :         FOR( m = -l; m <= l; m++ )
    2328             :         {
    2329      644895 :             d = 0;
    2330      644895 :             move16();
    2331      644895 :             if ( m == 0 )
    2332             :             {
    2333      111485 :                 d = 1;
    2334      111485 :                 move16();
    2335             :             }
    2336      644895 :             absm = extract_l( L_abs( m ) );
    2337             : 
    2338      644895 :             sql2mm2 = square_root30_q12[l * l - m * m]; // Q12
    2339      644895 :             move16();
    2340      644895 :             sqdabsm = square_root30_q12[( 1 + d ) * ( l + absm - 1 ) * ( l + absm )]; // Q12
    2341      644895 :             move16();
    2342      644895 :             sqlabsm = square_root30_q12[( ( l - ( absm + 1 ) ) * ( l - absm ) )]; // Q12
    2343      644895 :             move16();
    2344             : 
    2345     4481660 :             FOR( n = -l; n <= l; n++ )
    2346             :             {
    2347     3836765 :                 IF( EQ_16( abs_s( n ), l ) )
    2348             :                 {
    2349     1289790 :                     sqdenom = square_root30_q12[( ( 2 * l ) * ( 2 * l - 1 ) )]; // Q12
    2350     1289790 :                     move16();
    2351             :                 }
    2352             :                 ELSE
    2353             :                 {
    2354     2546975 :                     sqdenom = square_root30_q12[( l * l - n * n )]; // Q12
    2355     2546975 :                     move16();
    2356             :                 }
    2357             : 
    2358             : 
    2359     3836765 :                 u = div_l( L_shl( (Word32) sql2mm2, 15 ), sqdenom );                                     // Q14
    2360     3836765 :                 v = imult1616( div_l( L_shl( (Word32) sqdabsm, 14 ), sqdenom ), sub( 1, shl( d, 1 ) ) ); // Q14
    2361     3836765 :                 w = imult1616( div_l( L_shl( (Word32) sqlabsm, 14 ), sqdenom ), negate( sub( 1, d ) ) ); // Q14
    2362             : 
    2363     3836765 :                 IF( u != 0 )
    2364             :                 {
    2365     2546975 :                     result = SHrot_u_fx( l, m, n, SHrotmat, R_lm1 ); // Q28
    2366     2546975 :                     u32_fx = Mpy_32_16_r( result, u );               // Q27
    2367             :                 }
    2368     3836765 :                 IF( v != 0 )
    2369             :                 {
    2370     3836765 :                     result = SHrot_v_fx( l, m, n, SHrotmat, R_lm1 );
    2371     3836765 :                     v32_fx = Mpy_32_16_r( result, v ); // Q26
    2372             :                 }
    2373     3836765 :                 IF( w != 0 )
    2374             :                 {
    2375      612290 :                     result = SHrot_w_fx( l, m, n, SHrotmat, R_lm1 );
    2376      612290 :                     w32_fx = Mpy_32_16_r( result, w ); // Q27
    2377             :                 }
    2378             :                 // Addind and converting to 16 bit integer of Q14
    2379     3836765 :                 R_l[m + l][n + l] = extract_h( L_shl( L_add( L_add( L_shr( u32_fx, 1 ), v32_fx ), L_shr( w32_fx, 1 ) ), 4 ) ); // Q14
    2380     3836765 :                 move16();
    2381             :             }
    2382             :         }
    2383             : 
    2384      756380 :         FOR( i = 0; i < 2 * l + 1; i++ )
    2385             :         {
    2386     4481660 :             FOR( j = 0; j < 2 * l + 1; j++ )
    2387             :             {
    2388     3836765 :                 SHrotmat[band_idx + i][band_idx + j] = R_l[i][j]; // Q14
    2389     3836765 :                 move16();
    2390             :             }
    2391             :         }
    2392             : 
    2393      756380 :         FOR( i = 0; i < 2 * l + 1; i++ )
    2394             :         {
    2395     4481660 :             FOR( j = 0; j < 2 * l + 1; j++ )
    2396             :             {
    2397     3836765 :                 R_lm1[i][j] = R_l[i][j];
    2398     3836765 :                 move16();
    2399             :             }
    2400             :         }
    2401             : 
    2402      111485 :         band_idx = add( band_idx, add( shl( l, 1 ), 1 ) );
    2403             :     }
    2404             : 
    2405       91765 :     return;
    2406             : }
    2407             : 
    2408             : 
    2409             : /*-------------------------------------------------------------------------
    2410             :  * ivas_combined_orientation_update_index()
    2411             :  *
    2412             :  * update read index based on the number of rendered samples
    2413             :  *------------------------------------------------------------------------*/
    2414             : 
    2415     2032459 : void ivas_combined_orientation_update_index(
    2416             :     COMBINED_ORIENTATION_HANDLE hCombinedOrientationData, /* i/o: combined orientation handle           */
    2417             :     const Word16 samples_rendered                         /* i  : samples rendered since the last call  */
    2418             : )
    2419             : {
    2420             :     Word16 exp, div_result;
    2421     2032459 :     IF( hCombinedOrientationData != NULL )
    2422             :     {
    2423      795410 :         IF( EQ_16( hCombinedOrientationData->num_subframes, 1 ) )
    2424             :         {
    2425             :             /* only one orientation available anyway or split rendering with low resolution*/
    2426      263202 :             hCombinedOrientationData->subframe_idx = 0;
    2427      263202 :             move16();
    2428             :         }
    2429             :         ELSE
    2430             :         {
    2431      532208 :             hCombinedOrientationData->cur_subframe_samples_rendered = add( hCombinedOrientationData->cur_subframe_samples_rendered, samples_rendered );
    2432      532208 :             move16();
    2433      532208 :             div_result = BASOP_Util_Divide3216_Scale( hCombinedOrientationData->cur_subframe_samples_rendered, hCombinedOrientationData->subframe_size, &exp );
    2434      532208 :             hCombinedOrientationData->subframe_idx = add( hCombinedOrientationData->subframe_idx, shl( div_result, add( exp, 1 ) ) );
    2435      532208 :             move16();
    2436      532208 :             hCombinedOrientationData->cur_subframe_samples_rendered = hCombinedOrientationData->cur_subframe_samples_rendered % hCombinedOrientationData->subframe_size;
    2437      532208 :             move16();
    2438      532208 :             hCombinedOrientationData->subframe_idx = s_min( hCombinedOrientationData->subframe_idx, sub( hCombinedOrientationData->num_subframes, 1 ) );
    2439      532208 :             move16();
    2440             :         }
    2441             :     }
    2442             : 
    2443     2032459 :     return;
    2444             : }
    2445             : 
    2446             : 
    2447             : /*-------------------------------------------------------------------------
    2448             :  * ivas_combined_orientation_set_to_start_index()
    2449             :  *
    2450             :  * update read index based on the number of rendered samples
    2451             :  *------------------------------------------------------------------------*/
    2452             : 
    2453     1946663 : void ivas_combined_orientation_set_to_start_index(
    2454             :     COMBINED_ORIENTATION_HANDLE hCombinedOrientationData /* i/o: combined orientation handle    */
    2455             : )
    2456             : {
    2457     1946663 :     IF( hCombinedOrientationData != NULL )
    2458             :     {
    2459     1946440 :         hCombinedOrientationData->subframe_idx = hCombinedOrientationData->subframe_idx_start;
    2460     1946440 :         move16();
    2461     1946440 :         hCombinedOrientationData->cur_subframe_samples_rendered = hCombinedOrientationData->cur_subframe_samples_rendered_start;
    2462     1946440 :         move16();
    2463             :     }
    2464             : 
    2465     1946663 :     return;
    2466             : }
    2467             : /*-------------------------------------------------------------------------
    2468             :  * ivas_combined_orientation_update_start_index()
    2469             :  *
    2470             :  * update start index based on the number of rendered samples
    2471             :  *------------------------------------------------------------------------*/
    2472             : 
    2473     1538230 : void ivas_combined_orientation_update_start_index(
    2474             :     COMBINED_ORIENTATION_HANDLE hCombinedOrientationData, /* i/o: combined orientation handle           */
    2475             :     const Word16 samples_rendered                         /* i  : samples rendered since the last call  */
    2476             : )
    2477             : {
    2478     1538230 :     IF( hCombinedOrientationData != NULL )
    2479             :     {
    2480     1195490 :         IF( EQ_16( hCombinedOrientationData->num_subframes, 1 ) )
    2481             :         {
    2482             :             /* only one orientation available anyway or split rendering with low resolution*/
    2483      862064 :             hCombinedOrientationData->subframe_idx = 0;
    2484      862064 :             move16();
    2485             :         }
    2486             :         ELSE
    2487             :         {
    2488      333426 :             hCombinedOrientationData->cur_subframe_samples_rendered_start = add( hCombinedOrientationData->cur_subframe_samples_rendered_start, samples_rendered );
    2489      333426 :             move16();
    2490      333426 :             hCombinedOrientationData->subframe_idx_start = add( hCombinedOrientationData->subframe_idx_start, mult( hCombinedOrientationData->cur_subframe_samples_rendered, div_s( 1, hCombinedOrientationData->subframe_size ) ) );
    2491      333426 :             move16();
    2492      333426 :             hCombinedOrientationData->cur_subframe_samples_rendered_start = hCombinedOrientationData->cur_subframe_samples_rendered % hCombinedOrientationData->subframe_size; /* No operator to calculate modulo*/
    2493      333426 :             move16();
    2494      333426 :             hCombinedOrientationData->subframe_idx_start = s_min( hCombinedOrientationData->subframe_idx, sub( hCombinedOrientationData->num_subframes, 1 ) );
    2495      333426 :             move16();
    2496             :         }
    2497             :     }
    2498             : 
    2499     1538230 :     return;
    2500             : }

Generated by: LCOV version 1.14