LCOV - code coverage report
Current view: top level - lib_rend - ivas_vbap_fx.c (source / functions) Hit Total Coverage
Test: Coverage on main -- dec/rend @ 633e3f2e309758d10805ef21e0436356fe719b7a Lines: 1010 1100 91.8 %
Date: 2025-08-23 01:22:27 Functions: 22 22 100.0 %

          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 <stdint.h>
      34             : #include "options.h"
      35             : #include <assert.h>
      36             : #include <math.h>
      37             : #include "prot_fx.h"
      38             : #include "ivas_prot_rend_fx.h"
      39             : #include "ivas_stat_dec.h"
      40             : #include "wmc_auto.h"
      41             : #include "ivas_prot_fx.h"
      42             : 
      43             : /*-----------------------------------------------------------------------*
      44             :  * Local constants
      45             :  *-----------------------------------------------------------------------*/
      46             : 
      47             : /* 128 is maximum num_speaker_nodes number. This relates to memory optimization and maximum of triplets:
      48             :  - triplet indices are unsigned_char (see below structs) --> max triplets is 256
      49             :  - num_speaker_nodes_internal = num_speaker_nodes + 2 (potential virtual node channels, bottom and top)
      50             :  - max_num_triplets = 256 = (max_num_ls_internal - 2) * 2 = (max_num_ls) * 2
      51             :  --> max_num_ls = 128
      52             :  */
      53             : #define VBAP_MAX_NUM_SPEAKER_NODES                  128
      54             : #define VBAP_MAX_NUM_TRIPLETS                       256
      55             : #define VBAP_EPSILON                                0.001f /* The fairly large epsilon is for detecting planes etc and accounts for rounding issues */
      56             : #define VBAP_EPSILON_Q3O                            1073741
      57             : #define VBAP_EPSILON_Q31                            2147483
      58             : #define VBAP_MAX_PLANES                             50
      59             : #define VBAP_MAX_HORIZONTAL_GAP_FOR_PLANE_DETECTION 140.0f
      60             : /* If a speaker node is found
      61             :  - above VBAP_NO_VIRTUAL_SPEAKER_NODE_ELE_LIMIT, no virtual node is used
      62             :  - above VBAP_DISTRIBUTE_VIRTUAL_SPEAKER_NODE_ELE_LIMIT, energy-spreading virtual node is used
      63             :  - not above VBAP_DISTRIBUTE_VIRTUAL_SPEAKER_NODE_ELE_LIMIT, energy-omitting virtual node is used
      64             :  Same applies for both elevations and inclinations, i.e., the two half-spheres. */
      65             : #define VBAP_NO_VIRTUAL_SPEAKER_NODE_ELE_LIMIT         45.0f
      66             : #define VBAP_DISTRIBUTE_VIRTUAL_SPEAKER_NODE_ELE_LIMIT 20.0f
      67             : #define VBAP_VIRTUAL_BACK_ELE_LIMIT                    45.0f
      68             : #define VBAP_NOT_VALID_CONNECTION                      ( -1 )
      69             : 
      70             : /* Maximum azimuth gap between speaker nodes for detecting zero elevation horizontal plane  */
      71             : #define VBAP_MAX_HORIZONTAL_GAP 170u
      72             : 
      73             : #define VBAP_SEARCH_SECTOR_SIZE    ( 360.0f / ( VBAP_NUM_SEARCH_SECTORS ) )
      74             : #define VBAP_SEARCH_SECTOR_SIZE_Q0 90
      75             : 
      76             : 
      77             : enum VirtualSpeakerNodeType
      78             : {
      79             :     NO_VIRTUAL_SPEAKER_NODE,
      80             :     VIRTUAL_SPEAKER_NODE_DISCARD_ENERGY,
      81             :     VIRTUAL_SPEAKER_NODE_DISTRIBUTE_ENERGY
      82             : };
      83             : 
      84             : enum ConnectionClass
      85             : {
      86             :     REGULAR_CONNECTION,
      87             :     ELEVATED_PLANE_THIN_TRIANGLE_CONNECTION,
      88             :     CONNECTION_WITH_SPEAKER_NODE_BEHIND
      89             : };
      90             : 
      91             : typedef struct connection_option
      92             : {
      93             :     Word16 chA;
      94             :     Word16 chB;
      95             :     Word32 arc_weighted_fx; /* Q29 */
      96             :     Word32 arc_fx;          /* Q29 */
      97             : } ConnectionOption;
      98             : 
      99             : enum SpeakerNodeGroup
     100             : {
     101             :     SPEAKER_NODE_BOTTOM_HALF,
     102             :     SPEAKER_NODE_HORIZONTAL,
     103             :     SPEAKER_NODE_TOP_HALF,
     104             :     SPEAKER_NODE_BACK,
     105             :     SPEAKER_NODE_ALL
     106             : };
     107             : 
     108             : /* Defines a single speaker node */
     109             : typedef struct vbap_speaker_node_structure
     110             : {
     111             :     Word32 azi_deg_fx;     /* Q22 */
     112             :     Word32 ele_deg_fx;     /* Q22 */
     113             :     Word32 unit_vec_fx[3]; /* Q30 */
     114             :     enum SpeakerNodeGroup group;
     115             : 
     116             : } VBAP_SPEAKER_NODE;
     117             : 
     118             : 
     119             : /*-----------------------------------------------------------------------*
     120             :  * Local function prototypes
     121             :  *-----------------------------------------------------------------------*/
     122             : 
     123             : static UWord8 vector_matrix_multiply_3x3_fx( const Word16 *src_vector, Word32 matrix[3][3], Word32 *result, Word16 q_matrix );
     124             : 
     125             : static UWord8 vector_matrix_multiply_3x3_32_fx( const Word32 *src_vector, Word32 matrix[3][3], Word32 *result, Word16 q_matrix );
     126             : 
     127             : static void init_speaker_node_direction_data_fx( VBAP_SPEAKER_NODE *speaker_node_data, const Word32 *speaker_node_azi_deg_fx, const Word32 *speaker_node_ele_deg_fx, const Word16 num_speaker_nodes );
     128             : 
     129             : static Word16 determine_virtual_surface_triplets_fx( const Word16 num_speaker_nodes, const VBAP_SPEAKER_NODE *speaker_node_data, Word16 connections[][2], const Word16 max_num_connections, VBAP_VS_TRIPLET *triplets, Word16 initial_search_indices[VBAP_NUM_SEARCH_SECTORS], enum SpeakerNodeGroup allowed_group );
     130             : 
     131             : static void determine_initial_search_indices_fx( const Word16 num_triplets, const Word32 triplet_azidegs_fx[VBAP_MAX_NUM_TRIPLETS], Word16 initial_search_indices[VBAP_NUM_SEARCH_SECTORS] );
     132             : 
     133             : static ivas_error determine_connections_fx( const Word16 num_speaker_nodes, const VBAP_SPEAKER_NODE *speaker_node_data, Word16 connections[][2], const Word16 max_num_connections, Word16 *group1_count, Word16 *group2_start, Word16 *group2_count );
     134             : 
     135             : static void formulate_horizontal_connections_fx( const VBAP_SPEAKER_NODE *speaker_node_data, const Word16 num_speaker_nodes, Word16 connections[][2], Word16 *connection_write_index );
     136             : 
     137             : static ivas_error get_half_sphere_connection_options_fx( const VBAP_SPEAKER_NODE *speaker_node_data, const enum SpeakerNodeGroup group, const Word16 num_speaker_nodes, const Word16 num_non_crossing_planes, const Word32 *non_crossing_plane_elevation_deg_fx, ConnectionOption **connection_options_pr, Word16 *num_connection_options );
     138             : 
     139             : static ivas_error formulate_half_sphere_connections_fx( const VBAP_SPEAKER_NODE *speaker_node_data, const Word16 num_speaker_nodes, const enum SpeakerNodeGroup group, Word16 connections[][2], Word16 *connection_write_index, const Word16 max_num_connections, const Word16 num_non_crossing_planes, const Word32 *non_crossing_plane_elevation_deg_fx );
     140             : 
     141             : static Word16 determine_non_crossing_planes_fx( const Word16 num_speaker_nodes, const VBAP_SPEAKER_NODE *node_data, Word32 *non_crossing_plane_elevation_deg_fx );
     142             : 
     143             : static enum VirtualSpeakerNodeType check_need_of_virtual_speaker_node_fx( VBAP_HANDLE hVBAPdata, const Word32 *speaker_node_azi_deg, const Word32 *speaker_node_ele_deg, enum SpeakerNodeGroup group );
     144             : 
     145             : static Word16 determine_best_triplet_and_gains_fx( VBAP_SEARCH_STRUCT *search_struct, const Word16 panning_unit_vec_fx[3], const Word16 azi_deg, Word32 gains_fx[3] );
     146             : 
     147             : static void determine_virtual_speaker_node_division_gains_fx( const Word16 virtual_speaker_node_index, Word16 *virtual_node_division_gains_fx, Word16 connections[][2], const enum VirtualSpeakerNodeType type, const Word16 max_num_connections, const Word16 num_speaker_nodes, const Word16 use_object_mode );
     148             : 
     149             : static void reorder_triplets_fx( VBAP_VS_TRIPLET *triplets, const Word16 *target_order, const Word16 num_triplets );
     150             : 
     151             : /*-------------------------------------------------------------------------*
     152             :  * vbap_init_data()
     153             :  *
     154             :  * Initialize VBAP data structure for the speaker node set
     155             :  *-------------------------------------------------------------------------*/
     156             : 
     157         896 : ivas_error vbap_init_data_fx(
     158             :     VBAP_HANDLE *hVBAPdata,                /* i/o: handle for VBAP data structure that will be initialized */
     159             :     const Word32 *speaker_node_azi_deg_fx, /* i  : vector of speaker node azimuths (positive left)     Q22 */
     160             :     const Word32 *speaker_node_ele_deg_fx, /* i  : vector of speaker node elevations (positive up)     Q22 */
     161             :     const Word16 num_speaker_nodes,        /* i  : number of speaker nodes in the set                      */
     162             :     const IVAS_FORMAT ivas_format          /* i  : IVAS format                                             */
     163             : )
     164             : {
     165             :     /* Variables */
     166             :     Word16 connections[VBAP_MAX_NUM_SPEAKER_NODES][2];
     167             :     Word16 max_num_connections;
     168             :     Word16 is_success;
     169             :     Word16 connection_group1_count;
     170             :     Word16 connection_group2_start;
     171             :     Word16 connection_group2_count;
     172             :     enum VirtualSpeakerNodeType virtual_top_type;
     173             :     enum VirtualSpeakerNodeType virtual_bottom_type;
     174             :     enum VirtualSpeakerNodeType virtual_back_type;
     175             : 
     176             :     Word32 speaker_node_azi_deg_internal_fx[VBAP_MAX_NUM_SPEAKER_NODES]; /* Q22 */
     177             :     Word32 speaker_node_ele_deg_internal_fx[VBAP_MAX_NUM_SPEAKER_NODES]; /* Q22 */
     178             : 
     179             :     VBAP_SPEAKER_NODE speaker_node_data[VBAP_MAX_NUM_SPEAKER_NODES];
     180             :     VBAP_DATA *vbap;
     181             :     ivas_error error;
     182             : 
     183         896 :     push_wmops( "vbap_init" );
     184             : 
     185         896 :     set32_fx( speaker_node_azi_deg_internal_fx, 0, VBAP_MAX_NUM_SPEAKER_NODES );
     186         896 :     set32_fx( speaker_node_ele_deg_internal_fx, 0, VBAP_MAX_NUM_SPEAKER_NODES );
     187             : 
     188             :     /* Basic init checks */
     189             :     /* If the requested layout is invalid, hVBAPdata is set to NULL and the signal will
     190             :      * be distributed with an equal gain into all output channels.
     191             :      * The surrounding code needs to handle the NULL pointer properly. */
     192         896 :     test();
     193         896 :     IF( GT_16( num_speaker_nodes, VBAP_MAX_NUM_SPEAKER_NODES ) || LT_16( num_speaker_nodes, 3 ) )
     194             :     {
     195           0 :         hVBAPdata = NULL;
     196           0 :         pop_wmops();
     197           0 :         return IVAS_ERR_OK;
     198             :     }
     199             : 
     200         896 :     test();
     201         896 :     IF( !speaker_node_azi_deg_fx || !speaker_node_ele_deg_fx )
     202             :     {
     203           0 :         hVBAPdata = NULL;
     204           0 :         return IVAS_ERR_OK;
     205             :     }
     206             : 
     207             :     /* Allocate VBAP structure */
     208         896 :     IF( ( vbap = (VBAP_HANDLE) malloc( sizeof( VBAP_DATA ) ) ) == NULL )
     209             :     {
     210           0 :         return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for VBAP data\n" ) );
     211             :     }
     212             : 
     213         896 :     is_success = 1;
     214         896 :     move16();
     215         896 :     vbap->bottom_virtual_speaker_node_index = -1;
     216         896 :     move16();
     217         896 :     vbap->top_virtual_speaker_node_index = -1;
     218         896 :     move16();
     219         896 :     vbap->back_virtual_speaker_node_index = -1;
     220         896 :     move16();
     221         896 :     vbap->num_speaker_nodes = num_speaker_nodes;
     222         896 :     move16();
     223         896 :     vbap->num_speaker_nodes_internal = num_speaker_nodes;
     224         896 :     move16();
     225         896 :     vbap->bottom_virtual_speaker_node_division_gains_fx = NULL;
     226         896 :     vbap->top_virtual_speaker_node_division_gains_fx = NULL;
     227         896 :     vbap->back_virtual_speaker_node_division_gains_fx = NULL;
     228         896 :     vbap->object_mode_bottom_virtual_speaker_node_division_gains_fx = NULL;
     229         896 :     vbap->object_mode_top_virtual_speaker_node_division_gains_fx = NULL;
     230         896 :     vbap->object_mode_back_virtual_speaker_node_division_gains_fx = NULL;
     231             : 
     232             :     /* Check if the speaker node setup needs a virtual top or bottom node
     233             :          (function also increments vbap->num_speaker_nodes_internal when necessary) */
     234         896 :     virtual_bottom_type = check_need_of_virtual_speaker_node_fx( vbap, speaker_node_azi_deg_fx, speaker_node_ele_deg_fx, SPEAKER_NODE_BOTTOM_HALF );
     235         896 :     virtual_top_type = check_need_of_virtual_speaker_node_fx( vbap, speaker_node_azi_deg_fx, speaker_node_ele_deg_fx, SPEAKER_NODE_TOP_HALF );
     236         896 :     virtual_back_type = check_need_of_virtual_speaker_node_fx( vbap, speaker_node_azi_deg_fx, speaker_node_ele_deg_fx, SPEAKER_NODE_BACK );
     237             : 
     238             :     /* Init internal speaker node configuration, which is the original configuration
     239             :          potentially appended with virtual top and/or bottom loudspeakers */
     240         896 :     Copy32( speaker_node_azi_deg_fx, speaker_node_azi_deg_internal_fx, num_speaker_nodes );
     241         896 :     Copy32( speaker_node_ele_deg_fx, speaker_node_ele_deg_internal_fx, num_speaker_nodes );
     242         896 :     test();
     243         896 :     IF( is_success && NE_16( virtual_bottom_type, NO_VIRTUAL_SPEAKER_NODE ) )
     244             :     {
     245         896 :         IF( ( vbap->bottom_virtual_speaker_node_division_gains_fx = (Word16 *) malloc( num_speaker_nodes * sizeof( Word16 ) ) ) == NULL )
     246             :         {
     247           0 :             return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for VBAP data\n" ) );
     248             :         }
     249         896 :         set16_fx( vbap->bottom_virtual_speaker_node_division_gains_fx, 0, num_speaker_nodes );
     250         896 :         is_success &= vbap->bottom_virtual_speaker_node_division_gains_fx != NULL;
     251             : 
     252         896 :         IF( EQ_16( ivas_format, MASA_ISM_FORMAT ) )
     253             :         {
     254         339 :             IF( ( vbap->object_mode_bottom_virtual_speaker_node_division_gains_fx = (Word16 *) malloc( num_speaker_nodes * sizeof( Word16 ) ) ) == NULL )
     255             :             {
     256           0 :                 return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for VBAP data\n" ) );
     257             :             }
     258         339 :             set16_fx( vbap->object_mode_bottom_virtual_speaker_node_division_gains_fx, 0, num_speaker_nodes );
     259         339 :             is_success &= vbap->object_mode_bottom_virtual_speaker_node_division_gains_fx != NULL;
     260             :         }
     261         896 :         speaker_node_azi_deg_internal_fx[vbap->bottom_virtual_speaker_node_index] = 0;
     262         896 :         speaker_node_ele_deg_internal_fx[vbap->bottom_virtual_speaker_node_index] = -377487360; /* -90.0f in Q22 */
     263         896 :         move32();
     264         896 :         move32();
     265             :     }
     266         896 :     test();
     267         896 :     IF( is_success && NE_16( virtual_top_type, NO_VIRTUAL_SPEAKER_NODE ) )
     268             :     {
     269         896 :         IF( ( vbap->top_virtual_speaker_node_division_gains_fx = (Word16 *) malloc( num_speaker_nodes * sizeof( Word16 ) ) ) == NULL )
     270             :         {
     271           0 :             return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for VBAP data\n" ) );
     272             :         }
     273         896 :         set16_fx( vbap->top_virtual_speaker_node_division_gains_fx, 0, num_speaker_nodes );
     274         896 :         is_success &= vbap->top_virtual_speaker_node_division_gains_fx != NULL;
     275             : 
     276         896 :         IF( EQ_16( ivas_format, MASA_ISM_FORMAT ) )
     277             :         {
     278         339 :             IF( ( vbap->object_mode_top_virtual_speaker_node_division_gains_fx = (Word16 *) malloc( num_speaker_nodes * sizeof( Word16 ) ) ) == NULL )
     279             :             {
     280           0 :                 return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for VBAP data\n" ) );
     281             :             }
     282         339 :             set16_fx( vbap->object_mode_top_virtual_speaker_node_division_gains_fx, 0, num_speaker_nodes );
     283         339 :             is_success &= vbap->object_mode_top_virtual_speaker_node_division_gains_fx != NULL;
     284             :         }
     285         896 :         speaker_node_azi_deg_internal_fx[vbap->top_virtual_speaker_node_index] = 0;
     286         896 :         speaker_node_ele_deg_internal_fx[vbap->top_virtual_speaker_node_index] = 377487360; /* 90.0f in Q22 */
     287         896 :         move32();
     288         896 :         move16();
     289             :     }
     290         896 :     test();
     291         896 :     IF( is_success && NE_16( virtual_back_type, NO_VIRTUAL_SPEAKER_NODE ) )
     292             :     {
     293           0 :         IF( ( vbap->back_virtual_speaker_node_division_gains_fx = (Word16 *) malloc( num_speaker_nodes * sizeof( Word16 ) ) ) == NULL )
     294             :         {
     295           0 :             return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for VBAP data\n" ) );
     296             :         }
     297           0 :         set16_fx( vbap->back_virtual_speaker_node_division_gains_fx, 0, num_speaker_nodes );
     298           0 :         is_success &= vbap->back_virtual_speaker_node_division_gains_fx != NULL;
     299             : 
     300           0 :         IF( EQ_16( ivas_format, MASA_ISM_FORMAT ) )
     301             :         {
     302           0 :             IF( ( vbap->object_mode_back_virtual_speaker_node_division_gains_fx = (Word16 *) malloc( num_speaker_nodes * sizeof( Word16 ) ) ) == NULL )
     303             :             {
     304           0 :                 return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for VBAP data\n" ) );
     305             :             }
     306           0 :             set16_fx( vbap->object_mode_back_virtual_speaker_node_division_gains_fx, 0, num_speaker_nodes );
     307           0 :             is_success &= vbap->object_mode_back_virtual_speaker_node_division_gains_fx != NULL;
     308             :         }
     309           0 :         speaker_node_azi_deg_internal_fx[vbap->back_virtual_speaker_node_index] = 754974720; /* 180.0f in Q22 */
     310           0 :         speaker_node_ele_deg_internal_fx[vbap->back_virtual_speaker_node_index] = 0;
     311             : 
     312           0 :         move32();
     313           0 :         move16();
     314             :     }
     315             : 
     316         896 :     init_speaker_node_direction_data_fx( speaker_node_data, speaker_node_azi_deg_internal_fx, speaker_node_ele_deg_internal_fx, vbap->num_speaker_nodes_internal );
     317             : 
     318             :     /* Allocate and determine node-node connections */
     319         896 :     max_num_connections = mult0( ( sub( vbap->num_speaker_nodes_internal, 2 ) ), 3 ); /* Theoretical maximum */
     320         896 :     IF( NE_32( ( error = determine_connections_fx( vbap->num_speaker_nodes_internal, speaker_node_data, connections, max_num_connections, &connection_group1_count, &connection_group2_start, &connection_group2_count ) ), IVAS_ERR_OK ) )
     321             :     {
     322           0 :         return error;
     323             :     }
     324             :     /* Allocate and determine virtual surface speaker node triplets */
     325         896 :     IF( is_success )
     326             :     {
     327             :         Word16 ch;
     328         896 :         Word16 speaker_nodes_group1_internal = 0;
     329         896 :         move16();
     330         896 :         Word16 speaker_nodes_group2_internal = 0;
     331         896 :         move16();
     332         896 :         Word16 speaker_nodes_horiz_internal = 0;
     333         896 :         move16();
     334         896 :         UWord8 loop_done = 0;
     335         896 :         move16();
     336             : 
     337             :         /* Count nodes in different groups to reserve correct memory */
     338        9986 :         FOR( ch = 0; ch < vbap->num_speaker_nodes_internal && !loop_done; ch++ )
     339             :         {
     340        9090 :             test();
     341        9090 :             SWITCH( speaker_node_data[ch].group )
     342             :             {
     343           0 :                 case SPEAKER_NODE_ALL:
     344             :                     /* If there is even one speaker belonging to "all" group, then all speakers belong to the "all" group.
     345             :                      * We can skip further counts here. */
     346           0 :                     speaker_nodes_group1_internal = vbap->num_speaker_nodes_internal;
     347           0 :                     move16();
     348           0 :                     loop_done = 1;
     349           0 :                     move16();
     350           0 :                     BREAK;
     351         900 :                 case SPEAKER_NODE_BOTTOM_HALF:
     352         900 :                     speaker_nodes_group1_internal = add( speaker_nodes_group1_internal, 1 );
     353         900 :                     BREAK;
     354        3000 :                 case SPEAKER_NODE_TOP_HALF:
     355        3000 :                     speaker_nodes_group2_internal = add( speaker_nodes_group2_internal, 1 );
     356        3000 :                     BREAK;
     357        5190 :                 case SPEAKER_NODE_HORIZONTAL:
     358             :                 case SPEAKER_NODE_BACK:
     359        5190 :                     speaker_nodes_group1_internal = add( speaker_nodes_group1_internal, 1 );
     360        5190 :                     speaker_nodes_group2_internal = add( speaker_nodes_group2_internal, 1 );
     361        5190 :                     speaker_nodes_horiz_internal = add( speaker_nodes_horiz_internal, 1 );
     362        5190 :                     BREAK;
     363             :             }
     364        9090 :         }
     365             : 
     366         896 :         IF( ( vbap->search_struct[0].triplets = (VBAP_VS_TRIPLET *) malloc( ( ( speaker_nodes_group1_internal - 2 ) * 2 - ( s_max( 0, ( speaker_nodes_horiz_internal - 2 ) ) ) ) * sizeof( VBAP_VS_TRIPLET ) ) ) == NULL )
     367             :         {
     368           0 :             return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for VBAP data\n" ) );
     369             :         }
     370         896 :         is_success = s_and( is_success, vbap->search_struct[0].triplets != NULL );
     371             : 
     372         896 :         IF( speaker_nodes_group2_internal > 0 )
     373             :         {
     374         896 :             vbap->num_search_structs = 2;
     375         896 :             move16();
     376         896 :             IF( ( vbap->search_struct[1].triplets = (VBAP_VS_TRIPLET *) malloc( ( ( speaker_nodes_group2_internal - 2 ) * 2 - ( s_max( 0, ( speaker_nodes_horiz_internal - 2 ) ) ) ) * sizeof( VBAP_VS_TRIPLET ) ) ) == NULL )
     377             :             {
     378           0 :                 return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for VBAP data\n" ) );
     379             :             }
     380         896 :             is_success = s_and( is_success, vbap->search_struct[1].triplets != NULL );
     381             :         }
     382             :         ELSE
     383             :         {
     384           0 :             vbap->num_search_structs = 1;
     385           0 :             move16();
     386           0 :             vbap->search_struct[1].triplets = NULL;
     387             :         }
     388             :     }
     389             : 
     390         896 :     IF( is_success )
     391             :     {
     392         896 :         IF( EQ_16( vbap->num_search_structs, 1 ) )
     393             :         {
     394             :             /* If all speaker nodes belong to ALL set, then we only create one triplet set and search structure */
     395           0 :             vbap->search_struct[0].num_triplets = determine_virtual_surface_triplets_fx( vbap->num_speaker_nodes_internal, speaker_node_data, connections, max_num_connections, vbap->search_struct[0].triplets, vbap->search_struct[0].initial_search_indices, SPEAKER_NODE_ALL );
     396           0 :             move16();
     397             :         }
     398             :         ELSE
     399             :         {
     400             :             /* Otherwise, we have two sets and can handle them separately for more opmitized processing. */
     401         896 :             vbap->search_struct[0].num_triplets = determine_virtual_surface_triplets_fx( vbap->num_speaker_nodes_internal, speaker_node_data, connections, connection_group1_count, vbap->search_struct[0].triplets, vbap->search_struct[0].initial_search_indices, SPEAKER_NODE_BOTTOM_HALF );
     402         896 :             move16();
     403         896 :             vbap->search_struct[1].num_triplets = determine_virtual_surface_triplets_fx( vbap->num_speaker_nodes_internal, speaker_node_data, connections + connection_group2_start, connection_group2_count, vbap->search_struct[1].triplets, vbap->search_struct[1].initial_search_indices, SPEAKER_NODE_TOP_HALF );
     404         896 :             move16();
     405             :         }
     406             :     }
     407             : 
     408             :     /* Determine how the virtual node gains should be distributed to real nodes, if necessary (checked within function). */
     409         896 :     IF( is_success )
     410             :     {
     411         896 :         determine_virtual_speaker_node_division_gains_fx( vbap->top_virtual_speaker_node_index, vbap->top_virtual_speaker_node_division_gains_fx, connections, virtual_top_type, max_num_connections, num_speaker_nodes, 0 );
     412         896 :         determine_virtual_speaker_node_division_gains_fx( vbap->bottom_virtual_speaker_node_index, vbap->bottom_virtual_speaker_node_division_gains_fx, connections, virtual_bottom_type, max_num_connections, num_speaker_nodes, 0 );
     413         896 :         determine_virtual_speaker_node_division_gains_fx( vbap->back_virtual_speaker_node_index, vbap->back_virtual_speaker_node_division_gains_fx, connections, virtual_back_type, max_num_connections, num_speaker_nodes, 0 );
     414         896 :         IF( EQ_32( ivas_format, MASA_ISM_FORMAT ) )
     415             :         {
     416         339 :             determine_virtual_speaker_node_division_gains_fx( vbap->top_virtual_speaker_node_index, vbap->object_mode_top_virtual_speaker_node_division_gains_fx, connections, virtual_top_type == NO_VIRTUAL_SPEAKER_NODE ? NO_VIRTUAL_SPEAKER_NODE : VIRTUAL_SPEAKER_NODE_DISTRIBUTE_ENERGY, max_num_connections, num_speaker_nodes, 1 );
     417         339 :             determine_virtual_speaker_node_division_gains_fx( vbap->bottom_virtual_speaker_node_index, vbap->object_mode_bottom_virtual_speaker_node_division_gains_fx, connections, virtual_bottom_type == NO_VIRTUAL_SPEAKER_NODE ? NO_VIRTUAL_SPEAKER_NODE : VIRTUAL_SPEAKER_NODE_DISTRIBUTE_ENERGY, max_num_connections, num_speaker_nodes, 1 );
     418         339 :             determine_virtual_speaker_node_division_gains_fx( vbap->back_virtual_speaker_node_index, vbap->object_mode_back_virtual_speaker_node_division_gains_fx, connections, virtual_back_type == NO_VIRTUAL_SPEAKER_NODE ? NO_VIRTUAL_SPEAKER_NODE : VIRTUAL_SPEAKER_NODE_DISTRIBUTE_ENERGY, max_num_connections, num_speaker_nodes, 1 );
     419             :         }
     420             :     }
     421             : 
     422         896 :     pop_wmops();
     423             : 
     424         896 :     IF( is_success )
     425             :     {
     426         896 :         *hVBAPdata = vbap;
     427             :     }
     428             :     ELSE
     429             :     {
     430           0 :         vbap_free_data_fx( &vbap );
     431             :     }
     432             : 
     433         896 :     return IVAS_ERR_OK;
     434             : }
     435             : 
     436             : 
     437             : /*-------------------------------------------------------------------------*
     438             :  * vbap_free_data()
     439             :  *
     440             :  * Free VBAP data structure
     441             :  *-------------------------------------------------------------------------*/
     442             : 
     443        1836 : void vbap_free_data_fx(
     444             :     VBAP_HANDLE *hVBAPdata /* i/o: VBAP handle to be freed    */
     445             : )
     446             : {
     447        1836 :     test();
     448        1836 :     IF( hVBAPdata == NULL || *hVBAPdata == NULL )
     449             :     {
     450         940 :         return;
     451             :     }
     452             : 
     453         896 :     IF( ( *hVBAPdata )->bottom_virtual_speaker_node_division_gains_fx != NULL )
     454             :     {
     455         896 :         free( ( *hVBAPdata )->bottom_virtual_speaker_node_division_gains_fx );
     456             :     }
     457         896 :     IF( ( *hVBAPdata )->top_virtual_speaker_node_division_gains_fx != NULL )
     458             :     {
     459         896 :         free( ( *hVBAPdata )->top_virtual_speaker_node_division_gains_fx );
     460             :     }
     461         896 :     IF( ( *hVBAPdata )->back_virtual_speaker_node_division_gains_fx != NULL )
     462             :     {
     463           0 :         free( ( *hVBAPdata )->back_virtual_speaker_node_division_gains_fx );
     464             :     }
     465         896 :     IF( ( *hVBAPdata )->object_mode_bottom_virtual_speaker_node_division_gains_fx != NULL )
     466             :     {
     467         339 :         free( ( *hVBAPdata )->object_mode_bottom_virtual_speaker_node_division_gains_fx );
     468             :     }
     469         896 :     IF( ( *hVBAPdata )->object_mode_top_virtual_speaker_node_division_gains_fx != NULL )
     470             :     {
     471         339 :         free( ( *hVBAPdata )->object_mode_top_virtual_speaker_node_division_gains_fx );
     472             :     }
     473         896 :     IF( ( *hVBAPdata )->object_mode_back_virtual_speaker_node_division_gains_fx != NULL )
     474             :     {
     475           0 :         free( ( *hVBAPdata )->object_mode_back_virtual_speaker_node_division_gains_fx );
     476             :     }
     477         896 :     IF( ( *hVBAPdata )->search_struct[0].triplets != NULL )
     478             :     {
     479         896 :         free( ( *hVBAPdata )->search_struct[0].triplets );
     480             :     }
     481             : 
     482         896 :     test();
     483         896 :     IF( EQ_16( ( *hVBAPdata )->num_search_structs, 2 ) && ( *hVBAPdata )->search_struct[1].triplets != NULL )
     484             :     {
     485         896 :         free( ( *hVBAPdata )->search_struct[1].triplets );
     486             :     }
     487         896 :     free( *hVBAPdata );
     488         896 :     *hVBAPdata = NULL;
     489             : 
     490         896 :     return;
     491             : }
     492             : 
     493             : /*-------------------------------------------------------------------------*
     494             :  * vbap_determine_gains()
     495             :  *
     496             :  * Obtain panning gains for all speaker nodes based on the given direction
     497             :  *-------------------------------------------------------------------------*/
     498     5121378 : void vbap_determine_gains_fx(
     499             :     const VBAP_HANDLE hVBAPdata, /* i  : prepared VBAP structure                                         */
     500             :     Word32 *gains_fx,            /* o  : gain vector for loudspeakers for given direction            Q29 */
     501             :     const Word16 azi_deg,        /* i  : azimuth in degrees for panning direction (positive left)     Q0 */
     502             :     const Word16 ele_deg,        /* i  : elevation in degrees for panning direction (positive up)     Q0 */
     503             :     const Word16 use_object_mode /* i  : select between object mode panning and spatial mode panning     */
     504             : )
     505             : {
     506             :     /* This function formulates gains for the given angle. The triplet-selection has been pre-formulated. */
     507             :     Word16 ch, ch2;
     508             :     Word16 triplet_ch;
     509             :     Word16 triplet_index;
     510             :     Word16 panning_unit_vec_fx[3]; /* Q15 */
     511             :     Word32 gain_triplet_fx[3];     /* Q16 */
     512             :     Word32 norm_value_fx;
     513             :     Word32 gain_ene_fx;
     514             :     Word16 azi_norm;    /* Q15 */
     515             :     Word16 ele_norm;    /* Q15 */
     516             :     Word32 azi_temp_fx; /* Q22 */
     517             :     Word32 ele_temp_fx; /* Q22 */
     518             :     Word16 num_speaker_nodes;
     519             :     Word16 bottom_virtual_speaker_node_index;
     520             :     Word16 top_virtual_speaker_node_index;
     521             :     Word16 back_virtual_speaker_node_index;
     522             :     VBAP_VS_TRIPLET *selected_triplet;
     523             :     Word16 *bottom_virtual_speaker_node_division_gains_fx; /* Q16 */
     524             :     Word16 *top_virtual_speaker_node_division_gains_fx;    /* Q16 */
     525             :     Word16 *back_virtual_speaker_node_division_gains_fx;   /* Q16 */
     526             : 
     527             : 
     528     5121378 :     push_wmops( "vbap_gains" );
     529     5121378 :     num_speaker_nodes = hVBAPdata->num_speaker_nodes;
     530     5121378 :     move16();
     531     5121378 :     bottom_virtual_speaker_node_index = hVBAPdata->bottom_virtual_speaker_node_index;
     532     5121378 :     move16();
     533     5121378 :     top_virtual_speaker_node_index = hVBAPdata->top_virtual_speaker_node_index;
     534     5121378 :     move16();
     535     5121378 :     back_virtual_speaker_node_index = hVBAPdata->back_virtual_speaker_node_index;
     536     5121378 :     move16();
     537             : 
     538     5121378 :     IF( use_object_mode )
     539             :     {
     540       32603 :         bottom_virtual_speaker_node_division_gains_fx = hVBAPdata->object_mode_bottom_virtual_speaker_node_division_gains_fx;
     541       32603 :         top_virtual_speaker_node_division_gains_fx = hVBAPdata->object_mode_top_virtual_speaker_node_division_gains_fx;
     542       32603 :         back_virtual_speaker_node_division_gains_fx = hVBAPdata->object_mode_back_virtual_speaker_node_division_gains_fx;
     543             :     }
     544             :     ELSE
     545             :     {
     546     5088775 :         bottom_virtual_speaker_node_division_gains_fx = hVBAPdata->bottom_virtual_speaker_node_division_gains_fx;
     547     5088775 :         top_virtual_speaker_node_division_gains_fx = hVBAPdata->top_virtual_speaker_node_division_gains_fx;
     548     5088775 :         back_virtual_speaker_node_division_gains_fx = hVBAPdata->back_virtual_speaker_node_division_gains_fx;
     549             :     }
     550             : 
     551     5121378 :     panning_wrap_angles_fx( L_shl( azi_deg, Q22 ), L_shl( ele_deg, Q22 ), &azi_temp_fx, &ele_temp_fx );
     552     5121378 :     azi_norm = extract_l( L_shr( Mpy_32_32( azi_temp_fx, ONE_BY_360_Q31 ), Q7 ) ); /* Q15 */
     553     5121378 :     ele_norm = extract_l( L_shr( Mpy_32_32( ele_temp_fx, ONE_BY_360_Q31 ), Q7 ) ); /* Q15 */
     554             : 
     555     5121378 :     panning_unit_vec_fx[0] = mult( getCosWord16R2( azi_norm ), getCosWord16R2( ele_norm ) );  /* Q15 */
     556     5121378 :     panning_unit_vec_fx[1] = mult( getSineWord16R2( azi_norm ), getCosWord16R2( ele_norm ) ); /* Q15 */
     557     5121378 :     panning_unit_vec_fx[2] = getSineWord16R2( ele_norm );                                     /* Q15 */
     558     5121378 :     move16();
     559     5121378 :     move16();
     560     5121378 :     move16();
     561             : 
     562             : 
     563             :     /* Find the best VS triplet and speaker node gains for the panning direction using the prepared search structures. */
     564     5121378 :     test();
     565     5121378 :     IF( EQ_16( hVBAPdata->num_search_structs, 2 ) && ele_deg > 0 )
     566             :     {
     567     2178766 :         triplet_index = determine_best_triplet_and_gains_fx( &( hVBAPdata->search_struct[1] ), panning_unit_vec_fx, azi_deg, gain_triplet_fx );
     568     2178766 :         selected_triplet = &hVBAPdata->search_struct[1].triplets[triplet_index];
     569             :     }
     570             :     ELSE
     571             :     {
     572     2942612 :         triplet_index = determine_best_triplet_and_gains_fx( &( hVBAPdata->search_struct[0] ), panning_unit_vec_fx, azi_deg, gain_triplet_fx );
     573     2942612 :         selected_triplet = &hVBAPdata->search_struct[0].triplets[triplet_index];
     574             :     }
     575             : 
     576             :     /* Normalize to unit energy */
     577     5121378 :     gain_ene_fx = 1; /* Add small value to avoid divide by zero. */
     578     5121378 :     move32();
     579    20485512 :     FOR( ch = 0; ch < 3; ch++ )
     580             :     {
     581    15364134 :         gain_ene_fx = Madd_32_32( gain_ene_fx, gain_triplet_fx[ch], gain_triplet_fx[ch] ); /* Q(2 * VBAP_VS_TRIPLET.q_inverse_matrix - 31) */
     582             :     }
     583             : 
     584     5121378 :     norm_value_fx = Isqrt( L_shr( gain_ene_fx, 1 ) ); /* Q(31 - (2 * VBAP_VS_TRIPLET.q_inverse_matrix - 31 - 1) / 2 ) = Q(47 - VBAP_VS_TRIPLET.q_inverse_matrix) */
     585             : 
     586    20485512 :     FOR( ch = 0; ch < 3; ch++ )
     587             :     {
     588    15364134 :         gain_triplet_fx[ch] = Mpy_32_32( gain_triplet_fx[ch], norm_value_fx ); /* 47 - VBAP_VS_TRIPLET.q_inverse_matrix + VBAP_VS_TRIPLET.q_inverse_matrix - 31 = Q16 */
     589    15364134 :         move32();
     590             : 
     591             :         /* Sanity check for rounding issues */
     592    15364134 :         if ( gain_triplet_fx[ch] < 0 )
     593             :         {
     594         383 :             gain_triplet_fx[ch] = 0;
     595         383 :             move32();
     596             :         }
     597             :     }
     598             : 
     599             :     /* Flush gain target */
     600     5121378 :     set32_fx( gains_fx, 0, num_speaker_nodes );
     601             : 
     602             :     /* Map gain triplet (internal speaker node configuration) to speaker node output (actual speaker node configuration) */
     603    20485512 :     FOR( ch = 0; ch < 3; ch++ )
     604             :     {
     605    15364134 :         triplet_ch = selected_triplet->speaker_node[ch];
     606             : 
     607    15364134 :         IF( EQ_16( triplet_ch, bottom_virtual_speaker_node_index ) )
     608             :         {
     609    21850914 :             FOR( ch2 = 0; ch2 < num_speaker_nodes; ch2++ )
     610             :             {
     611    19322941 :                 gains_fx[ch2] = L_add( gains_fx[ch2], L_shl( Mpy_32_16_1( gain_triplet_fx[ch], bottom_virtual_speaker_node_division_gains_fx[ch2] ), Q12 ) ); /* Q16 + Q16 - Q15 + Q12 = Q29 */
     612    19322941 :                 move32();
     613             :             }
     614             :         }
     615    12836161 :         ELSE IF( EQ_16( triplet_ch, top_virtual_speaker_node_index ) )
     616             :         {
     617    11761705 :             FOR( ch2 = 0; ch2 < num_speaker_nodes; ch2++ )
     618             :             {
     619    10319730 :                 gains_fx[ch2] = L_add( gains_fx[ch2], L_shl( Mpy_32_16_1( gain_triplet_fx[ch], top_virtual_speaker_node_division_gains_fx[ch2] ), Q12 ) ); /* Q16 + Q16 - Q15 + Q12 = Q29 */
     620    10319730 :                 move32();
     621             :             }
     622             :         }
     623    11394186 :         ELSE IF( EQ_16( triplet_ch, back_virtual_speaker_node_index ) )
     624             :         {
     625           0 :             FOR( ch2 = 0; ch2 < num_speaker_nodes; ch2++ )
     626             :             {
     627           0 :                 gains_fx[ch2] = L_add( gains_fx[ch2], L_shl( Mpy_32_16_1( gain_triplet_fx[ch], back_virtual_speaker_node_division_gains_fx[ch2] ), Q12 ) ); /* Q16 + Q16 - Q15 + Q12 = Q29 */
     628           0 :                 move32();
     629             :             }
     630             :         }
     631             :         ELSE
     632             :         {
     633    11394186 :             gains_fx[triplet_ch] = L_add( gains_fx[triplet_ch], L_shl( gain_triplet_fx[ch], Q13 ) ); /* Q16 + Q13 = Q29 */
     634    11394186 :             move32();
     635             :         }
     636             :     }
     637             : 
     638     5121378 :     pop_wmops();
     639             : 
     640     5121378 :     return;
     641             : }
     642             : 
     643             : /*-----------------------------------------------------------------------*
     644             :  * Local functions
     645             :  *-----------------------------------------------------------------------*/
     646             : 
     647             : /*-------------------------------------------------------------------------*
     648             :  * vbap_crossp()
     649             :  *
     650             :  * 3-by-3 vector cross product
     651             :  *-------------------------------------------------------------------------*/
     652             : 
     653      120868 : static void vbap_crossp_fx(
     654             :     const Word32 *vec1_fx,  /* i  : input vector 1                 Qx */
     655             :     const Word32 *vec2_fx,  /* i  : input vector 2                 Qy */
     656             :     Word32 *crossProduct_fx /* o  : cross product output Qx + Qy - 31 */
     657             : )
     658             : {
     659             : 
     660      120868 :     crossProduct_fx[0] = L_sub( Mpy_32_32( vec1_fx[1], vec2_fx[2] ), Mpy_32_32( vec1_fx[2], vec2_fx[1] ) );
     661      120868 :     move32();
     662      120868 :     crossProduct_fx[1] = L_sub( Mpy_32_32( vec1_fx[2], vec2_fx[0] ), Mpy_32_32( vec1_fx[0], vec2_fx[2] ) );
     663      120868 :     move32();
     664      120868 :     crossProduct_fx[2] = L_sub( Mpy_32_32( vec1_fx[0], vec2_fx[1] ), Mpy_32_32( vec1_fx[1], vec2_fx[0] ) );
     665      120868 :     move32();
     666             : 
     667      120868 :     return;
     668             : }
     669             : 
     670             : /*-------------------------------------------------------------------------*
     671             :  * vector_matrix_multiply_3x3()
     672             :  *
     673             :  * 3-by-3 vector multiply with matrix
     674             :  *-------------------------------------------------------------------------*/
     675             : 
     676             : 
     677             : /*! r: Status result if triplet is usable for panning. Allows early exit. */
     678    17453730 : static UWord8 vector_matrix_multiply_3x3_fx(
     679             :     const Word16 *src_vector, /* i  : input vector          Q15 */
     680             :     Word32 matrix[3][3],      /* i  : input matrix  Q(q_matrix) */
     681             :     Word32 *result,           /* o  : output vector Q(q_matrix) */
     682             :     Word16 q_matrix )
     683             : {
     684    17453730 :     Word32 pointzero_one = Mpy_32_16_1( L_lshl( 1, q_matrix ), -327 /* -0.01 in Q15 */ );
     685    17453730 :     result[0] = Madd_32_16( Madd_32_16( Mpy_32_16_1( matrix[0][0], src_vector[0] ), matrix[1][0], src_vector[1] ), matrix[2][0], src_vector[2] ); /* Q(q_matrix) */
     686    17453730 :     move32();
     687             : 
     688    17453730 :     IF( LT_32( result[0], pointzero_one ) )
     689             :     {
     690     1794783 :         return 0;
     691             :     }
     692             : 
     693    15658947 :     result[1] = Madd_32_16( Madd_32_16( Mpy_32_16_1( matrix[0][1], src_vector[0] ), matrix[1][1], src_vector[1] ), matrix[2][1], src_vector[2] ); /* Q(q_matrix) */
     694    15658947 :     move32();
     695             : 
     696    15658947 :     IF( LT_32( result[1], pointzero_one ) )
     697             :     {
     698     6285442 :         return 0;
     699             :     }
     700             : 
     701     9373505 :     result[2] = Madd_32_16( Madd_32_16( Mpy_32_16_1( matrix[0][2], src_vector[0] ), matrix[1][2], src_vector[1] ), matrix[2][2], src_vector[2] ); /* Q(q_matrix) */
     702     9373505 :     move32();
     703             : 
     704     9373505 :     IF( LT_32( result[2], pointzero_one ) )
     705             :     {
     706     4194790 :         return 0;
     707             :     }
     708     5178715 :     return 1;
     709             : }
     710             : 
     711      110824 : static UWord8 vector_matrix_multiply_3x3_32_fx(
     712             :     const Word32 *src_vector, /* i  : input vector              Q30 */
     713             :     Word32 matrix[3][3],      /* i  : input matrix      Q(q_matrix) */
     714             :     Word32 *result,           /* o  : output vector Q(q_matrix - 1) */
     715             :     Word16 q_matrix )
     716             : {
     717      110824 :     result[0] = Mpy_32_32( matrix[0][0], src_vector[0] );                     /* Q(q_matrix - 1) */
     718      110824 :     result[0] = L_add( result[0], Mpy_32_32( matrix[1][0], src_vector[1] ) ); /* Q(q_matrix - 1) */
     719      110824 :     result[0] = L_add( result[0], Mpy_32_32( matrix[2][0], src_vector[2] ) ); /* Q(q_matrix - 1) */
     720      110824 :     move32();
     721      110824 :     move32();
     722      110824 :     move32();
     723      110824 :     IF( LT_32( result[0], Mpy_32_32( L_shl( 1, ( sub( q_matrix, 1 ) ) ), -21474836 /* -0.01 in Q31 */ ) ) )
     724             :     {
     725       35458 :         return 0;
     726             :     }
     727             : 
     728       75366 :     result[1] = Mpy_32_32( matrix[0][1], src_vector[0] );                     /* Q(q_matrix - 1) */
     729       75366 :     result[1] = L_add( result[1], Mpy_32_32( matrix[1][1], src_vector[1] ) ); /* Q(q_matrix - 1) */
     730       75366 :     result[1] = L_add( result[1], Mpy_32_32( matrix[2][1], src_vector[2] ) ); /* Q(q_matrix - 1) */
     731             : 
     732       75366 :     move32();
     733       75366 :     move32();
     734       75366 :     move32();
     735       75366 :     IF( LT_32( result[1], Mpy_32_32( L_shl( 1, ( sub( q_matrix, 1 ) ) ), -21474836 /* -0.01 in Q31 */ ) ) )
     736             :     {
     737       49744 :         return 0;
     738             :     }
     739             : 
     740       25622 :     result[2] = Mpy_32_32( matrix[0][2], src_vector[0] );                     /* Q(q_matrix - 1) */
     741       25622 :     result[2] = L_add( result[2], Mpy_32_32( matrix[1][2], src_vector[1] ) ); /* Q(q_matrix - 1) */
     742       25622 :     result[2] = L_add( result[2], Mpy_32_32( matrix[2][2], src_vector[2] ) ); /* Q(q_matrix - 1) */
     743             : 
     744       25622 :     move32();
     745       25622 :     move32();
     746       25622 :     move32();
     747       25622 :     IF( LT_32( result[2], Mpy_32_32( L_shl( 1, ( sub( q_matrix, 1 ) ) ), -21474836 /* -0.01 in Q31 */ ) ) )
     748             :     {
     749       25622 :         return 0;
     750             :     }
     751             : 
     752           0 :     return 1;
     753             : }
     754             : 
     755             : /*----------------------------------------------------------------------------------------------*
     756             :  * determine_best_triplet_and_gains()
     757             :  *
     758             :  * Determine the best speaker node triplet and associated gains for panning to defined direction
     759             :  *----------------------------------------------------------------------------------------------*/
     760             : 
     761             : /*! r: triplet id */
     762     5121378 : static Word16 determine_best_triplet_and_gains_fx(
     763             :     VBAP_SEARCH_STRUCT *search_struct,   /* i  : VBAP search struct                                      */
     764             :     const Word16 panning_unit_vec_fx[3], /* i  : panning unit vector                                 Q15 */
     765             :     const Word16 azi_deg,                /* i  : panning azimuth                                         */
     766             :     Word32 gains_fx[3]                   /* o  : panning gains       Q(VBAP_VS_TRIPLET.q_inverse_matrix) */
     767             : )
     768             : {
     769             :     Word16 i, tr, k;
     770             :     UWord8 triplet_ok;
     771             :     Word16 best_triplet;
     772             :     Word32 best_min_gain_fx;
     773             :     Word32 min_gain_this_fx;
     774             :     Word32 unnormalized_gains_fx[3];
     775             :     Word16 sector;
     776             :     Word16 first_triplet;
     777             :     Word16 jump;
     778             :     Word16 num_triplets;
     779             : 
     780     5121378 :     num_triplets = search_struct->num_triplets;
     781     5121378 :     move16();
     782     5121378 :     best_min_gain_fx = -2147483647;
     783     5121378 :     move32();
     784     5121378 :     best_triplet = 0;
     785     5121378 :     move16();
     786     5121378 :     set32_fx( gains_fx, 0, 3 );
     787             : 
     788             :     /* Determine the correct search sector for that target panning direction using an optimized algorithm for
     789             :      * the chosen four sectors. */
     790     5121378 :     IF( GT_16( abs_s( azi_deg ), 90 ) )
     791             :     {
     792     2351162 :         sector = 1;
     793     2351162 :         move16();
     794     2351162 :         if ( azi_deg < 0 )
     795             :         {
     796      369437 :             sector = 2;
     797      369437 :             move16();
     798             :         }
     799             :     }
     800             :     ELSE
     801             :     {
     802     2770216 :         sector = 0;
     803     2770216 :         move16();
     804     2770216 :         if ( azi_deg < 0 )
     805             :         {
     806     1278249 :             sector = 3;
     807     1278249 :             move16();
     808             :         }
     809             :     }
     810     5121378 :     first_triplet = search_struct->initial_search_indices[sector];
     811     5121378 :     move16();
     812             : 
     813     5121378 :     tr = first_triplet;
     814     5121378 :     move16();
     815     5121378 :     jump = 1;
     816     5121378 :     move16();
     817    17454113 :     FOR( i = 0; i < num_triplets; i++ )
     818             :     {
     819    17453730 :         triplet_ok = vector_matrix_multiply_3x3_fx( panning_unit_vec_fx, search_struct->triplets[tr].inverse_matrix_fx, unnormalized_gains_fx, search_struct->triplets[tr].q_inverse_matrix );
     820    17453730 :         IF( triplet_ok )
     821             :         {
     822     5178715 :             min_gain_this_fx = L_min( ( L_min( unnormalized_gains_fx[0], unnormalized_gains_fx[1] ) ), unnormalized_gains_fx[2] );
     823             : 
     824     5178715 :             IF( GT_32( min_gain_this_fx, best_min_gain_fx ) )
     825             :             {
     826     5177022 :                 best_min_gain_fx = min_gain_this_fx;
     827     5177022 :                 move32();
     828     5177022 :                 best_triplet = tr;
     829     5177022 :                 move16();
     830    20708088 :                 FOR( k = 0; k < 3; k++ )
     831             :                 {
     832    15531066 :                     gains_fx[k] = unnormalized_gains_fx[k]; /* Q(VBAP_VS_TRIPLET.q_inverse_matrix) */
     833    15531066 :                     move32();
     834             :                 }
     835     5177022 :                 IF( best_min_gain_fx >= 0 )
     836             :                 {
     837     5120995 :                     return best_triplet;
     838             :                 }
     839             :             }
     840             :         }
     841    12332735 :         tr = add( first_triplet, jump );
     842    12332735 :         IF( tr < 0 )
     843             :         {
     844       89614 :             tr = add( tr, num_triplets );
     845             :         }
     846    12243121 :         ELSE IF( GE_16( tr, num_triplets ) )
     847             :         {
     848     2851182 :             tr = sub( tr, num_triplets );
     849             :         }
     850             : 
     851    12332735 :         jump = negate( jump );
     852    12332735 :         IF( jump > 0 )
     853             :         {
     854     5180226 :             jump = add( jump, 1 );
     855             :         }
     856             :     }
     857             : 
     858         383 :     return best_triplet;
     859             : }
     860             : 
     861             : /*-------------------------------------------------------------------------*
     862             :  * determine_virtual_speaker_node_division_gains()
     863             :  *
     864             :  * Determines how the virtual node gains are distributed to real nodes
     865             :  *-------------------------------------------------------------------------*/
     866             : 
     867        3705 : static void determine_virtual_speaker_node_division_gains_fx(
     868             :     const Word16 virtual_speaker_node_index, /* i  : virtual speaker node index                               */
     869             :     Word16 *virtual_node_division_gains_fx,  /* o  : virtual speaker node division gains                  Q16 */
     870             :     Word16 connections[][2],                 /* i  : vector of all connections                                */
     871             :     const enum VirtualSpeakerNodeType type,  /* i  : virtual speaker node typel                               */
     872             :     const Word16 max_num_connections,        /* i  : max number of connections                                */
     873             :     const Word16 num_speaker_nodes,          /* i  : max number of speaker nodes                              */
     874             :     const Word16 use_object_mode             /* i  : use VBAP in object panning mode vs. spatial panning mode */
     875             : )
     876             : {
     877             :     /* When node type is VIRTUAL_SPEAKER_NODE_DISTRIBUTE_ENERGY, the gains of the virtual node
     878             :      are distributed to all neighboring real speaker nodes. An amplitude-division
     879             :      instead of energy division is utilized just in case to avoid excessive emphasis
     880             :      on the coherent distributed sound. */
     881             :     Word16 c, ch, i;
     882             :     Word16 sum_val_fx;
     883             :     Word16 *exp_virtual_node_division_gains;
     884        3705 :     exp_virtual_node_division_gains = (Word16 *) malloc( num_speaker_nodes * sizeof( Word16 ) );
     885             : 
     886        3705 :     set16_fx( exp_virtual_node_division_gains, 0, num_speaker_nodes );
     887             : 
     888        3705 :     IF( EQ_16( type, VIRTUAL_SPEAKER_NODE_DISTRIBUTE_ENERGY ) )
     889             :     {
     890       34150 :         FOR( c = 0; c < max_num_connections; c++ )
     891             :         {
     892       32844 :             IF( NE_16( connections[c][0], VBAP_NOT_VALID_CONNECTION ) )
     893             :             {
     894       32844 :                 Word16 connection_node = -1;
     895       32844 :                 move16();
     896       32844 :                 IF( EQ_16( connections[c][0], virtual_speaker_node_index ) )
     897             :                 {
     898           0 :                     connection_node = connections[c][1];
     899           0 :                     move16();
     900             :                 }
     901       32844 :                 ELSE IF( EQ_16( connections[c][1], virtual_speaker_node_index ) )
     902             :                 {
     903        5895 :                     connection_node = connections[c][0];
     904        5895 :                     move16();
     905             :                 }
     906             : 
     907             :                 /* The second condition allows division gains only to actual loudspeakers */
     908       32844 :                 test();
     909       32844 :                 IF( connection_node >= 0 && ( LT_16( connection_node, num_speaker_nodes ) ) )
     910             :                 {
     911        5895 :                     virtual_node_division_gains_fx[connection_node] = ONE_IN_Q14;
     912        5895 :                     move16();
     913        5895 :                     exp_virtual_node_division_gains[connection_node] = 1;
     914        5895 :                     move16();
     915             :                 }
     916             :             }
     917             :         }
     918             : 
     919        1306 :         sum_val_fx = 0;
     920        1306 :         move16();
     921        1306 :         Word16 guard_bits = find_guarded_bits_fx( num_speaker_nodes );
     922       12254 :         FOR( ch = 0; ch < num_speaker_nodes; ch++ )
     923             :         {
     924       10948 :             sum_val_fx = add( sum_val_fx, shr( virtual_node_division_gains_fx[ch], guard_bits ) );
     925             :         }
     926        1306 :         Word16 final_exp = 0, res_exp;
     927             :         Word32 tmp_1, tmp_2, tmp_3;
     928        1306 :         move16();
     929       12254 :         FOR( ch = 0; ch < num_speaker_nodes; ch++ )
     930             :         {
     931       10948 :             IF( virtual_node_division_gains_fx[ch] != 0 )
     932             :             {
     933        5895 :                 BASOP_Util_Divide_MantExp( virtual_node_division_gains_fx[ch], 1, sum_val_fx, add( guard_bits, 1 ), &virtual_node_division_gains_fx[ch], &final_exp );
     934             :             }
     935             :             ELSE
     936             :             {
     937        5053 :                 virtual_node_division_gains_fx[ch] = 0;
     938        5053 :                 move16();
     939             :             }
     940       10948 :             exp_virtual_node_division_gains[ch] = final_exp;
     941       10948 :             move16();
     942       10948 :             IF( use_object_mode )
     943             :             {
     944        5310 :                 IF( virtual_node_division_gains_fx[ch] == 0 )
     945             :                 {
     946        1927 :                     tmp_1 = 0;
     947        1927 :                     move16();
     948        1927 :                     virtual_node_division_gains_fx[ch] = 0;
     949        1927 :                     move16();
     950             :                 }
     951             :                 ELSE
     952             :                 {
     953        3383 :                     Word32 tmp32 = L_deposit_h( virtual_node_division_gains_fx[ch] );
     954        3383 :                     tmp_1 = L_add( BASOP_Util_Log2( tmp32 ), L_shl( ( sub( 31, sub( 31, final_exp ) ) ), 25 ) ); // Q25
     955        3383 :                     tmp_2 = Mpy_32_32( 26843546 /* 0.8f in Q25 */, tmp_1 );
     956        3383 :                     tmp_3 = BASOP_util_Pow2( tmp_2, Q31 - Q19, &res_exp );
     957        3383 :                     exp_virtual_node_division_gains[ch] = res_exp;
     958        3383 :                     move16();
     959        3383 :                     virtual_node_division_gains_fx[ch] = extract_h( tmp_3 );
     960        3383 :                     move16();
     961             :                 }
     962             :             }
     963             :         }
     964             :         /*make a common exp*/
     965       12254 :         FOR( i = 0; i < num_speaker_nodes; i++ )
     966             :         {
     967       10948 :             virtual_node_division_gains_fx[i] = shr( virtual_node_division_gains_fx[i], sub( -1, exp_virtual_node_division_gains[i] ) ); /* Q16 */
     968       10948 :             move16();
     969             :         }
     970             :     }
     971             : 
     972        3705 :     free( exp_virtual_node_division_gains );
     973             : 
     974        3705 :     return;
     975             : }
     976             : 
     977             : /*-------------------------------------------------------------------------*
     978             :  * check_need_of_virtual_speaker_node()
     979             :  *
     980             :  * Check if virtual speaker node is required
     981             :  *-------------------------------------------------------------------------*/
     982             : 
     983             : /*! r: virtual speaker node type */
     984             : 
     985        2688 : static enum VirtualSpeakerNodeType check_need_of_virtual_speaker_node_fx(
     986             :     VBAP_HANDLE hVBAPdata,                 /* i/o: VBAP structure                            */
     987             :     const Word32 *speaker_node_azi_deg_fx, /* i  : vector of speaker node azimuths       Q22 */
     988             :     const Word32 *speaker_node_ele_deg_fx, /* i  : vector of speaker node elevations     Q22 */
     989             :     enum SpeakerNodeGroup group            /* i  : group of speaker nodes where this belongs */
     990             : )
     991             : {
     992             :     Word16 ch;
     993        2688 :     Word32 max_elevation_fx = 0; /* Q22 */
     994             :     Word16 Flag1, Flag2, Flag3;
     995        2688 :     move32();
     996             : 
     997             :     /* The following considers if SPEAKER_NODE_BACK virtual speaker is needed */
     998        2688 :     IF( EQ_16( group, SPEAKER_NODE_BACK ) )
     999             :     {
    1000         896 :         Word16 virtual_back_needed = 1;
    1001         896 :         move16();
    1002         896 :         const Word16 virtual_back_epsilon_fx = -573; /* -0.0175f in Q15 */
    1003         896 :         move16();
    1004        3559 :         FOR( ch = 0; ch < hVBAPdata->num_speaker_nodes; ch++ )
    1005             :         {
    1006        3559 :             Flag1 = BASOP_Util_Cmp_Mant32Exp( speaker_node_ele_deg_fx[ch], Q31 - Q22, 23040 /*45.0f Q9*/, Q31 - Q9 );
    1007        3559 :             IF( EQ_16( Flag1, -1 ) )
    1008             :             {
    1009             :                 Word16 azi_temp;
    1010             : 
    1011        3559 :                 azi_temp = extract_l( L_shr( Mpy_32_32( speaker_node_azi_deg_fx[ch], ONE_BY_360_Q31 ), Q7 ) ); /* Q15 */
    1012        3559 :                 Word16 cos_res = getCosWord16R2( azi_temp );                                                   /* Q15 */
    1013             : 
    1014        3559 :                 IF( LT_16( cos_res, virtual_back_epsilon_fx ) )
    1015             :                 {
    1016         896 :                     virtual_back_needed = 0;
    1017         896 :                     move16();
    1018         896 :                     BREAK;
    1019             :                 }
    1020             :             }
    1021             :         }
    1022             : 
    1023         896 :         IF( virtual_back_needed )
    1024             :         {
    1025           0 :             hVBAPdata->back_virtual_speaker_node_index = hVBAPdata->num_speaker_nodes_internal;
    1026           0 :             move16();
    1027           0 :             hVBAPdata->num_speaker_nodes_internal = add( hVBAPdata->num_speaker_nodes_internal, 1 );
    1028           0 :             move16();
    1029           0 :             return VIRTUAL_SPEAKER_NODE_DISTRIBUTE_ENERGY;
    1030             :         }
    1031             : 
    1032         896 :         return NO_VIRTUAL_SPEAKER_NODE; /* No virtual back needed */
    1033             :     }
    1034             : 
    1035             :     /* The following considers if TOP or BOTTOM virtual speaker is needed */
    1036       16388 :     FOR( ch = 0; ch < hVBAPdata->num_speaker_nodes; ch++ )
    1037             :     {
    1038       14596 :         IF( EQ_16( group, SPEAKER_NODE_TOP_HALF ) )
    1039             :         {
    1040        7298 :             if ( GT_32( speaker_node_ele_deg_fx[ch], max_elevation_fx ) )
    1041             :             {
    1042         627 :                 max_elevation_fx = speaker_node_ele_deg_fx[ch];
    1043         627 :                 move32();
    1044             :             }
    1045             :         }
    1046             :         ELSE
    1047             :         {
    1048        7298 :             IF( GT_32( ( L_negate( speaker_node_ele_deg_fx[ch] ) ), max_elevation_fx ) )
    1049             :             {
    1050           1 :                 max_elevation_fx = L_negate( speaker_node_ele_deg_fx[ch] );
    1051             :             }
    1052             :         }
    1053             :     }
    1054        1792 :     Flag2 = BASOP_Util_Cmp_Mant32Exp( max_elevation_fx, Q31 - Q22, 23039 /* 44.9990005 in Q9 */, Q31 - Q9 );
    1055        1792 :     IF( EQ_16( Flag2, 1 ) )
    1056             :     {
    1057           0 :         return NO_VIRTUAL_SPEAKER_NODE;
    1058             :     }
    1059             : 
    1060             :     /* Use virtual node */
    1061        1792 :     IF( EQ_16( group, SPEAKER_NODE_BOTTOM_HALF ) )
    1062             :     {
    1063         896 :         hVBAPdata->bottom_virtual_speaker_node_index = hVBAPdata->num_speaker_nodes_internal;
    1064         896 :         move16();
    1065             :     }
    1066             :     ELSE
    1067             :     {
    1068         896 :         hVBAPdata->top_virtual_speaker_node_index = hVBAPdata->num_speaker_nodes_internal;
    1069         896 :         move16();
    1070             :     }
    1071             : 
    1072        1792 :     hVBAPdata->num_speaker_nodes_internal = add( hVBAPdata->num_speaker_nodes_internal, 1 );
    1073        1792 :     move16();
    1074        1792 :     Flag3 = BASOP_Util_Cmp_Mant32Exp( max_elevation_fx, Q31 - Q22, 20478 /* 19.9990005 in Q10 */, Q31 - Q10 );
    1075             : 
    1076        1792 :     IF( EQ_16( Flag3, 1 ) )
    1077             :     {
    1078         628 :         return VIRTUAL_SPEAKER_NODE_DISTRIBUTE_ENERGY;
    1079             :     }
    1080             : 
    1081        1164 :     return VIRTUAL_SPEAKER_NODE_DISCARD_ENERGY;
    1082             : }
    1083             : 
    1084             : 
    1085             : /*-------------------------------------------------------------------------*
    1086             :  * init_speaker_node_direction_data()
    1087             :  *
    1088             :  * Initialize speaker node data
    1089             :  *-------------------------------------------------------------------------*/
    1090             : 
    1091         896 : static void init_speaker_node_direction_data_fx(
    1092             :     VBAP_SPEAKER_NODE *speaker_node_data,  /* o  : storage for speaker node data         */
    1093             :     const Word32 *speaker_node_azi_deg_fx, /* i  : vector of speaker node azimuths   Q22 */
    1094             :     const Word32 *speaker_node_ele_deg_fx, /* i  : vector of speaker node elevations Q22 */
    1095             :     const Word16 num_speaker_nodes         /* i  : number of speaker nodes               */
    1096             : )
    1097             : {
    1098             :     Word16 ch;
    1099             :     Word16 azi_rad_fx;
    1100             :     Word16 ele_rad_fx;
    1101         896 :     Word16 num_horiz = 0;
    1102         896 :     UWord8 in_all_mode = TRUE;
    1103         896 :     move16();
    1104         896 :     move16();
    1105             : 
    1106        9986 :     FOR( ch = 0; ch < num_speaker_nodes; ch++ )
    1107             :     {
    1108        9090 :         speaker_node_data[ch].azi_deg_fx = speaker_node_azi_deg_fx[ch];
    1109        9090 :         move32();
    1110             : 
    1111        9090 :         azi_rad_fx = extract_l( L_shr( Mpy_32_32( speaker_node_azi_deg_fx[ch], ONE_BY_360_Q31 ), Q7 ) ); /* Q15 */
    1112        9090 :         test();
    1113        9090 :         IF( GE_32( L_shr( speaker_node_ele_deg_fx[ch], Q22 ), -5 ) && LE_32( L_shr( speaker_node_ele_deg_fx[ch], Q22 ), 5 ) )
    1114             :         {
    1115        5190 :             speaker_node_data[ch].ele_deg_fx = 0;
    1116        5190 :             move32();
    1117        5190 :             ele_rad_fx = 0;
    1118        5190 :             move32();
    1119        5190 :             speaker_node_data[ch].group = SPEAKER_NODE_HORIZONTAL;
    1120        5190 :             move16();
    1121        5190 :             num_horiz = add( num_horiz, 1 );
    1122             :         }
    1123             :         ELSE
    1124             :         {
    1125        3900 :             speaker_node_data[ch].ele_deg_fx = speaker_node_ele_deg_fx[ch];
    1126        3900 :             move32();
    1127        3900 :             ele_rad_fx = extract_l( L_shr( Mpy_32_32( speaker_node_ele_deg_fx[ch], ONE_BY_360_Q31 ), Q7 ) ); /* Q15 */
    1128             : 
    1129        3900 :             IF( ele_rad_fx < 0 )
    1130             :             {
    1131         900 :                 speaker_node_data[ch].group = SPEAKER_NODE_BOTTOM_HALF;
    1132         900 :                 move16();
    1133             :             }
    1134             :             ELSE
    1135             :             {
    1136        3000 :                 speaker_node_data[ch].group = SPEAKER_NODE_TOP_HALF;
    1137        3000 :                 move16();
    1138             :             }
    1139             :         }
    1140             : 
    1141        9090 :         speaker_node_data[ch].unit_vec_fx[0] = L_shr( L_mult( getCosWord16R2( azi_rad_fx ), getCosWord16R2( ele_rad_fx ) ), 1 ); /* Q15 + Q15 + Q1 - Q1 = Q30 */
    1142        9090 :         move32();
    1143        9090 :         speaker_node_data[ch].unit_vec_fx[1] = L_shr( L_mult( getSineWord16R2( azi_rad_fx ), getCosWord16R2( ele_rad_fx ) ), 1 ); /* Q15 + Q15 + Q1 - Q1 = Q30 */
    1144        9090 :         move32();
    1145        9090 :         speaker_node_data[ch].unit_vec_fx[2] = L_shr( L_deposit_h( getSineWord16R2( ele_rad_fx ) ), 1 ); /* Q15 + Q16 - Q1 = Q30 */
    1146        9090 :         move32();
    1147             :     }
    1148             :     /* Check for largest horizontal gap if there are at least 3 horizontal speaker nodes */
    1149         896 :     IF( GE_16( num_horiz, 3 ) )
    1150             :     {
    1151             :         Word16 i;
    1152             :         UWord16 horiz_azi[VBAP_MAX_NUM_SPEAKER_NODES];
    1153             : 
    1154             :         UWord16 largest_gap;
    1155             :         UWord16 temp;
    1156             : 
    1157         896 :         i = 0;
    1158         896 :         move16();
    1159        6086 :         FOR( ch = 0; ch < num_speaker_nodes && i < num_horiz; ch++ )
    1160             :         {
    1161        5190 :             test();
    1162        5190 :             IF( EQ_16( speaker_node_data[ch].group, SPEAKER_NODE_HORIZONTAL ) )
    1163             :             {
    1164             :                 Word16 exp1;
    1165        5190 :                 Word32 Mant2 = BASOP_Util_Add_Mant32Exp( speaker_node_azi_deg_fx[ch], Q31 - Q22, 23040 /* 360.0f in Q6 */, Q31 - Q6, &exp1 );
    1166             : 
    1167        5190 :                 IF( L_shr( speaker_node_azi_deg_fx[ch], 22 ) < 0 )
    1168             :                 {
    1169        2159 :                     horiz_azi[i] = (UWord16) L_shr( Mant2, sub( 31, exp1 ) ); /* Q0 */
    1170             :                 }
    1171             :                 ELSE
    1172             :                 {
    1173        3031 :                     horiz_azi[i] = (UWord16) L_shr( speaker_node_azi_deg_fx[ch], Q22 ); /* Q0 */
    1174             :                 }
    1175        5190 :                 i = add( i, 1 );
    1176             :             }
    1177             :         }
    1178             : 
    1179             :         /* Reorder horizontal azi to increasing order */
    1180         896 :         sort( horiz_azi, num_horiz );
    1181             : 
    1182             :         /* Find largest gap. Initialize with the wrap over gap. */
    1183         896 :         largest_gap = add( sub( horiz_azi[0], horiz_azi[num_horiz - 1] ), 360 );
    1184             : 
    1185        5190 :         FOR( ch = 0; ch < num_horiz - 1; ch++ )
    1186             :         {
    1187        4294 :             temp = sub( horiz_azi[ch + 1], horiz_azi[ch] );
    1188        4294 :             if ( GT_16( temp, largest_gap ) )
    1189             :             {
    1190        1771 :                 largest_gap = temp;
    1191        1771 :                 move16();
    1192             :             }
    1193             :         }
    1194             : 
    1195             :         /* If largest gap is small enough, we have definitive zero elevation plane.
    1196             :          * Otherwise, we should assign all speaker nodes to one group. */
    1197         896 :         if ( LE_16( largest_gap, VBAP_MAX_HORIZONTAL_GAP ) )
    1198             :         {
    1199         896 :             in_all_mode = FALSE;
    1200         896 :             move16();
    1201             :         }
    1202             :     }
    1203             : 
    1204             :     /* Designate all speaker nodes to same group if there was no definitive zero
    1205             :      * elevation plane. */
    1206         896 :     IF( in_all_mode )
    1207             :     {
    1208           0 :         FOR( ch = 0; ch < num_speaker_nodes; ch++ )
    1209             :         {
    1210           0 :             speaker_node_data[ch].group = SPEAKER_NODE_ALL;
    1211           0 :             move16();
    1212             :         }
    1213             :     }
    1214             : 
    1215         896 :     return;
    1216             : }
    1217             : /*-------------------------------------------------------------------------*
    1218             :  * matrix_inverse_3x3()
    1219             :  *
    1220             :  * 3-by-3 matrix inverse
    1221             :  *-------------------------------------------------------------------------*/
    1222             : 
    1223       14596 : static void matrix_inverse_3x3_32_fx(
    1224             :     const Word32 **input_matrix_fx, /* i : input matrix                  Q30 */
    1225             :     Word32 inverse_matrix_fx[3][3], /* o : output matrix Q(31 - exp_inv_mat) */
    1226             :     Word16 *exp_inv_mat )
    1227             : {
    1228             :     Word16 k;
    1229             :     Word32 determinant_fx;  /* Q28 */
    1230             :     Word32 cross_vec_fx[3]; /* Q29 */
    1231             :     Word16 exp_inverse_matrix_fx[3][3];
    1232       14596 :     vbap_crossp_fx( input_matrix_fx[1], input_matrix_fx[2], cross_vec_fx );
    1233       14596 :     determinant_fx = dotp_fixed( input_matrix_fx[0], cross_vec_fx, 3 );
    1234       14596 :     Word16 inv_mat_exp = 0;
    1235       14596 :     move16();
    1236             : 
    1237       58384 :     FOR( k = 0; k < 3; k++ )
    1238             :     {
    1239       43788 :         inverse_matrix_fx[k][0] = L_deposit_h( BASOP_Util_Divide3232_Scale( cross_vec_fx[k], determinant_fx, &inv_mat_exp ) );
    1240       43788 :         move32();
    1241       43788 :         inv_mat_exp = add( inv_mat_exp, ( ( Q31 - Q29 ) - ( Q31 - Q28 ) ) );
    1242       43788 :         exp_inverse_matrix_fx[k][0] = inv_mat_exp;
    1243       43788 :         move16();
    1244             :     }
    1245             : 
    1246       14596 :     vbap_crossp_fx( input_matrix_fx[2], input_matrix_fx[0], cross_vec_fx );
    1247             : 
    1248       58384 :     FOR( k = 0; k < 3; k++ )
    1249             :     {
    1250       43788 :         inverse_matrix_fx[k][1] = L_deposit_h( BASOP_Util_Divide3232_Scale( cross_vec_fx[k], determinant_fx, &inv_mat_exp ) );
    1251       43788 :         move32();
    1252       43788 :         inv_mat_exp = add( inv_mat_exp, ( ( Q31 - Q29 ) - ( Q31 - Q28 ) ) );
    1253       43788 :         exp_inverse_matrix_fx[k][1] = inv_mat_exp;
    1254       43788 :         move16();
    1255             :     }
    1256             : 
    1257       14596 :     vbap_crossp_fx( input_matrix_fx[0], input_matrix_fx[1], cross_vec_fx );
    1258             : 
    1259       58384 :     FOR( k = 0; k < 3; k++ )
    1260             :     {
    1261       43788 :         inverse_matrix_fx[k][2] = L_deposit_h( BASOP_Util_Divide3232_Scale( cross_vec_fx[k], determinant_fx, &inv_mat_exp ) );
    1262       43788 :         move32();
    1263       43788 :         inv_mat_exp = add( inv_mat_exp, ( ( Q31 - Q29 ) - ( Q31 - Q28 ) ) );
    1264       43788 :         exp_inverse_matrix_fx[k][2] = inv_mat_exp;
    1265       43788 :         move16();
    1266             :     }
    1267             : 
    1268             :     /*make common exponant*/
    1269       14596 :     Word16 max_exp = 0, i, j;
    1270       58384 :     FOR( i = 0; i < 3; i++ )
    1271             :     {
    1272      175152 :         FOR( j = 0; j < 3; j++ )
    1273             :         {
    1274      131364 :             max_exp = s_max( max_exp, exp_inverse_matrix_fx[i][j] );
    1275             :         }
    1276             :     }
    1277             : 
    1278       14596 :     *exp_inv_mat = add( max_exp, 1 );
    1279       14596 :     move16();
    1280             : 
    1281       58384 :     FOR( i = 0; i < 3; i++ )
    1282             :     {
    1283      175152 :         FOR( j = 0; j < 3; j++ )
    1284             :         {
    1285      131364 :             test();
    1286      131364 :             IF( LT_16( exp_inverse_matrix_fx[i][j], -15 ) && inverse_matrix_fx[i][j] != 0 )
    1287             :             {
    1288           0 :                 inverse_matrix_fx[i][j] = 1;
    1289           0 :                 move32();
    1290           0 :                 exp_inverse_matrix_fx[i][j] = 0;
    1291           0 :                 move16();
    1292             :             }
    1293             :             ELSE
    1294             :             {
    1295      131364 :                 inverse_matrix_fx[i][j] = L_shr( inverse_matrix_fx[i][j], sub( *exp_inv_mat, exp_inverse_matrix_fx[i][j] ) ); /* Q(31 - *exp_inv_mat) */
    1296      131364 :                 move32();
    1297             :             }
    1298             :         }
    1299             :     }
    1300             : 
    1301       14596 :     return;
    1302             : }
    1303             : /*-------------------------------------------------------------------------*
    1304             :  * check_and_store_triplet()
    1305             :  *
    1306             :  * Check if the given loudspeaker triplet is a valid one and store data when
    1307             :  * valid triplet is found.
    1308             :  *-------------------------------------------------------------------------*/
    1309             : 
    1310       14596 : static Word16 check_and_store_triplet_fx(
    1311             :     const Word16 chA,                           /* i  : first channel index that forms the loudspeaker triplet      */
    1312             :     const Word16 chB,                           /* i  : second channel index that forms the loudspeaker triplet     */
    1313             :     const Word16 chC,                           /* i  : third channel index that forms the loudspeaker triplet      */
    1314             :     const Word16 num_speaker_nodes,             /* i  : number of speaker nodes                                     */
    1315             :     const VBAP_SPEAKER_NODE *speaker_node_data, /* i  : speaker node data structure                                 */
    1316             :     VBAP_VS_TRIPLET *triplets,                  /* o  : vector of virtual surface triplets                          */
    1317             :     Word16 *triplet_index,                      /* i/o: index for the next free triplet slot                        */
    1318             :     Word32 *triplet_azidegs_fx,                 /* o  : center azimuths of the found triplets                   Q19 */
    1319             :     Word16 *triplet_order                       /* o  : initial order of triplet indices                            */
    1320             : )
    1321             : {
    1322             :     Word16 ch_check;
    1323             :     Word16 k;
    1324             :     Word16 speaker_node_found_inside_triplet;
    1325             :     UWord8 triplet_ok;
    1326             :     Word16 exp_inv_mat;
    1327             : 
    1328             :     Word32 inverse_matrix_fx[3][3] /* Q(31 - exp_inv_mat) */, unnormalized_gains_fx[3] /* Q(31 - exp_inv_mat - 1) */;
    1329       14596 :     set32_fx( unnormalized_gains_fx, 0, 3 );
    1330             :     const Word32 *speaker_node_triplet_unit_vec_matrix_fx[3]; /* Q30 */
    1331             : 
    1332             :     /* Triplet found, determine inverse matrix for VBAP formulation */
    1333             : 
    1334       14596 :     speaker_node_triplet_unit_vec_matrix_fx[0] = speaker_node_data[chA].unit_vec_fx;
    1335       14596 :     move32();
    1336       14596 :     speaker_node_triplet_unit_vec_matrix_fx[1] = speaker_node_data[chB].unit_vec_fx;
    1337       14596 :     move32();
    1338       14596 :     speaker_node_triplet_unit_vec_matrix_fx[2] = speaker_node_data[chC].unit_vec_fx;
    1339       14596 :     move32();
    1340       14596 :     matrix_inverse_3x3_32_fx( speaker_node_triplet_unit_vec_matrix_fx, inverse_matrix_fx, &exp_inv_mat );
    1341       14596 :     triplets[*triplet_index].q_inverse_matrix = sub( 31, exp_inv_mat );
    1342       14596 :     move16();
    1343             : 
    1344             :     /* Check through all speaker nodes that none of them are within the triplet.
    1345             :      * Node within the triplet is identified by that all three panning gains are positive.
    1346             :      * Epsilon-condition is for some small rounding issues.*/
    1347       14596 :     speaker_node_found_inside_triplet = 0;
    1348       14596 :     move16();
    1349      169208 :     FOR( ch_check = 0; ch_check < num_speaker_nodes; ch_check++ )
    1350             :     {
    1351      154612 :         test();
    1352      154612 :         test();
    1353      154612 :         IF( ( NE_16( ch_check, chA ) ) && NE_16( ch_check, chB ) && NE_16( ch_check, chC ) )
    1354             :         {
    1355      110824 :             triplet_ok = vector_matrix_multiply_3x3_32_fx( speaker_node_data[ch_check].unit_vec_fx, inverse_matrix_fx, unnormalized_gains_fx, sub( Q31, exp_inv_mat ) );
    1356      110824 :             test();
    1357      110824 :             test();
    1358      110824 :             test();
    1359      110824 :             IF( triplet_ok && ( GT_32( unnormalized_gains_fx[0], L_shl( VBAP_EPSILON_Q31, sub( Q30, exp_inv_mat ) ) ) ) && ( GT_32( unnormalized_gains_fx[1], L_shl( VBAP_EPSILON_Q31, sub( Q30, exp_inv_mat ) ) ) ) && ( GT_32( unnormalized_gains_fx[2], L_shl( VBAP_EPSILON_Q31, sub( Q30, exp_inv_mat ) ) ) ) )
    1360             :             {
    1361           0 :                 speaker_node_found_inside_triplet = 1;
    1362           0 :                 move16();
    1363           0 :                 BREAK;
    1364             :             }
    1365             :         }
    1366             :     }
    1367             : 
    1368             :     /* No speaker node inside triplet -> appropriate triplet found, save data. */
    1369       14596 :     IF( speaker_node_found_inside_triplet == 0 )
    1370             :     {
    1371       14596 :         triplets[*triplet_index].speaker_node[0] = (UWord8) chA;
    1372       14596 :         triplets[*triplet_index].speaker_node[1] = (UWord8) chB;
    1373       14596 :         triplets[*triplet_index].speaker_node[2] = (UWord8) chC;
    1374       14596 :         move16();
    1375       14596 :         move16();
    1376       14596 :         move16();
    1377       58384 :         FOR( k = 0; k < 3; k++ )
    1378             :         {
    1379       43788 :             Copy32( inverse_matrix_fx[k], triplets[*triplet_index].inverse_matrix_fx[k], 3 );
    1380             :         }
    1381             :         /* Get center azimuth for fast search use */
    1382       14596 :         Word32 tmp_a = L_add( L_shr( L_add( speaker_node_data[chA].unit_vec_fx[1], speaker_node_data[chB].unit_vec_fx[1] ), Q2 ), L_shr( speaker_node_data[chC].unit_vec_fx[1], Q2 ) ); /* Q28 */
    1383             :         /*Condition to make tmp_a 0 to adress precision loss seen*/
    1384       14596 :         if ( EQ_32( tmp_a, -8193 /* -0.0000305 in Q28 */ ) )
    1385             :         {
    1386           0 :             tmp_a = 0;
    1387           0 :             move32();
    1388             :         }
    1389       14596 :         Word32 tmp_b = L_add( L_shr( L_add( speaker_node_data[chA].unit_vec_fx[0], speaker_node_data[chB].unit_vec_fx[0] ), 2 ), L_shr( speaker_node_data[chC].unit_vec_fx[0], 2 ) ); /* Q28 */
    1390       14596 :         Word16 tmp_tan = shr( BASOP_util_atan2( tmp_a, tmp_b, 0 ), Q13 - Q9 );                                                                                                        /* Q9 */
    1391       14596 :         triplet_azidegs_fx[*triplet_index] = L_mult( tmp_tan, 29335 /*_180_OVER_PI in Q9*/ );                                                                                         /* Q19 */
    1392       14596 :         move32();
    1393             :         /* Store increasing order indices for the later sorting step. */
    1394       14596 :         triplet_order[*triplet_index] = *triplet_index;
    1395       14596 :         move16();
    1396             : 
    1397       14596 :         *triplet_index = add( *triplet_index, 1 );
    1398       14596 :         move16();
    1399             : 
    1400       14596 :         return 1;
    1401             :     }
    1402             : 
    1403             :     /* Triplet was not good */
    1404           0 :     return 0;
    1405             : }
    1406             : /*-------------------------------------------------------------------------*
    1407             :  * determine_virtual_surface_triplets()
    1408             :  *
    1409             :  * Determine virtual surface triples that are used for panning. This
    1410             :  * function is optimized for the use in cases where speaker nodes are in
    1411             :  * one group or divided into two separate groups divided by a horizontal
    1412             :  * layer.
    1413             :  *-------------------------------------------------------------------------*/
    1414             : 
    1415             : /*! r: number of virtual surface triplets */
    1416             : 
    1417        1792 : static Word16 determine_virtual_surface_triplets_fx(
    1418             :     const Word16 num_speaker_nodes,                         /* i  : number of speaker nodes                                                            */
    1419             :     const VBAP_SPEAKER_NODE *speaker_node_data,             /* i  : speaker node data structure                                                        */
    1420             :     Word16 connections[][2],                                /* i  : vector of all connections                                                          */
    1421             :     const Word16 max_num_connections,                       /* i  : max number of connections                                                          */
    1422             :     VBAP_VS_TRIPLET *triplets,                              /* o  : vector of virtual surface triplets                                                 */
    1423             :     Word16 initial_search_indices[VBAP_NUM_SEARCH_SECTORS], /* o  : initial search indices for this set of triplets corresponding to the search struct */
    1424             :     enum SpeakerNodeGroup allowed_group                     /* i  : group of allowed speaker nodes for forming the triplets in this call               */
    1425             : )
    1426             : {
    1427             :     Word16 chA, chB, chC, k, l, m;
    1428        1792 :     Word16 num_triplets = 0;
    1429        1792 :     move16();
    1430             :     Word16 num_connected_to_chA;
    1431             :     Word16 connected_to_chA[VBAP_MAX_NUM_SPEAKER_NODES];
    1432             :     Word16 connection_uses_left[VBAP_MAX_NUM_SPEAKER_NODES];
    1433             :     Word32 triplet_azidegs_fx[VBAP_MAX_NUM_TRIPLETS]; /* Q19 */
    1434             :     Word16 triplet_order[VBAP_MAX_NUM_TRIPLETS];
    1435             : 
    1436             :     /* Each connection can be used exactly by two different virtual surface triplets. */
    1437        1792 :     set16_fx( connection_uses_left, 2, VBAP_MAX_NUM_SPEAKER_NODES );
    1438             : 
    1439       19972 :     FOR( chA = 0; chA < num_speaker_nodes; chA++ )
    1440             :     {
    1441             :         /* Early skip if not in correct group. */
    1442       18180 :         IF( NE_16( speaker_node_data[chA].group, allowed_group ) )
    1443             :         {
    1444       14280 :             CONTINUE;
    1445             :         }
    1446             : 
    1447             :         /* Get all connections connected to current chA that have not been used by
    1448             :          * two triplets yet. */
    1449        3900 :         num_connected_to_chA = 0;
    1450        3900 :         move16();
    1451       77992 :         FOR( k = 0; k < max_num_connections; k++ )
    1452             :         {
    1453       74092 :             test();
    1454       74092 :             test();
    1455       74092 :             IF( ( EQ_16( connections[k][0], chA ) || EQ_16( connections[k][1], chA ) ) && ( connection_uses_left[k] > 0 ) )
    1456             :             {
    1457       16054 :                 connected_to_chA[num_connected_to_chA] = k;
    1458       16054 :                 move16();
    1459       16054 :                 num_connected_to_chA = add( num_connected_to_chA, 1 );
    1460             :             }
    1461             :         }
    1462             : 
    1463             :         /* Check that we have enough connections to use. We need at least two available connections to form a triplet.
    1464             :          * This can fail in later stages when all connections are already used. */
    1465        3900 :         IF( LT_16( num_connected_to_chA, 2 ) )
    1466             :         {
    1467         650 :             CONTINUE;
    1468             :         }
    1469             : 
    1470             :         /* Try to form triplets from each valid connection. */
    1471       19304 :         FOR( k = 0; k < num_connected_to_chA; k++ )
    1472             :         {
    1473       16054 :             Word16 connect_index_k = connected_to_chA[k];
    1474       16054 :             move16();
    1475       16054 :             chB = connections[connect_index_k][0];
    1476       16054 :             move16();
    1477       16054 :             if ( EQ_16( connections[connect_index_k][0], chA ) )
    1478             :             {
    1479        3588 :                 chB = connections[connect_index_k][1];
    1480        3588 :                 move16();
    1481             :             }
    1482       27843 :             FOR( l = k + 1; l < num_connected_to_chA; l++ )
    1483             :             {
    1484       24593 :                 Word16 connect_index_l = connected_to_chA[l];
    1485       24593 :                 move16();
    1486       24593 :                 chC = connections[connect_index_l][0];
    1487       24593 :                 move16();
    1488       24593 :                 if ( EQ_16( connections[connect_index_l][0], chA ) )
    1489             :                 {
    1490        6930 :                     chC = connections[connect_index_l][1];
    1491        6930 :                     move16();
    1492             :                 }
    1493             : 
    1494             :                 /* With chA, chB, and chC selected, we still need to find connection between chB and chC and verify that the triplet is valid */
    1495      301225 :                 FOR( m = 0; m < max_num_connections; m++ )
    1496             :                 {
    1497      291228 :                     test();
    1498      291228 :                     test();
    1499      291228 :                     test();
    1500      291228 :                     IF( ( EQ_16( connections[m][0], chB ) && EQ_16( connections[m][1], chC ) ) || ( EQ_16( connections[m][1], chB ) && EQ_16( connections[m][0], chC ) ) )
    1501             :                     {
    1502       14596 :                         Word16 flag = check_and_store_triplet_fx( chA, chB, chC, num_speaker_nodes, speaker_node_data, triplets, &num_triplets, triplet_azidegs_fx, triplet_order );
    1503       14596 :                         IF( EQ_16( flag, 1 ) )
    1504             :                         {
    1505       14596 :                             connection_uses_left[connect_index_k] = sub( connection_uses_left[connect_index_k], 1 );
    1506       14596 :                             move16();
    1507       14596 :                             connection_uses_left[connect_index_l] = sub( connection_uses_left[connect_index_l], 1 );
    1508       14596 :                             move16();
    1509       14596 :                             connection_uses_left[m] = sub( connection_uses_left[m], 1 );
    1510       14596 :                             move16();
    1511       14596 :                             BREAK;
    1512             :                         }
    1513             :                     }
    1514             :                 }
    1515             : 
    1516             :                 /* Check if chA-chB connection has been used in two triplets already. If yes, then break out of loop
    1517             :                  * as this connection cannot be used for more triplets and we need to continue with another chA-chB
    1518             :                  * connection. */
    1519       24593 :                 IF( LT_16( connection_uses_left[connect_index_k], 1 ) )
    1520             :                 {
    1521       12804 :                     BREAK;
    1522             :                 }
    1523             :             }
    1524             :         }
    1525             :     }
    1526             : 
    1527             :     /* All triplets should be stored now. Sort them for search use and then determine the initial search indices for
    1528             :      * each search sector for this search struct. */
    1529        1792 :     v_sort_ind_fixed( triplet_azidegs_fx, triplet_order, num_triplets );
    1530        1792 :     reorder_triplets_fx( triplets, triplet_order, num_triplets );
    1531        1792 :     determine_initial_search_indices_fx( num_triplets, triplet_azidegs_fx, initial_search_indices );
    1532             : 
    1533        1792 :     return num_triplets;
    1534             : }
    1535             : /*-------------------------------------------------------------------------*
    1536             :  * determine_initial_search_indices()
    1537             :  *
    1538             :  * Determine initial search indices used for fast search of correct triangle
    1539             :  *-------------------------------------------------------------------------*/
    1540             : 
    1541             : 
    1542        1792 : static void determine_initial_search_indices_fx(
    1543             :     const Word16 num_triplets,                              /* i  : number of triplets                    */
    1544             :     const Word32 triplet_azidegs_fx[VBAP_MAX_NUM_TRIPLETS], /* i  : azimuths of triplets (in degrees) Q19 */
    1545             :     Word16 initial_search_indices[VBAP_NUM_SEARCH_SECTORS]  /* o  : initial search indices                */
    1546             : )
    1547             : {
    1548             :     Word16 i, j;
    1549             :     Word32 sector_reference_azideg_fx;    /* Q0 */
    1550             :     Word32 sector_border_start_azideg_fx; /* Q0 */
    1551             :     Word32 sector_border_end_azideg_fx;   /* Q0 */
    1552             :     Word16 best_index;
    1553             :     Word32 min_azideg_diff_fx;
    1554             :     Word32 azideg_diff_fx; /* Q19 */
    1555             : 
    1556        8960 :     FOR( i = 0; i < VBAP_NUM_SEARCH_SECTORS; i++ )
    1557             :     {
    1558        7168 :         sector_border_start_azideg_fx = imult3216( i, VBAP_SEARCH_SECTOR_SIZE_Q0 );
    1559        7168 :         sector_border_end_azideg_fx = imult3216( add( i, 1 ), VBAP_SEARCH_SECTOR_SIZE_Q0 );
    1560        7168 :         sector_reference_azideg_fx = L_shr( L_add( sector_border_start_azideg_fx, sector_border_end_azideg_fx ), 1 );
    1561             : 
    1562             : 
    1563        7168 :         best_index = 0;
    1564        7168 :         move16();
    1565        7168 :         min_azideg_diff_fx = ONE_IN_Q31;
    1566        7168 :         move32();
    1567             : 
    1568       65552 :         FOR( j = 0; j < num_triplets; j++ )
    1569             :         {
    1570       58384 :             azideg_diff_fx = L_sub( L_shl( sector_reference_azideg_fx, Q19 ), triplet_azidegs_fx[j] );
    1571             : 
    1572       58384 :             IF( GT_32( azideg_diff_fx, 94371840 /* 180.0f in Q19 */ ) )
    1573             :             {
    1574       25380 :                 azideg_diff_fx = L_sub( azideg_diff_fx, 188743680 /* 360.0f in Q19 */ );
    1575             :             }
    1576       33004 :             ELSE IF( LT_32( azideg_diff_fx, -94371840 /* -180.0f in Q19 */ ) )
    1577             :             {
    1578           0 :                 azideg_diff_fx = L_add( azideg_diff_fx, 188743680 /* 360.0f in Q19 */ );
    1579             :             }
    1580       58384 :             azideg_diff_fx = L_abs( azideg_diff_fx );
    1581             : 
    1582       58384 :             IF( LT_32( azideg_diff_fx, min_azideg_diff_fx ) )
    1583             :             {
    1584       24506 :                 min_azideg_diff_fx = azideg_diff_fx;
    1585       24506 :                 move32();
    1586       24506 :                 best_index = j;
    1587       24506 :                 move16();
    1588             :             }
    1589             :         }
    1590             : 
    1591        7168 :         initial_search_indices[i] = best_index;
    1592        7168 :         move16();
    1593             :     }
    1594             : 
    1595        1792 :     return;
    1596             : }
    1597             : 
    1598             : /*-------------------------------------------------------------------------*
    1599             :  * determine_connections()
    1600             :  *
    1601             :  * Determine all valid connections between all speaker nodes
    1602             :  *-------------------------------------------------------------------------*/
    1603             : 
    1604         896 : static ivas_error determine_connections_fx(
    1605             :     const Word16 num_speaker_nodes,             /* i  : number of speaker nodes               */
    1606             :     const VBAP_SPEAKER_NODE *speaker_node_data, /* i  : speaker node data                     */
    1607             :     Word16 connections[][2],                    /* o  : vector of connections                 */
    1608             :     const Word16 max_num_connections,           /* i  : max number of connections             */
    1609             :     Word16 *group1_count,                       /* o  : number of connections in first group  */
    1610             :     Word16 *group2_start,                       /* o  : start of second group of connections  */
    1611             :     Word16 *group2_count                        /* o  : number of connections in second group */
    1612             : )
    1613             : {
    1614             :     Word16 num_non_crossing_planes;
    1615             :     Word16 c;
    1616         896 :     Word16 connection_write_index = 0;
    1617         896 :     move16();
    1618             :     Word32 non_crossing_plane_elevation_deg_fx[VBAP_MAX_PLANES]; /* Q14 */
    1619             : 
    1620             :     ivas_error error;
    1621             : 
    1622         896 :     set32_fx( non_crossing_plane_elevation_deg_fx, 0, VBAP_MAX_PLANES );
    1623             : 
    1624             :     /* Reset connection data */
    1625       22790 :     FOR( c = 0; c < max_num_connections; c++ )
    1626             :     {
    1627       21894 :         connections[c][0] = VBAP_NOT_VALID_CONNECTION;
    1628       21894 :         move16();
    1629             :     }
    1630             : 
    1631             :     /* This function determines some prominent elevated planes, that are favoured in making node-node connections. */
    1632         896 :     num_non_crossing_planes = determine_non_crossing_planes_fx( num_speaker_nodes, speaker_node_data, non_crossing_plane_elevation_deg_fx );
    1633             : 
    1634             :     /* Process in different mode based on the grouping. It is enough to check for first node. */
    1635         896 :     IF( EQ_16( speaker_node_data[0].group, SPEAKER_NODE_ALL ) )
    1636             :     {
    1637           0 :         IF( NE_32( ( error = formulate_half_sphere_connections_fx( speaker_node_data, num_speaker_nodes, SPEAKER_NODE_ALL, connections, &connection_write_index, max_num_connections, num_non_crossing_planes, non_crossing_plane_elevation_deg_fx ) ), IVAS_ERR_OK ) )
    1638             :         {
    1639           0 :             return error;
    1640             :         }
    1641             :     }
    1642             :     ELSE
    1643             :     {
    1644             :         /* The node-node connections are determined in three stages: bottom, horizontal, and top. */
    1645         896 :         IF( NE_32( ( error = formulate_half_sphere_connections_fx( speaker_node_data, num_speaker_nodes, SPEAKER_NODE_BOTTOM_HALF, connections, &connection_write_index, max_num_connections, num_non_crossing_planes, non_crossing_plane_elevation_deg_fx ) ), IVAS_ERR_OK ) )
    1646             :         {
    1647           0 :             return error;
    1648             :         }
    1649         896 :         *group2_start = connection_write_index;
    1650         896 :         move16();
    1651             : 
    1652         896 :         formulate_horizontal_connections_fx( speaker_node_data, num_speaker_nodes, connections, &connection_write_index );
    1653         896 :         *group1_count = connection_write_index;
    1654         896 :         move16();
    1655             : 
    1656         896 :         IF( NE_32( ( error = formulate_half_sphere_connections_fx( speaker_node_data, num_speaker_nodes, SPEAKER_NODE_TOP_HALF, connections, &connection_write_index, max_num_connections, num_non_crossing_planes, non_crossing_plane_elevation_deg_fx ) ), IVAS_ERR_OK ) )
    1657             :         {
    1658           0 :             return error;
    1659             :         }
    1660             : 
    1661         896 :         *group2_count = sub( connection_write_index, *group2_start );
    1662         896 :         move16();
    1663             :     }
    1664             : 
    1665         896 :     return IVAS_ERR_OK;
    1666             : }
    1667             : /*-------------------------------------------------------------------------*
    1668             :  * determine_connection_class()
    1669             :  *
    1670             :  * Determine the type of connection
    1671             :  *-------------------------------------------------------------------------*/
    1672             : 
    1673             : /*! r: type of connection */
    1674       27314 : static enum ConnectionClass determine_connection_class_fx(
    1675             :     const VBAP_SPEAKER_NODE *node_data, /* i  : speaker node data       */
    1676             :     const Word16 num_speaker_nodes,     /* i  : number of speaker nodes */
    1677             :     const enum SpeakerNodeGroup group,  /* i  : speaker node group      */
    1678             :     const Word16 chA,                   /* i  : speaker node counter 1  */
    1679             :     const Word16 chB                    /* i  : speaker node counter 2  */
    1680             : )
    1681             : {
    1682             :     Word16 ch, k;
    1683             : 
    1684             :     const Word32 *p1_fx, *v2_fx;
    1685             :     Word32 v1v1_fx, v1v2_fx, v2v2_fx, v1p1_fx, v2p1_fx; /* Q25, Q27, Q29, Q27, Q29 */
    1686             :     Word32 determinant_fx;                              /* Q23 */
    1687             :     Word32 norm_distance_on_v1_fx;
    1688             :     Word32 vec_diff_fx[3];
    1689             :     Word32 v1_fx[3]; /* Q28 */
    1690             :     Word32 vTarget_fx[3];
    1691             :     Word32 energy_sum_fx;
    1692             :     Word32 eq_value_fx;
    1693             :     Word32 uvecdot_fx; /* Q30 */
    1694             : 
    1695             :     /* Check if connection passes through origin. This is not desired.
    1696             :      * When this happens, unit vectors point in opposite directions. */
    1697       27314 :     uvecdot_fx = L_add( L_shl( dotp_fixed( node_data[chA].unit_vec_fx, node_data[chB].unit_vec_fx, 3 ), 1 ), ONE_IN_Q30 ); // Q30 - adding one guard bit
    1698             : 
    1699       27314 :     test();
    1700       27314 :     IF( LT_32( uvecdot_fx, VBAP_EPSILON_Q3O ) && GT_32( uvecdot_fx, L_negate( VBAP_EPSILON_Q3O ) ) )
    1701             :     {
    1702           0 :         return CONNECTION_WITH_SPEAKER_NODE_BEHIND;
    1703             :     }
    1704             : 
    1705             :     /* This loop checks the chA-chB connection with respect to all loudspeakers:
    1706             :      - in case there is a node behind or nearly behind the connection line. These
    1707             :      connections need to be discarded.
    1708             :      - in case there is a node that is closer to the connection line than 1/5 of the
    1709             :      connection length AND at the same horizontal plane. These connections need to be
    1710             :      weighted with a penalty (a special case, for example avoiding elevated L,R,C triplet)
    1711             :      */
    1712      317266 :     FOR( ch = 0; ch < num_speaker_nodes; ch++ )
    1713             :     {
    1714             :         /* Select speaker_node only within TOP or BOTTOM sphere half, not being part of chA-chB pair */
    1715      292852 :         test();
    1716      292852 :         test();
    1717      292852 :         IF( ( EQ_16( group, node_data[ch].group ) ) && ( NE_16( ch, chA ) ) && ( NE_16( ch, chB ) ) )
    1718             :         {
    1719             :             /* The following lines formulate the point on the chA-chB-connection that is
    1720             :              nearest to the origo-ch-line */
    1721       65340 :             p1_fx = node_data[chA].unit_vec_fx; // Q30
    1722       65340 :             move32();
    1723             : 
    1724      261360 :             FOR( k = 0; k < 3; k++ )
    1725             :             {
    1726      196020 :                 v1_fx[k] = L_sub( L_shr( node_data[chB].unit_vec_fx[k], 2 ), L_shr( node_data[chA].unit_vec_fx[k], 2 ) ); /* Q28 (Add two guard bit) */
    1727      196020 :                 move32();
    1728             :             }
    1729       65340 :             v2_fx = node_data[ch].unit_vec_fx; // Q30
    1730       65340 :             move32();
    1731             : 
    1732       65340 :             v1v1_fx = dotp_fixed( v1_fx, v1_fx, 3 ); // Q25
    1733       65340 :             move32();
    1734       65340 :             v1v2_fx = dotp_fixed( v1_fx, v2_fx, 3 ); // Q27
    1735       65340 :             move32();
    1736       65340 :             v2v2_fx = ONE_IN_Q29;
    1737       65340 :             move32();
    1738             : 
    1739       65340 :             v1p1_fx = dotp_fixed( v1_fx, p1_fx, 3 ); // Q27
    1740       65340 :             move32();
    1741       65340 :             v2p1_fx = dotp_fixed( v2_fx, p1_fx, 3 ); // Q29
    1742       65340 :             move32();
    1743             : 
    1744       65340 :             Word32 tmp1 = Mpy_32_32( v1v2_fx, v1v2_fx );             // Q23
    1745       65340 :             Word32 tmp2 = Mpy_32_32( v1v1_fx, L_negate( v2v2_fx ) ); // Q23
    1746       65340 :             Word32 tmp3 = L_add( tmp1, tmp2 );                       // Q23
    1747             : 
    1748       65340 :             determinant_fx = tmp3; // Q23
    1749       65340 :             move32();
    1750             : 
    1751             :             /* Norm distance = distance parameter on line chA-chB, determines point that is
    1752             :              nearest to origo-ch line. Distance 0 means chA and distance 1 means chB. Can be
    1753             :              outside this region as well.*/
    1754             : 
    1755       65340 :             Word32 tmp4 = Mpy_32_32( v2v2_fx, v1p1_fx );             // q25
    1756       65340 :             Word32 tmp5 = Mpy_32_32( L_negate( v1v2_fx ), v2p1_fx ); // q25
    1757       65340 :             Word32 tmp6 = L_add( tmp4, tmp5 );                       // q25
    1758       65340 :             Word16 tmp7, exp = 0;
    1759       65340 :             move16();
    1760       65340 :             if ( determinant_fx == 0 )
    1761             :             {
    1762           0 :                 determinant_fx = 1;
    1763           0 :                 move32();
    1764             :             }
    1765       65340 :             tmp7 = BASOP_Util_Divide3232_Scale( ONE_IN_Q23, determinant_fx, &exp ); // Q(15 - exp)
    1766       65340 :             norm_distance_on_v1_fx = Mpy_32_16_1( tmp6, tmp7 );                     // Q25 + Q(15 - exp) - Q15 = Q(25 - exp)
    1767             : 
    1768             :             /* Continue only if the nearest point is between chA and chB */
    1769       65340 :             Word16 exp_vTarget = 0, exp_energy_sum = 0, exp_vec_diff = 0;
    1770             :             Word32 var1, var2;
    1771             :             Word16 vTarget_fx_e[3], vec_diff_e[3];
    1772       65340 :             test();
    1773       65340 :             IF( norm_distance_on_v1_fx > 0 && LT_32( norm_distance_on_v1_fx, L_shr( ONE_IN_Q31, sub( 31, sub( Q25, exp ) ) ) ) )
    1774             :             {
    1775             :                 /* Formulate vTarget, that is an unit vector that goes through the determined point on chA-chB connection */
    1776       53542 :                 energy_sum_fx = 0;
    1777       53542 :                 move32();
    1778      214168 :                 FOR( k = 0; k < 3; k++ )
    1779             :                 {
    1780      160626 :                     var1 = Mpy_32_32( norm_distance_on_v1_fx, v1_fx[k] ); // Q(25 - exp) + Q28 - 31
    1781      160626 :                     vTarget_fx[k] = BASOP_Util_Add_Mant32Exp( p1_fx[k], 1, var1, sub( Q31, add( sub( Q25, exp ), Q28 - Q31 ) ), &exp_vTarget );
    1782      160626 :                     move16();
    1783      160626 :                     vTarget_fx_e[k] = exp_vTarget;
    1784      160626 :                     move16();
    1785             : 
    1786      160626 :                     var2 = Mpy_32_32( vTarget_fx[k], vTarget_fx[k] ); // 2*exp_vTarget
    1787      160626 :                     energy_sum_fx = BASOP_Util_Add_Mant32Exp( energy_sum_fx, exp_energy_sum, var2, shl( exp_vTarget, 1 ), &exp_energy_sum );
    1788      160626 :                     vec_diff_fx[k] = BASOP_Util_Add_Mant32Exp( vTarget_fx[k], exp_vTarget, L_negate( v2_fx[k] ), 1, &exp_vec_diff );
    1789      160626 :                     move16();
    1790      160626 :                     vec_diff_e[k] = exp_vec_diff;
    1791      160626 :                     move16();
    1792             :                 }
    1793       53542 :                 Word16 exp_eq = exp_energy_sum;
    1794       53542 :                 move16();
    1795       53542 :                 eq_value_fx = ISqrt32( energy_sum_fx, &exp_eq );
    1796      214168 :                 FOR( k = 0; k < 3; k++ )
    1797             :                 {
    1798      160626 :                     vTarget_fx[k] = Mpy_32_32( vTarget_fx[k], eq_value_fx );
    1799      160626 :                     move32();
    1800      160626 :                     vTarget_fx_e[k] = add( vTarget_fx_e[k], exp_eq );
    1801      160626 :                     move16();
    1802             :                 }
    1803             :                 /*make a common exponent*/
    1804       53542 :                 Word16 max_vTarget_e = 0, max_vec_diff_e = 0;
    1805       53542 :                 move16();
    1806       53542 :                 move16();
    1807      214168 :                 FOR( k = 0; k < 3; k++ )
    1808             :                 {
    1809      160626 :                     max_vTarget_e = s_max( vTarget_fx_e[k], max_vTarget_e );
    1810      160626 :                     max_vec_diff_e = s_max( vec_diff_e[k], max_vec_diff_e );
    1811             :                 }
    1812      214168 :                 FOR( k = 0; k < 3; k++ )
    1813             :                 {
    1814      160626 :                     vTarget_fx[k] = L_shr( vTarget_fx[k], sub( max_vTarget_e, vTarget_fx_e[k] ) );
    1815      160626 :                     move32();
    1816      160626 :                     vec_diff_fx[k] = L_shr( vec_diff_fx[k], sub( max_vec_diff_e, vec_diff_e[k] ) );
    1817      160626 :                     move32();
    1818             :                 }
    1819             :                 /* A check if the angle between vTarget and node_data[ch].unit_vec is less than 1 degree.
    1820             :                  Essentially reveals if there is a speaker node too close "behind" the connection. Such
    1821             :                  connections should be rejected.*/
    1822      214168 :                 FOR( Word16 i = 0; i < 3; i++ )
    1823             :                 {
    1824      160626 :                     vTarget_fx[i] = L_shr( vTarget_fx[i], 2 ); // add guard bits
    1825      160626 :                     move32();
    1826      160626 :                     vec_diff_fx[i] = L_shr( vec_diff_fx[i], 2 );
    1827      160626 :                     move32();
    1828             :                 }
    1829       53542 :                 Word32 res = dotp_fixed( vTarget_fx, v2_fx, 3 ); // 31 - (max_vTarget_e + 2) + 30 - 31 = 28 - max_vTarget_e
    1830       53542 :                 move32();
    1831             : 
    1832       53542 :                 IF( GT_32( res, L_shr( 2147054208 /* 0.9998f in Q31 */, sub( 31, sub( 28, max_vTarget_e ) ) ) ) )
    1833             :                 {
    1834        2900 :                     return CONNECTION_WITH_SPEAKER_NODE_BEHIND;
    1835             :                 }
    1836             : 
    1837             :                 /* A special case, mainly accounting for ELEVATED L,R,C speaker nodes.
    1838             :                  A triplet between these nodes is not desired if there is a top node,
    1839             :                  a penalty is implemented to take care of this. */
    1840       50642 :                 Word32 vec_diff_dotp = dotp_fixed( vec_diff_fx, vec_diff_fx, 3 ); // exp :  2 * max_vec_diff_e + 4
    1841       50642 :                 move32();
    1842       50642 :                 Word32 var = Mpy_32_32( vec_diff_dotp, 51200 /*25.0f in Q11*/ ); // exp : 2 * max_vec_diff_e + 4 + 20
    1843       50642 :                 Word16 Flag1 = BASOP_Util_Cmp_Mant32Exp( v1v1_fx, Q31 - Q25, var, add( shl( max_vec_diff_e, 1 ), 4 + 20 ) );
    1844       50642 :                 IF( EQ_16( Flag1, 1 ) )
    1845             :                 {
    1846           0 :                     IF( LT_32( L_abs( L_sub( node_data[chB].unit_vec_fx[2], node_data[chA].unit_vec_fx[2] ) ), 1073742 /* 0.001f in Q30 */ ) )
    1847             :                     {
    1848           0 :                         return ELEVATED_PLANE_THIN_TRIANGLE_CONNECTION;
    1849             :                     }
    1850             :                 }
    1851             :             }
    1852             :         }
    1853             :     }
    1854             : 
    1855       24414 :     return REGULAR_CONNECTION;
    1856             : }
    1857             : 
    1858             : /*-------------------------------------------------------------------------*
    1859             :  * formulate_horizontal_connections()
    1860             :  *
    1861             :  * Formulate connections in the horizontal plane
    1862             :  *-------------------------------------------------------------------------*/
    1863             : 
    1864         896 : static void formulate_horizontal_connections_fx(
    1865             :     const VBAP_SPEAKER_NODE *speaker_node_data, /* i  : speaker node data         */
    1866             :     const Word16 num_speaker_nodes,             /* i  : number of speaker nodes   */
    1867             :     Word16 connections[][2],                    /* o  : vector of all connections */
    1868             :     Word16 *connection_write_index )
    1869             : {
    1870             :     Word16 ch;
    1871             :     Word16 chCheck;
    1872             :     Word16 next_index;
    1873             : 
    1874             :     Word32 min_arc_diff_fx;
    1875             :     Word32 arc_diff_fx;
    1876             :     Word16 Q_min_arc_diff;
    1877             : 
    1878        9986 :     FOR( ch = 0; ch < num_speaker_nodes; ch++ )
    1879             :     {
    1880             :         /* Find next horizontal speaker node */
    1881        9090 :         IF( EQ_16( speaker_node_data[ch].group, SPEAKER_NODE_HORIZONTAL ) )
    1882             :         {
    1883        5190 :             next_index = -1;
    1884        5190 :             move16();
    1885        5190 :             min_arc_diff_fx = 19998 /*9999.0f in Q1*/;
    1886        5190 :             move32();
    1887        5190 :             Q_min_arc_diff = 1;
    1888        5190 :             move16();
    1889             : 
    1890       58556 :             FOR( chCheck = 0; chCheck < num_speaker_nodes; chCheck++ )
    1891             :             {
    1892       53366 :                 test();
    1893       53366 :                 IF( ( NE_16( ch, chCheck ) ) && ( EQ_16( speaker_node_data[chCheck].group, SPEAKER_NODE_HORIZONTAL ) ) )
    1894             :                 {
    1895       25728 :                     arc_diff_fx = L_sub( speaker_node_data[chCheck].azi_deg_fx, speaker_node_data[ch].azi_deg_fx ); // Q22
    1896             : 
    1897       38592 :                     WHILE( arc_diff_fx < 0 )
    1898             :                     {
    1899       12864 :                         arc_diff_fx = L_add( arc_diff_fx, 1509949440 /*360.0f in Q22*/ );
    1900             :                     }
    1901       25728 :                     IF( LT_32( L_shr( arc_diff_fx, sub( 22, Q_min_arc_diff ) ), min_arc_diff_fx ) )
    1902             :                     {
    1903       11649 :                         min_arc_diff_fx = arc_diff_fx;
    1904       11649 :                         move32();
    1905       11649 :                         Q_min_arc_diff = 22;
    1906       11649 :                         move16();
    1907       11649 :                         next_index = chCheck;
    1908       11649 :                         move16();
    1909             :                     }
    1910             :                 }
    1911             :             }
    1912        5190 :             connections[*connection_write_index][0] = ch;
    1913        5190 :             move16();
    1914        5190 :             connections[*connection_write_index][1] = next_index;
    1915        5190 :             move16();
    1916        5190 :             *connection_write_index = add( *connection_write_index, 1 );
    1917        5190 :             move16();
    1918             :         }
    1919             :     }
    1920             : 
    1921         896 :     return;
    1922             : }
    1923             : 
    1924             : /*-------------------------------------------------------------------------*
    1925             :  * check_plane_crossing()
    1926             :  *
    1927             :  * Check crossing of non-allowed planes
    1928             :  *-------------------------------------------------------------------------*/
    1929             : 
    1930             : /*! r: truth value for crossing */
    1931       24414 : static Word16 check_plane_crossing_fx(
    1932             :     const Word32 ele1_deg_fx,                         /* i  : speaker node 1 elevation             Q22 */
    1933             :     const Word32 ele2_deg_fx,                         /* i  : speaker node 2 elevation             Q22 */
    1934             :     const Word16 num_non_crossing_planes,             /* i  : number of non-crossing planes            */
    1935             :     const Word32 *non_crossing_plane_elevation_deg_fx /* i  : vector non-crossing plane elevations Q14 */
    1936             : )
    1937             : {
    1938             :     /* Find if the connection crosses a non-crossing plane, with 1-degree threshold. */
    1939             :     Word16 plane;
    1940             : 
    1941       40510 :     FOR( plane = 0; plane < num_non_crossing_planes; plane++ )
    1942             :     {
    1943       16912 :         test();
    1944       16912 :         IF( ( GT_32( L_shr( ele1_deg_fx, 8 ), L_add( non_crossing_plane_elevation_deg_fx[plane], ONE_IN_Q14 ) ) ) && ( LT_32( L_shr( ele2_deg_fx, 8 ), L_sub( non_crossing_plane_elevation_deg_fx[plane], ONE_IN_Q14 ) ) ) )
    1945             :         {
    1946           8 :             return 1;
    1947             :         }
    1948       16904 :         test();
    1949       16904 :         IF( ( GT_32( L_shr( ele2_deg_fx, 8 ), L_add( non_crossing_plane_elevation_deg_fx[plane], ONE_IN_Q14 ) ) ) && ( LT_32( L_shr( ele1_deg_fx, 8 ), L_sub( non_crossing_plane_elevation_deg_fx[plane], ONE_IN_Q14 ) ) ) )
    1950             :         {
    1951         808 :             return 1;
    1952             :         }
    1953             :     }
    1954             : 
    1955       23598 :     return 0;
    1956             : }
    1957             : 
    1958             : /*-------------------------------------------------------------------------*
    1959             :  * get_half_sphere_connection_options()
    1960             :  *
    1961             :  * Get list of all potential connections at the half-sphere
    1962             :  *-------------------------------------------------------------------------*/
    1963        1792 : static ivas_error get_half_sphere_connection_options_fx(
    1964             :     const VBAP_SPEAKER_NODE *speaker_node_data,        /* i  : speaker node data                           */
    1965             :     const enum SpeakerNodeGroup group,                 /* i  : speaker node group                          */
    1966             :     const Word16 num_speaker_nodes,                    /* i  : number of speaker nodes                     */
    1967             :     const Word16 num_non_crossing_planes,              /* i  : number of non-crossing planes               */
    1968             :     const Word32 *non_crossing_plane_elevation_deg_fx, /* i  : vector of non-crossing plane elevations Q14 */
    1969             :     ConnectionOption **connection_options_pr,          /* o  : list of connection options                  */
    1970             :     Word16 *num_connection_options                     /* o  : number of connection options                */
    1971             : )
    1972             : {
    1973        1792 :     Word16 max_num_connection_options = 0;
    1974        1792 :     Word16 index = 0;
    1975             :     Word16 node, chA, chB, c, c_cmp;
    1976             :     ConnectionOption *c_options, *c_options_reorder;
    1977             : 
    1978        1792 :     move16();
    1979        1792 :     move16();
    1980             : 
    1981             :     /* Count max num connection options at the half sphere */
    1982       19972 :     FOR( node = 0; node < num_speaker_nodes; node++ )
    1983             :     {
    1984       18180 :         test();
    1985       18180 :         IF( EQ_16( speaker_node_data[node].group, group ) || EQ_16( speaker_node_data[node].group, SPEAKER_NODE_HORIZONTAL ) )
    1986             :         {
    1987       14280 :             max_num_connection_options = add( max_num_connection_options, index );
    1988       14280 :             index = add( index, 1 );
    1989             :         }
    1990             :     }
    1991             : 
    1992             :     /* Init memory for connection options */
    1993        1792 :     IF( ( c_options = (ConnectionOption *) malloc( sizeof( ConnectionOption ) * max_num_connection_options ) ) == NULL )
    1994             :     {
    1995           0 :         return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for VBAP data\n" ) );
    1996             :     }
    1997             : 
    1998       54834 :     FOR( c = 0; c < max_num_connection_options; c++ )
    1999             :     {
    2000       53042 :         c_options[c].chA = -1;
    2001       53042 :         move16();
    2002       53042 :         c_options[c].chB = -1;
    2003       53042 :         move16();
    2004       53042 :         c_options[c].arc_fx = MAX_32;
    2005       53042 :         move32();
    2006       53042 :         c_options[c].arc_weighted_fx = MAX_32;
    2007       53042 :         move32();
    2008             :     }
    2009             : 
    2010             :     /* Determine connection options for the half-sphere */
    2011        1792 :     index = 0;
    2012        1792 :     move16();
    2013       19972 :     FOR( chA = 0; chA < num_speaker_nodes; chA++ )
    2014             :     {
    2015             :         /* First loudspeaker at the connection is at the half sphere */
    2016       18180 :         test();
    2017       18180 :         IF( EQ_16( speaker_node_data[chA].group, group ) || EQ_16( speaker_node_data[chA].group, SPEAKER_NODE_HORIZONTAL ) )
    2018             :         {
    2019       92790 :             FOR( chB = chA + 1; chB < num_speaker_nodes; chB++ )
    2020             :             {
    2021             :                 /* Second loudspeaker at the connection is at the half sphere, but so that first and second are not both horizontal. */
    2022       78510 :                 test();
    2023       78510 :                 test();
    2024       78510 :                 IF( ( EQ_16( speaker_node_data[chB].group, group ) ) || ( EQ_16( speaker_node_data[chB].group, SPEAKER_NODE_HORIZONTAL ) && EQ_16( speaker_node_data[chA].group, group ) ) )
    2025             :                 {
    2026       27314 :                     Word16 ConnectionClass = determine_connection_class_fx( speaker_node_data, num_speaker_nodes, group, chA, chB );
    2027             :                     Word32 unit_vec_dotp, unit_vec_dotp_sq, one_minus_unit_vec_dotp_sq, one_minus_unit_vec_dotp_sq_root;
    2028             :                     Word16 acos_val;
    2029             :                     /* Connection is considered only if there is no speaker node behind it */
    2030       27314 :                     IF( NE_16( ConnectionClass, CONNECTION_WITH_SPEAKER_NODE_BEHIND ) )
    2031             :                     {
    2032             :                         /* Store connection information */
    2033       24414 :                         c_options[index].chA = chA;
    2034       24414 :                         move16();
    2035       24414 :                         c_options[index].chB = chB;
    2036       24414 :                         move16();
    2037             : 
    2038       24414 :                         unit_vec_dotp = dotp_fixed( speaker_node_data[chA].unit_vec_fx, speaker_node_data[chB].unit_vec_fx, 3 ); // Q29
    2039       24414 :                         unit_vec_dotp_sq = Mpy_32_32( unit_vec_dotp, unit_vec_dotp );                                            // Q27
    2040       24414 :                         one_minus_unit_vec_dotp_sq = L_sub( ONE_IN_Q27, unit_vec_dotp_sq );
    2041       24414 :                         Word16 exp_uv = Q31 - Q27;
    2042       24414 :                         move16();
    2043       24414 :                         one_minus_unit_vec_dotp_sq_root = Sqrt32( one_minus_unit_vec_dotp_sq, &exp_uv );
    2044       24414 :                         acos_val = BASOP_util_atan2( one_minus_unit_vec_dotp_sq_root, unit_vec_dotp, sub( exp_uv, 2 ) ); // Q13
    2045       24414 :                         c_options[index].arc_fx = L_deposit_h( acos_val );                                               // Q29
    2046       24414 :                         move32();
    2047       24414 :                         c_options[index].arc_weighted_fx = c_options[index].arc_fx; // Q29
    2048       24414 :                         move32();
    2049             : 
    2050             :                         /* A special case, mainly accounting for ELEVATED L,R,C speaker nodes.
    2051             :                         A triplet between these nodes is not desired if there is a top node,
    2052             :                         a penalty is implemented to take care of this. */
    2053       24414 :                         IF( EQ_16( ConnectionClass, ELEVATED_PLANE_THIN_TRIANGLE_CONNECTION ) )
    2054             :                         {
    2055           0 :                             c_options[index].arc_weighted_fx = L_shl( c_options[index].arc_weighted_fx, 1 );
    2056           0 :                             move32();
    2057             :                         }
    2058             : 
    2059             :                         /* If the connection passes a pre-determined plane of speaker nodes, then add further penalty */
    2060             : 
    2061       24414 :                         IF( check_plane_crossing_fx( speaker_node_data[chA].ele_deg_fx, speaker_node_data[chB].ele_deg_fx, num_non_crossing_planes, non_crossing_plane_elevation_deg_fx ) )
    2062             :                         {
    2063         816 :                             c_options[index].arc_weighted_fx = L_shl( c_options[index].arc_weighted_fx, 1 );
    2064         816 :                             move32();
    2065             :                         }
    2066       24414 :                         index = add( index, 1 );
    2067             :                     }
    2068             :                 }
    2069             :             }
    2070             :         }
    2071             :     }
    2072             :     /* Number of found connection options at the half sphere */
    2073        1792 :     *num_connection_options = index;
    2074        1792 :     move16();
    2075             :     /* Init memory for reordered connection options and order by arc_weighted,
    2076             :      * which informs of the preference order of the connections in case they cross */
    2077        1792 :     IF( ( c_options_reorder = (ConnectionOption *) malloc( sizeof( ConnectionOption ) * ( *num_connection_options ) ) ) == NULL )
    2078             :     {
    2079           0 :         return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for VBAP data\n" ) );
    2080             :     }
    2081             : 
    2082       26206 :     FOR( c = 0; c < *num_connection_options; c++ )
    2083             :     {
    2084             :         Word16 min_arc_index;
    2085       24414 :         min_arc_index = -1;
    2086       24414 :         move16();
    2087       24414 :         Word32 min_arc_weighted_fx_new = MAX_32;
    2088       24414 :         move16();
    2089       24414 :         Word16 exp_min_arc_weighted_fx = 31;
    2090       24414 :         move16();
    2091       24414 :         Word16 res = 0;
    2092       24414 :         move16();
    2093      603704 :         FOR( c_cmp = 0; c_cmp < *num_connection_options; c_cmp++ )
    2094             :         {
    2095      579290 :             res = BASOP_Util_Cmp_Mant32Exp( c_options[c_cmp].arc_weighted_fx, 31 - Q29, min_arc_weighted_fx_new, exp_min_arc_weighted_fx );
    2096      579290 :             test();
    2097      579290 :             IF( EQ_16( res, -1 ) || res == 0 )
    2098             :             {
    2099      126474 :                 min_arc_weighted_fx_new = c_options[c_cmp].arc_weighted_fx;
    2100      126474 :                 move32();
    2101      126474 :                 exp_min_arc_weighted_fx = Q31 - Q29;
    2102      126474 :                 move16();
    2103      126474 :                 min_arc_index = c_cmp;
    2104      126474 :                 move16();
    2105             :             }
    2106             :         }
    2107       24414 :         c_options_reorder[c].chA = c_options[min_arc_index].chA;
    2108       24414 :         move16();
    2109       24414 :         c_options_reorder[c].chB = c_options[min_arc_index].chB;
    2110       24414 :         move16();
    2111             : 
    2112       24414 :         c_options_reorder[c].arc_fx = c_options[min_arc_index].arc_fx;
    2113       24414 :         move32();
    2114       24414 :         c_options_reorder[c].arc_weighted_fx = c_options[min_arc_index].arc_weighted_fx;
    2115       24414 :         move32();
    2116       24414 :         c_options[min_arc_index].arc_weighted_fx = MAX_32;
    2117       24414 :         move32();
    2118             :     }
    2119             : 
    2120             :     /* Set reordered connections as output and free temporary data */
    2121        1792 :     *connection_options_pr = c_options_reorder;
    2122        1792 :     free( c_options );
    2123             : 
    2124        1792 :     return IVAS_ERR_OK;
    2125             : }
    2126             : 
    2127             : /*-------------------------------------------------------------------------*
    2128             :  * formulate_half_sphere_connections()
    2129             :  *
    2130             :  * Formulate half-sphere connections
    2131             :  *-------------------------------------------------------------------------*/
    2132        1792 : static ivas_error formulate_half_sphere_connections_fx(
    2133             :     const VBAP_SPEAKER_NODE *speaker_node_data, /* i  : speaker node data                       */
    2134             :     const Word16 num_speaker_nodes,             /* i  : number of speaker nodes                 */
    2135             :     const enum SpeakerNodeGroup group,          /* i  : speaker node group                      */
    2136             :     Word16 connections[][2],                    /* o  : vector of connections                   */
    2137             :     Word16 *connection_write_index,
    2138             :     const Word16 max_num_connections,                 /* i  : max number of connections                  */
    2139             :     const Word16 num_non_crossing_planes,             /* i  : number of non-crossing planes              */
    2140             :     const Word32 *non_crossing_plane_elevation_deg_fx /* i  : vector of non-crossing plane elevations Q14*/
    2141             : )
    2142             : {
    2143             :     /* Variable initializations */
    2144             :     Word16 c, chA, chB, cmp_chA, cmp_chB, k, c_opt;
    2145             :     Word16 new_connection_is_valid;
    2146             :     Word16 within_first_arc;
    2147             :     Word32 new_cross_fx[3];
    2148             :     Word32 planeCrossingVec_fx[3];
    2149             :     Word16 Q_planeCrossingVec;
    2150             :     Word32 new_arc_fx;                                                     /* Q29 */
    2151             :     Word32 connection_arc_fx[( VBAP_MAX_NUM_SPEAKER_NODES - 2 ) * 3];      /* Q29 */
    2152             :     Word32 connection_cross_fx[( VBAP_MAX_NUM_SPEAKER_NODES - 2 ) * 3][3]; /* Q29 */
    2153             :     Word32 tmpFloat_fx;
    2154             :     Word32 cmp_arc_fx; /* Q29 */
    2155             :     Word32 normVal_fx;
    2156             :     Word16 angleCmp_fx;
    2157             :     ConnectionOption *connection_options;
    2158             :     Word16 num_connection_options;
    2159             :     Word16 half_sphere_first_connection;
    2160             :     ivas_error error;
    2161             : 
    2162        1792 :     half_sphere_first_connection = *connection_write_index;
    2163        1792 :     move16();
    2164             : 
    2165             :     /* Obtain all connection options (i.e., channel pairs) at the half sphere. The function orders them
    2166             :      * in terms of which connection to keep if two connections would cross each other. */
    2167        1792 :     IF( NE_32( ( error = get_half_sphere_connection_options_fx(
    2168             :                      speaker_node_data,
    2169             :                      group,
    2170             :                      num_speaker_nodes,
    2171             :                      num_non_crossing_planes,
    2172             :                      non_crossing_plane_elevation_deg_fx,
    2173             :                      &connection_options,
    2174             :                      &num_connection_options ) ),
    2175             :                IVAS_ERR_OK ) )
    2176             :     {
    2177           0 :         return error;
    2178             :     }
    2179             : 
    2180        1792 :     set32_fx( connection_arc_fx, 0, max_num_connections );
    2181       45580 :     FOR( c = 0; c < max_num_connections; c++ )
    2182             :     {
    2183       43788 :         set32_fx( connection_cross_fx[c], 0, 3 );
    2184             :     }
    2185             : 
    2186             :     /* The following loop goes through all reasonable chA - chB pairs for the half-sphere */
    2187        1792 :     c_opt = 0;
    2188        1792 :     move16();
    2189       21748 :     WHILE( c_opt < num_connection_options && *connection_write_index < max_num_connections )
    2190             :     {
    2191       19956 :         test();
    2192       19956 :         chA = connection_options[c_opt].chA;
    2193       19956 :         move16();
    2194       19956 :         chB = connection_options[c_opt].chB;
    2195       19956 :         move16();
    2196       19956 :         new_arc_fx = connection_options[c_opt].arc_fx;
    2197       19956 :         move32();
    2198             : 
    2199             :         /* Cross-product is needed for later stages */
    2200       19956 :         vbap_crossp_fx( speaker_node_data[chA].unit_vec_fx, speaker_node_data[chB].unit_vec_fx, new_cross_fx );
    2201             : 
    2202             :         /* Determine if new connection between chA and chB is valid */
    2203       19956 :         new_connection_is_valid = 1;
    2204       19956 :         move16();
    2205       19956 :         c = half_sphere_first_connection;
    2206       19956 :         move16();
    2207             :         Word32 var1, var2, var1_sq, var2_sq, var1_sqrt, var2_sqrt, one_minus_var1_sq, one_minus_var2_sq, var_a, var_b, var_c;
    2208       19956 :         Word16 var1_cos, var2_cos, final_exp, final_exp_A, final_exp_B, exp_var1_sq, exp_var2_sq, comp1, comp2, sub_exp = 0, sub_exp_2 = 0, sub_final_exp = 0;
    2209             : 
    2210       19956 :         test();
    2211      135329 :         WHILE( ( LT_16( c, *connection_write_index ) ) && new_connection_is_valid )
    2212             :         {
    2213      115373 :             cmp_chA = connections[c][0];
    2214      115373 :             move16();
    2215      115373 :             cmp_chB = connections[c][1];
    2216      115373 :             move16();
    2217             :             /* The connections are compared only if they don't involve same speaker nodes */
    2218      115373 :             test();
    2219      115373 :             test();
    2220      115373 :             test();
    2221      115373 :             IF( ( NE_16( cmp_chA, chA ) ) && ( NE_16( cmp_chA, chB ) ) && ( NE_16( cmp_chB, chA ) ) && ( NE_16( cmp_chB, chB ) ) )
    2222             :             {
    2223             :                 /* The following lines determine if the connection chA-chB crosses with the connection cmp_chA-cmp_chB.*/
    2224             :                 /* The connections, i.e., node-pairs determine a plane. The crossing can be determined by
    2225             :                  * studying the intersection of these planes. */
    2226       57124 :                 vbap_crossp_fx( connection_cross_fx[c], new_cross_fx, planeCrossingVec_fx );
    2227             : 
    2228       57124 :                 tmpFloat_fx = 0;
    2229       57124 :                 move16();
    2230             : 
    2231       57124 :                 cmp_arc_fx = connection_arc_fx[c];
    2232       57124 :                 move16();
    2233      228496 :                 FOR( k = 0; k < 3; k++ )
    2234             :                 {
    2235      171372 :                     tmpFloat_fx = L_add( tmpFloat_fx, Mpy_32_32( planeCrossingVec_fx[k], planeCrossingVec_fx[k] ) );
    2236             :                 }
    2237             : 
    2238       57124 :                 Word16 tmp_exp = sub( 31, Q23 );
    2239       57124 :                 if ( tmpFloat_fx == 0 )
    2240             :                 {
    2241         400 :                     tmpFloat_fx = 1;
    2242         400 :                     move16();
    2243             :                 }
    2244       57124 :                 normVal_fx = ISqrt32( tmpFloat_fx, &tmp_exp );
    2245             : 
    2246      228496 :                 FOR( k = 0; k < 3; k++ )
    2247             :                 {
    2248      171372 :                     planeCrossingVec_fx[k] = Mpy_32_32( planeCrossingVec_fx[k], normVal_fx ); // Q27 + Q31 - tmp_exp - Q31
    2249      171372 :                     move32();
    2250             :                 }
    2251             :                 /*update Q for planeCrossingVec */
    2252       57124 :                 Q_planeCrossingVec = sub( sub( Q27 + Q31, tmp_exp ), Q31 );
    2253             :                 /* If the plane intersection is between both connections, then the two connections cross. */
    2254             :                 /* Study first if the crossing is between arc chA-chB */
    2255             : 
    2256       57124 :                 var1 = dotp_fixed( planeCrossingVec_fx, speaker_node_data[chA].unit_vec_fx, 3 ); // Q24 = 26 - tmp_exp
    2257       57124 :                 var2 = dotp_fixed( planeCrossingVec_fx, speaker_node_data[chB].unit_vec_fx, 3 ); // Q24 = 26 - tmp_exp
    2258             : 
    2259       57124 :                 var1_sq = Mpy_32_32( var1, var1 ); //(2 * (Q_planeCrossingVec - Q1) ) - Q31
    2260       57124 :                 var2_sq = Mpy_32_32( var2, var2 );
    2261       57124 :                 exp_var1_sq = shl( sub( Q31, sub( Q_planeCrossingVec, Q1 ) ), 1 );
    2262       57124 :                 exp_var2_sq = shl( sub( Q31, sub( Q_planeCrossingVec, Q1 ) ), 1 );
    2263       57124 :                 IF( GT_32( L_abs( var1_sq ), L_shr( ONE_IN_Q31, exp_var1_sq ) ) )
    2264             :                 {
    2265           2 :                     var1_sq = ONE_IN_Q31;
    2266           2 :                     move16();
    2267           2 :                     exp_var1_sq = 0;
    2268           2 :                     move16();
    2269             :                 }
    2270       57124 :                 IF( GT_32( L_abs( var2_sq ), L_shr( ONE_IN_Q31, exp_var2_sq ) ) )
    2271             :                 {
    2272           0 :                     var2_sq = ONE_IN_Q31;
    2273           0 :                     move16();
    2274           0 :                     exp_var2_sq = 0;
    2275           0 :                     move16();
    2276             :                 }
    2277       57124 :                 one_minus_var1_sq = BASOP_Util_Add_Mant32Exp( ONE_IN_Q30, 1, L_negate( var1_sq ), exp_var1_sq, &final_exp_A );
    2278       57124 :                 var1_sqrt = Sqrt32( one_minus_var1_sq, &final_exp_A );
    2279       57124 :                 var1_cos = BASOP_util_atan2( var1_sqrt, var1, sub( final_exp_A, ( Q31 - ( Q_planeCrossingVec + Q30 - Q31 ) ) ) ); // Q13
    2280       57124 :                 angleCmp_fx = var1_cos;
    2281       57124 :                 move16();
    2282             : 
    2283       57124 :                 one_minus_var2_sq = BASOP_Util_Add_Mant32Exp( ONE_IN_Q30, 1, L_negate( var2_sq ), exp_var2_sq, &final_exp_B );
    2284       57124 :                 var2_sqrt = Sqrt32( one_minus_var2_sq, &final_exp_B );
    2285       57124 :                 var2_cos = BASOP_util_atan2( var2_sqrt, var2, sub( final_exp_B, ( Q31 - ( Q_planeCrossingVec + Q30 - Q31 ) ) ) ); // Q13
    2286             : 
    2287       57124 :                 final_exp = BASOP_Util_Add_MantExp( angleCmp_fx, Q15 - Q13, var2_cos, Q15 - Q13, &angleCmp_fx );
    2288             : 
    2289       57124 :                 sub_exp = 0, sub_exp_2 = 0, sub_final_exp = 0;
    2290       57124 :                 var_a = BASOP_Util_Add_Mant32Exp( new_arc_fx, Q31 - Q29, L_negate( L_deposit_h( angleCmp_fx ) ), final_exp, &sub_exp );
    2291       57124 :                 comp1 = BASOP_Util_Cmp_Mant32Exp( L_abs( var_a ), sub_exp, 21474836 /* 0.01f in Q31 */, 0 );
    2292       57124 :                 var_b = BASOP_Util_Add_Mant32Exp( 25735, Q31 - Q12, L_negate( L_deposit_h( angleCmp_fx ) ), final_exp, &sub_exp_2 );
    2293       57124 :                 var_c = BASOP_Util_Add_Mant32Exp( new_arc_fx, Q31 - Q29, L_negate( var_b ), sub_exp_2, &sub_final_exp );
    2294       57124 :                 comp2 = BASOP_Util_Cmp_Mant32Exp( L_abs( var_c ), sub_final_exp, 21474836 /* 0.01f in Q31 */, 0 );
    2295             : 
    2296       57124 :                 within_first_arc = 0;
    2297       57124 :                 move16();
    2298       57124 :                 IF( EQ_16( comp1, -1 ) )
    2299             :                 {
    2300       13120 :                     within_first_arc = 1;
    2301       13120 :                     move16();
    2302             :                 }
    2303       44004 :                 ELSE IF( EQ_16( comp2, -1 ) )
    2304             :                 {
    2305       14144 :                     within_first_arc = 1;
    2306       14144 :                     move16();
    2307             :                     /* In this case, the plane crossing vector is inverted. The inverse is another
    2308             :                      * plane-crossing vector, and detected to be between chA-chB connection.*/
    2309       56576 :                     FOR( k = 0; k < 3; k++ )
    2310             :                     {
    2311       42432 :                         planeCrossingVec_fx[k] = Mpy_32_32( planeCrossingVec_fx[k], -134217728 /*-1.0f in Q27*/ ); // 27 + 27 - tmp_exp - 31 = 23 - tmp_exp
    2312       42432 :                         move32();
    2313             :                     }
    2314             :                     /*update Q for planeCrossingVec */
    2315       14144 :                     Q_planeCrossingVec = sub( sub( Q27 + Q27, tmp_exp ), Q31 );
    2316             :                 }
    2317             : 
    2318             :                 /* Study if the crossing is also between arc cmp_chA-cmp_chB */
    2319       57124 :                 IF( within_first_arc > 0 )
    2320             :                 {
    2321       27264 :                     var1 = dotp_fixed( planeCrossingVec_fx, speaker_node_data[cmp_chA].unit_vec_fx, 3 );
    2322       27264 :                     move32();
    2323       27264 :                     var2 = dotp_fixed( planeCrossingVec_fx, speaker_node_data[cmp_chB].unit_vec_fx, 3 );
    2324       27264 :                     move32();
    2325             : 
    2326             :                     // final_exp_A, final_exp_B, exp_var1_sq, exp_var2_sq;
    2327       27264 :                     var1_sq = Mpy_32_32( var1, var1 ); //(2 * (Q_planeCrossingVec - Q1) ) - Q31
    2328       27264 :                     var2_sq = Mpy_32_32( var2, var2 );
    2329       27264 :                     exp_var1_sq = shl( ( sub( Q31, sub( Q_planeCrossingVec, Q1 ) ) ), 1 );
    2330       27264 :                     exp_var2_sq = shl( ( sub( Q31, sub( Q_planeCrossingVec, Q1 ) ) ), 1 );
    2331       27264 :                     IF( GT_32( L_abs( var1_sq ), L_shr( ONE_IN_Q31, exp_var1_sq ) ) )
    2332             :                     {
    2333           2 :                         var1_sq = ONE_IN_Q31;
    2334           2 :                         move16();
    2335           2 :                         exp_var1_sq = 0;
    2336           2 :                         move16();
    2337             :                     }
    2338       27264 :                     IF( GT_32( L_abs( var2_sq ), L_shr( ONE_IN_Q31, exp_var2_sq ) ) )
    2339             :                     {
    2340           0 :                         var2_sq = ONE_IN_Q31;
    2341           0 :                         move16();
    2342           0 :                         exp_var2_sq = 0;
    2343           0 :                         move16();
    2344             :                     }
    2345       27264 :                     one_minus_var1_sq = BASOP_Util_Add_Mant32Exp( ONE_IN_Q30, 1, L_negate( var1_sq ), exp_var1_sq, &final_exp_A );
    2346       27264 :                     var1_sqrt = Sqrt32( one_minus_var1_sq, &final_exp_A );
    2347       27264 :                     var1_cos = BASOP_util_atan2( var1_sqrt, var1, sub( final_exp_A, sub( Q31, add( Q_planeCrossingVec, Q30 - Q31 ) ) ) ); // Q13
    2348       27264 :                     angleCmp_fx = var1_cos;
    2349       27264 :                     move16();
    2350             : 
    2351       27264 :                     one_minus_var2_sq = BASOP_Util_Add_Mant32Exp( ONE_IN_Q30, 1, L_negate( var2_sq ), exp_var2_sq, &final_exp_B );
    2352       27264 :                     var2_sqrt = Sqrt32( one_minus_var2_sq, &final_exp_B );
    2353       27264 :                     var2_cos = BASOP_util_atan2( var2_sqrt, var2, sub( final_exp_B, sub( Q31, add( Q_planeCrossingVec, Q30 - Q31 ) ) ) ); // Q13
    2354             : 
    2355       27264 :                     final_exp = BASOP_Util_Add_MantExp( angleCmp_fx, Q15 - Q13, var2_cos, Q15 - Q13, &angleCmp_fx );
    2356             : 
    2357       27264 :                     sub_exp = 0, sub_exp_2 = 0, sub_final_exp = 0;
    2358       27264 :                     move16();
    2359       27264 :                     move16();
    2360       27264 :                     move16();
    2361       27264 :                     var_a = BASOP_Util_Add_Mant32Exp( cmp_arc_fx, Q31 - Q29, L_negate( L_deposit_h( angleCmp_fx ) ), final_exp, &sub_exp );
    2362       27264 :                     comp1 = BASOP_Util_Cmp_Mant32Exp( L_abs( var_a ), sub_exp, 21474836 /* 0.01f in Q31 */, 0 );
    2363             : 
    2364       27264 :                     if ( EQ_16( comp1, -1 ) )
    2365             :                     {
    2366             :                         /* A crossing is detected. The new connection is not valid, because
    2367             :                          * the connections were ordered in order of preference (arc_weighted) */
    2368        3252 :                         new_connection_is_valid = 0;
    2369        3252 :                         move16();
    2370             :                     }
    2371             :                 }
    2372             :             }
    2373      115373 :             c = add( c, 1 );
    2374             :         }
    2375             : 
    2376             :         /* Store the new connection which has been confirmed valid */
    2377       19956 :         IF( new_connection_is_valid > 0 )
    2378             :         {
    2379       16704 :             connections[*connection_write_index][0] = chA;
    2380       16704 :             move16();
    2381       16704 :             connections[*connection_write_index][1] = chB;
    2382       16704 :             move16();
    2383       16704 :             connection_arc_fx[*connection_write_index] = new_arc_fx; /* Q29 */
    2384       16704 :             move16();
    2385       16704 :             connection_cross_fx[*connection_write_index][0] = new_cross_fx[0]; /* Q29 */
    2386       16704 :             move16();
    2387       16704 :             connection_cross_fx[*connection_write_index][1] = new_cross_fx[1]; /* Q29 */
    2388       16704 :             move16();
    2389       16704 :             connection_cross_fx[*connection_write_index][2] = new_cross_fx[2]; /* Q29 */
    2390       16704 :             move16();
    2391       16704 :             *connection_write_index = add( *connection_write_index, 1 );
    2392             :         }
    2393       19956 :         c_opt = add( c_opt, 1 );
    2394             :     }
    2395             : 
    2396        1792 :     free( connection_options );
    2397             : 
    2398        1792 :     return IVAS_ERR_OK;
    2399             : }
    2400             : 
    2401             : /*-------------------------------------------------------------------------*
    2402             :  * determine_non_crossing_planes()
    2403             :  *
    2404             :  * Determine non-crossing planes
    2405             :  *-------------------------------------------------------------------------*/
    2406             : 
    2407             : /*! r: number of non-crossing planes */
    2408             : 
    2409         896 : static Word16 determine_non_crossing_planes_fx(
    2410             :     const Word16 num_speaker_nodes,             /* i  : number of speaker nodes                     */
    2411             :     const VBAP_SPEAKER_NODE *node_data,         /* i  : speaker node data                           */
    2412             :     Word32 *non_crossing_plane_elevation_deg_fx /* o  : vector of non-crossing plane elevations Q14 */
    2413             : )
    2414             : {
    2415             :     Word32 next_ele_check_fx; /* Q14 */
    2416             :     Word32 ele_check_fx;      /* Q14 */
    2417             :     Word32 max_gap_fx;        /* Q14 */
    2418             :     Word32 gap_to_next_ls_fx; /* Q14 */
    2419             : 
    2420             :     Word16 ch, ch_cmp;
    2421             :     Word16 num_planes;
    2422             : 
    2423         896 :     ele_check_fx = -16382362; /*Q14*/
    2424         896 :     move32();
    2425         896 :     num_planes = 0;
    2426         896 :     move16();
    2427             : 
    2428             :     /* For each plane, check if a non-crossing plane should be determined */
    2429        3316 :     WHILE( LT_32( ele_check_fx, 1474560 ) /*90.0f Q14*/ )
    2430             :     {
    2431        2420 :         next_ele_check_fx = 16382362; /*Q14*/
    2432        2420 :         move32();
    2433             : 
    2434             :         Word32 tmp1, tmp2;
    2435             : 
    2436             :         /* Find next node elevation that is not in horizontal plane */
    2437       27494 :         FOR( ch = 0; ch < num_speaker_nodes; ch++ )
    2438             :         {
    2439       25074 :             tmp1 = L_add( ele_check_fx, 16 /*VBAP_EPSILON in Q14*/ );
    2440       25074 :             tmp2 = L_sub( next_ele_check_fx, 16 /*VBAP_EPSILON in Q14*/ );
    2441       25074 :             test();
    2442       25074 :             test();
    2443       25074 :             IF( NE_32( node_data[ch].group, SPEAKER_NODE_HORIZONTAL ) && GT_32( L_shr( node_data[ch].ele_deg_fx, 8 ), tmp1 ) && LT_32( L_shr( node_data[ch].ele_deg_fx, 8 ), tmp2 ) )
    2444             :             {
    2445        3049 :                 next_ele_check_fx = L_shr( node_data[ch].ele_deg_fx, 8 ); // shift due to comparision with 90.0f
    2446             :             }
    2447             :         }
    2448        2420 :         ele_check_fx = next_ele_check_fx; // Q14
    2449        2420 :         move32();
    2450             : 
    2451        2420 :         IF( GT_32( ele_check_fx, 1474560 ) /*90.0f in Q14*/ )
    2452             :         {
    2453             :             /* When no next node elevation found, break loop */
    2454           0 :             BREAK;
    2455             :         }
    2456             : 
    2457        2420 :         max_gap_fx = -163838368; // Q14
    2458        2420 :         move32();
    2459       27494 :         FOR( ch = 0; ch < num_speaker_nodes; ch++ )
    2460             :         {
    2461             :             /* Find gap to the next speaker node at the same plane */
    2462       25074 :             IF( LT_32( L_abs( L_sub( L_shr( node_data[ch].ele_deg_fx, 8 ), ele_check_fx ) ), 16 /*0.001f in Q14*/ ) )
    2463             :             {
    2464        3900 :                 gap_to_next_ls_fx = 1638398336; // Q14
    2465        3900 :                 move32();
    2466       46020 :                 FOR( ch_cmp = 0; ch_cmp < num_speaker_nodes; ch_cmp++ )
    2467             :                 {
    2468       42120 :                     test();
    2469       42120 :                     IF( NE_16( ch_cmp, ch ) && LT_32( L_abs( L_sub( L_shr( node_data[ch_cmp].ele_deg_fx, 8 ), ele_check_fx ) ), 16 /*0.001f in Q14*/ ) )
    2470             :                     {
    2471        5516 :                         Word32 gap_fx = L_sub( node_data[ch_cmp].azi_deg_fx, node_data[ch].azi_deg_fx );
    2472        8274 :                         WHILE( gap_fx < 0 )
    2473             :                         {
    2474        2758 :                             gap_fx = L_add( gap_fx, 1509949440 /*360.0f in Q22*/ );
    2475             :                         }
    2476        5516 :                         IF( LT_32( L_shr( gap_fx, 8 ), gap_to_next_ls_fx ) )
    2477             :                         {
    2478        3812 :                             gap_to_next_ls_fx = L_shr( gap_fx, 8 );
    2479             :                         }
    2480             :                     }
    2481             :                 }
    2482             :                 /* Find maximum gap on that plane */
    2483        3900 :                 IF( GT_32( gap_to_next_ls_fx, max_gap_fx ) )
    2484             :                 {
    2485        2648 :                     max_gap_fx = gap_to_next_ls_fx;
    2486        2648 :                     move32();
    2487             :                 }
    2488             :             }
    2489             :         }
    2490             : 
    2491             :         /* If maximum gap is small enough, then a non-crossing plane is detected */
    2492        2420 :         test();
    2493        2420 :         IF( LT_32( max_gap_fx, 2293776 /*Q14*/ ) && max_gap_fx > 0 )
    2494             :         {
    2495         426 :             non_crossing_plane_elevation_deg_fx[num_planes] = ele_check_fx; /* Q14 */
    2496         426 :             move32();
    2497         426 :             num_planes = add( num_planes, 1 );
    2498         426 :             IF( EQ_16( num_planes, VBAP_MAX_PLANES ) )
    2499             :             {
    2500             :                 /* Memory init limit. Does not happen with any real speaker node configuration.
    2501             :                  Triangulation succeeds even if number of non_crossing_planes are limited. */
    2502           0 :                 BREAK;
    2503             :             }
    2504             :         }
    2505             :     }
    2506             : 
    2507         896 :     return num_planes;
    2508             : }
    2509             : 
    2510             : /*-------------------------------------------------------------------------*
    2511             :  * reorder_triplets()
    2512             :  *
    2513             :  * Reorder virtual surface triplets into provided target order.
    2514             :  *-------------------------------------------------------------------------*/
    2515             : 
    2516        1792 : static void reorder_triplets_fx(
    2517             :     VBAP_VS_TRIPLET *triplets,  /* i/o: VS triplets to be reordered  */
    2518             :     const Word16 *target_order, /* i  : Target order for VS triplets */
    2519             :     const Word16 num_triplets   /* i  : Number of VS triplets        */
    2520             : )
    2521             : {
    2522             :     VBAP_VS_TRIPLET tempTriplets[VBAP_MAX_NUM_TRIPLETS];
    2523             :     Word16 c;
    2524             : 
    2525             :     /* First copy to temp array */
    2526       16388 :     FOR( c = 0; c < num_triplets; c++ )
    2527             :     {
    2528       14596 :         tempTriplets[c] = triplets[c];
    2529             :     }
    2530             : 
    2531             :     /* Then move back in sorted order */
    2532       16388 :     FOR( c = 0; c < num_triplets; c++ )
    2533             :     {
    2534       14596 :         triplets[c] = tempTriplets[target_order[c]];
    2535             :     }
    2536             : 
    2537        1792 :     return;
    2538             : }

Generated by: LCOV version 1.14