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 @ 3b2f07138c61dcf997bbf4165d0882f794b2995f Lines: 1059 1170 90.5 %
Date: 2025-05-03 01:55:50 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     2501413 : 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     2501413 :     Word32 w = quat.w_fx; // Qx
     192     2501413 :     move32();
     193     2501413 :     Word32 x = quat.x_fx;
     194     2501413 :     move32();
     195     2501413 :     Word32 y = quat.y_fx;
     196     2501413 :     move32();
     197     2501413 :     Word32 z = quat.z_fx;
     198     2501413 :     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     2501413 :     Word32 ww = L_shr( Mpy_32_32( w, w ), 1 ); // 2 * Qx - 31 - 1 = 2*Qx-32
     203     2501413 :     Word32 xx = L_shr( Mpy_32_32( x, x ), 1 );
     204     2501413 :     Word32 yy = L_shr( Mpy_32_32( y, y ), 1 );
     205     2501413 :     Word32 zz = L_shr( Mpy_32_32( z, z ), 1 );
     206             : 
     207     2501413 :     Word32 wx = Mpy_32_32( w, x ); // 2 * Qx - 31
     208     2501413 :     Word32 wz = Mpy_32_32( w, z );
     209     2501413 :     Word32 wy = Mpy_32_32( w, y );
     210             : 
     211     2501413 :     Word32 xy = Mpy_32_32( x, y );
     212     2501413 :     Word32 xz = Mpy_32_32( x, z );
     213             : 
     214     2501413 :     Word32 yz = Mpy_32_32( y, z );
     215             : 
     216     2501413 :     Rmat[0][0] = L_sub( L_sub( L_add( ww, xx ), yy ), zz ); // 2 * Qx - 31 - 1 = 2*Qx-32
     217     2501413 :     move32();
     218     2501413 :     Rmat[0][1] = L_sub( xy, wz );
     219     2501413 :     move32();
     220     2501413 :     Rmat[0][2] = L_add( xz, wy );
     221     2501413 :     move32();
     222             : 
     223     2501413 :     Rmat[1][0] = L_add( xy, wz ); // 2 * Qx - 32
     224     2501413 :     move32();
     225     2501413 :     Rmat[1][1] = L_sub( L_add( L_sub( ww, xx ), yy ), zz );
     226     2501413 :     move32();
     227     2501413 :     Rmat[1][2] = L_sub( yz, wx );
     228     2501413 :     move32();
     229             : 
     230     2501413 :     Rmat[2][0] = L_sub( xz, wy ); // 2 * Qx - 32
     231     2501413 :     move32();
     232     2501413 :     Rmat[2][1] = L_add( yz, wx );
     233     2501413 :     move32();
     234     2501413 :     Rmat[2][2] = L_add( L_sub( L_sub( ww, xx ), yy ), zz );
     235     2501413 :     move32();
     236             : 
     237     2501413 :     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     3473157 : 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     3473157 :     IF( GT_16( abs_s( azi_in ), 180 ) )
     333             :     {
     334     1061950 :         IF( azi_in > 0 )
     335             :         {
     336     1061950 :             azi_in = sub( azi_in, 360 );
     337             :         }
     338             :         ELSE
     339             :         {
     340           0 :             azi_in = add( azi_in, 360 );
     341             :         }
     342             :     }
     343     3473157 :     temp_16 = ele_in;
     344     3473157 :     move16();
     345     3473157 :     w_fx = cosine_table_Q31[abs_s( temp_16 )]; // Q31
     346     3473157 :     move32();
     347     3473157 :     temp_16 = azi_in;
     348     3473157 :     move16();
     349     3473157 :     dv_fx[0] = Mpy_32_32( w_fx, cosine_table_Q31[abs_s( temp_16 )] ); // Q31
     350     3473157 :     move32();
     351     3473157 :     temp_16 = azi_in;
     352     3473157 :     move16();
     353     3473157 :     dv_fx[1] = Mpy_32_32( w_fx, sine_table_Q31[add( temp_16, 180 )] ); // Q31
     354     3473157 :     move32();
     355     3473157 :     temp_16 = ele_in;
     356     3473157 :     move16();
     357     3473157 :     dv_fx[2] = sine_table_Q31[add( temp_16, 180 )]; // Q31
     358     3473157 :     move32();
     359             :     /*Rotation mtx multiplication*/
     360    13892628 :     FOR( n = 0; n < 3; n++ )
     361             :     {
     362    10419471 :         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    10419471 :         dv_r_fx[n] = L_add( Mpy_32_32( dv_fx[2], Rmat_fx[n][2] ), temp );                           // Q30
     364    10419471 :         move32();
     365             :     }
     366             : 
     367             :     /*Conversion cartesian to spherical coordinates*/
     368     3473157 :     y = dv_r_fx[1];
     369     3473157 :     move32();
     370     3473157 :     x = dv_r_fx[0];
     371     3473157 :     move32();
     372     3473157 :     radian = atan2_fx( L_abs( L_shr( y, 15 ) ), L_abs( L_shr( x, 15 ) ) ); // Q14
     373             : 
     374     3473157 :     if ( y <= 0 )
     375             :     {
     376     1735938 :         radian = negate( radian );
     377             :     }
     378             : 
     379     3473157 :     IF( x < 0 )
     380             :     {
     381     1256036 :         IF( radian < 0 )
     382             :         {
     383      592505 :             angle = negate( add( 180, extract_l( L_shr( Mpy_32_16_1( _180_OVER_PI_Q25, radian ), ( 24 ) ) ) ) ); // Q0
     384             :         }
     385             :         ELSE
     386             :         {
     387      663531 :             angle = sub( 180, extract_l( L_shr( Mpy_32_16_1( _180_OVER_PI_Q25, radian ), ( 24 ) ) ) ); // Q0
     388             :         }
     389     1256036 :         IF( radian == 0 )
     390             :         {
     391         847 :             if ( y < 0 )
     392             :             {
     393           3 :                 angle = negate( angle );
     394             :             }
     395             :         }
     396             :     }
     397             :     ELSE
     398             :     {
     399     2217121 :         angle = extract_l( L_shr( Mpy_32_16_1( _180_OVER_PI_Q25, radian ), 24 ) ); // Q0
     400             :     }
     401             : 
     402     3473157 :     *azi = s_max( -180, s_min( 180, angle ) );
     403     3473157 :     move16();
     404             : 
     405     3473157 :     IF( isPlanar == 0 )
     406             :     {
     407     3273157 :         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     3273157 :         y = dv_r_fx[2];
     409     3273157 :         move32();
     410     3273157 :         x = sqrt_fx;
     411     3273157 :         move32();
     412     3273157 :         radian = atan2_fx( L_abs( L_shr( y, 15 ) ), L_abs( L_shr( x, 15 ) ) ); // Q14
     413     3273157 :         if ( y <= 0 )
     414             :         {
     415     1909266 :             radian = negate( radian );
     416             :         }
     417             : 
     418     3273157 :         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     3273157 :             angle = extract_l( L_shr( Mpy_32_16_r( _180_OVER_PI_Q25, radian ), ( 24 ) ) ); // Q0
     439             :         }
     440             : 
     441     3273157 :         *ele = s_max( -90, s_min( 90, angle ) ); // Q0
     442     3273157 :         move16();
     443             :     }
     444             :     ELSE
     445             :     {
     446      200000 :         *ele = 0;
     447      200000 :         move16();
     448             :     }
     449             : 
     450     3473157 :     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             :     Word32 temp1, temp2;
    1050       18520 :     move16();
    1051       18520 :     move16();
    1052       18520 :     move16();
    1053       18520 :     move16();
    1054       18520 :     move16();
    1055       18520 :     move16();
    1056       18520 :     move16();
    1057       18520 :     move16();
    1058       18520 :     move16();
    1059       18520 :     assert( nInChannels == HEADROT_SHMAT_DIM && "Number of channels must be 16!" );
    1060             : 
    1061             :     /* initialize rotation matrices with zeros */
    1062      314840 :     FOR( i = 0; i < HEADROT_SHMAT_DIM; i++ )
    1063             :     {
    1064      296320 :         set16_fx( SHrotmat[i], 0, HEADROT_SHMAT_DIM );
    1065             :     }
    1066             : 
    1067             :     /* calculate Ambisonics rotation matrix from the quaternion */
    1068       18520 :     SHrotmatgen_fx( SHrotmat, Rmat, shd_rot_max_order );
    1069             : 
    1070             : #ifdef DEBUGGING
    1071             :     dbgwrite_txt( (const float *) SHrotmat[0], HEADROT_SHMAT_DIM * HEADROT_SHMAT_DIM, "Fixed_SHrotmat.txt", NULL );
    1072             : #endif
    1073             : 
    1074             :     /* rotation by mtx multiplication */
    1075       92600 :     FOR( i = 0; i < numTimeSlots; i++ )
    1076             :     {
    1077     4518880 :         FOR( iBand = 0; iBand < CLDFB_NO_CHANNELS_MAX; iBand++ )
    1078             :         {
    1079             :             /*As the rotation matrix becomes block diagonal in a SH basis, we can
    1080             :             apply each angular-momentum block individually to save complexity. */
    1081             : 
    1082             :             /* loop over l blocks */
    1083     4444800 :             m1 = 1;
    1084     4444800 :             move16();
    1085     4444800 :             m2 = 4;
    1086     4444800 :             move16();
    1087    17779200 :             FOR( l = 1; l <= shd_rot_max_order; l++ )
    1088             :             {
    1089             :                 /* compute mtx-vector product for this l */
    1090    80006400 :                 FOR( n = m1; n < m2; n++ )
    1091             :                 {
    1092    66672000 :                     realRot[n - m1] = 0;
    1093    66672000 :                     move32();
    1094    66672000 :                     imagRot[n - m1] = 0;
    1095    66672000 :                     move32();
    1096   435590400 :                     FOR( m = m1; m < m2; m++ )
    1097             :                     {
    1098   368918400 :                         temp1 = Mpy_32_16_r( Cldfb_RealBuffer[m][i][iBand], SHrotmat[n][m] ); // Q(x + 14 - 15)
    1099   368918400 :                         temp2 = Mpy_32_16_r( Cldfb_ImagBuffer[m][i][iBand], SHrotmat[n][m] ); // Q(x + 14 - 15)
    1100   368918400 :                         realRot[n - m1] = L_add( temp1, realRot[n - m1] );                    // Q(x + 14 - 15)
    1101   368918400 :                         move32();
    1102   368918400 :                         imagRot[n - m1] = L_add( temp2, imagRot[n - m1] ); // Q(x + 14 - 15)
    1103   368918400 :                         move32();
    1104             :                     }
    1105             :                 }
    1106             :                 /* write back the result */
    1107    80006400 :                 FOR( n = m1; n < m2; n++ )
    1108             :                 {
    1109    66672000 :                     Cldfb_RealBuffer[n][i][iBand] = realRot[n - m1]; // Qx - 1
    1110    66672000 :                     move32();
    1111    66672000 :                     Cldfb_ImagBuffer[n][i][iBand] = imagRot[n - m1]; // Qx - 1
    1112    66672000 :                     move32();
    1113             :                 }
    1114    13334400 :                 m1 = m2;
    1115    13334400 :                 move16();
    1116    13334400 :                 m2 = add( m2, add( shl( add( l, 1 ), 1 ), 1 ) );
    1117             :             }
    1118             : 
    1119             :             /* unoptimized code for reference (full matrix multiplication)
    1120             :             for (n = 0; n < nInChannels; n++)
    1121             :             {
    1122             :                 realRot[n] = 0.f;
    1123             :                 imagRot[n] = 0.f;
    1124             : 
    1125             :                 for (m = 0; m < nInChannels; m++)
    1126             :                 {
    1127             :                     realRot[n] += SHrotmat[n][m] * Cldfb_RealBuffer[m][i][iBand];
    1128             :                     imagRot[n] += SHrotmat[n][m] * Cldfb_ImagBuffer[m][i][iBand];
    1129             :                 }
    1130             :             }
    1131             :             for (n = 0; n < nInChannels; n++)
    1132             :             {
    1133             :                 Cldfb_RealBuffer[n][i][iBand] = realRot[n];
    1134             :                 Cldfb_ImagBuffer[n][i][iBand] = imagRot[n];
    1135             :             }
    1136             :             */
    1137             :         }
    1138             :     }
    1139             :     // To make the Q factor same as tha of the remaining vector values
    1140       92600 :     FOR( j = 0; j < MAX_PARAM_SPATIAL_SUBFRAMES; j++ )
    1141             :     {
    1142     4518880 :         FOR( k = 0; k < CLDFB_NO_CHANNELS_MAX; k++ )
    1143             :         {
    1144     4444800 :             Cldfb_RealBuffer[0][j][k] = L_shr( Cldfb_RealBuffer[0][j][k], 1 ); // Qx - 1
    1145     4444800 :             move32();
    1146     4444800 :             Cldfb_ImagBuffer[0][j][k] = L_shr( Cldfb_ImagBuffer[0][j][k], 1 ); // Qx - 1
    1147     4444800 :             move32();
    1148             :         }
    1149             :     }
    1150       18520 :     return;
    1151             : }
    1152             : /*-------------------------------------------------------------------------
    1153             :  * rotateFrame_sd_cldfb()
    1154             :  *
    1155             :  * Apply rotation to signals in Spatial Domain and in CLDFB
    1156             :  *------------------------------------------------------------------------*/
    1157             : 
    1158        8000 : void rotateFrame_sd_cldfb_fixed(
    1159             :     Word32 Rmat_fx[3][3],                                                          /* i  : real-space rotation matrix (Q30)                          */
    1160             :     Word32 Cldfb_RealBuffer[][MAX_PARAM_SPATIAL_SUBFRAMES][CLDFB_NO_CHANNELS_MAX], /* i/o: unrotated HOA3 signal buffer in cldfb domain real part Qx */
    1161             :     Word32 Cldfb_ImagBuffer[][MAX_PARAM_SPATIAL_SUBFRAMES][CLDFB_NO_CHANNELS_MAX], /* i/o: unrotated HOA3 signal buffer in cldfb domain imag part Qx */
    1162             :     const IVAS_OUTPUT_SETUP_HANDLE hOutputSetup,                                   /* i  : output format setup number of channels                    */
    1163             :     const EFAP_HANDLE hEFAPdata,                                                   /* i  : EFAP structure                                            */
    1164             :     const Word16 numTimeSlots,                                                     /* i  : number of time slots to process                           */
    1165             :     const Word16 nb_band                                                           /* i  : number of CLDFB bands to process                          */
    1166             : )
    1167             : {
    1168             :     Word16 iBlock, iBand, m, n;
    1169             :     Word32 gains_fx[MAX_CICP_CHANNELS - 1][MAX_CICP_CHANNELS - 1];
    1170             :     Word16 azimuth, elevation;
    1171             :     Word32 g1_fx;
    1172             :     Word32 realRot_fx[MAX_CICP_CHANNELS - 1][MAX_PARAM_SPATIAL_SUBFRAMES * CLDFB_NO_CHANNELS_MAX];
    1173             :     Word32 imagRot_fx[MAX_CICP_CHANNELS - 1][MAX_PARAM_SPATIAL_SUBFRAMES * CLDFB_NO_CHANNELS_MAX];
    1174             :     Word32 *p_realRot_fx, *p_imagRot_fx;
    1175             :     Word32 *p_real_fx, *p_imag_fx;
    1176             :     Word16 nInChannels;
    1177             :     Word16 isPlanar;
    1178        8000 :     push_wmops( "rotateFrame_sd_cldfb" );
    1179             : 
    1180        8000 :     nInChannels = hOutputSetup->nchan_out_woLFE;
    1181        8000 :     move16();
    1182        8000 :     isPlanar = 1;
    1183        8000 :     move16();
    1184       48000 :     FOR( n = 0; n < nInChannels; n++ )
    1185             :     {
    1186       40000 :         IF( hOutputSetup->ls_elevation_fx[n] != 0 )
    1187             :         {
    1188           0 :             isPlanar = 0;
    1189           0 :             move16();
    1190           0 :             BREAK;
    1191             :         }
    1192             :     }
    1193             : 
    1194             :     /* rotation of Euler angles */
    1195       48000 :     FOR( n = 0; n < nInChannels; n++ )
    1196             :     {
    1197       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 );
    1198       40000 :         test();
    1199       40000 :         test();
    1200       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 ) ) )
    1201             :         {
    1202           0 :             efap_determine_gains_fx( hEFAPdata, gains_fx[n], L_shl( azimuth, Q22 ), L_shl( elevation, Q22 ), EFAP_MODE_EFAP );
    1203             :         }
    1204             :         ELSE
    1205             :         {
    1206       40000 :             set32_fx( gains_fx[n], 0, nInChannels );
    1207       40000 :             gains_fx[n][n] = 0x7fffffff; // 1 in Q31
    1208       40000 :             move32();
    1209             :         }
    1210             :     }
    1211             : 
    1212             :     /* Apply panning gains by mtx multiplication*/
    1213       48000 :     FOR( n = 0; n < nInChannels; n++ )
    1214             :     {
    1215       40000 :         set32_fx( realRot_fx[n], 0, shl( nb_band, 2 ) );
    1216       40000 :         set32_fx( imagRot_fx[n], 0, shl( nb_band, 2 ) );
    1217      240000 :         FOR( m = 0; m < nInChannels; m++ )
    1218             :         {
    1219      200000 :             g1_fx = gains_fx[m][n]; // Q31
    1220      200000 :             move32();
    1221      200000 :             p_realRot_fx = realRot_fx[n];
    1222      200000 :             p_imagRot_fx = imagRot_fx[n];
    1223             : 
    1224      200000 :             IF( g1_fx > 0 )
    1225             :             {
    1226      200000 :                 FOR( iBlock = 0; iBlock < numTimeSlots; iBlock++ )
    1227             :                 {
    1228      160000 :                     p_real_fx = Cldfb_RealBuffer[m][iBlock];
    1229      160000 :                     p_imag_fx = Cldfb_ImagBuffer[m][iBlock];
    1230             : 
    1231     8160000 :                     FOR( iBand = 0; iBand < nb_band; iBand++ )
    1232             :                     {
    1233     8000000 :                         *( p_realRot_fx ) = L_add( *p_realRot_fx, Mpy_32_32( g1_fx, *( p_real_fx++ ) ) ); // Qx
    1234     8000000 :                         move32();
    1235     8000000 :                         *( p_imagRot_fx ) = L_add( *p_imagRot_fx, Mpy_32_32( g1_fx, *( p_imag_fx++ ) ) ); // Qx
    1236     8000000 :                         move32();
    1237     8000000 :                         p_realRot_fx++;
    1238     8000000 :                         p_imagRot_fx++;
    1239             :                     }
    1240             :                 }
    1241             :             }
    1242             :         }
    1243             :     }
    1244             : 
    1245       48000 :     FOR( n = 0; n < nInChannels; n++ )
    1246             :     {
    1247       40000 :         p_realRot_fx = realRot_fx[n];
    1248       40000 :         p_imagRot_fx = imagRot_fx[n];
    1249             : 
    1250      200000 :         FOR( iBlock = 0; iBlock < numTimeSlots; iBlock++ )
    1251             :         {
    1252      160000 :             p_real_fx = Cldfb_RealBuffer[n][iBlock];
    1253      160000 :             p_imag_fx = Cldfb_ImagBuffer[n][iBlock];
    1254             : 
    1255     8160000 :             FOR( iBand = 0; iBand < nb_band; iBand++ )
    1256             :             {
    1257     8000000 :                 *( p_real_fx++ ) = *( p_realRot_fx++ );
    1258     8000000 :                 move32();
    1259     8000000 :                 *( p_imag_fx++ ) = *( p_imagRot_fx++ );
    1260     8000000 :                 move32();
    1261             :             }
    1262     1760000 :             FOR( ; iBand < CLDFB_NO_CHANNELS_MAX; iBand++ )
    1263             :             {
    1264     1600000 :                 *( p_real_fx++ ) = 0;
    1265     1600000 :                 move32();
    1266     1600000 :                 *( p_imag_fx++ ) = 0;
    1267     1600000 :                 move32();
    1268             :             }
    1269             :         }
    1270             :     }
    1271        8000 :     pop_wmops();
    1272             : 
    1273        8000 :     return;
    1274             : }
    1275             : 
    1276             : /*-----------------------------------------------------------------------*
    1277             :  * ivas_external_orientation_open()
    1278             :  *
    1279             :  * Allocate and initialize external orientation handle
    1280             :  *-----------------------------------------------------------------------*/
    1281         689 : ivas_error ivas_external_orientation_open(
    1282             :     EXTERNAL_ORIENTATION_HANDLE *hExtOrientationData, /* o  : external orientation handle    */
    1283             :     const Word16 num_subframes                        /* i  : number of subframes            */
    1284             : )
    1285             : {
    1286             : 
    1287             :     Word16 i;
    1288             :     IVAS_QUATERNION identity;
    1289         689 :     identity.w_fx = ONE_IN_Q31;
    1290         689 :     move32();
    1291         689 :     identity.x_fx = 0;
    1292         689 :     move32();
    1293         689 :     identity.y_fx = 0;
    1294         689 :     move32();
    1295         689 :     identity.z_fx = 0;
    1296         689 :     move32();
    1297         689 :     identity.q_fact = 31;
    1298         689 :     move16();
    1299             :     /* Allocate handle */
    1300         689 :     IF( ( *hExtOrientationData = (EXTERNAL_ORIENTATION_HANDLE) malloc( sizeof( EXTERNAL_ORIENTATION_DATA ) ) ) == NULL )
    1301             :     {
    1302           0 :         return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for external orientation memory\n" ) );
    1303             :     }
    1304         689 :     ( *hExtOrientationData )->num_subframes = num_subframes;
    1305         689 :     move16();
    1306             :     /* Enable head rotation and disable external orientation as default */
    1307        3445 :     FOR( i = 0; i < MAX_PARAM_SPATIAL_SUBFRAMES; i++ )
    1308             :     {
    1309        2756 :         ( *hExtOrientationData )->enableHeadRotation[i] = 1;
    1310        2756 :         move16();
    1311        2756 :         ( *hExtOrientationData )->enableExternalOrientation[i] = 0;
    1312        2756 :         move16();
    1313        2756 :         ( *hExtOrientationData )->enableRotationInterpolation[i] = 0;
    1314        2756 :         move16();
    1315        2756 :         ( *hExtOrientationData )->numFramesToTargetOrientation[i] = 0;
    1316        2756 :         move16();
    1317             : 
    1318        2756 :         ( *hExtOrientationData )->Quaternions[i] = identity;
    1319             :     }
    1320         689 :     return IVAS_ERR_OK;
    1321             : }
    1322             : 
    1323             : /*-----------------------------------------------------------------------*
    1324             :  * ivas_external_orientation_close()
    1325             :  *
    1326             :  * Deallocate external orientation handle
    1327             :  *-----------------------------------------------------------------------*/
    1328        1262 : void ivas_external_orientation_close_fx(
    1329             :     EXTERNAL_ORIENTATION_HANDLE *hExtOrientationData /* i/o: external orientation handle    */
    1330             : )
    1331             : {
    1332        1262 :     test();
    1333        1262 :     IF( hExtOrientationData == NULL || *hExtOrientationData == NULL )
    1334             :     {
    1335         573 :         return;
    1336             :     }
    1337             : 
    1338         689 :     free( ( *hExtOrientationData ) );
    1339         689 :     *hExtOrientationData = NULL;
    1340             : 
    1341         689 :     return;
    1342             : }
    1343             : 
    1344             : 
    1345             : /*-----------------------------------------------------------------------*
    1346             :  * ivas_combined_orientation_open()
    1347             :  *
    1348             :  * Allocate and initialize combined orientation handle
    1349             :  *-----------------------------------------------------------------------*/
    1350         739 : ivas_error ivas_combined_orientation_open(
    1351             :     COMBINED_ORIENTATION_HANDLE *hCombinedOrientationData, /* o  : combined orientation handle   */
    1352             :     const Word32 fs,                                       /* i  : sampling rate                 */
    1353             :     const Word16 num_subframes                             /* i  : number of subframes           */
    1354             : )
    1355             : {
    1356             :     Word16 i;
    1357             :     Word16 j;
    1358             :     IVAS_QUATERNION identity;
    1359             :     IVAS_VECTOR3 origo;
    1360         739 :     identity.w_fx = ONE_IN_Q31;
    1361         739 :     move32();
    1362         739 :     identity.x_fx = 0;
    1363         739 :     move32();
    1364         739 :     identity.y_fx = 0;
    1365         739 :     move32();
    1366         739 :     identity.z_fx = 0;
    1367         739 :     move32();
    1368         739 :     identity.q_fact = 31;
    1369         739 :     move16();
    1370         739 :     origo.x_fx = 0;
    1371         739 :     move32();
    1372         739 :     origo.y_fx = 0;
    1373         739 :     move32();
    1374         739 :     origo.z_fx = 0;
    1375         739 :     move32();
    1376         739 :     origo.q_fact = 31;
    1377         739 :     move16();
    1378             :     /* Allocate handle */
    1379         739 :     IF( ( *hCombinedOrientationData = (COMBINED_ORIENTATION_HANDLE) malloc( sizeof( COMBINED_ORIENTATION_DATA ) ) ) == NULL )
    1380             :     {
    1381           0 :         return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for combined orientation memory\n" ) );
    1382             :     }
    1383             : 
    1384             :     /* Initialization */
    1385         739 :     ( *hCombinedOrientationData )->num_subframes = num_subframes;
    1386         739 :     move16();
    1387         739 :     ( *hCombinedOrientationData )->interpolationCoefficient_fx = ONE_IN_Q30; // Q30
    1388         739 :     move32();
    1389         739 :     ( *hCombinedOrientationData )->interpolationIncrement_fx = ONE_IN_Q30; // Q30
    1390         739 :     move32();
    1391         739 :     IF( EQ_16( num_subframes, 1 ) )
    1392             :     {
    1393         304 :         ( *hCombinedOrientationData )->maximumFramesToTargetOrientation = 2000;
    1394         304 :         move16();
    1395             :     }
    1396             :     ELSE
    1397             :     {
    1398         435 :         ( *hCombinedOrientationData )->maximumFramesToTargetOrientation = 500;
    1399         435 :         move16();
    1400             :     }
    1401         739 :     ( *hCombinedOrientationData )->lrSwitchedNext = 0;
    1402         739 :     move16();
    1403         739 :     ( *hCombinedOrientationData )->lrSwitchedCurrent = 0;
    1404         739 :     move16();
    1405             : 
    1406         739 :     ( *hCombinedOrientationData )->lrSwitchInterpVal_fx = 0;
    1407         739 :     move32();
    1408         739 :     ( *hCombinedOrientationData )->isInterpolationOngoing = FALSE;
    1409         739 :     move16();
    1410         739 :     ( *hCombinedOrientationData )->Quaternions_ext_interpolation_start = identity;
    1411         739 :     ( *hCombinedOrientationData )->Quaternions_ext_interpolation_target = identity;
    1412             : 
    1413             :     /* Initialise orientations to identity */
    1414        3695 :     FOR( i = 0; i < MAX_PARAM_SPATIAL_SUBFRAMES; i++ )
    1415             :     {
    1416        2956 :         ( *hCombinedOrientationData )->enableCombinedOrientation[i] = 0;
    1417        2956 :         move16();
    1418        2956 :         ( *hCombinedOrientationData )->Quaternions[i] = identity;
    1419        2956 :         ( *hCombinedOrientationData )->listenerPos[i] = origo;
    1420             : 
    1421       11824 :         FOR( j = 0; j < 3; j++ )
    1422             :         {
    1423        8868 :             set32_fx( ( *hCombinedOrientationData )->Rmat_fx[i][j], 0, 3 );
    1424        8868 :             ( *hCombinedOrientationData )->Rmat_fx[i][j][j] = ONE_IN_Q30; // Q30
    1425        8868 :             move32();
    1426             :         }
    1427             :     }
    1428             : 
    1429        2956 :     FOR( j = 0; j < 3; j++ )
    1430             :     {
    1431        2217 :         set32_fx( ( *hCombinedOrientationData )->Rmat_prev_fx[j], 0, 3 );
    1432        2217 :         ( *hCombinedOrientationData )->Rmat_prev_fx[j][j] = ONE_IN_Q30;
    1433        2217 :         move32();
    1434             :     }
    1435         739 :     ( *hCombinedOrientationData )->Quaternion_prev_extOrientation = identity;
    1436         739 :     ( *hCombinedOrientationData )->Quaternion_frozen_ext = identity;
    1437         739 :     ( *hCombinedOrientationData )->Quaternion_frozen_head = identity;
    1438         739 :     set_zero_fx( ( *hCombinedOrientationData )->chEneIIR_fx[0], MASA_FREQUENCY_BANDS );
    1439         739 :     set_zero_fx( ( *hCombinedOrientationData )->chEneIIR_fx[1], MASA_FREQUENCY_BANDS );
    1440         739 :     set_zero_fx( ( *hCombinedOrientationData )->procChEneIIR_fx[0], MASA_FREQUENCY_BANDS );
    1441         739 :     set_zero_fx( ( *hCombinedOrientationData )->procChEneIIR_fx[1], MASA_FREQUENCY_BANDS );
    1442         739 :     ( *hCombinedOrientationData )->q_chEneIIR = Q31;
    1443         739 :     move16();
    1444         739 :     ( *hCombinedOrientationData )->q_procChEneIIR = Q31;
    1445         739 :     move16();
    1446         739 :     ( *hCombinedOrientationData )->isExtOrientationFrozen = 0;
    1447         739 :     move16();
    1448         739 :     ( *hCombinedOrientationData )->isHeadRotationFrozen = 0;
    1449         739 :     move16();
    1450             : 
    1451         739 :     ( *hCombinedOrientationData )->subframe_idx = 0;
    1452         739 :     move16();
    1453             :     /* ( *hCombinedOrientationData )->subframe_size = (int16_t) ( fs / ( FRAMES_PER_SEC * MAX_PARAM_SPATIAL_SUBFRAMES ) ); */
    1454         739 :     ( *hCombinedOrientationData )->subframe_size = extract_l( Mpy_32_32_r( fs, 10737418 /* 1 / ( FRAMES_PER_SEC * MAX_PARAM_SPATIAL_SUBFRAMES ) in Q31 */ ) );
    1455         739 :     move16();
    1456         739 :     ( *hCombinedOrientationData )->cur_subframe_samples_rendered = 0;
    1457         739 :     move16();
    1458             : 
    1459         739 :     return IVAS_ERR_OK;
    1460             : }
    1461             : /*-----------------------------------------------------------------------*
    1462             :  * ivas_combined_orientation_close()
    1463             :  *
    1464             :  * Deallocate combined orientation handle
    1465             :  *-----------------------------------------------------------------------*/
    1466             : 
    1467        1262 : void ivas_combined_orientation_close_fx(
    1468             :     COMBINED_ORIENTATION_HANDLE *hCombinedOrientationData /* i/o: combined orientation handle   */
    1469             : )
    1470             : {
    1471        1262 :     test();
    1472        1262 :     IF( hCombinedOrientationData == NULL || *hCombinedOrientationData == NULL )
    1473             :     {
    1474         523 :         return;
    1475             :     }
    1476             : 
    1477         739 :     free( ( *hCombinedOrientationData ) );
    1478         739 :     *hCombinedOrientationData = NULL;
    1479             : 
    1480         739 :     return;
    1481             : }
    1482             : 
    1483             : 
    1484             : /*-------------------------------------------------------------------------
    1485             :  * combine_external_and_head_orientations_dec()
    1486             :  *
    1487             :  *
    1488             :  *------------------------------------------------------------------------*/
    1489             : 
    1490       69350 : ivas_error combine_external_and_head_orientations_dec(
    1491             :     HEAD_TRACK_DATA_HANDLE hHeadTrackData,               /* i  : head track handle              */
    1492             :     EXTERNAL_ORIENTATION_HANDLE hExtOrientationData,     /* i  : external orientation handle    */
    1493             :     COMBINED_ORIENTATION_HANDLE hCombinedOrientationData /* i/o: combined orientation handle    */
    1494             : )
    1495             : {
    1496       69350 :     IVAS_QUATERNION *pHeadRotQuaternion = NULL;
    1497       69350 :     IVAS_VECTOR3 *listenerPos = NULL;
    1498             : 
    1499       69350 :     IF( hHeadTrackData != NULL )
    1500             :     {
    1501       69350 :         pHeadRotQuaternion = hHeadTrackData->Quaternions;
    1502       69350 :         listenerPos = hHeadTrackData->Pos;
    1503             :     }
    1504       69350 :     return combine_external_and_head_orientations( pHeadRotQuaternion, listenerPos,
    1505             :                                                    hExtOrientationData, hCombinedOrientationData );
    1506             : }
    1507             : 
    1508             : 
    1509             : /*-------------------------------------------------------------------------
    1510             :  * combine_external_and_head_orientations_rend()
    1511             :  *
    1512             :  *
    1513             :  *------------------------------------------------------------------------*/
    1514             : 
    1515     1114140 : ivas_error combine_external_and_head_orientations_rend(
    1516             :     IVAS_REND_HeadRotData *hHeadTrackData,               /* i  : head track handle              */
    1517             :     EXTERNAL_ORIENTATION_HANDLE hExtOrientationData,     /* i  : external orientation handle    */
    1518             :     COMBINED_ORIENTATION_HANDLE hCombinedOrientationData /* i/o: combined orientation handle    */
    1519             : )
    1520             : {
    1521     1114140 :     IVAS_QUATERNION *headRotQuaternions = NULL;
    1522     1114140 :     IVAS_VECTOR3 *listenerPos = NULL;
    1523             :     Word16 i;
    1524             : 
    1525     1114140 :     IF( hHeadTrackData != NULL )
    1526             :     {
    1527     1114140 :         IF( hHeadTrackData->headRotEnabled )
    1528             :         {
    1529      180024 :             headRotQuaternions = hHeadTrackData->headPositions;
    1530      180024 :             listenerPos = hHeadTrackData->Pos;
    1531             :         }
    1532             :     }
    1533           0 :     ELSE IF( hExtOrientationData != NULL )
    1534             :     {
    1535             :         /* Head rotation data not available, use the freezed value or disable */
    1536           0 :         FOR( i = 0; i < hExtOrientationData->num_subframes; i++ )
    1537             :         {
    1538           0 :             IF( NE_16( hExtOrientationData->enableHeadRotation[i], 2 ) )
    1539             :             {
    1540           0 :                 hExtOrientationData->enableHeadRotation[i] = 0;
    1541           0 :                 move16();
    1542             :             }
    1543             :         }
    1544             :     }
    1545             : 
    1546     1114140 :     return combine_external_and_head_orientations( headRotQuaternions, listenerPos,
    1547             :                                                    hExtOrientationData, hCombinedOrientationData );
    1548             : }
    1549             : 
    1550             : 
    1551             : /*-------------------------------------------------------------------------
    1552             :  * combine_external_and_head_orientations()
    1553             :  *
    1554             :  * Combine the external orientations and the head orientation.
    1555             :  * NOTE that the external orientations are inversed.
    1556             :  *------------------------------------------------------------------------*/
    1557     1183490 : ivas_error combine_external_and_head_orientations(
    1558             :     IVAS_QUATERNION *headRotQuaternions,                 /* i  : quaternions for head rotation                            */
    1559             :     IVAS_VECTOR3 *listenerPos,                           /* i  : listener position                                        */
    1560             :     EXTERNAL_ORIENTATION_HANDLE hExtOrientationData,     /* i  : external orientation handle                              */
    1561             :     COMBINED_ORIENTATION_HANDLE hCombinedOrientationData /* i/o: combined orientation handle                              */
    1562             : )
    1563             : {
    1564             :     Word16 i;
    1565             :     Word16 j;
    1566             :     IVAS_QUATERNION identity;
    1567             :     IVAS_VECTOR3 origo;
    1568     1183490 :     identity.w_fx = ONE_IN_Q31;
    1569     1183490 :     move32();
    1570     1183490 :     identity.x_fx = 0;
    1571     1183490 :     move32();
    1572     1183490 :     identity.y_fx = 0;
    1573     1183490 :     move32();
    1574     1183490 :     identity.z_fx = 0;
    1575     1183490 :     move32();
    1576     1183490 :     identity.q_fact = 31;
    1577     1183490 :     move16();
    1578     1183490 :     origo.x_fx = 0;
    1579     1183490 :     move32();
    1580     1183490 :     origo.y_fx = 0;
    1581     1183490 :     move32();
    1582     1183490 :     origo.z_fx = 0;
    1583     1183490 :     move32();
    1584     1183490 :     origo.q_fact = 31;
    1585     1183490 :     move16();
    1586             : 
    1587             :     /* Form combined orientations or return if no data available */
    1588     1183490 :     test();
    1589     1183490 :     test();
    1590     1183490 :     IF( hCombinedOrientationData == NULL )
    1591             :     {
    1592           0 :         test();
    1593           0 :         IF( headRotQuaternions != NULL || hExtOrientationData != NULL )
    1594             :         {
    1595           0 :             return IVAS_ERR_UNEXPECTED_NULL_POINTER;
    1596             :         }
    1597             :         ELSE
    1598             :         {
    1599           0 :             return IVAS_ERR_OK;
    1600             :         }
    1601             :     }
    1602     1183490 :     ELSE IF( headRotQuaternions == NULL && hExtOrientationData == NULL )
    1603             :     {
    1604             :         /* Reset the combined orientations and rotations */
    1605           0 :         hCombinedOrientationData->isInterpolationOngoing = FALSE;
    1606           0 :         move16();
    1607           0 :         hCombinedOrientationData->interpolationCoefficient_fx = ONE_IN_Q30;
    1608           0 :         move32();
    1609           0 :         hCombinedOrientationData->interpolationIncrement_fx = ONE_IN_Q30;
    1610           0 :         move32();
    1611           0 :         hCombinedOrientationData->Quaternions_ext_interpolation_start = identity;
    1612           0 :         hCombinedOrientationData->Quaternions_ext_interpolation_target = identity;
    1613           0 :         FOR( i = 0; i < hCombinedOrientationData->num_subframes; i++ )
    1614             :         {
    1615           0 :             hCombinedOrientationData->enableCombinedOrientation[i] = 0;
    1616           0 :             move16();
    1617           0 :             hCombinedOrientationData->Quaternions[i] = identity;
    1618           0 :             hCombinedOrientationData->listenerPos[i] = origo;
    1619             : 
    1620           0 :             FOR( j = 0; j < 3; j++ )
    1621             :             {
    1622           0 :                 set_zero_fx( hCombinedOrientationData->Rmat_fx[i][j], 3 );
    1623           0 :                 hCombinedOrientationData->Rmat_fx[i][j][j] = ONE_IN_Q30;
    1624           0 :                 move32();
    1625             :             }
    1626             :         }
    1627             :     }
    1628     1183490 :     ELSE IF( hExtOrientationData == NULL && headRotQuaternions != NULL )
    1629             :     {
    1630             :         /* Head rotation only */
    1631      200750 :         FOR( i = 0; i < hCombinedOrientationData->num_subframes; i++ )
    1632             :         {
    1633      160600 :             hCombinedOrientationData->Quaternions[i] = headRotQuaternions[i];
    1634             :         }
    1635             :     }
    1636             : 
    1637     1183490 :     IF( hExtOrientationData != NULL )
    1638             :     {
    1639             :         /* External orientations */
    1640     3130508 :         FOR( i = 0; i < hCombinedOrientationData->num_subframes; i++ )
    1641             :         {
    1642             :             /* Check for frozen external orientation */
    1643     1987168 :             IF( EQ_16( hExtOrientationData->enableExternalOrientation[i], 2 ) )
    1644             :             {
    1645       17391 :                 IF( NE_16( hCombinedOrientationData->isExtOrientationFrozen, 1 ) )
    1646             :                 {
    1647          31 :                     hCombinedOrientationData->Quaternion_frozen_ext = hExtOrientationData->Quaternions[i];
    1648          31 :                     hCombinedOrientationData->isExtOrientationFrozen = 1;
    1649          31 :                     move16();
    1650             :                 }
    1651             :             }
    1652             :             ELSE
    1653             :             {
    1654     1969777 :                 hCombinedOrientationData->Quaternion_frozen_ext = identity;
    1655     1969777 :                 hCombinedOrientationData->isExtOrientationFrozen = 0;
    1656     1969777 :                 move16();
    1657             :             }
    1658     1987168 :             test();
    1659     1987168 :             IF( EQ_16( hExtOrientationData->enableRotationInterpolation[i], 1 ) && hExtOrientationData->enableExternalOrientation[i] > 0 )
    1660             :             {
    1661       60970 :                 test();
    1662       60970 :                 test();
    1663       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 ) )
    1664             :                 {
    1665             :                     /* Continue interpolation */
    1666       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
    1667       50636 :                     move32();
    1668       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
    1669       50636 :                     move32();
    1670       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
    1671       50636 :                     move32();
    1672       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
    1673       50636 :                     move32();
    1674             : 
    1675       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
    1676       50636 :                     move32();
    1677       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
    1678       50636 :                     move32();
    1679       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
    1680       50636 :                     move32();
    1681       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
    1682       50636 :                     move32();
    1683             : 
    1684       50636 :                     hCombinedOrientationData->Quaternions_ext_interpolation_start.q_fact = Q29;
    1685       50636 :                     move16();
    1686       50636 :                     hCombinedOrientationData->Quaternions_ext_interpolation_target.q_fact = Q29;
    1687       50636 :                     move16();
    1688       50636 :                     QuaternionSlerp_fx( hCombinedOrientationData->Quaternions_ext_interpolation_start, hCombinedOrientationData->Quaternions_ext_interpolation_target, hCombinedOrientationData->interpolationCoefficient_fx, &hCombinedOrientationData->Quaternions[i] );
    1689       50636 :                     hCombinedOrientationData->interpolationCoefficient_fx = L_add( hCombinedOrientationData->interpolationIncrement_fx, hCombinedOrientationData->interpolationCoefficient_fx );
    1690       50636 :                     move32();
    1691             :                 }
    1692             :                 ELSE
    1693             :                 {
    1694             :                     /* Stop interpolation or check for new interpolation */
    1695       10334 :                     hCombinedOrientationData->isInterpolationOngoing = FALSE;
    1696       10334 :                     move16();
    1697       10334 :                     hCombinedOrientationData->interpolationCoefficient_fx = ONE_IN_Q30;
    1698       10334 :                     move32();
    1699       10334 :                     hCombinedOrientationData->interpolationIncrement_fx = ONE_IN_Q30;
    1700       10334 :                     move32();
    1701       10334 :                     external_target_interpolation_fx( hExtOrientationData, hCombinedOrientationData, i );
    1702       10334 :                     Word16 l_shift = 0;
    1703       10334 :                     move16();
    1704       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 ) ) );
    1705       10334 :                     hCombinedOrientationData->Quaternions[i].w_fx = L_shl( hCombinedOrientationData->Quaternions[i].w_fx, l_shift ); // q_fact+l_shift
    1706       10334 :                     move32();
    1707       10334 :                     hCombinedOrientationData->Quaternions[i].x_fx = L_shl( hCombinedOrientationData->Quaternions[i].x_fx, l_shift ); // q_fact+l_shift
    1708       10334 :                     move32();
    1709       10334 :                     hCombinedOrientationData->Quaternions[i].y_fx = L_shl( hCombinedOrientationData->Quaternions[i].y_fx, l_shift ); // q_fact+l_shift
    1710       10334 :                     move32();
    1711       10334 :                     hCombinedOrientationData->Quaternions[i].z_fx = L_shl( hCombinedOrientationData->Quaternions[i].z_fx, l_shift ); // q_fact+l_shift
    1712       10334 :                     move32();
    1713       10334 :                     hCombinedOrientationData->Quaternions[i].q_fact = add( hCombinedOrientationData->Quaternions[i].q_fact, l_shift );
    1714       10334 :                     move16();
    1715             :                 }
    1716             :             }
    1717             :             ELSE
    1718             :             {
    1719             :                 /* Interpolation disabled, use the current orientation values */
    1720             : 
    1721             :                 /* Use the most recent external orientation */
    1722     1926198 :                 IF( EQ_16( hExtOrientationData->enableExternalOrientation[i], 1 ) )
    1723             :                 {
    1724       36393 :                     hCombinedOrientationData->Quaternions[i] = hExtOrientationData->Quaternions[i];
    1725             :                 }
    1726             :                 /* Use the freezed external orientation */
    1727     1889805 :                 ELSE IF( EQ_16( hExtOrientationData->enableExternalOrientation[i], 2 ) )
    1728             :                 {
    1729       12803 :                     hCombinedOrientationData->Quaternions[i] = hCombinedOrientationData->Quaternion_frozen_ext;
    1730             :                 }
    1731             :             }
    1732             :         }
    1733             :     }
    1734     1183490 :     test();
    1735     1183490 :     IF( hExtOrientationData != NULL && headRotQuaternions != NULL )
    1736             :     {
    1737             :         /* Combine head and external orientations */
    1738      614084 :         FOR( i = 0; i < hCombinedOrientationData->num_subframes; i++ )
    1739             :         {
    1740             :             /* Check for frozen head rotation */
    1741      404860 :             IF( EQ_16( hExtOrientationData->enableHeadRotation[i], 2 ) )
    1742             :             {
    1743       12400 :                 IF( NE_16( hCombinedOrientationData->isHeadRotationFrozen, 1 ) )
    1744             :                 {
    1745          62 :                     hCombinedOrientationData->Quaternion_frozen_head = headRotQuaternions[i];
    1746          62 :                     hCombinedOrientationData->isHeadRotationFrozen = 1;
    1747          62 :                     move16();
    1748             :                 }
    1749             :             }
    1750             :             ELSE
    1751             :             {
    1752      392460 :                 hCombinedOrientationData->Quaternion_frozen_head = identity;
    1753      392460 :                 hCombinedOrientationData->isHeadRotationFrozen = 0;
    1754      392460 :                 move16();
    1755             :             }
    1756             :             /* Use the most recent head rotation */
    1757      404860 :             IF( EQ_16( hExtOrientationData->enableHeadRotation[i], 1 ) )
    1758             :             {
    1759      371655 :                 IF( hExtOrientationData->enableExternalOrientation[i] > 0 )
    1760             :                 {
    1761       76961 :                     QuaternionProduct_fx( hCombinedOrientationData->Quaternions[i], headRotQuaternions[i], &hCombinedOrientationData->Quaternions[i] );
    1762             :                 }
    1763             :                 ELSE
    1764             :                 {
    1765      294694 :                     hCombinedOrientationData->Quaternions[i] = headRotQuaternions[i];
    1766             :                 }
    1767             :             }
    1768             :             /* Use the freezed head rotation */
    1769       33205 :             ELSE IF( EQ_16( hExtOrientationData->enableHeadRotation[i], 2 ) )
    1770             :             {
    1771       12400 :                 IF( hExtOrientationData->enableExternalOrientation[i] > 0 )
    1772             :                 {
    1773       12400 :                     QuaternionProduct_fx( hCombinedOrientationData->Quaternions[i], hCombinedOrientationData->Quaternion_frozen_head, &hCombinedOrientationData->Quaternions[i] );
    1774             :                 }
    1775             :                 ELSE
    1776             :                 {
    1777           0 :                     hCombinedOrientationData->Quaternions[i] = hCombinedOrientationData->Quaternion_frozen_head;
    1778             :                 }
    1779             :             }
    1780             :             /* Reset the combined orientations to identity */
    1781      404860 :             test();
    1782      404860 :             if ( hExtOrientationData->enableHeadRotation[i] == 0 && hExtOrientationData->enableExternalOrientation[i] == 0 )
    1783             :             {
    1784           0 :                 hCombinedOrientationData->Quaternions[i] = identity;
    1785             :             }
    1786             :         }
    1787             :     }
    1788     1183490 :     test();
    1789     1183490 :     IF( headRotQuaternions != NULL || hExtOrientationData != NULL )
    1790             :     {
    1791             :         /* Calculate the combined rotation matrix */
    1792     3331258 :         FOR( i = 0; i < hCombinedOrientationData->num_subframes; i++ )
    1793             :         {
    1794     2147768 :             QuatToRotMat_fx( hCombinedOrientationData->Quaternions[i], hCombinedOrientationData->Rmat_fx[i] );
    1795             :         }
    1796             :     }
    1797             : 
    1798             :     /* Save the current orientations */
    1799     1183490 :     IF( hExtOrientationData != NULL )
    1800             :     {
    1801     1143340 :         IF( hExtOrientationData->enableExternalOrientation[hExtOrientationData->num_subframes - 1] > 0 )
    1802             :         {
    1803       27557 :             hCombinedOrientationData->Quaternion_prev_extOrientation = hExtOrientationData->Quaternions[hExtOrientationData->num_subframes - 1];
    1804             :         }
    1805             :         ELSE
    1806             :         {
    1807     1115783 :             hCombinedOrientationData->Quaternion_prev_extOrientation = identity;
    1808             :         }
    1809             :     }
    1810     1183490 :     IF( headRotQuaternions != NULL )
    1811             :     {
    1812      814834 :         FOR( i = 0; i < hCombinedOrientationData->num_subframes; i++ )
    1813             :         {
    1814      565460 :             hCombinedOrientationData->listenerPos[i] = listenerPos[i];
    1815             :         }
    1816             :     }
    1817             :     /* Check if combined orientation is enabled */
    1818     1183490 :     test();
    1819     1183490 :     test();
    1820     1183490 :     test();
    1821     1183490 :     IF( headRotQuaternions != NULL && hExtOrientationData == NULL )
    1822             :     {
    1823      200750 :         FOR( i = 0; i < hCombinedOrientationData->num_subframes; i++ )
    1824             :         {
    1825      160600 :             hCombinedOrientationData->enableCombinedOrientation[i] = 1;
    1826      160600 :             move16();
    1827             :         }
    1828             :     }
    1829     1143340 :     ELSE IF( headRotQuaternions == NULL && hExtOrientationData != NULL )
    1830             :     {
    1831     2516424 :         FOR( i = 0; i < hCombinedOrientationData->num_subframes; i++ )
    1832             :         {
    1833     1582308 :             IF( hExtOrientationData->enableExternalOrientation[i] > 0 )
    1834             :             {
    1835           0 :                 hCombinedOrientationData->enableCombinedOrientation[i] = 1;
    1836           0 :                 move16();
    1837             :             }
    1838             :             ELSE
    1839             :             {
    1840     1582308 :                 hCombinedOrientationData->enableCombinedOrientation[i] = 0;
    1841     1582308 :                 move16();
    1842             :             }
    1843             :         }
    1844             :     }
    1845      209224 :     ELSE IF( headRotQuaternions != NULL && hExtOrientationData != NULL )
    1846             :     {
    1847      614084 :         FOR( i = 0; i < hCombinedOrientationData->num_subframes; i++ )
    1848             :         {
    1849      404860 :             test();
    1850      404860 :             IF( hExtOrientationData->enableExternalOrientation[i] > 0 || ( hExtOrientationData->enableHeadRotation[i] > 0 ) )
    1851             :             {
    1852      404860 :                 hCombinedOrientationData->enableCombinedOrientation[i] = 1;
    1853      404860 :                 move16();
    1854             :             }
    1855             :             ELSE
    1856             :             {
    1857           0 :                 hCombinedOrientationData->enableCombinedOrientation[i] = 0;
    1858           0 :                 move16();
    1859             :             }
    1860             :         }
    1861             :     }
    1862             :     ELSE
    1863             :     {
    1864           0 :         FOR( i = 0; i < hCombinedOrientationData->num_subframes; i++ )
    1865             :         {
    1866           0 :             hCombinedOrientationData->enableCombinedOrientation[i] = 0;
    1867           0 :             move16();
    1868             :         }
    1869             :     }
    1870             : 
    1871     1183490 :     hCombinedOrientationData->subframe_idx = 0;
    1872     1183490 :     move16();
    1873     1183490 :     hCombinedOrientationData->cur_subframe_samples_rendered = 0;
    1874     1183490 :     move16();
    1875     1183490 :     hCombinedOrientationData->subframe_idx_start = 0;
    1876     1183490 :     move16();
    1877     1183490 :     hCombinedOrientationData->cur_subframe_samples_rendered_start = 0;
    1878     1183490 :     move16();
    1879     3331258 :     FOR( i = 0; i < hCombinedOrientationData->num_subframes; i++ )
    1880             :     {
    1881     8591072 :         FOR( j = 0; j < 3; j++ )
    1882             :         {
    1883    25773216 :             FOR( Word16 k = 0; k < 3; k++ )
    1884             :             {
    1885    19329912 :                 hCombinedOrientationData->Rmat_fx[i][j][k] = L_shl( hCombinedOrientationData->Rmat_fx[i][j][k], sub( 62, shl( hCombinedOrientationData->Quaternions[i].q_fact, 1 ) ) ); // Q30
    1886    19329912 :                 move32();
    1887             :             }
    1888             :         }
    1889             :     }
    1890             : 
    1891     1183490 :     Word16 l_shift = 0;
    1892     1183490 :     move16();
    1893     3331258 :     FOR( i = 0; i < hCombinedOrientationData->num_subframes; i++ )
    1894             :     {
    1895     2147768 :         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 ) ) );
    1896     2147768 :         hCombinedOrientationData->Quaternions[i].w_fx = L_shl( hCombinedOrientationData->Quaternions[i].w_fx, l_shift ); // q_fact+l_shift
    1897     2147768 :         move32();
    1898     2147768 :         hCombinedOrientationData->Quaternions[i].x_fx = L_shl( hCombinedOrientationData->Quaternions[i].x_fx, l_shift ); // q_fact+l_shift
    1899     2147768 :         move32();
    1900     2147768 :         hCombinedOrientationData->Quaternions[i].y_fx = L_shl( hCombinedOrientationData->Quaternions[i].y_fx, l_shift ); // q_fact+l_shift
    1901     2147768 :         move32();
    1902     2147768 :         hCombinedOrientationData->Quaternions[i].z_fx = L_shl( hCombinedOrientationData->Quaternions[i].z_fx, l_shift ); // q_fact+l_shift
    1903     2147768 :         move32();
    1904     2147768 :         hCombinedOrientationData->Quaternions[i].q_fact = add( hCombinedOrientationData->Quaternions[i].q_fact, l_shift );
    1905     2147768 :         move16();
    1906             :     }
    1907     1183490 :     return IVAS_ERR_OK;
    1908             : }
    1909             : 
    1910             : /*-------------------------------------------------------------------------
    1911             :  * external_target_interpolation()
    1912             :  *
    1913             :  *
    1914             :  *------------------------------------------------------------------------*/
    1915       10334 : static void external_target_interpolation_fx(
    1916             :     EXTERNAL_ORIENTATION_HANDLE hExtOrientationData,      /* i  : external orientation handle   */
    1917             :     COMBINED_ORIENTATION_HANDLE hCombinedOrientationData, /* i/o: combined orientation handle   */
    1918             :     const Word16 i                                        /* i  : subframe index                */
    1919             : )
    1920             : {
    1921             :     /* Sanity check for number of frames */
    1922       10334 :     hExtOrientationData->numFramesToTargetOrientation[i] = s_min( hExtOrientationData->numFramesToTargetOrientation[i], hCombinedOrientationData->maximumFramesToTargetOrientation );
    1923       10334 :     move16();
    1924       10334 :     hExtOrientationData->numFramesToTargetOrientation[i] = s_max( hExtOrientationData->numFramesToTargetOrientation[i], 0 );
    1925       10334 :     move16();
    1926             : 
    1927             :     /* Interpolate from the current orientation to the target orientation */
    1928       10334 :     IF( hExtOrientationData->numFramesToTargetOrientation[i] > 0 )
    1929             :     {
    1930        3349 :         IF( EQ_16( are_orientations_same_fx( &hCombinedOrientationData->Quaternions_ext_interpolation_target, &hExtOrientationData->Quaternions[i] ), false ) )
    1931             :         {
    1932             :             /* Target orientation is different from the previous target, update the values */
    1933             : 
    1934             :             /* Set the received orientation as the target */
    1935         249 :             hCombinedOrientationData->Quaternions_ext_interpolation_target = hExtOrientationData->Quaternions[i];
    1936             : 
    1937             :             /* Use the most recent external orientation as the starting orientation */
    1938         249 :             IF( EQ_16( hExtOrientationData->enableExternalOrientation[i], 1 ) )
    1939             :             {
    1940         218 :                 IF( i > 0 )
    1941             :                 {
    1942         141 :                     IF( hExtOrientationData->enableExternalOrientation[i - 1] == 0 )
    1943             :                     {
    1944             :                         IVAS_QUATERNION identity;
    1945          31 :                         identity.w_fx = ONE_IN_Q31;
    1946          31 :                         move32();
    1947          31 :                         identity.x_fx = identity.y_fx = identity.z_fx = 0;
    1948          31 :                         move32();
    1949          31 :                         move32();
    1950          31 :                         move32();
    1951          31 :                         identity.q_fact = 31;
    1952          31 :                         move16();
    1953          31 :                         hCombinedOrientationData->Quaternions_ext_interpolation_start = identity;
    1954             :                     }
    1955         110 :                     ELSE IF( EQ_16( hExtOrientationData->enableExternalOrientation[i - 1], 2 ) )
    1956             :                     {
    1957           0 :                         hCombinedOrientationData->Quaternions_ext_interpolation_start = hCombinedOrientationData->Quaternion_frozen_ext;
    1958             :                     }
    1959             :                     ELSE
    1960             :                     {
    1961         110 :                         hCombinedOrientationData->Quaternions_ext_interpolation_start = hExtOrientationData->Quaternions[i - 1];
    1962             :                     }
    1963             :                 }
    1964             :                 ELSE
    1965             :                 {
    1966          77 :                     hCombinedOrientationData->Quaternions_ext_interpolation_start = hCombinedOrientationData->Quaternion_prev_extOrientation;
    1967             :                 }
    1968             :             }
    1969          31 :             ELSE IF( EQ_16( hExtOrientationData->enableExternalOrientation[i], 2 ) )
    1970             :             {
    1971          31 :                 hCombinedOrientationData->Quaternions_ext_interpolation_start = hCombinedOrientationData->Quaternion_frozen_ext;
    1972             :             }
    1973         249 :             Word16 tmp_e = 0;
    1974         249 :             move16();
    1975             :             Word32 tmp;
    1976             :             /* Calculate the interpolation increment and coefficient */
    1977         249 :             tmp = BASOP_Util_Divide3232_Scale_newton( ONE_IN_Q30, L_shl( L_deposit_l( hExtOrientationData->numFramesToTargetOrientation[i] ), 2 ), &tmp_e );
    1978         249 :             hCombinedOrientationData->interpolationIncrement_fx = L_shl( tmp, sub( tmp_e, 31 ) ); /* Q30 */
    1979         249 :             move32();
    1980         249 :             hCombinedOrientationData->interpolationCoefficient_fx = hCombinedOrientationData->interpolationIncrement_fx;
    1981         249 :             move32();
    1982             :         }
    1983             : 
    1984             :         /* Interpolate */
    1985        3349 :         hCombinedOrientationData->isInterpolationOngoing = TRUE;
    1986        3349 :         move16();
    1987        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
    1988        3349 :         move32();
    1989        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
    1990        3349 :         move32();
    1991        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
    1992        3349 :         move32();
    1993        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
    1994        3349 :         move32();
    1995             : 
    1996        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
    1997        3349 :         move32();
    1998        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
    1999        3349 :         move32();
    2000        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
    2001        3349 :         move32();
    2002        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
    2003        3349 :         move32();
    2004             : 
    2005        3349 :         hCombinedOrientationData->Quaternions_ext_interpolation_start.q_fact = Q29;
    2006        3349 :         move16();
    2007        3349 :         hCombinedOrientationData->Quaternions_ext_interpolation_target.q_fact = Q29;
    2008        3349 :         move16();
    2009        3349 :         QuaternionSlerp_fx( hCombinedOrientationData->Quaternions_ext_interpolation_start, hCombinedOrientationData->Quaternions_ext_interpolation_target, hCombinedOrientationData->interpolationCoefficient_fx, &hCombinedOrientationData->Quaternions[i] );
    2010        3349 :         hCombinedOrientationData->interpolationCoefficient_fx = L_add_sat( hCombinedOrientationData->interpolationCoefficient_fx, hCombinedOrientationData->interpolationIncrement_fx );
    2011        3349 :         move32();
    2012             :     }
    2013             :     ELSE
    2014             :     {
    2015             :         /* Use the target orientation immediately */
    2016        6985 :         hCombinedOrientationData->isInterpolationOngoing = FALSE;
    2017        6985 :         move16();
    2018        6985 :         hCombinedOrientationData->interpolationCoefficient_fx = ONE_IN_Q30;
    2019        6985 :         move32();
    2020        6985 :         hCombinedOrientationData->interpolationIncrement_fx = ONE_IN_Q30;
    2021        6985 :         move32();
    2022        6985 :         hCombinedOrientationData->Quaternions[i] = hExtOrientationData->Quaternions[i];
    2023             :     }
    2024             : 
    2025       10334 :     return;
    2026             : }
    2027             : 
    2028             : /*-------------------------------------------------------------------------
    2029             :  * are_orientations_same()
    2030             :  *
    2031             :  *
    2032             :  *------------------------------------------------------------------------*/
    2033       54203 : static bool are_orientations_same_fx(
    2034             :     const IVAS_QUATERNION *orientation1,
    2035             :     const IVAS_QUATERNION *orientation2 )
    2036             : {
    2037       54203 :     bool orientationsAreSame = true;
    2038       54203 :     move16();
    2039       54203 :     Word32 error_margin_fx = 107374182; // 0.05f in Q31
    2040       54203 :     move32();
    2041       54203 :     Word16 error_margin_e = 0;
    2042       54203 :     move16();
    2043       54203 :     Word16 w_e = 0, x_e = 0, y_e = 0, z_e = 0;
    2044       54203 :     move16();
    2045       54203 :     move16();
    2046       54203 :     move16();
    2047       54203 :     move16();
    2048       54203 :     Word32 w_result = 0, x_result = 0, y_result = 0, z_result = 0;
    2049       54203 :     move32();
    2050       54203 :     move32();
    2051       54203 :     move32();
    2052       54203 :     move32();
    2053       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 ) );
    2054       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 ) );
    2055       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 ) );
    2056       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 ) );
    2057       54203 :     Word16 Flag_1 = BASOP_Util_Cmp_Mant32Exp( w_result, w_e, error_margin_fx, error_margin_e );
    2058       54203 :     Word16 Flag_2 = BASOP_Util_Cmp_Mant32Exp( x_result, x_e, error_margin_fx, error_margin_e );
    2059       54203 :     Word16 Flag_3 = BASOP_Util_Cmp_Mant32Exp( y_result, y_e, error_margin_fx, error_margin_e );
    2060       54203 :     Word16 Flag_4 = BASOP_Util_Cmp_Mant32Exp( z_result, z_e, error_margin_fx, error_margin_e );
    2061             : 
    2062       54203 :     test();
    2063       54203 :     test();
    2064       54203 :     test();
    2065      108073 :     if ( EQ_16( Flag_1, 1 ) ||
    2066      107740 :          EQ_16( Flag_2, 1 ) ||
    2067      107740 :          EQ_16( Flag_3, 1 ) ||
    2068       53870 :          EQ_16( Flag_4, 1 ) )
    2069             :     {
    2070         467 :         orientationsAreSame = false;
    2071         467 :         move16();
    2072             :     }
    2073             : 
    2074       54203 :     return orientationsAreSame;
    2075             : }
    2076             : 
    2077             : 
    2078             : /*-----------------------------------------------------------------------*
    2079             :  * Local Function definitions
    2080             :  *-----------------------------------------------------------------------*/
    2081             : /*-------------------------------------------------------------------------
    2082             :  * Helper functions used by SHrotmatgen,
    2083             :  * an implementation of the algorithm in
    2084             :  * Ivanic, J. & Ruedenberg, K., J. Phys. Chem. 100, 6342 (1996)
    2085             :  *------------------------------------------------------------------------*/
    2086    11445085 : static Word32 SHrot_p_fx(
    2087             :     const Word16 i,
    2088             :     const Word16 l,
    2089             :     const Word16 a,
    2090             :     const Word16 b,
    2091             :     Word16 SHrotmat[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM], // Q14
    2092             :     Word16 R_lm1[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM] )   // Q14
    2093             : {
    2094             : 
    2095    11445085 :     Word16 ri1 = 0, rim1 = 0, ri0 = 0, R_lm1_1 = 0, R_lm1_2 = 0;
    2096    11445085 :     move16();
    2097    11445085 :     move16();
    2098    11445085 :     move16();
    2099    11445085 :     move16();
    2100    11445085 :     move16();
    2101             : 
    2102    11445085 :     Word32 p = 0;
    2103    11445085 :     move32();
    2104             : 
    2105    11445085 :     ri1 = SHrotmat[i + 1 + 1][1 + 1 + 1]; // Q14
    2106    11445085 :     move16();
    2107    11445085 :     rim1 = SHrotmat[i + 1 + 1][-1 + 1 + 1]; // Q14
    2108    11445085 :     move16();
    2109    11445085 :     ri0 = SHrotmat[i + 1 + 1][0 + 1 + 1]; // Q14
    2110    11445085 :     move16();
    2111             : 
    2112    11445085 :     IF( EQ_16( b, -l ) )
    2113             :     {
    2114     1886655 :         R_lm1_1 = R_lm1[a + l - 1][0]; // Q14
    2115     1886655 :         move16();
    2116     1886655 :         R_lm1_2 = R_lm1[a + l - 1][2 * l - 2]; // Q14
    2117     1886655 :         move16();
    2118     1886655 :         p = L_mac0( L_mult0( ri1, R_lm1_1 ), rim1, R_lm1_2 ); // Q28
    2119             :     }
    2120             :     ELSE
    2121             :     {
    2122     9558430 :         IF( EQ_16( b, l ) )
    2123             :         {
    2124     1886655 :             R_lm1_1 = R_lm1[a + l - 1][2 * l - 2]; // Q14
    2125     1886655 :             move16();
    2126     1886655 :             R_lm1_2 = R_lm1[a + l - 1][0]; // Q14
    2127     1886655 :             move16();
    2128     1886655 :             p = L_msu0( L_mult0( ri1, R_lm1_1 ), rim1, R_lm1_2 ); // Q28
    2129             :         }
    2130             :         ELSE
    2131             :         {
    2132     7671775 :             R_lm1_1 = R_lm1[a + l - 1][b + l - 1];
    2133     7671775 :             move16();
    2134     7671775 :             p = L_mult0( ri0, R_lm1_1 ); // Q28
    2135             :         }
    2136             :     }
    2137             : 
    2138    11445085 :     return p; // Q28
    2139             : }
    2140             : 
    2141     2546975 : static Word32 SHrot_u_fx(
    2142             :     const Word16 l,
    2143             :     const Word16 m,
    2144             :     const Word16 n,
    2145             :     Word16 SHrotmat[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM], // Q14
    2146             :     Word16 R_lm1[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM]     // Q14
    2147             : )
    2148             : {
    2149     2546975 :     return SHrot_p_fx( 0, l, m, n, SHrotmat, R_lm1 );
    2150             : }
    2151             : 
    2152     3836765 : static Word32 SHrot_v_fx(
    2153             :     const Word16 l,
    2154             :     const Word16 m,
    2155             :     const Word16 n,
    2156             :     Word16 SHrotmat[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM], // Q14
    2157             :     Word16 R_lm1[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM]     // Q14
    2158             : )
    2159             : {
    2160             : 
    2161             :     Word32 result;
    2162             :     Word32 p0, p1;
    2163             :     Word16 d;
    2164             : 
    2165     3836765 :     IF( m == 0 )
    2166             :     {
    2167      644895 :         p0 = SHrot_p_fx( 1, l, 1, n, SHrotmat, R_lm1 );   // Q28
    2168      644895 :         p1 = SHrot_p_fx( -1, l, -1, n, SHrotmat, R_lm1 ); // Q28
    2169      644895 :         result = L_shr( L_add( p0, p1 ), 2 );             // converting to Q27
    2170             :     }
    2171             :     ELSE
    2172             :     {
    2173     3191870 :         IF( m > 0 )
    2174             :         {
    2175     1595935 :             d = 0;
    2176     1595935 :             move16();
    2177     1595935 :             if ( EQ_16( m, 1 ) )
    2178             :             {
    2179      644895 :                 d = 1;
    2180      644895 :                 move16();
    2181             :             }
    2182     1595935 :             p0 = SHrot_p_fx( 1, l, sub( m, 1 ), n, SHrotmat, R_lm1 );                                           // Q28
    2183     1595935 :             p1 = SHrot_p_fx( -1, l, add( negate( m ), 1 ), n, SHrotmat, R_lm1 );                                // Q28
    2184     1595935 :             result = Msub_32_16_r( Mpy_32_16_r( p0, square_root16_table[1 + d] ), p1, shl( sub( 1, d ), 14 ) ); // Q27
    2185             :         }
    2186             :         ELSE
    2187             :         {
    2188     1595935 :             d = 0;
    2189     1595935 :             move16();
    2190     1595935 :             if ( EQ_16( m, -1 ) )
    2191             :             {
    2192      644895 :                 d = 1;
    2193      644895 :                 move16();
    2194             :             }
    2195     1595935 :             p0 = SHrot_p_fx( 1, l, add( m, 1 ), n, SHrotmat, R_lm1 );
    2196     1595935 :             p1 = SHrot_p_fx( -1, l, negate( add( m, 1 ) ), n, SHrotmat, R_lm1 );
    2197     1595935 :             result = Madd_32_16_r( Mpy_32_16_r( p0, shl( sub( 1, d ), 14 ) ), p1, square_root16_table[1 + d] ); // Q27
    2198             :         }
    2199             :     }
    2200     3836765 :     return result; // Q27
    2201             : }
    2202             : 
    2203      612290 : static Word32 SHrot_w_fx(
    2204             :     const Word16 l,
    2205             :     const Word16 m,
    2206             :     const Word16 n,
    2207             :     Word16 SHrotmat[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM], // Q14
    2208             :     Word16 R_lm1[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM]     // Q14
    2209             : )
    2210             : {
    2211             :     Word32 result, p0, p1;
    2212             : 
    2213      612290 :     IF( m == 0 )
    2214             :     {
    2215           0 :         printf( "ERROR should not be called\n" );
    2216           0 :         return 0;
    2217             :     }
    2218             :     ELSE
    2219             :     {
    2220      612290 :         IF( m > 0 )
    2221             :         {
    2222      306145 :             p0 = SHrot_p_fx( 1, l, add( m, 1 ), n, SHrotmat, R_lm1 );            // Q28
    2223      306145 :             p1 = SHrot_p_fx( -1, l, negate( add( m, 1 ) ), n, SHrotmat, R_lm1 ); // Q28
    2224      306145 :             result = L_add( p0, p1 );                                            // Q28
    2225             :         }
    2226             :         ELSE
    2227             :         {
    2228      306145 :             p0 = SHrot_p_fx( 1, l, sub( m, 1 ), n, SHrotmat, R_lm1 );  // Q28
    2229      306145 :             p1 = SHrot_p_fx( -1, l, sub( 1, m ), n, SHrotmat, R_lm1 ); // Q28
    2230      306145 :             result = L_sub( p0, p1 );                                  // Q28
    2231             :         }
    2232             :     }
    2233             : 
    2234      612290 :     return result; // Q28
    2235             : }
    2236             : 
    2237             : 
    2238             : /*-------------------------------------------------------------------------
    2239             :  * SHrotmatgen_fx()
    2240             :  *
    2241             :  *
    2242             :  *------------------------------------------------------------------------*/
    2243             : 
    2244       91765 : void SHrotmatgen_fx(
    2245             :     Word16 SHrotmat[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM], /* o  : rotation matrix in SHD     Q14 */
    2246             :     Word32 Rmat[3][3],                                     /* i  : real-space rotation matrix Q30 */
    2247             :     const Word16 order                                     /* i  : ambisonics order               */
    2248             : )
    2249             : {
    2250       91765 :     Word16 d = 0;
    2251       91765 :     move16();
    2252       91765 :     Word16 band_idx = 0;
    2253       91765 :     move16();
    2254             :     Word16 i, j;
    2255             :     Word16 l, m, n;
    2256             :     Word16 absm;
    2257       91765 :     Word16 sqdenom = 0, sql2mm2 = 0, sqdabsm = 0, sqlabsm = 0;
    2258       91765 :     Word16 u = 0, v = 0, w = 0;
    2259       91765 :     move16();
    2260       91765 :     move16();
    2261       91765 :     move16();
    2262       91765 :     move16();
    2263       91765 :     move16();
    2264       91765 :     move16();
    2265       91765 :     move16();
    2266             : 
    2267       91765 :     Word32 u32_fx = 0, v32_fx = 0, w32_fx = 0;
    2268       91765 :     move32();
    2269       91765 :     move32();
    2270       91765 :     move32();
    2271             : 
    2272             :     Word16 R_lm1[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM];
    2273             : 
    2274     1560005 :     FOR( i = 0; i < HEADROT_SHMAT_DIM; i++ )
    2275             :     {
    2276     1468240 :         set16_fx( R_lm1[i], 0, HEADROT_SHMAT_DIM );
    2277             :     }
    2278             :     Word16 R_l[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM];
    2279             :     Word32 result;
    2280       91765 :     SHrotmat[0][0] = ONE_IN_Q14;
    2281       91765 :     move16();
    2282             : 
    2283       91765 :     SHrotmat[1][1] = extract_h( Rmat[1][1] ); // Q14
    2284       91765 :     move16();
    2285       91765 :     SHrotmat[1][2] = extract_h( Rmat[1][2] );
    2286       91765 :     move16();
    2287       91765 :     SHrotmat[1][3] = extract_h( Rmat[1][0] );
    2288       91765 :     move16();
    2289             : 
    2290       91765 :     SHrotmat[2][1] = extract_h( Rmat[2][1] ); // Q14
    2291       91765 :     move16();
    2292       91765 :     SHrotmat[2][2] = extract_h( Rmat[2][2] );
    2293       91765 :     move16();
    2294       91765 :     SHrotmat[2][3] = extract_h( Rmat[2][0] );
    2295       91765 :     move16();
    2296             : 
    2297       91765 :     SHrotmat[3][1] = extract_h( Rmat[0][1] ); // Q14
    2298       91765 :     move16();
    2299       91765 :     SHrotmat[3][2] = extract_h( Rmat[0][2] );
    2300       91765 :     move16();
    2301       91765 :     SHrotmat[3][3] = extract_h( Rmat[0][0] );
    2302       91765 :     move16();
    2303             : 
    2304      367060 :     FOR( i = 0; i < 2 * 1 + 1; i++ )
    2305             :     {
    2306     1101180 :         FOR( j = 0; j < 2 * 1 + 1; j++ )
    2307             :         {
    2308      825885 :             R_lm1[i][j] = SHrotmat[i + 1][j + 1]; // Q14
    2309      825885 :             move16();
    2310             :         }
    2311             :     }
    2312       91765 :     band_idx = 4;
    2313       91765 :     move16();
    2314      203250 :     FOR( l = 2; l <= order; l++ )
    2315             :     {
    2316      111485 :         set16_fx( &R_l[0][0], 0, HEADROT_SHMAT_DIM2 );
    2317      756380 :         FOR( m = -l; m <= l; m++ )
    2318             :         {
    2319      644895 :             d = 0;
    2320      644895 :             move16();
    2321      644895 :             if ( m == 0 )
    2322             :             {
    2323      111485 :                 d = 1;
    2324      111485 :                 move16();
    2325             :             }
    2326      644895 :             absm = extract_l( L_abs( m ) );
    2327             : 
    2328      644895 :             sql2mm2 = square_root30_q12[l * l - m * m]; // Q12
    2329      644895 :             move16();
    2330      644895 :             sqdabsm = square_root30_q12[( 1 + d ) * ( l + absm - 1 ) * ( l + absm )]; // Q12
    2331      644895 :             move16();
    2332      644895 :             sqlabsm = square_root30_q12[( ( l - ( absm + 1 ) ) * ( l - absm ) )]; // Q12
    2333      644895 :             move16();
    2334             : 
    2335     4481660 :             FOR( n = -l; n <= l; n++ )
    2336             :             {
    2337     3836765 :                 IF( EQ_16( abs_s( n ), l ) )
    2338             :                 {
    2339     1289790 :                     sqdenom = square_root30_q12[( ( 2 * l ) * ( 2 * l - 1 ) )]; // Q12
    2340     1289790 :                     move16();
    2341             :                 }
    2342             :                 ELSE
    2343             :                 {
    2344     2546975 :                     sqdenom = square_root30_q12[( l * l - n * n )]; // Q12
    2345     2546975 :                     move16();
    2346             :                 }
    2347             : 
    2348             : 
    2349     3836765 :                 u = div_l( L_shl( (Word32) sql2mm2, 15 ), sqdenom );                                     // Q14
    2350     3836765 :                 v = imult1616( div_l( L_shl( (Word32) sqdabsm, 14 ), sqdenom ), sub( 1, shl( d, 1 ) ) ); // Q14
    2351     3836765 :                 w = imult1616( div_l( L_shl( (Word32) sqlabsm, 14 ), sqdenom ), negate( sub( 1, d ) ) ); // Q14
    2352             : 
    2353     3836765 :                 IF( u != 0 )
    2354             :                 {
    2355     2546975 :                     result = SHrot_u_fx( l, m, n, SHrotmat, R_lm1 ); // Q28
    2356     2546975 :                     u32_fx = Mpy_32_16_r( result, u );               // Q27
    2357             :                 }
    2358     3836765 :                 IF( v != 0 )
    2359             :                 {
    2360     3836765 :                     result = SHrot_v_fx( l, m, n, SHrotmat, R_lm1 );
    2361     3836765 :                     v32_fx = Mpy_32_16_r( result, v ); // Q26
    2362             :                 }
    2363     3836765 :                 IF( w != 0 )
    2364             :                 {
    2365      612290 :                     result = SHrot_w_fx( l, m, n, SHrotmat, R_lm1 );
    2366      612290 :                     w32_fx = Mpy_32_16_r( result, w ); // Q27
    2367             :                 }
    2368             :                 // Addind and converting to 16 bit integer of Q14
    2369     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
    2370     3836765 :                 move16();
    2371             :             }
    2372             :         }
    2373             : 
    2374      756380 :         FOR( i = 0; i < 2 * l + 1; i++ )
    2375             :         {
    2376     4481660 :             FOR( j = 0; j < 2 * l + 1; j++ )
    2377             :             {
    2378     3836765 :                 SHrotmat[band_idx + i][band_idx + j] = R_l[i][j]; // Q14
    2379     3836765 :                 move16();
    2380             :             }
    2381             :         }
    2382             : 
    2383      756380 :         FOR( i = 0; i < 2 * l + 1; i++ )
    2384             :         {
    2385     4481660 :             FOR( j = 0; j < 2 * l + 1; j++ )
    2386             :             {
    2387     3836765 :                 R_lm1[i][j] = R_l[i][j];
    2388     3836765 :                 move16();
    2389             :             }
    2390             :         }
    2391             : 
    2392      111485 :         band_idx = add( band_idx, add( shl( l, 1 ), 1 ) );
    2393             :     }
    2394             : 
    2395       91765 :     return;
    2396             : }
    2397             : 
    2398             : 
    2399             : /*-------------------------------------------------------------------------
    2400             :  * ivas_combined_orientation_update_index()
    2401             :  *
    2402             :  * update read index based on the number of rendered samples
    2403             :  *------------------------------------------------------------------------*/
    2404             : 
    2405     2032419 : void ivas_combined_orientation_update_index(
    2406             :     COMBINED_ORIENTATION_HANDLE hCombinedOrientationData, /* i/o: combined orientation handle           */
    2407             :     const Word16 samples_rendered                         /* i  : samples rendered since the last call  */
    2408             : )
    2409             : {
    2410             :     Word16 exp, div_result;
    2411     2032419 :     IF( hCombinedOrientationData != NULL )
    2412             :     {
    2413      795410 :         IF( EQ_16( hCombinedOrientationData->num_subframes, 1 ) )
    2414             :         {
    2415             :             /* only one orientation available anyway or split rendering with low resolution*/
    2416      263202 :             hCombinedOrientationData->subframe_idx = 0;
    2417      263202 :             move16();
    2418             :         }
    2419             :         ELSE
    2420             :         {
    2421      532208 :             hCombinedOrientationData->cur_subframe_samples_rendered = add( hCombinedOrientationData->cur_subframe_samples_rendered, samples_rendered );
    2422      532208 :             move16();
    2423      532208 :             div_result = BASOP_Util_Divide3216_Scale( hCombinedOrientationData->cur_subframe_samples_rendered, hCombinedOrientationData->subframe_size, &exp );
    2424      532208 :             hCombinedOrientationData->subframe_idx = add( hCombinedOrientationData->subframe_idx, shl( div_result, add( exp, 1 ) ) );
    2425      532208 :             move16();
    2426      532208 :             hCombinedOrientationData->cur_subframe_samples_rendered = hCombinedOrientationData->cur_subframe_samples_rendered % hCombinedOrientationData->subframe_size;
    2427      532208 :             move16();
    2428      532208 :             hCombinedOrientationData->subframe_idx = s_min( hCombinedOrientationData->subframe_idx, sub( hCombinedOrientationData->num_subframes, 1 ) );
    2429      532208 :             move16();
    2430             :         }
    2431             :     }
    2432             : 
    2433     2032419 :     return;
    2434             : }
    2435             : 
    2436             : 
    2437             : /*-------------------------------------------------------------------------
    2438             :  * ivas_combined_orientation_set_to_start_index()
    2439             :  *
    2440             :  * update read index based on the number of rendered samples
    2441             :  *------------------------------------------------------------------------*/
    2442             : 
    2443     1916663 : void ivas_combined_orientation_set_to_start_index(
    2444             :     COMBINED_ORIENTATION_HANDLE hCombinedOrientationData /* i/o: combined orientation handle    */
    2445             : )
    2446             : {
    2447     1916663 :     IF( hCombinedOrientationData != NULL )
    2448             :     {
    2449     1916440 :         hCombinedOrientationData->subframe_idx = hCombinedOrientationData->subframe_idx_start;
    2450     1916440 :         move16();
    2451     1916440 :         hCombinedOrientationData->cur_subframe_samples_rendered = hCombinedOrientationData->cur_subframe_samples_rendered_start;
    2452     1916440 :         move16();
    2453             :     }
    2454             : 
    2455     1916663 :     return;
    2456             : }
    2457             : /*-------------------------------------------------------------------------
    2458             :  * ivas_combined_orientation_update_start_index()
    2459             :  *
    2460             :  * update start index based on the number of rendered samples
    2461             :  *------------------------------------------------------------------------*/
    2462             : 
    2463     1526210 : void ivas_combined_orientation_update_start_index(
    2464             :     COMBINED_ORIENTATION_HANDLE hCombinedOrientationData, /* i/o: combined orientation handle           */
    2465             :     const Word16 samples_rendered                         /* i  : samples rendered since the last call  */
    2466             : )
    2467             : {
    2468     1526210 :     IF( hCombinedOrientationData != NULL )
    2469             :     {
    2470     1183490 :         IF( EQ_16( hCombinedOrientationData->num_subframes, 1 ) )
    2471             :         {
    2472             :             /* only one orientation available anyway or split rendering with low resolution*/
    2473      862064 :             hCombinedOrientationData->subframe_idx = 0;
    2474      862064 :             move16();
    2475             :         }
    2476             :         ELSE
    2477             :         {
    2478      321426 :             hCombinedOrientationData->cur_subframe_samples_rendered_start = add( hCombinedOrientationData->cur_subframe_samples_rendered_start, samples_rendered );
    2479      321426 :             move16();
    2480      321426 :             hCombinedOrientationData->subframe_idx_start = add( hCombinedOrientationData->subframe_idx_start, mult( hCombinedOrientationData->cur_subframe_samples_rendered, div_s( 1, hCombinedOrientationData->subframe_size ) ) );
    2481      321426 :             move16();
    2482      321426 :             hCombinedOrientationData->cur_subframe_samples_rendered_start = hCombinedOrientationData->cur_subframe_samples_rendered % hCombinedOrientationData->subframe_size; /* No operator to calculate modulo*/
    2483      321426 :             move16();
    2484      321426 :             hCombinedOrientationData->subframe_idx_start = s_min( hCombinedOrientationData->subframe_idx, sub( hCombinedOrientationData->num_subframes, 1 ) );
    2485      321426 :             move16();
    2486             :         }
    2487             :     }
    2488             : 
    2489     1526210 :     return;
    2490             : }

Generated by: LCOV version 1.14