diff --git a/apps/renderer.c b/apps/renderer.c index a2043e405d79d3151b6ec3e4d3b163c54fbe88ce..ea6e69ba358a660d35919a227f0b1aedbc8a7383 100644 --- a/apps/renderer.c +++ b/apps/renderer.c @@ -1559,6 +1559,9 @@ int main( free( inFloatBuffer ); free( outInt16Buffer ); free( outFloatBuffer ); +#ifdef IVAS_FLOAT_FIXED + free( outInt32Buffer ); +#endif for ( i = 0; i < RENDERER_MAX_MASA_INPUTS; ++i ) { MasaFileReader_close( &masaReaders[i] ); diff --git a/lib_rend/ivas_limiter.c b/lib_rend/ivas_limiter.c index ae7e3afb835593f8797e9a7679fd780982ef9dd8..5d540c7d3db6a5adf0cb96a2c7ec4ac85ebcc746 100644 --- a/lib_rend/ivas_limiter.c +++ b/lib_rend/ivas_limiter.c @@ -37,7 +37,12 @@ #include "ivas_prot_rend.h" #include "wmc_auto.h" #include - +#ifdef IVAS_FLOAT_FIXED +#define ATTACK_CNST_48k (2106670080) //Q31 +#define ATTACK_CNST_32k ( 2086555136 ) // Q31 +#define ATTACK_CNST_16k ( 2027355264 ) // Q31 +#define ATTACK_CNST_8k ( 1913946752 ) // Q31 +#endif /*-------------------------------------------------------------------* * detect_strong_saturations() @@ -93,7 +98,65 @@ static int16_t detect_strong_saturations( return apply_strong_limiting; } +#ifdef IVAS_FLOAT_FIXED +/*-------------------------------------------------------------------* + * detect_strong_saturations() + * + * Detection of very strong saturations, + * usually happens as a consequence of a heavy corrupted bitstream + *-------------------------------------------------------------------*/ + +/*! r: apply_strong_limiting flag */ +static Word16 detect_strong_saturations_fx( + const Word16 BER_detect, /* i : BER detect flag */ + Word16 *strong_saturation_cnt, /* i/o: counter of strong saturations */ + const Word32 max_val, /* i : maximum absolute value */ + Word16 *frame_gain, /* i/o: frame gain value */ + Word16 q_factor /*Q factor of the output samples*/ +) +{ + Word16 apply_strong_limiting; + Word32 compare_max_value_Mul_3, compare_max_value_Mul_10; + + apply_strong_limiting = 0; + compare_max_value_Mul_3 = L_lshl(98187,q_factor); // 3 * IVAS_LIMITER_THRESHOLD + compare_max_value_Mul_10 = L_lshl( 327290, q_factor );//10 * IVAS_LIMITER_THRESHOLD + IF ( BER_detect ) + { + *strong_saturation_cnt = 50; + apply_strong_limiting = 1; + } + ELSE IF( GT_32( max_val , compare_max_value_Mul_3) && GT_16(*strong_saturation_cnt , 0 )) + { + apply_strong_limiting = 1; + } + ELSE IF( GT_32 (max_val , compare_max_value_Mul_10) ) + { + *strong_saturation_cnt += 20; + *strong_saturation_cnt = min( *strong_saturation_cnt, 50 ); + apply_strong_limiting = 1; + } + else + { + ( *strong_saturation_cnt )--; + *strong_saturation_cnt = max( *strong_saturation_cnt, 0 ); + } + + if ( apply_strong_limiting ) + { + IF( LT_16 (* frame_gain , 4915) ) // Q14 of 0.3 is compared + { + //*frame_gain /= 3.0f; + *frame_gain = lshr(divide1616( *frame_gain, 24576 ) , 2);//Q14 + } + ELSE + { + apply_strong_limiting = 0; + } + } + return apply_strong_limiting; +} /*-------------------------------------------------------------------* * ivas_limiter_open() * @@ -109,7 +172,7 @@ ivas_error ivas_limiter_open( { int16_t i; IVAS_LIMITER_HANDLE hLimiter; - + Word32 attack_cnst_fx = 0; if ( max_num_channels <= 0 || sampling_rate <= 0 ) { return ( IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Wrong parameters for Limiter\n" ) ); @@ -127,22 +190,96 @@ ivas_error ivas_limiter_open( { return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for Limiter handle\n" ) ); } + if ( ( hLimiter->channel_ptrs_fx = malloc( max_num_channels * sizeof( Word32 * ) ) ) == NULL ) + { + return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for Limiter handle\n" ) ); + } hLimiter->sampling_rate = sampling_rate; hLimiter->gain = 1.f; + hLimiter->gain_fx = ONE_IN_Q14; hLimiter->release_heuristic = 0.f; + hLimiter->release_heuristic_fx = 0; hLimiter->attack_constant = powf( 0.01f, 1.0f / ( IVAS_LIMITER_ATTACK_SECONDS * sampling_rate ) ); + SWITCH( sampling_rate ) + { + case 48000: + attack_cnst_fx = ATTACK_CNST_48k; + BREAK; + case 32000: + attack_cnst_fx = ATTACK_CNST_32k; + BREAK; + case 16000: + attack_cnst_fx = ATTACK_CNST_16k; + BREAK; + case 8000: + attack_cnst_fx = ATTACK_CNST_8k; + BREAK; + + + } + hLimiter->attack_constant_fx = attack_cnst_fx; hLimiter->strong_saturation_count = 0; for ( i = 0; i < max_num_channels; ++i ) { hLimiter->channel_ptrs[i] = NULL; + hLimiter->channel_ptrs_fx[i] = NULL; } *hLimiter_out = hLimiter; return IVAS_ERR_OK; } +#else +/*-------------------------------------------------------------------* + * ivas_limiter_open() + * + * Allocate and initialize limiter struct + *-------------------------------------------------------------------*/ + +/*! r : limiter struct handle */ +ivas_error ivas_limiter_open( + IVAS_LIMITER_HANDLE *hLimiter_out, /* o : limiter struct handle */ + const int16_t max_num_channels, /* i : maximum number of I/O channels to be processed */ + const int32_t sampling_rate /* i : sampling rate for processing */ +) +{ + int16_t i; + IVAS_LIMITER_HANDLE hLimiter; + + if ( max_num_channels <= 0 || sampling_rate <= 0 ) + { + return ( IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Wrong parameters for Limiter\n" ) ); + } + + if ( ( hLimiter = malloc( sizeof( IVAS_LIMITER ) ) ) == NULL ) + { + return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for Limiter handle\n" ) ); + } + + hLimiter->max_num_channels = max_num_channels; + hLimiter->num_channels = max_num_channels; + + if ( ( hLimiter->channel_ptrs = malloc( max_num_channels * sizeof( float * ) ) ) == NULL ) + { + return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for Limiter handle\n" ) ); + } + hLimiter->sampling_rate = sampling_rate; + hLimiter->gain = 1.f; + hLimiter->release_heuristic = 0.f; + hLimiter->attack_constant = powf( 0.01f, 1.0f / ( IVAS_LIMITER_ATTACK_SECONDS * sampling_rate ) ); + hLimiter->strong_saturation_count = 0; + + for ( i = 0; i < max_num_channels; ++i ) + { + hLimiter->channel_ptrs[i] = NULL; + } + *hLimiter_out = hLimiter; + + return IVAS_ERR_OK; +} +#endif /*-------------------------------------------------------------------* * ivas_limiter_close() @@ -206,7 +343,200 @@ void ivas_limiter_dec( return; } +#ifdef IVAS_FLOAT_FIXED +/*-------------------------------------------------------------------* + * limiter_process() + * + * hLimiter->channel_ptrs must be set before calling this function. + * Consider using a wrapper function like ivas_limiter_dec() instead + * of calling this directly. + *-------------------------------------------------------------------*/ + +void limiter_process_fx( + IVAS_LIMITER_HANDLE hLimiter, /* i/o: limiter struct handle */ + const Word16 output_frame, /* i : number of samples to be processed per channel in the I/O buffer */ + const Word32 threshold, /* i : signal amplitude above which limiting starts to be applied */ + const Word16 BER_detect, /* i : BER detect flag */ + Word16 *strong_saturation_cnt, /* i/o: counter of strong saturations (can be NULL) */ + Word16 q_factor /*Q factor of output samples*/ +) +{ + Word16 i, c; + Word32 tmp, max_val; + Word32 *sample; + Word32 attack_constant; + Word32 releaseHeuristic; + Word16 apply_limiting, apply_strong_limiting; + Word32 **output; + Word16 num_channels, q_fact_gain, div32, compare_value; + Word32 sampling_rate, release_constant; + Word16 gain, frame_gain, fact, fact_2, exp_pow, exp_pow_2 = 0, result; + Word32 mul; + + /* return early if given nonsensical values */ + IF ( hLimiter == NULL || output_frame <= 0 ) + { + return; + } + + apply_limiting = 1; + apply_strong_limiting = 0; + + gain = hLimiter->gain_fx; + output = hLimiter->channel_ptrs_fx; + num_channels = hLimiter->num_channels; + sampling_rate = hLimiter->sampling_rate; + attack_constant = hLimiter->attack_constant_fx; + + /*-----------------------------------------------------------------* + * Find highest absolute peak sample value + *-----------------------------------------------------------------*/ + + max_val = 0; + + FOR ( i = 0; i < output_frame; i++ ) + { + FOR( c = 0; c < num_channels; c++ ) + { + tmp = L_abs( output[c][i] ); + IF ( GT_32(tmp , max_val) ) + { + max_val = tmp; + } + } + } + + /* Release heuristic + * + * Value ranging from 0.f to 1.f. Increases on each frame that contains + * a sample with a value above threshold and decreases on each frame + * with all sample values below threshold. + * + * Values of 0 and 1 map to the shortest and longest release time, respectively. + * + * The goal of this heuristic is to avoid the "pumping" effect when only + * sharp transients exceed the threshold (use short release time), but also + * keep the gain curve smoother if the threshold is exceeded in many frames + * in a short span of time. + */ + Word16 sample_num = 0; + SWITCH (sampling_rate) + { + case 48000: + sample_num = L_SUBFRAME_48k; + BREAK; + case 32000: + sample_num = L_SUBFRAME_32k; + BREAK; + case 16000: + sample_num = L_SUBFRAME_16k; + BREAK; + case 8000: + sample_num = L_SUBFRAME_8k; + BREAK; + + + } + releaseHeuristic = hLimiter->release_heuristic_fx; + q_fact_gain = Q14; + if ( max_val > threshold ) + { + frame_gain = shr(divide3232( threshold, max_val ),1);//to q14 + releaseHeuristic = min( ONE_IN_Q30, L_add( releaseHeuristic, 21474836 ) ); + // release_constant = powf( 0.01f, 1.0f / ( 0.005f * powf( 200.f, .02 ) * sampling_rate ) ); + /* Unoptimized code for reference */ + /* releaseHeuristic = min( 1.f, releaseHeuristic + ( (float) 2.f * output_frame / sampling_rate / adaptiveReleaseWindowLengthInSeconds ) ); + * ^ + * React faster when release time should be increased + */ + } + else + { + releaseHeuristic = max( 0, L_sub( releaseHeuristic, 5368709 ) ); + /* Unoptimized code for reference */ + /*releaseHeuristic = max( 0.f, releaseHeuristic - ( (float) 0.5f * output_frame / sampling_rate / adaptiveReleaseWindowLengthInSeconds ) ); + * ^ + * React slower when release time should be decreased + */ + /* No samples above threshold and gain from previous frame is already 1.f, + * therefore gain == 1.f for the entire frame. Skip processing. */ + if ( gain >= ( lshl( 1, q_fact_gain ) )) + { + apply_limiting = 0; + } + /* No samples above threshold but gain from previous frame is not 1.f, + * transition to gain == 1.f */ + frame_gain = lshl( 1, q_fact_gain ); + + } + + /* Detection of very strong saturations */ + if ( strong_saturation_cnt != NULL ) + { + apply_strong_limiting = detect_strong_saturations_fx( BER_detect, strong_saturation_cnt, max_val, &frame_gain, q_factor ); + } + compare_value = 3277;//Q14 of 0.1f + /* Limit gain reduction to 20dB. Any peaks that require gain reduction + * higher than this are most likely due to bit errors during decoding */ + if ( frame_gain < compare_value && !apply_strong_limiting ) + { + frame_gain = compare_value; + } + + if ( apply_limiting ) + { + /* 99% time constants of the gain curve + * + * The denominator of the second argument determines after how many + * samples the gain curve will reach 99% of its target value + */ + mul = Mpy_32_32( 1025941011, releaseHeuristic ) >> 10; // Q16 + /*For powf(200.0f,x)*/ + fact = L_Extract_lc( mul, &exp_pow ); + result = extract_l( Pow2( 14, fact ) ); + div32 = divide3232( L_lshl( 1, ( 14 - exp_pow + 1 ) ), L_mult( result, sample_num ) ); + /*For powf(0.01f,x)*/ + fact_2 = shr(norm_l(div32 ),1) ; + release_constant = pow_10( L_lshl( -div32, fact_2 ), &exp_pow_2 ); + /* Unoptimized code for reference */ + /* releaseTimeInSeconds = 0.005f * powf(200.f, releaseHeuristic); <-- Map heuristic value (0; 1) exponentially to range (0.005; 1) + * release_constant = powf( 0.01f, 1.0f / ( releaseTimeInSeconds * sampling_rate ) ); + */ + + /*-----------------------------------------------------------------* + * Apply limiting + *-----------------------------------------------------------------*/ + + for ( i = 0; i < output_frame; i++ ) + { + /* Update gain */ + if ( frame_gain < gain ) + { + gain = add(extract_h(Mpy_32_16_r(attack_constant ,( sub(gain , frame_gain) ))) , frame_gain);//q14 + } + else + { + Word16 temp = extract_l( L_lshl( Mpy_32_16_r( release_constant, sub( gain, frame_gain ) ), ( 15 - (exp_pow_2) ) ) ); + gain = add( temp, frame_gain ); // q14 + } + for ( c = 0; c < num_channels; c++ ) + { + sample = &output[c][i]; + + /* Apply gain */ + *sample = Mpy_32_16_r(L_lshl( *sample,1 ),gain ) ;//q_factor + } + } + } + + /* Save last gain and release heuristic values for next frame */ + hLimiter->gain_fx = gain; + hLimiter->release_heuristic_fx = releaseHeuristic; + + return; +} +#endif /*-------------------------------------------------------------------* * limiter_process() * diff --git a/lib_rend/ivas_prot_rend.h b/lib_rend/ivas_prot_rend.h index 93535b0d5361a7fc8210b15064034fbde008f027..69f1abe9da23d166c8e4b9420977d3f5a48e5744 100644 --- a/lib_rend/ivas_prot_rend.h +++ b/lib_rend/ivas_prot_rend.h @@ -127,7 +127,16 @@ void limiter_process( const int16_t BER_detect, /* i : BER detect flag */ int16_t *strong_saturation_cnt /* i/o: counter of strong saturations (can be NULL) */ ); - +#ifdef IVAS_FLOAT_FIXED +void limiter_process_fx( + IVAS_LIMITER_HANDLE hLimiter, /* i/o: limiter struct handle */ + const Word16 output_frame, /* i : number of samples to be processed per channel in the I/O buffer */ + const Word32 threshold, /* i : signal amplitude above which limiting starts to be applied */ + const Word16 BER_detect, /* i : BER detect flag */ + Word16 *strong_saturation_cnt, /* i/o: counter of strong saturations (can be NULL) */ + Word16 q_factor + ); +#endif /*----------------------------------------------------------------------------------* * TD decorr. function prototypes *----------------------------------------------------------------------------------*/ @@ -1496,12 +1505,17 @@ void ivas_combined_orientation_update_index( COMBINED_ORIENTATION_HANDLE hCombinedOrientationData, /* i/o: combined orientation handle */ const int16_t samples_rendered /* i : samples rendered since the last call */ ); - +#ifdef IVAS_FLOAT_FIXED +void ivas_combined_orientation_update_start_index( + COMBINED_ORIENTATION_HANDLE hCombinedOrientationData, /* i/o: combined orientation handle */ + const Word16 samples_rendered /* i : samples rendered since the last call */ +); +#else void ivas_combined_orientation_update_start_index( COMBINED_ORIENTATION_HANDLE hCombinedOrientationData, /* i/o: combined orientation handle */ const int16_t samples_rendered /* i : samples rendered since the last call */ ); - +#endif void ivas_combined_orientation_set_to_start_index( COMBINED_ORIENTATION_HANDLE hCombinedOrientationData /* i/o: combined orientation handle */ ); diff --git a/lib_rend/ivas_reflections.c b/lib_rend/ivas_reflections.c index 4f7c04be62a20b3ef8a53806678e72a635719ddd..6e5c6781ef374a3d98f09f873ea941a2f3b472f3 100644 --- a/lib_rend/ivas_reflections.c +++ b/lib_rend/ivas_reflections.c @@ -371,6 +371,10 @@ ivas_error ivas_er_compute_reflections( /* If size is different, reallocate circ buffers */ /* Otherwise allocate new circ buffers */ #ifdef IVAS_FLOAT_FIXED + for ( i = 0; i < 150; i++ ) + { + reflections->shoebox_data.gains.data_fx[i] = (Word32) float_to_fix( reflections->shoebox_data.gains.data[i], 15 ); + } if ( reflections->circ_buffers ) { if ( reflections->circ_len == circ_len ) diff --git a/lib_rend/ivas_rotation.c b/lib_rend/ivas_rotation.c index 6f7c0ddf7f4753a3619c3643130fdf7d5d44a166..e9810c293cccf4801e4e49e87e342ad38a16ca53 100644 --- a/lib_rend/ivas_rotation.c +++ b/lib_rend/ivas_rotation.c @@ -1761,7 +1761,18 @@ ivas_error combine_external_and_head_orientations( hCombinedOrientationData->cur_subframe_samples_rendered = 0; hCombinedOrientationData->subframe_idx_start = 0; hCombinedOrientationData->cur_subframe_samples_rendered_start = 0; - +#ifdef IVAS_FLOAT_FIXED + for ( Word16 k = 0; k < 4; k++ ) + { + for ( i = 0; i < 3; i++ ) + { + for ( j = 0; j < 3; j++ ) + { + hCombinedOrientationData->Rmat_fx[k][i][j] = (Word32) float_to_fix( hCombinedOrientationData->Rmat[k][i][j], 30 ); + } + } + } +#endif return IVAS_ERR_OK; } @@ -2384,7 +2395,29 @@ void ivas_combined_orientation_update_index( return; } +#ifdef IVAS_FLOAT_FIXED + +/*------------------------------------------------------------------------- + * ivas_combined_orientation_update_index() + * + * update read index based on the number of rendered samples + *------------------------------------------------------------------------*/ +void ivas_combined_orientation_set_to_start_index( + COMBINED_ORIENTATION_HANDLE hCombinedOrientationData /* i/o: combined orientation handle */ +) +{ + IF ( hCombinedOrientationData != NULL ) + { + hCombinedOrientationData->subframe_idx = hCombinedOrientationData->subframe_idx_start; + move16(); + hCombinedOrientationData->cur_subframe_samples_rendered = hCombinedOrientationData->cur_subframe_samples_rendered_start; + move16(); + } + + return; +} +#else /*------------------------------------------------------------------------- * ivas_combined_orientation_update_index() @@ -2404,8 +2437,38 @@ void ivas_combined_orientation_set_to_start_index( return; } +#endif +#ifdef IVAS_FLOAT_FIXED +/*------------------------------------------------------------------------- + * ivas_combined_orientation_update_start_index() + * + * update start index based on the number of rendered samples + *------------------------------------------------------------------------*/ +void ivas_combined_orientation_update_start_index( + COMBINED_ORIENTATION_HANDLE hCombinedOrientationData, /* i/o: combined orientation handle */ + const Word16 samples_rendered /* i : samples rendered since the last call */ +) +{ + IF ( hCombinedOrientationData != NULL ) + { + IF ( hCombinedOrientationData->num_subframes == 1 ) + { + /* only one orientation available anyway or split rendering with low resolution*/ + hCombinedOrientationData->subframe_idx = 0; + } + ELSE + { + hCombinedOrientationData->cur_subframe_samples_rendered_start += samples_rendered; + hCombinedOrientationData->subframe_idx_start += hCombinedOrientationData->cur_subframe_samples_rendered / hCombinedOrientationData->subframe_size; + hCombinedOrientationData->cur_subframe_samples_rendered_start = hCombinedOrientationData->cur_subframe_samples_rendered % hCombinedOrientationData->subframe_size; + hCombinedOrientationData->subframe_idx_start = min( hCombinedOrientationData->subframe_idx, hCombinedOrientationData->num_subframes - 1 ); + } + } + return; +} +#else /*------------------------------------------------------------------------- * ivas_combined_orientation_update_start_index() * @@ -2435,3 +2498,4 @@ void ivas_combined_orientation_update_start_index( return; } +#endif \ No newline at end of file diff --git a/lib_rend/ivas_stat_rend.h b/lib_rend/ivas_stat_rend.h index 4e33a019e1859114100e72c462f290aeafa2dac2..498704a2fe216eb8f10e2087060046f63db94192 100644 --- a/lib_rend/ivas_stat_rend.h +++ b/lib_rend/ivas_stat_rend.h @@ -1626,7 +1626,24 @@ typedef struct ivas_hrtfs_parambin_struct /*----------------------------------------------------------------------------------* * Limiter structure *----------------------------------------------------------------------------------*/ +#ifdef IVAS_FLOAT_FIXED +typedef struct +{ + Word16 max_num_channels; + Word16 num_channels; + float **channel_ptrs; + Word32 **channel_ptrs_fx; + Word32 sampling_rate; + float gain; + Word16 gain_fx; + float release_heuristic; + Word32 release_heuristic_fx; + float attack_constant; + Word32 attack_constant_fx; + Word16 strong_saturation_count; +} IVAS_LIMITER, *IVAS_LIMITER_HANDLE; +#else typedef struct { int16_t max_num_channels; @@ -1639,7 +1656,7 @@ typedef struct int16_t strong_saturation_count; } IVAS_LIMITER, *IVAS_LIMITER_HANDLE; - +#endif /*----------------------------------------------------------------------------------* * Loudspeaker Configuration Conversion structure diff --git a/lib_rend/lib_rend.c b/lib_rend/lib_rend.c index 2f5afba06861675417fd7834975776c3de8824ea..6d14e94e09afd86b99d22db7a2b5e2f5b02a721f 100644 --- a/lib_rend/lib_rend.c +++ b/lib_rend/lib_rend.c @@ -75,6 +75,9 @@ typedef float pan_vector[MAX_OUTPUT_CHANNELS]; typedef float pan_matrix[MAX_INPUT_CHANNELS][MAX_OUTPUT_CHANNELS]; typedef float rotation_gains[MAX_INPUT_CHANNELS][MAX_INPUT_CHANNELS]; +#ifdef IVAS_FLOAT_FIXED +typedef Word16 rotation_gains_fx[MAX_INPUT_CHANNELS][MAX_INPUT_CHANNELS]; +#endif typedef float rotation_matrix[3][3]; /* EFAP wrapper to simplify writing panning gains to a vector that includes LFE channels */ @@ -99,7 +102,7 @@ typedef struct const AUDIO_CONFIG *pOutConfig; const LSSETUP_CUSTOM_STRUCT *pCustomLsOut; const EFAP_WRAPPER *pEfapOutWrapper; - IVAS_REND_HeadRotData *pHeadRotData;// for now removing the const qualifier TODO: will modify later + IVAS_REND_HeadRotData *pHeadRotData; // for now removing the const qualifier TODO: will modify later const COMBINED_ORIENTATION_HANDLE *pCombinedOrientationData; } rendering_context; #else @@ -114,6 +117,18 @@ typedef struct } rendering_context; #endif /* Common base for input structs */ +#ifdef IVAS_FLOAT_FIXED +typedef struct +{ + AUDIO_CONFIG inConfig; + IVAS_REND_InputId id; + IVAS_REND_AudioBuffer inputBuffer; + float gain; /* Linear, not in dB */ + Word32 gain_fx; /* Linear, not in dB */ + rendering_context ctx; + Word32 numNewSamplesPerChannel; /* Used to keep track how much new audio was fed before rendering current frame */ +} input_base; +#else typedef struct { AUDIO_CONFIG inConfig; @@ -123,7 +138,7 @@ typedef struct rendering_context ctx; int32_t numNewSamplesPerChannel; /* Used to keep track how much new audio was fed before rendering current frame */ } input_base; - +#endif typedef struct { input_base base; @@ -182,8 +197,9 @@ typedef struct pan_matrix hoaDecMtx; CREND_WRAPPER_HANDLE crendWrapper; rotation_gains rot_gains_prev; - Word16 rot_gains_prev_fx[MAX_INPUT_CHANNELS][MAX_INPUT_CHANNELS]; + rotation_gains_fx rot_gains_prev_fx; float *bufferData; + Word32 *bufferData_fx; DIRAC_ANA_HANDLE hDirAC; } input_sba; #else @@ -247,7 +263,20 @@ static void freeMasaExtRenderer( MASA_EXT_REND_HANDLE *hMasaExtRendOut ); /*-------------------------------------------------------------------* * Local functions *-------------------------------------------------------------------*/ +#ifdef IVAS_FLOAT_FIXED +static ivas_error allocateInputBaseBufferData_fx( + Word32 **data, + const Word16 data_size ) +{ + *data = (Word32 *) malloc( data_size * sizeof( Word32 ) ); + IF( *data == NULL ) + { + return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for input base buffer data" ); + } + return IVAS_ERR_OK; +} +#endif static ivas_error allocateInputBaseBufferData( float **data, const int16_t data_size ) @@ -260,7 +289,19 @@ static ivas_error allocateInputBaseBufferData( return IVAS_ERR_OK; } +#ifdef IVAS_FLOAT_FIXED +static void freeInputBaseBufferData_fx( + Word32 **data ) +{ + IF( *data != NULL ) + { + free( *data ); + *data = NULL; + } + return; +} +#endif static void freeInputBaseBufferData( float **data ) { @@ -322,6 +363,28 @@ static float *getSmplPtr( { return buffer.data + chnlIdx * buffer.config.numSamplesPerChannel + smplIdx; } +#ifdef IVAS_FLOAT_FIXED +static void copyBufferTo2dArray_fx( + const IVAS_REND_AudioBuffer buffer, + Word32 array[][L_FRAME48k] ) +{ + UWord32 smplIdx; + UWord32 chnlIdx; + const Word32 *readPtr; + + readPtr = buffer.data_fx; + + FOR( chnlIdx = 0; chnlIdx < (UWord32) buffer.config.numChannels; ++chnlIdx ) + { + FOR( smplIdx = 0; smplIdx < (UWord32) buffer.config.numSamplesPerChannel; ++smplIdx ) + { + array[chnlIdx][smplIdx] = *readPtr++; + } + } + + return; +} +#endif static void copyBufferTo2dArray( const IVAS_REND_AudioBuffer buffer, float array[][L_FRAME48k] ) @@ -342,7 +405,26 @@ static void copyBufferTo2dArray( return; } +#ifdef IVAS_FLOAT_FIXED +static void accumulate2dArrayToBuffer_fx( + Word32 array[][L_FRAME48k], + IVAS_REND_AudioBuffer *buffer ) +{ + Word16 smplIdx, chnlIdx; + Word32 *writePtr; + writePtr = buffer->data_fx; + FOR( chnlIdx = 0; chnlIdx < buffer->config.numChannels; ++chnlIdx ) + { + FOR( smplIdx = 0; smplIdx < buffer->config.numSamplesPerChannel; ++smplIdx ) + { + *writePtr++ += array[chnlIdx][smplIdx]; + } + } + + return; +} +#endif static void accumulate2dArrayToBuffer( float array[][L_FRAME48k], IVAS_REND_AudioBuffer *buffer ) @@ -361,7 +443,53 @@ static void accumulate2dArrayToBuffer( return; } +#ifdef IVAS_FLOAT_FIXED +/*-------------------------------------------------------------------* + * limitRendererOutput() + * + * In-place saturation control for multichannel buffers with adaptive release time + *-------------------------------------------------------------------*/ + +/*! r: number of clipped output samples */ +static Word32 limitRendererOutput_fx( + IVAS_LIMITER_HANDLE hLimiter, /* i/o: limiter struct handle */ + Word32 *output, /* i/o: I/O buffer */ + const Word16 output_frame, /* i : number of samples per channel in the buffer */ + const Word32 threshold, /* i : signal amplitude above which limiting starts to be applied */ + Word16 q_factor ) /* i : q factor of output samples */ +{ + Word16 i; + Word32 **channels; + Word16 num_channels; + Word32 numClipping = 0; + + /* return early if given bad parameters */ + IF ( hLimiter == NULL || output == NULL || output_frame <= 0 ) + { + return 0; + } + + channels = hLimiter->channel_ptrs_fx; + num_channels = hLimiter->num_channels; + + FOR ( i = 0; i < num_channels; ++i ) + { + channels[i] = output + i * output_frame; + } + + limiter_process_fx( hLimiter, output_frame, threshold, 0, NULL, q_factor ); + + /* Apply clipping to buffer in case the limiter let through some samples > 1.0f */ + FOR ( i = 0; i < output_frame * num_channels; ++i ) + { + + output[i] = min( max( L_lshl( INT16_MIN, q_factor ), output[i] ), L_lshl( INT16_MAX, q_factor ) ); + } + + return numClipping; +} +#endif /*-------------------------------------------------------------------* * limitRendererOutput() * @@ -1047,7 +1175,7 @@ static ivas_error getEfapGains( return IVAS_ERR_OK; } - +#ifdef IVAS_FLOAT_FIXED static ivas_error initHeadRotation( IVAS_REND_HANDLE hIvasRend ) { @@ -1064,6 +1192,7 @@ static ivas_error initHeadRotation( for ( i = 0; i < crossfade_len; i++ ) { hIvasRend->headRotData.crossfade[i] = i * tmp; + hIvasRend->headRotData.crossfade_fx[i] = (Word16) float_to_fix( hIvasRend->headRotData.crossfade[i], 14 ); } /* Initialize with unit quaternions */ @@ -1085,7 +1214,45 @@ static ivas_error initHeadRotation( return IVAS_ERR_OK; } +#else +static ivas_error initHeadRotation( + IVAS_REND_HANDLE hIvasRend ) +{ + int16_t i, crossfade_len; + float tmp; + ivas_error error; + + /* Head rotation is enabled by default */ + hIvasRend->headRotData.headRotEnabled = 1; + + /* Initialize 5ms crossfade */ + crossfade_len = L_FRAME48k / MAX_PARAM_SPATIAL_SUBFRAMES; + tmp = 1.f / ( crossfade_len - 1 ); + for ( i = 0; i < crossfade_len; i++ ) + { + hIvasRend->headRotData.crossfade[i] = i * tmp; + } + + /* Initialize with unit quaternions */ + for ( i = 0; i < hIvasRend->num_subframes; ++i ) + { + hIvasRend->headRotData.headPositions[i] = quaternionInit(); + } + + + if ( ( hIvasRend->headRotData.hOrientationTracker = (ivas_orient_trk_state_t *) malloc( sizeof( ivas_orient_trk_state_t ) ) ) == NULL ) + { + return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for Orientation tracking" ); + } + if ( ( error = ivas_orient_trk_Init( hIvasRend->headRotData.hOrientationTracker ) ) != IVAS_ERR_OK ) + { + return error; + } + + return IVAS_ERR_OK; +} +#endif static void closeHeadRotation( IVAS_REND_HANDLE hIvasRend ) { @@ -1113,7 +1280,22 @@ static void initRotMatrix( return; } +#ifdef IVAS_FLOAT_FIXED +static void initRotGains_fx( + rotation_gains_fx rot_gains ) +{ + Word16 i; + + /* Set gains to passthrough */ + FOR( i = 0; i < MAX_INPUT_CHANNELS; i++ ) + { + set_val_Word16( rot_gains[i], 0, MAX_INPUT_CHANNELS ); + rot_gains[i][i] = ONE_IN_Q14; + } + return; +} +#endif static void initRotGains( rotation_gains rot_gains ) { @@ -1128,8 +1310,32 @@ static void initRotGains( return; } +#ifdef IVAS_FLOAT_FIXED +static void initRendInputBase_fx( + input_base *inputBase, + const AUDIO_CONFIG inConfig, + const IVAS_REND_InputId id, + const rendering_context rendCtx, + Word32 *dataBuf, + const Word16 dataBufSize ) +{ + inputBase->inConfig = inConfig; + inputBase->id = id; + inputBase->gain_fx = ONE_IN_Q30; + inputBase->ctx = rendCtx; + inputBase->numNewSamplesPerChannel = 0; + inputBase->inputBuffer.config.numSamplesPerChannel = 0; + inputBase->inputBuffer.config.numChannels = 0; + inputBase->inputBuffer.data_fx = dataBuf; + IF( inputBase->inputBuffer.data != NULL ) + { + set_val_Word32( inputBase->inputBuffer.data_fx, 0, dataBufSize ); + } + return; +} +#endif static void initRendInputBase( input_base *inputBase, const AUDIO_CONFIG inConfig, @@ -2452,7 +2658,60 @@ static ivas_error initSbaMasaRendering( return IVAS_ERR_OK; } +#ifdef IVAS_FLOAT_FIXED +static ivas_error setRendInputActiveSba( + void *input, + const AUDIO_CONFIG inConfig, + const IVAS_REND_InputId id, + RENDER_CONFIG_DATA *hRendCfg ) +{ + ivas_error error; + rendering_context rendCtx; + AUDIO_CONFIG outConfig; + input_sba *inputSba; + + inputSba = (input_sba *) input; + rendCtx = inputSba->base.ctx; + outConfig = *rendCtx.pOutConfig; + + if ( !isIoConfigPairSupported( inConfig, outConfig ) ) + { + return IVAS_ERR_IO_CONFIG_PAIR_NOT_SUPPORTED; + } + + if ( ( error = allocateInputBaseBufferData( &inputSba->bufferData, MAX_BUFFER_LENGTH ) ) != IVAS_ERR_OK ) + { + return error; + } + if ( ( error = allocateInputBaseBufferData_fx( &inputSba->bufferData_fx, MAX_BUFFER_LENGTH ) ) != IVAS_ERR_OK ) + { + return error; + } + initRendInputBase( &inputSba->base, inConfig, id, rendCtx, inputSba->bufferData, MAX_BUFFER_LENGTH ); + initRendInputBase_fx( &inputSba->base, inConfig, id, rendCtx, inputSba->bufferData_fx, MAX_BUFFER_LENGTH ); + + setZeroPanMatrix( inputSba->hoaDecMtx ); + + inputSba->crendWrapper = NULL; + inputSba->hDirAC = NULL; + initRotGains_fx( inputSba->rot_gains_prev_fx ); + + if ( outConfig == IVAS_AUDIO_CONFIG_MASA1 || outConfig == IVAS_AUDIO_CONFIG_MASA2 ) + { + if ( ( error = initSbaMasaRendering( inputSba, *rendCtx.pOutSampleRate ) ) != IVAS_ERR_OK ) + { + return error; + } + } + + if ( ( error = updateSbaPanGains( inputSba, outConfig, hRendCfg ) ) != IVAS_ERR_OK ) + { + return error; + } + return error; +} +#else static ivas_error setRendInputActiveSba( void *input, const AUDIO_CONFIG inConfig, @@ -2501,8 +2760,29 @@ static ivas_error setRendInputActiveSba( return error; } +#endif +#ifdef IVAS_FLOAT_FIXED +static void clearInputSba( + input_sba *inputSba ) +{ + rendering_context rendCtx; + + rendCtx = inputSba->base.ctx; + + freeInputBaseBufferData( &inputSba->bufferData ); + freeInputBaseBufferData_fx( &inputSba->bufferData_fx ); + + initRendInputBase( &inputSba->base, IVAS_AUDIO_CONFIG_INVALID, 0, rendCtx, NULL, 0 ); + initRendInputBase_fx( &inputSba->base, IVAS_AUDIO_CONFIG_INVALID, 0, rendCtx, NULL, 0 ); + /* Free input's internal handles */ + ivas_rend_closeCrend( &inputSba->crendWrapper ); + + ivas_dirac_ana_close( &( inputSba->hDirAC ) ); + return; +} +#else static void clearInputSba( input_sba *inputSba ) { @@ -2521,7 +2801,7 @@ static void clearInputSba( return; } - +#endif static ivas_error setRendInputActiveMasa( void *input, @@ -2595,6 +2875,7 @@ static void clearInputMasa( return; } +#ifdef IVAS_FLOAT_FIXED /*------------------------------------------------------------------------- * IVAS_REND_Open() @@ -2717,6 +2998,7 @@ ivas_error IVAS_REND_Open( hIvasRend->inputsSba[i].crendWrapper = NULL; hIvasRend->inputsSba[i].bufferData = NULL; + hIvasRend->inputsSba[i].bufferData_fx = NULL; hIvasRend->inputsSba[i].hDirAC = NULL; } @@ -2733,23 +3015,161 @@ ivas_error IVAS_REND_Open( return IVAS_ERR_OK; } +#else +/*------------------------------------------------------------------------- + * IVAS_REND_Open() + * + * + *------------------------------------------------------------------------*/ - -static LSSETUP_CUSTOM_STRUCT makeCustomLsSetup( - const IVAS_CUSTOM_LS_DATA rendCustomLsLayout ) +ivas_error IVAS_REND_Open( + IVAS_REND_HANDLE *phIvasRend, + const int32_t outputSampleRate, + const AUDIO_CONFIG outConfig, + const int16_t nonDiegeticPan, + const float nonDiegeticPanGain, + const int16_t num_subframes ) { int16_t i; - LSSETUP_CUSTOM_STRUCT customLs; - - /* Copy layout description */ - customLs.num_spk = rendCustomLsLayout.num_spk; - mvr2r( rendCustomLsLayout.azimuth, customLs.ls_azimuth, rendCustomLsLayout.num_spk ); - mvr2r( rendCustomLsLayout.elevation, customLs.ls_elevation, rendCustomLsLayout.num_spk ); + IVAS_REND_HANDLE hIvasRend; + ivas_error error; + int16_t numOutChannels; - customLs.is_planar_setup = 1; - for ( i = 0; i < rendCustomLsLayout.num_spk; ++i ) + /* Validate function arguments */ + if ( phIvasRend == NULL ) { - if ( fabsf( rendCustomLsLayout.elevation[i] ) > EPSILON ) + return IVAS_ERR_UNEXPECTED_NULL_POINTER; + } + + if ( ( error = validateOutputAudioConfig( outConfig ) ) != IVAS_ERR_OK ) + { + return error; + } + + if ( ( error = validateOutputSampleRate( outputSampleRate, outConfig ) ) != IVAS_ERR_OK ) + { + return error; + } + + *phIvasRend = (IVAS_REND_HANDLE) malloc( sizeof( struct IVAS_REND ) ); + if ( *phIvasRend == NULL ) + { + return IVAS_ERR_FAILED_ALLOC; + } + + hIvasRend = *phIvasRend; + hIvasRend->sampleRateOut = outputSampleRate; + hIvasRend->outputConfig = outConfig; + hIvasRend->customLsOut = defaultCustomLs(); + hIvasRend->hLimiter = NULL; + hIvasRend->efapOutWrapper.hEfap = NULL; + hIvasRend->efapOutWrapper.pCustomLsSetup = NULL; + hIvasRend->num_subframes = num_subframes; + + /* Initialize limiter */ + if ( ( error = IVAS_REND_NumOutChannels( hIvasRend, &numOutChannels ) ) != IVAS_ERR_OK ) + { + return error; + } + + if ( ( error = initLimiter( &hIvasRend->hLimiter, numOutChannels, outputSampleRate ) ) != IVAS_ERR_OK ) + { + return error; + } + + /* Initialize headrotation data */ + if ( ( error = initHeadRotation( hIvasRend ) ) != IVAS_ERR_OK ) + { + return error; + } + + /* Initialize external orientation data */ + if ( ( error = ivas_external_orientation_open( &( hIvasRend->hExternalOrientationData ), num_subframes ) ) != IVAS_ERR_OK ) + { + return error; + } + + /* Initilize combined orientation data */ + if ( ( error = ivas_combined_orientation_open( &( hIvasRend->hCombinedOrientationData ), outputSampleRate, num_subframes ) ) != IVAS_ERR_OK ) + { + return error; + } + + /* Initialize EFAP */ + if ( ( error = initEfap( &hIvasRend->efapOutWrapper, outConfig, &hIvasRend->customLsOut ) ) != IVAS_ERR_OK ) + { + return error; + } + + /* Initialize inputs */ + + for ( i = 0; i < RENDERER_MAX_ISM_INPUTS; ++i ) + { + initRendInputBase( &hIvasRend->inputsIsm[i].base, IVAS_AUDIO_CONFIG_INVALID, 0, getRendCtx( hIvasRend ), NULL, 0 ); + + hIvasRend->inputsIsm[i].crendWrapper = NULL; + hIvasRend->inputsIsm[i].hReverb = NULL; + hIvasRend->inputsIsm[i].tdRendWrapper.hBinRendererTd = NULL; + hIvasRend->inputsIsm[i].bufferData = NULL; + hIvasRend->inputsIsm[i].nonDiegeticPan = nonDiegeticPan; + hIvasRend->inputsIsm[i].nonDiegeticPanGain = nonDiegeticPanGain; + hIvasRend->inputsIsm[i].hOMasa = NULL; + } + + for ( i = 0; i < RENDERER_MAX_MC_INPUTS; ++i ) + { + initRendInputBase( &hIvasRend->inputsMc[i].base, IVAS_AUDIO_CONFIG_INVALID, 0, getRendCtx( hIvasRend ), NULL, 0 ); + + hIvasRend->inputsMc[i].efapInWrapper.hEfap = NULL; + hIvasRend->inputsMc[i].crendWrapper = NULL; + hIvasRend->inputsMc[i].hReverb = NULL; + hIvasRend->inputsMc[i].tdRendWrapper.hBinRendererTd = NULL; + hIvasRend->inputsMc[i].bufferData = NULL; + hIvasRend->inputsMc[i].lfeDelayBuffer = NULL; + hIvasRend->inputsMc[i].nonDiegeticPan = nonDiegeticPan; + hIvasRend->inputsMc[i].nonDiegeticPanGain = nonDiegeticPanGain; + hIvasRend->inputsMc[i].hMcMasa = NULL; + } + + for ( i = 0; i < RENDERER_MAX_SBA_INPUTS; ++i ) + { + initRendInputBase( &hIvasRend->inputsSba[i].base, IVAS_AUDIO_CONFIG_INVALID, 0, getRendCtx( hIvasRend ), NULL, 0 ); + + hIvasRend->inputsSba[i].crendWrapper = NULL; + hIvasRend->inputsSba[i].bufferData = NULL; + hIvasRend->inputsSba[i].hDirAC = NULL; + } + + for ( i = 0; i < RENDERER_MAX_MASA_INPUTS; ++i ) + { + initRendInputBase( &hIvasRend->inputsMasa[i].base, IVAS_AUDIO_CONFIG_INVALID, 0, getRendCtx( hIvasRend ), NULL, 0 ); + + hIvasRend->inputsMasa[i].metadataHasBeenFed = false; + hIvasRend->inputsMasa[i].bufferData = NULL; + hIvasRend->inputsMasa[i].hMasaPrerend = NULL; + hIvasRend->inputsMasa[i].hMasaExtRend = NULL; + } + + + return IVAS_ERR_OK; +} +#endif + +static LSSETUP_CUSTOM_STRUCT makeCustomLsSetup( + const IVAS_CUSTOM_LS_DATA rendCustomLsLayout ) +{ + int16_t i; + LSSETUP_CUSTOM_STRUCT customLs; + + /* Copy layout description */ + customLs.num_spk = rendCustomLsLayout.num_spk; + mvr2r( rendCustomLsLayout.azimuth, customLs.ls_azimuth, rendCustomLsLayout.num_spk ); + mvr2r( rendCustomLsLayout.elevation, customLs.ls_elevation, rendCustomLsLayout.num_spk ); + + customLs.is_planar_setup = 1; + for ( i = 0; i < rendCustomLsLayout.num_spk; ++i ) + { + if ( fabsf( rendCustomLsLayout.elevation[i] ) > EPSILON ) { customLs.is_planar_setup = 0; break; @@ -4453,7 +4873,8 @@ static ivas_error rotateFrameSba_fx( Word32 Rmat[3][3]; ivas_error error; Word16 idx; - Word16 val, cf, oneminuscf; + Word16 cf, oneminuscf; + Word32 val; push_wmops( "rotateFrameSba" ); @@ -4521,16 +4942,16 @@ static ivas_error rotateFrameSba_fx( tmpRot[n - m1] = 0; for ( m = m1; m < m2; m++ ) { - val = (Word16) extract_h(inAudio.data_fx[m * inAudio.config.numSamplesPerChannel + idx] * ONE_IN_Q3); + val = inAudio.data_fx[m * inAudio.config.numSamplesPerChannel + idx]; /* crossfade with previous rotation gains */ - temp = Mpy_32_16_r( L_add( L_mult0( cf, gains[n][m] ), ( L_mult0( oneminuscf, gains_prev[n][m] ) ) ), val ); - tmpRot[n - m1] = L_add( temp, tmpRot[n - m1] ); + temp = Mpy_32_32( L_add( L_mult0( cf, gains[n][m] ), ( L_mult0( oneminuscf, gains_prev[n][m] ) ) ), val ); + tmpRot[n - m1] = L_add( temp << 3, tmpRot[n - m1] ); // Qexp } } /* write back the result */ for ( n = m1; n < m2; n++ ) { - writePtr = getSmplPtr_fx( outAudio, n, idx );//Q13 + writePtr = getSmplPtr_fx( outAudio, n, idx ); ( *writePtr ) = tmpRot[n - m1]; } m1 = m2; @@ -4541,10 +4962,9 @@ static ivas_error rotateFrameSba_fx( /* move SHrotmat to SHrotmat_prev */ for ( i = 0; i < HEADROT_SHMAT_DIM; i++ ) { - Copy( gains[i], gains_prev[i], HEADROT_SHMAT_DIM );//Q14 + Copy( gains[i], gains_prev[i], HEADROT_SHMAT_DIM ); // Q14 } } - pop_wmops(); return IVAS_ERR_OK; @@ -6347,173 +6767,77 @@ static ivas_error renderSbaToBinaural( const AUDIO_CONFIG outConfig, IVAS_REND_AudioBuffer outAudio ) { - float tmpCrendBuffer[MAX_OUTPUT_CHANNELS][L_FRAME48k]; ivas_error error; IVAS_REND_AudioBuffer tmpRotBuffer; - float *p_tmpCrendBuffer[MAX_OUTPUT_CHANNELS]; - int16_t i, j; + Word16 i; const COMBINED_ORIENTATION_HANDLE *hCombinedOrientationData; - int8_t combinedOrientationEnabled; - int16_t subframe_idx; + Word8 combinedOrientationEnabled; + Word16 subframe_idx; + Word32 output_buffer_fx[MAX_OUTPUT_CHANNELS][L_FRAME48k]; + Word32 *output_fx[MAX_OUTPUT_CHANNELS]; push_wmops( "renderSbaToBinaural" ); { - for ( i = 0; i < MAX_OUTPUT_CHANNELS; i++ ) + + FOR( i = 0; i < MAX_OUTPUT_CHANNELS; i++ ) { - p_tmpCrendBuffer[i] = tmpCrendBuffer[i]; + output_fx[i] = output_buffer_fx[i]; } hCombinedOrientationData = sbaInput->base.ctx.pCombinedOrientationData; combinedOrientationEnabled = 0; - if ( hCombinedOrientationData != NULL ) + IF( hCombinedOrientationData != NULL ) { - for ( subframe_idx = 0; subframe_idx < ( *hCombinedOrientationData )->num_subframes; subframe_idx++ ) + FOR( subframe_idx = 0; subframe_idx < ( *hCombinedOrientationData )->num_subframes; subframe_idx++ ) { - if ( ( *hCombinedOrientationData )->enableCombinedOrientation[subframe_idx] != 0 ) + IF( ( *hCombinedOrientationData )->enableCombinedOrientation[subframe_idx] != 0 ) { combinedOrientationEnabled = 1; - break; + BREAK; } } } - /* apply rotation */ - if ( combinedOrientationEnabled ) + IF( combinedOrientationEnabled ) { tmpRotBuffer = sbaInput->base.inputBuffer; - tmpRotBuffer.data = malloc( tmpRotBuffer.config.numSamplesPerChannel * tmpRotBuffer.config.numChannels * sizeof( float ) ); + tmpRotBuffer.data_fx = malloc( tmpRotBuffer.config.numSamplesPerChannel * tmpRotBuffer.config.numChannels * sizeof( Word32 ) ); - sbaInput->base.inputBuffer.data_fx = malloc( tmpRotBuffer.config.numSamplesPerChannel * tmpRotBuffer.config.numChannels * sizeof( Word32 ) ); // for now keeping the buffer local /* copy input for in-place rotation */ - mvr2r( sbaInput->base.inputBuffer.data, tmpRotBuffer.data, tmpRotBuffer.config.numChannels * tmpRotBuffer.config.numSamplesPerChannel ); - for ( Word16 smpl = 0; smpl < tmpRotBuffer.config.numSamplesPerChannel; ++smpl ) - { - for ( Word16 chnl = 0; chnl < tmpRotBuffer.config.numChannels; ++chnl ) - { - tmpRotBuffer.data_fx[chnl * tmpRotBuffer.config.numSamplesPerChannel + smpl] = (Word32) float_to_fix( tmpRotBuffer.data[chnl * tmpRotBuffer.config.numSamplesPerChannel + smpl], 13 ); - sbaInput->base.inputBuffer.data_fx[chnl * tmpRotBuffer.config.numSamplesPerChannel + smpl] = (Word32) float_to_fix( sbaInput->base.inputBuffer.data[chnl * tmpRotBuffer.config.numSamplesPerChannel + smpl], 13 ); - } - } - for ( i = 0; i < L_FRAME48k / MAX_PARAM_SPATIAL_SUBFRAMES; i++ ) - { - sbaInput->base.ctx.pHeadRotData->crossfade_fx[i] = (Word16) float_to_fix( sbaInput->base.ctx.pHeadRotData->crossfade[i], 14 ); - } - for ( i = 0; i < HEADROT_SHMAT_DIM; i++ ) - { - for ( j = 0; j < HEADROT_SHMAT_DIM; j++ ) - { - sbaInput->rot_gains_prev_fx[i][j] = (Word16) float_to_fix( sbaInput->rot_gains_prev[i][j], 14 ); - } - } - for ( subframe_idx = 0; subframe_idx < ( *hCombinedOrientationData )->num_subframes; subframe_idx++ ) - { - for ( i = 0; i < 3; i++ ) - { - for ( j = 0; j < 3; j++ ) - { - ( *sbaInput->base.ctx.pCombinedOrientationData )->Rmat_fx[subframe_idx][i][j] = (Word32) float_to_fix( ( *sbaInput->base.ctx.pCombinedOrientationData )->Rmat[subframe_idx][i][j], 30 ); - } - } - } - if ( ( error = rotateFrameSba_fx( sbaInput->base.inputBuffer, sbaInput->base.inConfig, sbaInput->base.ctx.pHeadRotData, - sbaInput->base.ctx.pCombinedOrientationData, sbaInput->rot_gains_prev_fx, tmpRotBuffer ) ) != IVAS_ERR_OK ) + + mvr2r_Word32( sbaInput->base.inputBuffer.data_fx, tmpRotBuffer.data_fx, tmpRotBuffer.config.numChannels * tmpRotBuffer.config.numSamplesPerChannel ); + + IF( ( error = rotateFrameSba_fx( sbaInput->base.inputBuffer, sbaInput->base.inConfig, sbaInput->base.ctx.pHeadRotData, + sbaInput->base.ctx.pCombinedOrientationData, sbaInput->rot_gains_prev_fx, tmpRotBuffer ) ) != IVAS_ERR_OK ) { return error; } - for ( Word16 smpl = 0; smpl < tmpRotBuffer.config.numSamplesPerChannel; ++smpl ) - { - for ( Word16 chnl = 0; chnl < tmpRotBuffer.config.numChannels; ++chnl ) - { - tmpRotBuffer.data[chnl * tmpRotBuffer.config.numSamplesPerChannel + smpl] = fix_to_float( tmpRotBuffer.data_fx[chnl * tmpRotBuffer.config.numSamplesPerChannel + smpl], 13 ); - } - } - for ( i = 0; i < HEADROT_SHMAT_DIM; i++ ) - { - for ( j = 0; j < HEADROT_SHMAT_DIM; j++ ) - { - sbaInput->rot_gains_prev[i][j] = fix_to_float( sbaInput->rot_gains_prev_fx[i][j], 14 ); - } - } - copyBufferTo2dArray( tmpRotBuffer, tmpCrendBuffer ); - free( tmpRotBuffer.data ); + + copyBufferTo2dArray_fx( tmpRotBuffer, output_buffer_fx ); free( tmpRotBuffer.data_fx ); - free( sbaInput->base.inputBuffer.data_fx ); // for now keeping the buffer local } - else + ELSE { - copyBufferTo2dArray( sbaInput->base.inputBuffer, tmpCrendBuffer ); + copyBufferTo2dArray_fx( sbaInput->base.inputBuffer, output_buffer_fx ); } // Porting Crend_process function - Word16 nchan_out = 2, nchan_in = 12, subframe_len; CREND_HANDLE hCrend; - Word32 output_buffer_fx[MAX_OUTPUT_CHANNELS][L_FRAME48k]; - Word32 *output_fx[MAX_OUTPUT_CHANNELS]; + hCrend = sbaInput->crendWrapper->hCrend; IVAS_REND_AudioConfigType inConfigType; - subframe_len = (Word16) ( *sbaInput->base.ctx.pOutSampleRate / ( FRAMES_PER_SEC * MAX_PARAM_SPATIAL_SUBFRAMES ) ); - Word16 gd_bits = find_guarded_bits_fx( subframe_len ); - Word16 exp = 15; - exp -= gd_bits; - Word16 nchan = nchan_in; inConfigType = getAudioConfigType( sbaInput->base.inConfig ); - - if ( ( error = getAudioConfigNumChannels( outConfig, &nchan_out ) ) != IVAS_ERR_OK ) - { - return error; - } - // TODO:will remove later for temporary conversion; not there in the float code code - if ( ( error = getAudioConfigNumChannels( sbaInput->base.inConfig, &nchan_in ) ) != IVAS_ERR_OK ) - { - return error; - } - for ( i = 0; i < MAX_OUTPUT_CHANNELS; i++ ) - { - output_fx[i] = output_buffer_fx[i]; - } - if ( hCrend->reflections != NULL ) - { - if ( hCrend->reflections->use_er == 1 && hCrend->reflections->is_ready == 1 ) - { - nchan = hCrend->reflections->shoebox_data.n_sources + 1; - for ( i = 0; i < 150; i++ ) - { - hCrend->reflections->shoebox_data.gains.data_fx[i] = (Word32) float_to_fix( hCrend->reflections->shoebox_data.gains.data[i], 15 ); - } - } - } - - if ( inConfigType == IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED || inConfigType == IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS ) - { - nchan = nchan_in; - } - for ( i = 0; i < nchan; i++ ) - { - for ( j = 0; j < L_FRAME48k; j++ ) - { - - output_fx[i][j] = (Word32) float_to_fix( p_tmpCrendBuffer[i][j], exp ); - } - } /* call CREND */ - if ( ( error = ivas_rend_crendProcess( sbaInput->crendWrapper, sbaInput->base.inConfig, outConfig, NULL, NULL, NULL, NULL, output_fx, *sbaInput->base.ctx.pOutSampleRate, - getNumSubframesInBuffer( &outAudio, *sbaInput->base.ctx.pOutSampleRate ) ) ) != IVAS_ERR_OK ) + IF( ( error = ivas_rend_crendProcess( sbaInput->crendWrapper, sbaInput->base.inConfig, outConfig, NULL, NULL, NULL, NULL, output_fx, *sbaInput->base.ctx.pOutSampleRate, + getNumSubframesInBuffer( &outAudio, *sbaInput->base.ctx.pOutSampleRate ) ) ) != IVAS_ERR_OK ) { return error; } - if ( hCrend->hReverb != NULL ) + IF( hCrend->hReverb != NULL ) { - exp -= 2; + *outAudio.q_fact -= 2; } - for ( i = 0; i < nchan_out; i++ ) - { - - for ( j = 0; j < L_FRAME48k; j++ ) - { + accumulate2dArrayToBuffer_fx( output_buffer_fx, &outAudio ); - p_tmpCrendBuffer[i][j] = fix_to_float( output_fx[i][j], exp ); - } - } - accumulate2dArrayToBuffer( tmpCrendBuffer, &outAudio ); } pop_wmops(); @@ -6599,7 +6923,7 @@ static ivas_error renderSbaToBinauralRoom( const AUDIO_CONFIG outConfig, IVAS_REND_AudioBuffer outAudio ) { - int16_t i,j; + int16_t i, j; int16_t tmp; float tmpCrendBuffer[MAX_OUTPUT_CHANNELS][L_FRAME48k]; ivas_error error; @@ -6639,7 +6963,6 @@ static ivas_error renderSbaToBinauralRoom( tmpRotBuffer = sbaInput->base.inputBuffer; tmpRotBuffer.data = malloc( tmpRotBuffer.config.numSamplesPerChannel * tmpRotBuffer.config.numChannels * sizeof( float ) ); tmpRotBuffer.data_fx = malloc( tmpRotBuffer.config.numSamplesPerChannel * tmpRotBuffer.config.numChannels * sizeof( Word32 ) ); - sbaInput->base.inputBuffer.data_fx = malloc( tmpRotBuffer.config.numSamplesPerChannel * tmpRotBuffer.config.numChannels * sizeof( Word32 ) ); // for now keeping the buffer local /* copy input for in-place rotation */ mvr2r( sbaInput->base.inputBuffer.data, tmpRotBuffer.data, tmpRotBuffer.config.numChannels * tmpRotBuffer.config.numSamplesPerChannel ); @@ -6647,31 +6970,10 @@ static ivas_error renderSbaToBinauralRoom( { for ( Word16 chnl = 0; chnl < tmpRotBuffer.config.numChannels; ++chnl ) { - tmpRotBuffer.data_fx[chnl * tmpRotBuffer.config.numSamplesPerChannel + smpl] = (Word32) float_to_fix( tmpRotBuffer.data[chnl * tmpRotBuffer.config.numSamplesPerChannel + smpl], 13 ); - sbaInput->base.inputBuffer.data_fx[chnl * tmpRotBuffer.config.numSamplesPerChannel + smpl] = (Word32) float_to_fix( sbaInput->base.inputBuffer.data[chnl * tmpRotBuffer.config.numSamplesPerChannel + smpl], 13 ); - } - } - for ( i = 0; i < L_FRAME48k / MAX_PARAM_SPATIAL_SUBFRAMES; i++ ) - { - sbaInput->base.ctx.pHeadRotData->crossfade_fx[i] = (Word16) float_to_fix( sbaInput->base.ctx.pHeadRotData->crossfade[i], 14 ); - } - for ( i = 0; i < HEADROT_SHMAT_DIM; i++ ) - { - for ( j = 0; j < HEADROT_SHMAT_DIM; j++ ) - { - sbaInput->rot_gains_prev_fx[i][j] = (Word16) float_to_fix( sbaInput->rot_gains_prev[i][j], 14 ); - } - } - for ( subframe_idx = 0; subframe_idx < ( *hCombinedOrientationData )->num_subframes; subframe_idx++ ) - { - for ( i = 0; i < 3; i++ ) - { - for ( j = 0; j < 3; j++ ) - { - ( *sbaInput->base.ctx.pCombinedOrientationData )->Rmat_fx[subframe_idx][i][j] = (Word32) float_to_fix( ( *sbaInput->base.ctx.pCombinedOrientationData )->Rmat[subframe_idx][i][j], 30 ); - } + tmpRotBuffer.data_fx[chnl * tmpRotBuffer.config.numSamplesPerChannel + smpl] = (Word32) float_to_fix( tmpRotBuffer.data[chnl * tmpRotBuffer.config.numSamplesPerChannel + smpl], *outAudio.q_fact ); } } + if ( ( error = rotateFrameSba_fx( sbaInput->base.inputBuffer, sbaInput->base.inConfig, sbaInput->base.ctx.pHeadRotData, sbaInput->base.ctx.pCombinedOrientationData, sbaInput->rot_gains_prev_fx, @@ -6683,14 +6985,7 @@ static ivas_error renderSbaToBinauralRoom( { for ( Word16 chnl = 0; chnl < tmpRotBuffer.config.numChannels; ++chnl ) { - tmpRotBuffer.data[chnl * tmpRotBuffer.config.numSamplesPerChannel + smpl] = fix_to_float( tmpRotBuffer.data_fx[chnl * tmpRotBuffer.config.numSamplesPerChannel + smpl], 13 ); - } - } - for ( i = 0; i < HEADROT_SHMAT_DIM; i++ ) - { - for ( j = 0; j < HEADROT_SHMAT_DIM; j++ ) - { - sbaInput->rot_gains_prev[i][j] = fix_to_float( sbaInput->rot_gains_prev_fx[i][j], 14 ); + tmpRotBuffer.data[chnl * tmpRotBuffer.config.numSamplesPerChannel + smpl] = fix_to_float( tmpRotBuffer.data_fx[chnl * tmpRotBuffer.config.numSamplesPerChannel + smpl], *outAudio.q_fact ); } } } @@ -6715,18 +7010,12 @@ static ivas_error renderSbaToBinauralRoom( copyBufferTo2dArray( tmpMcBuffer, tmpCrendBuffer ); // Porting Crend_process function - Word16 nchan_out , nchan_in , subframe_len; + Word16 nchan_out, nchan_in; CREND_HANDLE hCrend; Word32 output_buffer_fx[MAX_OUTPUT_CHANNELS][L_FRAME48k]; Word32 *output_fx[MAX_OUTPUT_CHANNELS]; hCrend = sbaInput->crendWrapper->hCrend; - IVAS_REND_AudioConfigType inConfigType; - subframe_len = (Word16) ( *sbaInput->base.ctx.pOutSampleRate / ( FRAMES_PER_SEC * MAX_PARAM_SPATIAL_SUBFRAMES ) ); - Word16 gd_bits = find_guarded_bits_fx( subframe_len ); - Word16 exp = 15; - exp -= gd_bits; - Word16 nchan ; - inConfigType = getAudioConfigType( sbaInput->base.inConfig ); + Word16 nchan; if ( ( error = getAudioConfigNumChannels( outConfig, &nchan_out ) ) != IVAS_ERR_OK ) { @@ -6737,26 +7026,12 @@ static ivas_error renderSbaToBinauralRoom( { output_fx[i] = output_buffer_fx[i]; } - if ( hCrend->reflections != NULL ) - { - if ( hCrend->reflections->use_er == 1 && hCrend->reflections->is_ready == 1 ) - { - nchan = hCrend->reflections->shoebox_data.n_sources + 1; - for ( i = 0; i < 150; i++ ) - { - hCrend->reflections->shoebox_data.gains.data_fx[i] = (Word32) float_to_fix( hCrend->reflections->shoebox_data.gains.data[i], 15 ); - } - } - } - - nchan = nchan_in; for ( i = 0; i < nchan; i++ ) { for ( j = 0; j < L_FRAME48k; j++ ) { - - output_fx[i][j] = (Word32) float_to_fix( p_tmpCrendBuffer[i][j], exp ); + output_fx[i][j] = (Word32) float_to_fix( p_tmpCrendBuffer[i][j], *outAudio.q_fact ); } } /* call CREND */ @@ -6768,7 +7043,7 @@ static ivas_error renderSbaToBinauralRoom( } if ( hCrend->hReverb != NULL ) { - exp -= 2; + *outAudio.q_fact -= 2; } for ( i = 0; i < nchan_out; i++ ) { @@ -6776,7 +7051,7 @@ static ivas_error renderSbaToBinauralRoom( for ( j = 0; j < L_FRAME48k; j++ ) { - p_tmpCrendBuffer[i][j] = fix_to_float( output_fx[i][j], exp ); + p_tmpCrendBuffer[i][j] = fix_to_float( output_fx[i][j], *outAudio.q_fact ); } } accumulate2dArrayToBuffer( tmpCrendBuffer, &outAudio ); @@ -6785,7 +7060,6 @@ static ivas_error renderSbaToBinauralRoom( { free( tmpRotBuffer.data ); free( tmpRotBuffer.data_fx ); - free( sbaInput->base.inputBuffer.data_fx ); // for now keeping the buffer local } free( tmpMcBuffer.data ); @@ -6906,7 +7180,65 @@ static void renderSbaToMasa( return; } +#ifdef IVAS_FLOAT_FIXED +static ivas_error renderInputSba( + input_sba *sbaInput, + const AUDIO_CONFIG outConfig, + IVAS_REND_AudioBuffer outAudio ) +{ + ivas_error error; + IVAS_REND_AudioBuffer inAudio; + error = IVAS_ERR_OK; + inAudio = sbaInput->base.inputBuffer; + /*CREND_HANDLE hCrend; + const COMBINED_ORIENTATION_HANDLE *hCombinedOrientationData; + hCrend = sbaInput->crendWrapper->hCrend;*/ + + IF( NE_32( sbaInput->base.numNewSamplesPerChannel, outAudio.config.numSamplesPerChannel ) ) + { + return IVAS_ERROR( IVAS_ERR_INVALID_BUFFER_SIZE, "Mismatch between the number of input samples vs number of requested output samples - currently not allowed" ); + } + sbaInput->base.numNewSamplesPerChannel = 0; + + /* Apply input gain to new audio */ + v_multc_fixed( inAudio.data_fx, sbaInput->base.gain_fx, inAudio.data_fx, inAudio.config.numSamplesPerChannel * inAudio.config.numChannels ); + *outAudio.q_fact -= 1; // to compensate for the qfactor reduction in gain multiplication. + + /* set combined orientation subframe info to start info */ + ivas_combined_orientation_set_to_start_index( *( sbaInput->base.ctx.pCombinedOrientationData ) ); + + SWITCH( getAudioConfigType( outConfig ) ) + { + case IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED: + renderSbaToMc( sbaInput, outAudio ); + BREAK; + case IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS: + renderSbaToSba( sbaInput, outAudio ); + BREAK; + case IVAS_REND_AUDIO_CONFIG_TYPE_BINAURAL: + switch ( outConfig ) + { + case IVAS_AUDIO_CONFIG_BINAURAL: + error = renderSbaToBinaural( sbaInput, outConfig, outAudio ); + BREAK; + case IVAS_AUDIO_CONFIG_BINAURAL_ROOM_IR: + case IVAS_AUDIO_CONFIG_BINAURAL_ROOM_REVERB: + error = renderSbaToBinauralRoom( sbaInput, outConfig, outAudio ); + BREAK; + default: + return IVAS_ERR_INVALID_OUTPUT_FORMAT; + } + BREAK; + case IVAS_REND_AUDIO_CONFIG_TYPE_MASA: + renderSbaToMasa( sbaInput, outAudio ); + BREAK; + default: + return IVAS_ERR_INVALID_OUTPUT_FORMAT; + } + return error; +} +#else static ivas_error renderInputSba( input_sba *sbaInput, const AUDIO_CONFIG outConfig, @@ -6961,8 +7293,31 @@ static ivas_error renderInputSba( return error; } - - +#endif +#ifdef IVAS_FLOAT_FIXED +static ivas_error renderActiveInputsSba( + IVAS_REND_HANDLE hIvasRend, + IVAS_REND_AudioBuffer outAudio ) +{ + Word16 i; + input_sba *pCurrentInput; + ivas_error error; + + for ( i = 0, pCurrentInput = hIvasRend->inputsSba; i < RENDERER_MAX_SBA_INPUTS; ++i, ++pCurrentInput ) + { + IF ( pCurrentInput->base.inConfig == IVAS_AUDIO_CONFIG_INVALID ) + { + /* Skip inactive inputs */ + CONTINUE; + } + IF ( ( error = renderInputSba( pCurrentInput, hIvasRend->outputConfig, outAudio ) ) != IVAS_ERR_OK ) + { + return error; + } + } + return IVAS_ERR_OK; +} +#else static ivas_error renderActiveInputsSba( IVAS_REND_HANDLE hIvasRend, IVAS_REND_AudioBuffer outAudio ) @@ -6987,7 +7342,7 @@ static ivas_error renderActiveInputsSba( return IVAS_ERR_OK; } - +#endif static void copyMasaMetadataToDiracRenderer( MASA_METADATA_FRAME *meta, @@ -7428,7 +7783,147 @@ ivas_error IVAS_REND_SetIsmMetadataDelay( return IVAS_ERR_OK; } +#ifdef IVAS_FLOAT_FIXED +/*-------------------------------------------------------------------* + * getSamplesInternal() + * + * + *-------------------------------------------------------------------*/ + +static ivas_error getSamplesInternal( + IVAS_REND_HANDLE hIvasRend, /* i/o: Renderer handle */ + IVAS_REND_AudioBuffer outAudio /* i/o: buffer for output audio */ +) +{ + ivas_error error; + int16_t numOutChannels; + Word16 subframe_len, gd_bits,j; + if ( hIvasRend->inputsSba[0].base.inConfig != IVAS_AUDIO_CONFIG_INVALID ) + { + subframe_len = (Word16) ( *hIvasRend->inputsSba->base.ctx.pOutSampleRate / ( FRAMES_PER_SEC * MAX_PARAM_SPATIAL_SUBFRAMES ) ); + gd_bits = find_guarded_bits_fx( subframe_len ); + outAudio.q_fact = &outAudio.q_factor; + *outAudio.q_fact = 16; + *outAudio.q_fact -= gd_bits + 1; + for ( Word16 smpl = 0; smpl < hIvasRend->inputsSba->base.inputBuffer.config.numSamplesPerChannel; ++smpl ) + { + for ( Word16 chnl = 0; chnl < hIvasRend->inputsSba->base.inputBuffer.config.numChannels; ++chnl ) + { + hIvasRend->inputsSba->base.inputBuffer.data_fx[chnl * hIvasRend->inputsSba->base.inputBuffer.config.numSamplesPerChannel + smpl] = (Word32) float_to_fix( hIvasRend->inputsSba->base.inputBuffer.data[chnl * hIvasRend->inputsSba->base.inputBuffer.config.numSamplesPerChannel + smpl], *outAudio.q_fact ); + } + } + } + /* Validate function arguments */ + if ( hIvasRend == NULL || outAudio.data == NULL ) + { + return IVAS_ERR_UNEXPECTED_NULL_POINTER; + } + + if ( outAudio.config.numSamplesPerChannel <= 0 || MAX_BUFFER_LENGTH_PER_CHANNEL < outAudio.config.numSamplesPerChannel ) + { + return IVAS_ERR_INVALID_BUFFER_SIZE; + } + + if ( outAudio.config.numChannels <= 0 || MAX_OUTPUT_CHANNELS < outAudio.config.numChannels ) + { + return IVAS_ERR_WRONG_NUM_CHANNELS; + } + + if ( getAudioConfigType( hIvasRend->outputConfig ) == IVAS_REND_AUDIO_CONFIG_TYPE_BINAURAL && + outAudio.config.numSamplesPerChannel * 1000 != ( hIvasRend->num_subframes * BINAURAL_RENDERING_FRAME_SIZE_MS ) * hIvasRend->sampleRateOut ) + { + return IVAS_ERROR( IVAS_ERR_INVALID_BUFFER_SIZE, "Binaural rendering requires specific frame size" ); + } + + /* Check that there is allowed configuration for MASA format output */ + if ( getAudioConfigType( hIvasRend->outputConfig ) == IVAS_REND_AUDIO_CONFIG_TYPE_MASA ) + { + int16_t i; + int16_t numMasaInputs = 0; + int16_t numOtherInputs = 0; + + for ( i = 0; i < RENDERER_MAX_MASA_INPUTS; i++ ) + { + numMasaInputs += hIvasRend->inputsMasa[i].base.inConfig == IVAS_AUDIO_CONFIG_INVALID ? 0 : 1; + } + + for ( i = 0; i < RENDERER_MAX_MC_INPUTS; i++ ) + { + numOtherInputs += hIvasRend->inputsMc[i].base.inConfig == IVAS_AUDIO_CONFIG_INVALID ? 0 : 1; + } + + for ( i = 0; i < RENDERER_MAX_SBA_INPUTS; i++ ) + { + numOtherInputs += hIvasRend->inputsSba[i].base.inConfig == IVAS_AUDIO_CONFIG_INVALID ? 0 : 1; + } + + /* For ISM, we check only first as all ISMs are handled together via OMASA when merging to MASA. */ + numOtherInputs += hIvasRend->inputsIsm[0].base.inConfig == IVAS_AUDIO_CONFIG_INVALID ? 0 : 1; + + if ( numMasaInputs == 0 || numOtherInputs == 0 ) + { + return IVAS_ERR_IO_CONFIG_PAIR_NOT_SUPPORTED; + } + } + + if ( ( error = IVAS_REND_NumOutChannels( hIvasRend, &numOutChannels ) ) != IVAS_ERR_OK ) + { + return error; + } + + if ( numOutChannels != outAudio.config.numChannels ) + { + return IVAS_ERR_WRONG_NUM_CHANNELS; + } + + /* Clear original output buffer */ + set_zero( outAudio.data, outAudio.config.numChannels * outAudio.config.numSamplesPerChannel ); + set_val_Word32( outAudio.data_fx, 0, outAudio.config.numChannels * outAudio.config.numSamplesPerChannel ); + + if ( ( error = renderActiveInputsIsm( hIvasRend, outAudio ) ) != IVAS_ERR_OK ) + { + return error; + } + + if ( ( error = renderActiveInputsMc( hIvasRend, outAudio ) ) != IVAS_ERR_OK ) + { + return error; + } + if ( ( error = renderActiveInputsSba( hIvasRend, outAudio ) ) != IVAS_ERR_OK ) + { + return error; + } + if ( ( error = renderActiveInputsMasa( hIvasRend, outAudio ) ) != IVAS_ERR_OK ) + { + return error; + } + + if ( ( hIvasRend->inputsSba[0].base.inConfig != IVAS_AUDIO_CONFIG_INVALID ) && ( getAudioConfigType( hIvasRend->outputConfig ) == IVAS_REND_AUDIO_CONFIG_TYPE_BINAURAL ) && ( hIvasRend->outputConfig == IVAS_AUDIO_CONFIG_BINAURAL ) ) + { + +#ifndef DISABLE_LIMITER + Word32 limiter_thresold = L_lshl( IVAS_LIMITER_THRESHOLD, *outAudio.q_fact ); + limitRendererOutput_fx( hIvasRend->hLimiter, outAudio.data_fx, outAudio.config.numSamplesPerChannel, limiter_thresold, *outAudio.q_fact ); + for ( j = 0; j < outAudio.config.numChannels * outAudio.config.numSamplesPerChannel; j++ ) + { + outAudio.data[j] = fix_to_float( outAudio.data_fx[j], *outAudio.q_fact ); + } + +#endif + } + else + { +#ifndef DISABLE_LIMITER + limitRendererOutput( hIvasRend->hLimiter, outAudio.data, outAudio.config.numSamplesPerChannel, IVAS_LIMITER_THRESHOLD ); +#endif + } + /* update global cominbed orientation start index */ + ivas_combined_orientation_update_start_index( hIvasRend->hCombinedOrientationData, outAudio.config.numSamplesPerChannel ); + + return IVAS_ERR_OK; +} +#else /*-------------------------------------------------------------------* * getSamplesInternal() * @@ -7539,7 +8034,7 @@ static ivas_error getSamplesInternal( return IVAS_ERR_OK; } - +#endif /*-------------------------------------------------------------------* * IVAS_REND_GetSamples() diff --git a/lib_rend/lib_rend.h b/lib_rend/lib_rend.h index 9a20dc34b98aca1af0acc045efbe90d7f2733b92..cd6cd029d33f20c7da8bee41ec26d44864b1a675 100644 --- a/lib_rend/lib_rend.h +++ b/lib_rend/lib_rend.h @@ -76,6 +76,8 @@ typedef struct typedef struct { IVAS_REND_AudioBufferConfig config; + Word16 q_factor; + Word16 *q_fact; float *data; Word32 *data_fx; } IVAS_REND_AudioBuffer;