diff --git a/lib_com/options.h b/lib_com/options.h index ce0e122c4260aef18d6867c4cdc0eea93be1334f..2cf1458b54a34ef427040bd858ec60c6073d3f30 100644 --- a/lib_com/options.h +++ b/lib_com/options.h @@ -161,6 +161,8 @@ #define FIX_047_ERROR_ENABLING_AGC /* IVAS-047, Fix the error reporting when AGC is enabled*/ +#define REFACTOR_REVERB_INIT_ADD_CFG_CHECKS /* Simplify and improve reverb initialization + add range checks on config parameter values */ + /* ################## End DEVELOPMENT switches ######################### */ /* clang-format on */ #endif diff --git a/lib_dec/ivas_reverb.c b/lib_dec/ivas_reverb.c index e68cbc714fa672ff3494fc3ceb9cddd6254e2e84..fb28481f12ca8ff4b597b9183db793ec0ce1c57d 100644 --- a/lib_dec/ivas_reverb.c +++ b/lib_dec/ivas_reverb.c @@ -43,6 +43,7 @@ #include #include "wmops.h" +#ifndef REFACTOR_REVERB_INIT_ADD_CFG_CHECKS /*------------------------------------------------------------------------------------------* * Local constants @@ -1511,3 +1512,1379 @@ ivas_error ivas_reverb_process( return IVAS_ERR_OK; } + +#else /* REFACTOR_REVERB_INIT_ADD_CFG_CHECKS */ + +/*------------------------------------------------------------------------------------------* + * Local constants + *------------------------------------------------------------------------------------------*/ + +#define INNER_BLK_SIZE 80 /* size of data blocks used for more efficient delay line and IIR filter processing */ + /* should be a divisor of the frame length at any sampling rate and an even number*/ +#define FFT_FILTER_WND_FLAT_REGION ( 0.40f ) /* flat section (==1) length of FFT filter window, in proportion to overlap */ +#define FFT_FILTER_WND_TRANS_REGION ( 0.15f ) /* transition (1->0) length of FFT filter window, in proportion to overlap */ +#define REF_LF_MIN ( 100.0f ) +#define REF_LF_MAX ( 250.0f ) +#define REF_HF_MIN ( 5000.0f ) +#define REF_HF_MAX ( 7950.0f ) +#define LF_BIAS ( 0.5f ) + +#define DEFAULT_SRC_DIST ( 1.5f ) /* default source distance [m] for reverb dmx factor computing */ + +#define IVAS_REVERB_FFT_SIZE_48K ( 512 ) +#define IVAS_REVERB_FFT_SIZE_32K ( 512 ) +#define IVAS_REVERB_FFT_SIZE_16K ( 256 ) +#define IVAS_REVERB_FFT_N_SUBBLOCKS_48K ( 1 ) +#define IVAS_REVERB_FFT_N_SUBBLOCKS_32K ( 1 ) +#define IVAS_REVERB_FFT_N_SUBBLOCKS_16K ( 1 ) + +#define MAX_NR_OUTPUTS ( 2 ) + +const int16_t init_loop_delay[IVAS_REV_MAX_NR_BRANCHES] = { 37, 31, 29, 23, 19, 17, 13, 11 }; +const int16_t default_loop_delay_48k[IVAS_REV_MAX_NR_BRANCHES] = { 2309, 1861, 1523, 1259, 1069, 919, 809, 719 }; +const int16_t default_loop_delay_32k[IVAS_REV_MAX_NR_BRANCHES] = { 1531, 1237, 1013, 839, 709, 613, 541, 479 }; +const int16_t default_loop_delay_16k[IVAS_REV_MAX_NR_BRANCHES] = { 769, 619, 509, 421, 353, 307, 269, 239 }; + +/*------------------------------------------------------------------------------------------* + * Local Struct definition + *------------------------------------------------------------------------------------------*/ + +typedef struct ivas_reverb_params_t +{ + int16_t pre_delay; /* Delay of the FDC reverb, first peak after pre_delay samples. Note that */ + /* there may be non-zero samples earlier due to the filters being */ + /* linear-phase. */ + int16_t nr_loops; /* Number of feedback loops (= L) */ + int16_t pLoop_delays[IVAS_REV_MAX_NR_BRANCHES]; /* Delay for each feedback loop in samples. */ + float pLoop_feedback_matrix[IVAS_REV_MAX_NR_BRANCHES * IVAS_REV_MAX_NR_BRANCHES]; /* Feedback [L][L] matrix that mixes the signals of the loops. */ + int16_t nr_outputs; /* Nr of signals extracted from the loops (= S). */ + /* Currently this is fixed to 2. */ + float pLoop_extract_matrix[MAX_NR_OUTPUTS * IVAS_REV_MAX_NR_BRANCHES]; /* Mix [S][L] matrix from feedback loops to outputs. */ + /* In Matlab: [S x L] - Currently S=2, later may be more than 2 for speaker playback. */ + int16_t t60_filter_order; /* Filter order (length of vector) */ + float pT60_filter_coeff[MAX_NR_OUTPUTS * IVAS_REV_MAX_NR_BRANCHES * IVAS_REV_MAX_IIR_FILTER_LENGTH];/* Filters [][] in feedback loops, controlling T60. */ + /* In Matlab: IIR: [(2 * L) x ( + 1)] (odd: b-vector, even: a-vector) */ + /* In Matlab: FIR: [L x ] */ + float *pFc; /* Center frequencies for FFT filter design */ + float *pRt60; /* RT60 values at these frequencies */ + float *pDsr; /* DSR values at these frequencies */ + float *pHrtf_avg_pwr_response_l; /* The HRTF set's average left ear power response */ + float *pHrtf_avg_pwr_response_r; /* The HRTF set's average right ear power response */ + float *pHrtf_inter_aural_coherence; /* The HRTF set's inter-aural coherence for diffuse sound */ + const float *pHrtf_avg_pwr_response_l_const; /* The HRTF set's average left ear power response */ + const float *pHrtf_avg_pwr_response_r_const; /* The HRTF set's average right ear power response */ + const float *pHrtf_inter_aural_coherence_const; /* The HRTF set's inter-aural coherence for diffuse sound */ + + int16_t do_corr_filter; /* Flag indicating whether correlation filters should be used. */ + /* Correlation only supported and needed for binaural playback (i.e. */ + /* when nr_outputs != 2 correlation filtering is never supported). */ +} ivas_reverb_params_t; + + +/*------------------------------------------------------------------------------------------* + * Static functions declarations + *------------------------------------------------------------------------------------------*/ + +static ivas_error calc_jot_t60_coeffs( float *pH_dB, const uint16_t nrFrequencies, float *pFrequencies, float *pCoeffA, float *pCoeffB, float fNyquist ); + +/*-----------------------------------------------------------------------------------------* + * Function compute_feedback_matrix() + * + * Compute the N x N matrix for the mixing the N feedback loop outputs into the N inputs again + *-----------------------------------------------------------------------------------------*/ + +static ivas_error compute_feedback_matrix( + float *pFeedbackMatrix, + const int16_t n ) +{ + float u, v; + int16_t i, j, x; + + if ( n == 6 ) + { + /* special case (there is no 6 x 6 Hadamard matrix in set R) */ + u = -1.0f / 3; + v = 1.0f + u; + for ( i = 0; i < n; i++ ) + { + for ( j = 0; j < n; j++ ) + { + if ( i == j ) + { + pFeedbackMatrix[i * n + j] = v; + } + else + { + pFeedbackMatrix[i * n + j] = u; + } + } + } + } + else + { + if ( !( n == 4 || n == 8 || n == 16 ) ) + { + return IVAS_ERR_INTERNAL; /* n must be 4, 6, 8 or 16, else ERROR */ + } + u = 1.0f / sqrtf( n ); + if ( n == 4 ) + { + u = -u; + } + + pFeedbackMatrix[0] = u; + for ( x = 1; x < n; x += x ) + { + for ( i = 0; i < x; i++ ) + { + for ( j = 0; j < x; j++ ) + { + pFeedbackMatrix[( i + x ) * n + j] = pFeedbackMatrix[i * n + j]; + pFeedbackMatrix[i * n + j + x] = pFeedbackMatrix[i * n + j]; + pFeedbackMatrix[( i + x ) * n + j + x] = -pFeedbackMatrix[i * n + j]; + } + } + } + + if ( n == 4 ) + { + /* special case */ + for ( j = 12; j < 16; j++ ) + { + pFeedbackMatrix[j] = -pFeedbackMatrix[j]; + } + } + } + + return IVAS_ERR_OK; +} + + +/*-----------------------------------------------------------------------------------------* + * Function compute_2_out_extract_matrix() + * + * Compute the N x 2 matrix for mixing the N Jot feedback loops to 2 outputs + *-----------------------------------------------------------------------------------------*/ + +static void compute_2_out_extract_matrix( + float *pExtractMatrix, + const int16_t n ) +{ + float ff; + int16_t i; + + ff = 1.0; + for ( i = 0; i < n; i++ ) + { + pExtractMatrix[i] = 1.0; + pExtractMatrix[i + n] = ff; + ff = -ff; + } + + return; +} + + +/*-----------------------------------------------------------------------------------------* + * Function set_base_config() + * + * Set all jot reverb parameters that are independent of the input reverb configuration + *-----------------------------------------------------------------------------------------*/ + +static ivas_error set_base_config( + ivas_reverb_params_t *pParams, + const int32_t output_Fs ) +{ + ivas_error error; + int16_t loop_idx; + const int16_t *selected_loop_delay = NULL; + + if ( pParams == NULL ) + { + return IVAS_ERR_INTERNAL; + } + + /* TODO: values below could be reconsidered, and/or be made variable */ + pParams->pre_delay = 0; + pParams->nr_outputs = BINAURAL_CHANNELS; + pParams->nr_loops = IVAS_REV_MAX_NR_BRANCHES; + + /* set loop delays to default */ + if ( output_Fs == 48000 ) + { + selected_loop_delay = default_loop_delay_48k; + } + else if ( output_Fs == 32000 ) + { + selected_loop_delay = default_loop_delay_32k; + } + else if ( output_Fs == 16000 ) + { + selected_loop_delay = default_loop_delay_16k; + } + + for ( loop_idx = 0; loop_idx < pParams->nr_loops; loop_idx++ ) + { + pParams->pLoop_delays[loop_idx] = selected_loop_delay[loop_idx]; + } + + /* set feedback and output matrices */ + if ( ( error = compute_feedback_matrix( pParams->pLoop_feedback_matrix, pParams->nr_loops ) ) != IVAS_ERR_OK ) + { + return error; + } + + compute_2_out_extract_matrix( pParams->pLoop_extract_matrix, pParams->nr_loops ); + + /* pre-set the various filters; they will be set later based on reverb configuration */ + pParams->t60_filter_order = 1; /* set to 1 in base config. */ + + if ( pParams->nr_outputs == 2 ) + { + pParams->do_corr_filter = 1; + } + else + { + pParams->do_corr_filter = 0; + } + + return IVAS_ERR_OK; +} + + +/*-----------------------------------------------------------------------------------------* + * Function calc_dmx_gain() + * + * Computes the downmix gain + *-----------------------------------------------------------------------------------------*/ + +static float calc_dmx_gain() +{ + const float dist = DEFAULT_SRC_DIST; + return sqrtf( 4.0f * EVS_PI * dist * dist / 0.001f ); +} + + +/*-----------------------------------------------------------------------------------------* + * Function calc_predelay() + * + * Calculate the predelay, taking shortest jot loop delay into account + *-----------------------------------------------------------------------------------------*/ + +static void calc_predelay( + ivas_reverb_params_t *pParams, + float acoustic_predelay_sec, + const int32_t output_Fs ) +{ + int16_t predelay, fbdelay, output_frame; + + predelay = (int16_t) roundf( acoustic_predelay_sec * (float)output_Fs ); + output_frame = (int16_t) ( output_Fs / FRAMES_PER_SEC ); + fbdelay = pParams->pLoop_delays[pParams->nr_loops - 1]; + predelay -= fbdelay; + + if ( predelay < 0 ) + { + predelay = 0; + } + + if ( output_frame < predelay ) + { + predelay = output_frame; + } + + pParams->pre_delay = predelay; + + return; +} + + +/*-----------------------------------------------------------------------------------------* + * Function compute_t60_coeffs() + * + * Calculate Jot reverb's T60 filter coefficients + *-----------------------------------------------------------------------------------------*/ + +static ivas_error compute_t60_coeffs( + ivas_reverb_params_t *pParams, + int16_t nr_fc_fft_filter, + const int32_t output_Fs ) +{ + int16_t bin_idx, loop_idx, tf_T60_len, len; + float loop_delay_sec, freq_Nyquist, inv_hfs; + float target_gains_db[RV_LENGTH_NR_FC]; + float norm_f[RV_LENGTH_NR_FC]; + float *pCoeffs_a, *pCoeffs_b; + float *targetT60, *freqT60; + ivas_error error; + + targetT60 = pParams->pRt60; + freqT60 = pParams->pFc; + + error = IVAS_ERR_OK; + tf_T60_len = nr_fc_fft_filter; + len = pParams->t60_filter_order + 1; + freq_Nyquist = 0.5f * (float)output_Fs; + + /* normalize pFrequencies: 0 .. 1/2 output_Fs --> 0.0 .. 1.0 */ + inv_hfs = 1.0f / freq_Nyquist; + for ( bin_idx = 0; bin_idx < tf_T60_len; bin_idx++ ) + { + norm_f[bin_idx] = freqT60[bin_idx] * inv_hfs; + } + + for ( loop_idx = 0; loop_idx < pParams->nr_loops; loop_idx++ ) + { + loop_delay_sec = (float)pParams->pLoop_delays[loop_idx] / (float)output_Fs; + for ( bin_idx = 0; bin_idx < tf_T60_len; bin_idx++ ) + { + target_gains_db[bin_idx] = -60.0f * loop_delay_sec / targetT60[bin_idx]; + target_gains_db[bin_idx]= max( target_gains_db[bin_idx], -120.0f ); + } + + pCoeffs_a = &pParams->pT60_filter_coeff[2 * len * loop_idx + len]; + pCoeffs_b = &pParams->pT60_filter_coeff[2 * len * loop_idx]; + if ( ( error = calc_jot_t60_coeffs( target_gains_db, tf_T60_len, norm_f, pCoeffs_a, pCoeffs_b, freq_Nyquist ) ) != IVAS_ERR_OK ) + { + return error; + } + } + + // TODO: Compute proper (group) Delay from IIR filter + len = ( pParams->t60_filter_order + 1 ) >> 1; /* == floor( (order+1) / 2) */ + for ( loop_idx = 0; loop_idx < pParams->nr_loops; loop_idx++ ) + { + pParams->pLoop_delays[loop_idx] -= len; + } + + return error; +} + + +/*-----------------------------------------------------------------------------------------* + * Function calc_low_shelf_first_order_filter() + * + * Calculate 1st order low shelf filter + *-----------------------------------------------------------------------------------------*/ + +static void calc_low_shelf_first_order_filter( + float *pNum, + float *pDen, + const float f0, + const float lin_gain_lf, + const float lin_gain_hf ) +{ + float w0, gain; + + w0 = tanf( EVS_PI * f0 / 2.0f ); + gain = lin_gain_lf / lin_gain_hf; + + if ( gain < 1.0f ) + { + pNum[0] = 1 + w0 * gain; + pNum[1] = w0 * gain - 1; + pDen[0] = 1 + w0; + pDen[1] = w0 - 1; + } + else + { + pNum[0] = 1 + w0; + pNum[1] = w0 - 1; + pDen[0] = 1 + w0 / gain; + pDen[1] = w0 / gain - 1; + } + + /* Normalize and adjust gain to match target amplitudes */ + pNum[0] = ( pNum[0] / pDen[0] ) * lin_gain_hf; + pNum[1] = ( pNum[1] / pDen[0] ) * lin_gain_hf; + pDen[1] = pDen[1] / pDen[0]; + pDen[0] = 1.0f; + + return; +} + + +/*-----------------------------------------------------------------------------------------* + * Function calc_jot_t60_coeffs() + * + * Calculate Jot reverb's T60 filters + *-----------------------------------------------------------------------------------------*/ + +static ivas_error calc_jot_t60_coeffs( + float *pH_dB, + const uint16_t nrFrequencies, + float *pFrequencies, + float *pCoeffA, + float *pCoeffB, + float fNyquist ) +{ + const float ref_lf_min_norm = REF_LF_MIN / fNyquist; + const float ref_lf_max_norm = REF_LF_MAX / fNyquist; + const float ref_hf_min_norm = REF_HF_MIN / fNyquist; + const float ref_hf_max_norm = REF_HF_MAX / fNyquist; + int16_t f_idx, minidx; + float f0, tmp, minval, lf_target_gain_dB, hf_target_gain_dB, mid_crossing_gain_dB; + uint16_t n_points_lf, n_points_hf; + float lin_gain_lf, lin_gain_hf; + + minidx = nrFrequencies - 1; + minval = 1e+20f; + lf_target_gain_dB = 0.0f; + hf_target_gain_dB = 0.0f; + n_points_lf = 0; + n_points_hf = 0; + + for ( f_idx = 0; f_idx < nrFrequencies; f_idx++ ) + { + if ( ( pFrequencies[f_idx] >= ref_lf_min_norm ) && ( pFrequencies[f_idx] <= ref_lf_max_norm ) ) + { + lf_target_gain_dB += pH_dB[f_idx]; + n_points_lf++; + } + if ( ( pFrequencies[f_idx] >= ref_hf_min_norm ) && ( pFrequencies[f_idx] <= ref_hf_max_norm ) ) + { + hf_target_gain_dB += pH_dB[f_idx]; + n_points_hf++; + } + } + + if ( ( n_points_lf == 0 ) || ( n_points_hf == 0 ) ) + { + return IVAS_ERR_INTERNAL; + } + + lf_target_gain_dB = lf_target_gain_dB / (float) n_points_lf; + hf_target_gain_dB = hf_target_gain_dB / (float) n_points_hf; + mid_crossing_gain_dB = hf_target_gain_dB + LF_BIAS * ( lf_target_gain_dB - hf_target_gain_dB ); + + for ( f_idx = 1; f_idx < nrFrequencies - 1; f_idx++ ) + { + tmp = fabsf( pH_dB[f_idx] - mid_crossing_gain_dB ); + if ( tmp < minval ) + { + minval = tmp; + minidx = f_idx; + } + } + + f0 = pFrequencies[minidx]; + lin_gain_lf = powf( 10.0f, lf_target_gain_dB * 0.05f ); + lin_gain_hf = powf( 10.0f, hf_target_gain_dB * 0.05f ); + + /* call low-pass iir shelf */ + calc_low_shelf_first_order_filter( pCoeffB, pCoeffA, f0, lin_gain_lf, lin_gain_hf ); + + return IVAS_ERR_OK; +} + + +/*-----------------------------------------------------------------------------------------* + * Function initialize_reverb_filters() + * + * Set the number of branches (feedback loops) and Initializes the memory structure (pointers to data) + *-----------------------------------------------------------------------------------------*/ + +static ivas_error initialize_reverb_filters( + REVERB_HANDLE pState ) +{ + ivas_error error; + + error = IVAS_ERR_OK; + + /* init correlation and coloration filters */ + if ( ( error = ivas_reverb_t2f_f2t_init( &pState->fft_filter_ols, pState->fft_size, pState->fft_subblock_size ) ) != IVAS_ERR_OK ) + { + return error; + } + + if ( ( error = ivas_reverb_fft_filter_init( &pState->fft_filter_correl_0, pState->fft_size ) ) != IVAS_ERR_OK ) + { + return error; + } + + if ( ( error = ivas_reverb_fft_filter_init( &pState->fft_filter_correl_1, pState->fft_size ) ) != IVAS_ERR_OK ) + { + return error; + } + + if ( ( error = ivas_reverb_fft_filter_init( &pState->fft_filter_color_0, pState->fft_size ) ) != IVAS_ERR_OK ) + { + return error; + } + + if ( ( error = ivas_reverb_fft_filter_init( &pState->fft_filter_color_1, pState->fft_size ) ) != IVAS_ERR_OK ) + { + return error; + } + + return error; +} + + +/*-----------------------------------------------------------------------------------------* + * Function set_t60_filter() + * + * Sets t60 number of taps and coefficients A and B + *-----------------------------------------------------------------------------------------*/ + +static ivas_error set_t60_filter( + REVERB_HANDLE pState, + const uint16_t branch, + const uint16_t nr_taps, + float coefA[], + float coefB[] ) +{ + if ( branch >= pState->nr_of_branches ) + { + return IVAS_ERR_INTERNAL; + } + + if ( nr_taps > IVAS_REV_MAX_IIR_FILTER_LENGTH ) + { + return IVAS_ERR_INTERNAL; + } + + ivas_reverb_iir_filt_set( &( pState->t60[branch] ), nr_taps, coefA, coefB ); + + return IVAS_ERR_OK; +} + + +/*-----------------------------------------------------------------------------------------* + * Function set_feedback_delay() + * + * Sets Delay of feedback branch in number of samples + *-----------------------------------------------------------------------------------------*/ + +static ivas_error set_feedback_delay( + REVERB_HANDLE pState, + const uint16_t branch, + const int16_t fb_delay ) +{ + if ( branch >= pState->nr_of_branches ) + { + return IVAS_ERR_INTERNAL; + } + + pState->delay_line[branch].Delay = fb_delay; + + return IVAS_ERR_OK; +} + + +/*-----------------------------------------------------------------------------------------* + * Function set_feedback_gain() + * + * Sets nr_of_branches feedback gain values in feedback matrix + *-----------------------------------------------------------------------------------------*/ + +static ivas_error set_feedback_gain( + REVERB_HANDLE pState, + const uint16_t branch, + float *pGain ) +{ + uint16_t gain_idx; + if ( branch >= pState->nr_of_branches ) + { + return IVAS_ERR_INTERNAL; + } + + for ( gain_idx = 0; gain_idx < pState->nr_of_branches; gain_idx++ ) + { + pState->gain_matrix[branch][gain_idx] = pGain[gain_idx]; + } + + return IVAS_ERR_OK; +} + + +/*-----------------------------------------------------------------------------------------* + * Function set_correl_fft_filter() + * + * Sets correlation filter complex gains + *-----------------------------------------------------------------------------------------*/ + +static ivas_error set_correl_fft_filter( + REVERB_HANDLE pState, + const uint16_t channel, + rv_fftwf_type_complex *pSpectrum ) +{ + if ( channel > 1 ) + { + return IVAS_ERR_INTERNAL; + } + + if ( channel == 0 ) + { + ivas_reverb_fft_filter_ConvertFFTWF_2_FFTR( pSpectrum, pState->fft_filter_correl_0.fft_spectrum, pState->fft_filter_correl_0.fft_size ); + } + else + { + ivas_reverb_fft_filter_ConvertFFTWF_2_FFTR( pSpectrum, pState->fft_filter_correl_1.fft_spectrum, pState->fft_filter_correl_1.fft_size ); + } + + return IVAS_ERR_OK; +} + + +/*-----------------------------------------------------------------------------------------* + * Function set_color_fft_filter() + * + * Sets coloration filter complex gains + *-----------------------------------------------------------------------------------------*/ + +static ivas_error set_color_fft_filter( + REVERB_HANDLE pState, + const uint16_t channel, + rv_fftwf_type_complex *pSpectrum ) +{ + if ( channel > 1 ) + { + return IVAS_ERR_INTERNAL; + } + + if ( channel == 0 ) + { + ivas_reverb_fft_filter_ConvertFFTWF_2_FFTR( pSpectrum, pState->fft_filter_color_0.fft_spectrum, pState->fft_filter_color_0.fft_size ); + } + else + { + ivas_reverb_fft_filter_ConvertFFTWF_2_FFTR( pSpectrum, pState->fft_filter_color_1.fft_spectrum, pState->fft_filter_color_1.fft_size ); + } + + return IVAS_ERR_OK; +} + + +/*-----------------------------------------------------------------------------------------* + * Function set_mixer_level() + * + * Sets Mixer level: to mix 2 output channels from 8 feedback branches + *-----------------------------------------------------------------------------------------*/ + +static ivas_error set_mixer_level( + REVERB_HANDLE pState, + const uint16_t channel, + float level[] ) +{ + uint16_t branch_idx; + if ( channel >= BINAURAL_CHANNELS ) + { + return IVAS_ERR_INTERNAL; + } + + for ( branch_idx = 0; branch_idx < pState->nr_of_branches; branch_idx++ ) + { + pState->mixer[channel][branch_idx] = level[branch_idx]; + } + + return IVAS_ERR_OK; +} + + +/*-----------------------------------------------------------------------------------------* + * Function clear_buffers() + * + * Clears buffers of delay lines and filters + *-----------------------------------------------------------------------------------------*/ + +static void clear_buffers( + REVERB_HANDLE pState ) +{ + int16_t branch_idx; + ivas_rev_iir_filter_t *iirFilter; + ivas_rev_delay_line_t *delay_line; + + for ( branch_idx = 0; branch_idx < IVAS_REV_MAX_NR_BRANCHES; branch_idx++ ) + { + delay_line = &( pState->delay_line[branch_idx] ); + set_f( delay_line->pBuffer, 0, delay_line->MaxDelay ); + delay_line->BufferPos = 0; + + iirFilter = &( pState->t60[branch_idx] ); + set_f( iirFilter->pBuffer, 0, iirFilter->MaxTaps ); + } + + ivas_reverb_t2f_f2t_ClearHistory( &pState->fft_filter_ols ); + + return; +} + + +/*-----------------------------------------------------------------------------------------* +* Function set_fft_and_datablock_sizes() +* +* Sets frame size and fft-filter related sizes +*-----------------------------------------------------------------------------------------*/ + +static void set_fft_and_datablock_sizes( REVERB_HANDLE pState, int16_t subframe_len ) +{ + pState->full_block_size = subframe_len; + if ( subframe_len == L_FRAME48k / MAX_PARAM_SPATIAL_SUBFRAMES ) + { + pState->fft_size = IVAS_REVERB_FFT_SIZE_48K; + pState->num_fft_subblocks = IVAS_REVERB_FFT_N_SUBBLOCKS_48K; + } + else if ( subframe_len == L_FRAME32k / MAX_PARAM_SPATIAL_SUBFRAMES ) + { + pState->fft_size = IVAS_REVERB_FFT_SIZE_32K; + pState->num_fft_subblocks = IVAS_REVERB_FFT_N_SUBBLOCKS_32K; + } + else if ( subframe_len == L_FRAME16k / MAX_PARAM_SPATIAL_SUBFRAMES ) + { + pState->fft_size = IVAS_REVERB_FFT_SIZE_16K; + pState->num_fft_subblocks = IVAS_REVERB_FFT_N_SUBBLOCKS_16K; + } + else + { + assert( 0 ); /* unsupported block size */ + } + pState->fft_subblock_size = subframe_len / pState->num_fft_subblocks; +} + + +/*-----------------------------------------------------------------------------------------* +* Function set_reverb_acoustic_data() +* +* Sets reverb acoustic data (room acoustics and HRTF), interpolating it to the filter grid +*-----------------------------------------------------------------------------------------*/ + +static void set_reverb_acoustic_data( ivas_reverb_params_t * pParams, const HRTFS_HANDLE hHrtf, + ivas_roomAcoustics_t * pRoomAcoustics, int16_t subframe_len, int16_t nr_fc_input, int16_t nr_fc_fft_filter ) +{ + int16_t nr_out_ch, hrtf_idx, offset, iter_idx, bin_idx; + float ln_1e6_inverted, delay_diff, exp_argument; + float *pHrtf_set_l_re[IVAS_MAX_NUM_CH]; + float *pHrtf_set_l_im[IVAS_MAX_NUM_CH]; + float *pHrtf_set_r_re[IVAS_MAX_NUM_CH]; + float *pHrtf_set_r_im[IVAS_MAX_NUM_CH]; + + /* use crend hrtf filters */ + if ( hHrtf != NULL ) + { + /* Compute HRTF set properties: average left/right energies, IA coherence */ + /* First, find the offset of the frequency-domain data for the 1st frame and assign HRTF pointers */ + for ( nr_out_ch = 0; nr_out_ch < BINAURAL_CHANNELS; nr_out_ch++ ) + { + for ( hrtf_idx = 0; hrtf_idx < hHrtf->max_num_ir; hrtf_idx++ ) + { + offset = 0; + for ( iter_idx = 0; iter_idx < hHrtf->num_iterations[hrtf_idx][nr_out_ch] - 1; iter_idx++ ) + { + offset += hHrtf->pIndex_frequency_max[hrtf_idx][nr_out_ch][iter_idx]; + } + + if ( nr_out_ch == 0 ) + { + pHrtf_set_l_re[hrtf_idx] = &hHrtf->pOut_to_bin_re[hrtf_idx][0][offset]; + pHrtf_set_l_im[hrtf_idx] = &hHrtf->pOut_to_bin_im[hrtf_idx][0][offset]; + } + else + { + pHrtf_set_r_re[hrtf_idx] = &hHrtf->pOut_to_bin_re[hrtf_idx][1][offset]; + pHrtf_set_r_im[hrtf_idx] = &hHrtf->pOut_to_bin_im[hrtf_idx][1][offset]; + } + } + } + + /* Compute HRTF set properties using frequency-domain HRTF data */ + ivas_reverb_get_hrtf_set_properties( pHrtf_set_l_re, pHrtf_set_l_im, pHrtf_set_r_re, pHrtf_set_r_im, hHrtf->max_num_ir, subframe_len, + nr_fc_fft_filter, pParams->pHrtf_avg_pwr_response_l, pParams->pHrtf_avg_pwr_response_r, pParams->pHrtf_inter_aural_coherence ); + + pParams->pHrtf_avg_pwr_response_l_const = (const float *) pParams->pHrtf_avg_pwr_response_l; + pParams->pHrtf_avg_pwr_response_r_const = (const float *) pParams->pHrtf_avg_pwr_response_r; + pParams->pHrtf_inter_aural_coherence_const = (const float *) pParams->pHrtf_inter_aural_coherence; + } + else + { + pParams->pHrtf_avg_pwr_response_l_const = orange53_left_avg_power; + pParams->pHrtf_avg_pwr_response_r_const = orange53_right_avg_power; + pParams->pHrtf_inter_aural_coherence_const = orange53_coherence; + } + + /* interpolate input table data for T60 and DSR to the FFT filter grid */ + ivas_reverb_interpolate_acoustic_data( nr_fc_input, pRoomAcoustics->pFc_input, pRoomAcoustics->pAcoustic_rt60, pRoomAcoustics->pAcoustic_dsr, + nr_fc_fft_filter, pParams->pFc, pParams->pRt60, pParams->pDsr ); + + /* adjust DSR for the delay difference */ + delay_diff = pRoomAcoustics->inputPreDelay - pRoomAcoustics->acousticPreDelay; + ln_1e6_inverted = 1.0f / logf( 1e06f ); + for ( bin_idx = 0; bin_idx < nr_fc_fft_filter; bin_idx++ ) + { + exp_argument = delay_diff / ( pParams->pRt60[bin_idx] * ln_1e6_inverted ); + /* Limit exponent to approx +/-100 dB in case of incoherent value of delay_diff, to prevent overflow */ + exp_argument = min( exp_argument, 23.0f ); + exp_argument = max( exp_argument, -23.0f ); + pParams->pDsr[bin_idx] *= expf( exp_argument ); + } +} + + +/*-----------------------------------------------------------------------------------------* +* Function setup_FDN_branches() +* +* Sets up feedback delay network system +*-----------------------------------------------------------------------------------------*/ + +static ivas_error setup_FDN_branches( REVERB_HANDLE pState, ivas_reverb_params_t * pParams ) +{ + int16_t nr_coefs, branch_idx, channel_idx; + ivas_error error; + float *pCoef_a, *pCoef_b; + error = IVAS_ERR_OK; + + /* initialize feedback branches */ + for ( branch_idx = 0; branch_idx < IVAS_REV_MAX_NR_BRANCHES; branch_idx++ ) + { + ivas_rev_delay_line_init( &( pState->delay_line[branch_idx] ), pState->loop_delay_buffer[branch_idx], init_loop_delay[branch_idx], pParams->pLoop_delays[branch_idx] ); + ivas_reverb_iir_filt_init( &( pState->t60[branch_idx] ), IVAS_REV_MAX_IIR_FILTER_LENGTH ); + pState->mixer[0][branch_idx] = 0.0f; + pState->mixer[1][branch_idx] = 0.0f; + } + clear_buffers( pState ); + nr_coefs = pParams->t60_filter_order + 1; + + if ( IVAS_REV_MAX_IIR_FILTER_LENGTH < nr_coefs ) + { + return IVAS_ERR_INTERNAL; + } + else + { + for ( branch_idx = 0; branch_idx < pParams->nr_loops; branch_idx++ ) + { + pCoef_a = &pParams->pT60_filter_coeff[2 * nr_coefs * branch_idx + nr_coefs]; + pCoef_b = &pParams->pT60_filter_coeff[2 * nr_coefs * branch_idx]; + + if ( ( error = set_t60_filter( pState, branch_idx, nr_coefs, pCoef_a, pCoef_b ) ) != IVAS_ERR_OK ) + { + return error; + } + + if ( ( error = set_feedback_delay( pState, branch_idx, pParams->pLoop_delays[branch_idx] ) ) != IVAS_ERR_OK ) + { + return error; + } + + if ( ( error = set_feedback_gain( pState, branch_idx, &( pParams->pLoop_feedback_matrix[branch_idx * pParams->nr_loops] ) ) ) != IVAS_ERR_OK ) + { + return error; + } + } + } + + for ( channel_idx = 0; channel_idx < pParams->nr_outputs; channel_idx++ ) + { + if ( ( error = set_mixer_level( pState, channel_idx, &( pParams->pLoop_extract_matrix[channel_idx * pParams->nr_loops] ) ) ) != IVAS_ERR_OK ) + { + return error; + } + } + + return error; +} + +/*------------------------------------------------------------------------- + * ivas_reverb_open() + * + * Allocate and initialize Crend reverberation handle + *------------------------------------------------------------------------*/ + +ivas_error ivas_reverb_open( + REVERB_HANDLE *hReverb, /* i/o: Reverberator handle */ + const HRTFS_HANDLE hHrtf, /* i : HRTF handle */ + RENDER_CONFIG_HANDLE hRenderConfig, /* i : Renderer configuration handle */ + const int32_t output_Fs /* i : output sampling rate */ +) +{ + ivas_error error; + REVERB_HANDLE pState = NULL; + int16_t bin_idx, subframe_len, output_frame, predelay_bf_len, loop_idx; + ivas_reverb_params_t params; + rv_fftwf_type_complex pFft_wf_filter_ch0[RV_LENGTH_NR_FC]; + rv_fftwf_type_complex pFft_wf_filter_ch1[RV_LENGTH_NR_FC]; + float pColor_target_l[RV_LENGTH_NR_FC]; + float pColor_target_r[RV_LENGTH_NR_FC]; + float pTime_window[RV_FILTER_MAX_FFT_SIZE]; + float freq_step; + int16_t fft_hist_size, transition_start, transition_length; + int16_t nr_fc_input, nr_fc_fft_filter; + + error = IVAS_ERR_OK; + output_frame = (int16_t) ( output_Fs / FRAMES_PER_SEC ); + subframe_len = output_frame / MAX_PARAM_SPATIAL_SUBFRAMES; + predelay_bf_len = output_frame; + nr_fc_input = hRenderConfig->roomAcoustics.nBands; + + /* Allocate main reverb. handle */ + if ( ( pState = (REVERB_HANDLE) count_malloc( sizeof( REVERB_DATA ) ) ) == NULL ) + { + return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for Crend Reverberator " ); + } + + if ( ( error = set_base_config( ¶ms, output_Fs ) ) != IVAS_ERR_OK ) + { + return error; + } + + /* Allocate memory for feedback delay lines */ + for ( loop_idx = 0; loop_idx < IVAS_REV_MAX_NR_BRANCHES; loop_idx++ ) + { + if ( ( pState->loop_delay_buffer[loop_idx] = (float *) count_malloc( params.pLoop_delays[loop_idx] * sizeof( float ) ) ) == NULL ) + { + return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for CREND Reverberator" ); + } + } + + /* Allocate memory for the pre-delay delay line */ + if ( ( pState->pPredelay_buffer = (float *) count_malloc( output_frame * sizeof( float ) ) ) == NULL ) + { + return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for CREND Reverberator" ); + } + + pState->nr_of_branches = IVAS_REV_MAX_NR_BRANCHES; + set_fft_and_datablock_sizes( pState, subframe_len ); + nr_fc_fft_filter = ( pState->fft_size >> 1 ) + 1; + + /* === 'Control logic': compute the reverb processing parameters from the === */ + /* === room, source and listener acoustic information provided in the reverb config === */ + /* Setting up shared temporary buffers for fc, RT60, DSR, etc. */ + params.pHrtf_avg_pwr_response_l = &pFft_wf_filter_ch0[0][0]; + params.pHrtf_avg_pwr_response_r = params.pHrtf_avg_pwr_response_l + nr_fc_fft_filter; + params.pRt60 = &pFft_wf_filter_ch1[0][0]; + params.pDsr = params.pRt60 + nr_fc_fft_filter; + params.pFc = &pState->fft_filter_color_0.fft_spectrum[0]; + params.pHrtf_inter_aural_coherence = &pState->fft_filter_color_1.fft_spectrum[0]; + + /* Note: these temp buffers can only be used before the final step of the FFT filter design : */ + /* before calls to ivas_reverb_calc_correl_filters(...) or to ivas_reverb_calc_color_filters(...) */ + + /* set the uniform frequency grid for FFT filtering */ + freq_step = 0.5f * output_Fs / ( nr_fc_fft_filter - 1 ); + for ( bin_idx = 0; bin_idx < nr_fc_fft_filter; bin_idx++ ) + { + params.pFc[bin_idx] = freq_step * bin_idx; + } + + /* set up reverb acoustic data on the basis of HRTF data and renderer config */ + set_reverb_acoustic_data( ¶ms, hHrtf, &hRenderConfig->roomAcoustics, subframe_len, nr_fc_input, nr_fc_fft_filter ); + + /* set up input downmix */ + pState->dmx_gain = calc_dmx_gain(); + + /* set up predelay - must be after set_base_config() and before compute_t60_coeffs() */ + calc_predelay( ¶ms, hRenderConfig->roomAcoustics.acousticPreDelay, output_Fs ); + + /* set up jot reverb 60 filters - must be set up after set_reverb_acoustic_data() */ + if ( ( error = compute_t60_coeffs( ¶ms, nr_fc_fft_filter, output_Fs ) ) != IVAS_ERR_OK ) + { + return error; + } + + /* Compute target levels (gains) for the coloration filters */ + ivas_reverb_calc_color_levels( output_Fs, nr_fc_fft_filter, params.nr_loops, params.pFc, params.pDsr, params.pHrtf_avg_pwr_response_l_const, params.pHrtf_avg_pwr_response_r_const, + params.pLoop_delays, params.pT60_filter_coeff, pColor_target_l, pColor_target_r ); + + /* Defining appropriate windowing parameters for FFT filters to prevent aliasing */ + fft_hist_size = pState->fft_size - pState->fft_subblock_size; + transition_start = (int16_t) round( FFT_FILTER_WND_FLAT_REGION * fft_hist_size ); + transition_length = (int16_t) round( FFT_FILTER_WND_TRANS_REGION * fft_hist_size ); + + /* Compute the window used for FFT filters */ + ivas_reverb_define_window_fft( pTime_window, transition_start, transition_length, nr_fc_fft_filter ); + + + /* === Now, copy parameters from ivas_reverb_params_t into DSP blocks === */ + /* === to be used for subsequent audio signal processing === */ + + pState->do_corr_filter = params.do_corr_filter; + + /* clear & init jot reverb fft filters */ + if ( ( error = initialize_reverb_filters( pState ) ) != IVAS_ERR_OK ) + { + return error; + } + + if ( pState->do_corr_filter ) + { + /* Computing correlation filters on the basis of target IA coherence */ + ivas_reverb_calc_correl_filters( params.pHrtf_inter_aural_coherence_const, pTime_window, pState->fft_size, 0.0f, pFft_wf_filter_ch0, pFft_wf_filter_ch1 ); + + /* Copying the computed FFT correlation filters to the fft_filter components */ + if ( ( error = set_correl_fft_filter( pState, 0, pFft_wf_filter_ch0 ) ) != IVAS_ERR_OK ) + { + return error; + } + + if ( ( error = set_correl_fft_filter( pState, 1, pFft_wf_filter_ch1 ) ) != IVAS_ERR_OK ) + { + return error; + } + } + + /* Computing coloration filters on the basis of target responses */ + ivas_reverb_calc_color_filters( pColor_target_l, pColor_target_r, pTime_window, pState->fft_size, 0.0f, pFft_wf_filter_ch0, pFft_wf_filter_ch1 ); + + /* Copying the computed FFT colorations filters to the fft_filter components */ + if ( ( error = set_color_fft_filter( pState, 0, pFft_wf_filter_ch0 ) ) != IVAS_ERR_OK ) + { + return error; + } + + if ( ( error = set_color_fft_filter( pState, 1, pFft_wf_filter_ch1 ) ) != IVAS_ERR_OK ) + { + return error; + } + + /* init predelay */ + ivas_rev_delay_line_init( &( pState->predelay_line ), pState->pPredelay_buffer, params.pre_delay, predelay_bf_len ); + + /* set up feedback delay network */ + if ( ( error = setup_FDN_branches( pState, ¶ms ) ) != IVAS_ERR_OK ) + { + return error; + } + + *hReverb = pState; + return error; +} + + +/*------------------------------------------------------------------------- + * ivas_reverb_close() + * + * Deallocate Crend reverberation handle + *------------------------------------------------------------------------*/ + +void ivas_reverb_close( + REVERB_HANDLE *hReverb_in /* i/o: Reverberator handle */ +) +{ + REVERB_HANDLE hReverb; + int16_t loop_idx; + + hReverb = *hReverb_in; + + if ( hReverb != NULL ) + { + for ( loop_idx = 0; loop_idx < IVAS_REV_MAX_NR_BRANCHES; loop_idx++ ) + { + if ( hReverb->loop_delay_buffer[loop_idx] != NULL ) + { + count_free( hReverb->loop_delay_buffer[loop_idx] ); + hReverb->loop_delay_buffer[loop_idx] = NULL; + } + } + + count_free( hReverb->pPredelay_buffer ); + hReverb->pPredelay_buffer = NULL; + + count_free( hReverb ); + hReverb = NULL; + } + + return; +} + + +/*-----------------------------------------------------------------------------------------* + * Function post_fft_filter() + * + * + *-----------------------------------------------------------------------------------------*/ + +static void post_fft_filter( + REVERB_HANDLE pState, + float *p0, + float *p1, + float *pBuffer_0, + float *pBuffer_1 ) +{ + if ( pState->do_corr_filter ) + { + ivas_reverb_t2f_f2t_in( &pState->fft_filter_ols, p0, p1, pBuffer_0, pBuffer_1 ); + ivas_reverb_fft_filter_ComplexMul( &pState->fft_filter_correl_0, pBuffer_0 ); + ivas_reverb_fft_filter_ComplexMul( &pState->fft_filter_correl_1, pBuffer_1 ); + ivas_reverb_fft_filter_CrossMix( pBuffer_0, pBuffer_1, pState->fft_filter_correl_0.fft_size ); + } + else + { + ivas_reverb_t2f_f2t_in( &pState->fft_filter_ols, p0, p1, pBuffer_0, pBuffer_1 ); + } + + ivas_reverb_fft_filter_ComplexMul( &pState->fft_filter_color_0, pBuffer_0 ); + ivas_reverb_fft_filter_ComplexMul( &pState->fft_filter_color_1, pBuffer_1 ); + ivas_reverb_t2f_f2t_out( &pState->fft_filter_ols, pBuffer_0, pBuffer_1, p0, p1 ); + + return; +} + + +/*-----------------------------------------------------------------------------------------* + * Function reverb_block() + * + * Input a block (mono) and calculate the 2 output blocks. + *-----------------------------------------------------------------------------------------*/ + +static void reverb_block( + REVERB_HANDLE pState, + float *pInput, + float *pOut0, + float *pOut1 ) + +{ + uint16_t nr_branches = pState->nr_of_branches; + uint16_t bsize = pState->full_block_size; + uint16_t inner_bsize = INNER_BLK_SIZE; + uint16_t i, j, k, ns, branch_idx, blk_idx, start_sample_idx; + + float *pFFT_buf[2], FFT_buf_1[RV_FILTER_MAX_FFT_SIZE], FFT_buf_2[RV_FILTER_MAX_FFT_SIZE]; + float pFeedback_input[INNER_BLK_SIZE]; + float pTemp[INNER_BLK_SIZE]; + float *ppOutput[IVAS_REV_MAX_NR_BRANCHES]; + float Output[IVAS_REV_MAX_NR_BRANCHES][INNER_BLK_SIZE]; + + pFFT_buf[0] = &FFT_buf_1[0]; + pFFT_buf[1] = &FFT_buf_2[0]; + + for ( branch_idx = 0; branch_idx < nr_branches; branch_idx++ ) + { + ppOutput[branch_idx] = (float *) Output + branch_idx * inner_bsize; + } + for ( k = 0; k < bsize; k += inner_bsize ) + { + float *pO0 = &pOut0[k]; + float *pO1 = &pOut1[k]; + for ( i = 0; i < inner_bsize; i++ ) + { + pO0[i] = 0.0f; + pO1[i] = 0.0f; + } + + /* feedback network: */ + for ( i = 0; i < nr_branches; i++ ) + { + float *pOutput_i = &ppOutput[i][0]; + float mixer_0_i = pState->mixer[0][i]; + float mixer_1_i = pState->mixer[1][i]; + + /* output and feedback are same, get sample from delay line ... */ + ivas_rev_delay_line_get_sample_blk( &( pState->delay_line[i] ), inner_bsize, pTemp ); + ivas_reverb_iir_filt_2taps_feed_blk( &( pState->t60[i] ), inner_bsize, pTemp, ppOutput[i] ); + for ( ns = 0; ns < inner_bsize; ns++ ) + { + pO0[ns] += pOutput_i[ns] * mixer_0_i; /* mixer ch 0 */ + pO1[ns] += pOutput_i[ns] * mixer_1_i; /* mixer ch 1 */ + } + } + + for ( i = 0; i < nr_branches; i++ ) + { + float *pIn = &pInput[k]; + for ( ns = 0; ns < inner_bsize; ns++ ) + { + pFeedback_input[ns] = pIn[ns]; + } + for ( j = 0; j < nr_branches; j++ ) + { + float gain_matrix_j_i = pState->gain_matrix[j][i]; + float *pOutput = &ppOutput[j][0]; + for ( ns = 0; ns < inner_bsize; ns++ ) + { + pFeedback_input[ns] += gain_matrix_j_i * pOutput[ns]; + } + } + ivas_rev_delay_line_feed_sample_blk( &( pState->delay_line[i] ), inner_bsize, pFeedback_input ); + } + } + + /* Applying FFT filter to each sub-frame */ + for ( blk_idx = 0; blk_idx < pState->num_fft_subblocks; blk_idx++ ) + { + start_sample_idx = blk_idx * pState->fft_subblock_size; + post_fft_filter( pState, pOut0 + start_sample_idx, pOut1 + start_sample_idx, pFFT_buf[0], pFFT_buf[1] ); + } + + return; +} + + +/*-----------------------------------------------------------------------------------------* + * Function downmix_input_block() + * + * Downmix input to mono, taking also DSR gain into account + *-----------------------------------------------------------------------------------------*/ + +static ivas_error downmix_input_block( + REVERB_HANDLE pState, + float pcm_in[][L_FRAME48k], + const AUDIO_CONFIG transport_config, + float *pPcm_out, + const int16_t input_offset ) +{ + int16_t i, s, nchan_transport; + float dmx_gain = pState->dmx_gain; + + switch ( transport_config ) + { + case AUDIO_CONFIG_STEREO: + case AUDIO_CONFIG_5_1: + case AUDIO_CONFIG_7_1: + case AUDIO_CONFIG_5_1_2: + case AUDIO_CONFIG_5_1_4: + case AUDIO_CONFIG_7_1_4: + case AUDIO_CONFIG_ISM1: + case AUDIO_CONFIG_ISM2: + case AUDIO_CONFIG_ISM3: + case AUDIO_CONFIG_ISM4: + { + nchan_transport = audioCfg2channels( transport_config ); + for ( s = 0; s < pState->full_block_size; s++ ) + { + float temp = pcm_in[0][input_offset + s]; + for ( i = 1; i < nchan_transport; i++ ) + { + temp += pcm_in[i][input_offset + s]; + } + pPcm_out[s] = dmx_gain * temp; + } + break; + } + case AUDIO_CONFIG_MONO: /* ~'ZOA_1' */ + case AUDIO_CONFIG_FOA: + case AUDIO_CONFIG_HOA2: + case AUDIO_CONFIG_HOA3: + { + for ( s = 0; s < pState->full_block_size; s++ ) + { + pPcm_out[s] = dmx_gain * pcm_in[0][input_offset + s]; + } + break; + } + default: + return IVAS_ERROR( IVAS_ERR_INTERNAL_FATAL, "Unsupported input format for reverb" ); + break; + } + + return IVAS_ERR_OK; +} + + +/*-----------------------------------------------------------------------------------------* + * Function predelay_block() + * + * Perform a predelay + *-----------------------------------------------------------------------------------------*/ + +static void predelay_block( + REVERB_HANDLE pState, + float *pInput, + float *pOutput ) +{ + uint16_t i, idx, n_samples, blk_size; + uint16_t max_blk_size = (uint16_t) pState->predelay_line.Delay; + + if ( max_blk_size < 2 ) + { + if ( max_blk_size == 0 ) /* zero-length delay line: just copy the data from input to output */ + { + for ( i = 0; i < pState->full_block_size; i++ ) + { + pOutput[i] = pInput[i]; + } + } + else /* 1-sample length delay line: feed the data sample-by-sample */ + { + for ( i = 0; i < pState->full_block_size; i++ ) + { + pOutput[i] = ivas_rev_delay_line_get_sample( &( pState->predelay_line ) ); + ivas_rev_delay_line_feed_sample( &( pState->predelay_line ), pInput[i] ); + } + } + } + else /* multiple-sample length delay line: use block processing */ + { + idx = 0; + n_samples = pState->full_block_size; + while ( n_samples > 0 ) + { + blk_size = n_samples; + if ( blk_size > max_blk_size ) + { + blk_size = max_blk_size; + } + ivas_rev_delay_line_get_sample_blk( &( pState->predelay_line ), blk_size, &pOutput[idx] ); + ivas_rev_delay_line_feed_sample_blk( &( pState->predelay_line ), blk_size, &pInput[idx] ); + idx += blk_size; + n_samples -= blk_size; + } + } + + return; +} + + +/*-----------------------------------------------------------------------------------------* + * Function mix_output_block() + * + * mix one block of *pInL and *pInR samples into *pOutL and *pOutL respectively + *-----------------------------------------------------------------------------------------*/ + +static void mix_output_block( + REVERB_HANDLE pState, + const float *pInL, + const float *pInR, + float *pOutL, + float *pOutR ) +{ + uint16_t i; + + for ( i = 0; i < pState->full_block_size; i++ ) + { + pOutL[i] += pInL[i]; + pOutR[i] += pInR[i]; + } + + return; +} + + +/*-----------------------------------------------------------------------------------------* + * ivas_reverb_process() + * + * Process the input PCM audio into output PCM audio, applying reverb + *-----------------------------------------------------------------------------------------*/ + +ivas_error ivas_reverb_process( + REVERB_HANDLE hReverb, /* i/o: reverb state */ + const AUDIO_CONFIG transport_config, /* i : reverb. input audio configuration */ + const int16_t mix_signals, /* i : add reverb to output signal */ + float pcm_in[][L_FRAME48k], /* i : the PCM audio to apply reverb on */ + float pcm_out[][L_FRAME48k], /* o : the PCM audio with reverb applied */ + const int16_t i_ts ) +{ + float tmp0[L_FRAME48k / MAX_PARAM_SPATIAL_SUBFRAMES], tmp1[L_FRAME48k / MAX_PARAM_SPATIAL_SUBFRAMES], tmp2[L_FRAME48k / MAX_PARAM_SPATIAL_SUBFRAMES]; + ivas_error error; + + if ( ( error = downmix_input_block( hReverb, pcm_in, transport_config, tmp1, i_ts * hReverb->full_block_size ) ) != IVAS_ERR_OK ) + { + return error; + } + + predelay_block( hReverb, tmp1, tmp0 ); + + reverb_block( hReverb, tmp0, tmp1, tmp2 ); + + if ( mix_signals ) + { + mix_output_block( hReverb, tmp1, tmp2, &pcm_out[0][i_ts * hReverb->full_block_size], &pcm_out[1][i_ts * hReverb->full_block_size] ); + } + else + { + mvr2r( tmp1, &pcm_out[0][i_ts * hReverb->full_block_size], hReverb->full_block_size ); + mvr2r( tmp2, &pcm_out[1][i_ts * hReverb->full_block_size], hReverb->full_block_size ); + } + + return IVAS_ERR_OK; +} + +#endif /* REFACTOR_REVERB_INIT_ADD_CFG_CHECKS */ diff --git a/lib_dec/ivas_reverb_filter_design.c b/lib_dec/ivas_reverb_filter_design.c index a101a2308b05cfe12e34a741673334146524ad50..e0dabaa73434c45bff61427baeb4c1f8c7d62123 100644 --- a/lib_dec/ivas_reverb_filter_design.c +++ b/lib_dec/ivas_reverb_filter_design.c @@ -540,9 +540,15 @@ void ivas_reverb_calc_color_levels( const float alpha = -log__0_001 / t60[freq_idx]; const float revPredNormEnergy = ( A0_square_est / ( 2.0f * alpha ) ) * output_Fs / ( 0.8776f * minDelayDiff + 26.7741f ); +#ifndef REFACTOR_REVERB_INIT_ADD_CFG_CHECKS pTarget_color_L[freq_idx] = sqrtf( pAcoustic_dsr[freq_idx] * revEnergyFactor * pHrtf_avg_pwr_L[freq_idx] ) / sqrtf( revPredNormEnergy ); pTarget_color_R[freq_idx] = sqrtf( pAcoustic_dsr[freq_idx] * revEnergyFactor * pHrtf_avg_pwr_R[freq_idx] ) / sqrtf( revPredNormEnergy ); +#else /* REFACTOR_REVERB_INIT_ADD_CFG_CHECKS */ + pTarget_color_L[freq_idx] = sqrtf( pAcoustic_dsr[freq_idx] * revEnergyFactor * pHrtf_avg_pwr_L[freq_idx] ) / max( sqrtf( revPredNormEnergy ), EPS ); + + pTarget_color_R[freq_idx] = sqrtf( pAcoustic_dsr[freq_idx] * revEnergyFactor * pHrtf_avg_pwr_R[freq_idx] ) / max( sqrtf( revPredNormEnergy ), EPS ); +#endif /* REFACTOR_REVERB_INIT_ADD_CFG_CHECKS */ } /* Limiting the frequency response gradients diff --git a/lib_dec/ivas_reverb_utils.c b/lib_dec/ivas_reverb_utils.c index bfb48f735b769cd49291f877b96b5edd7f747f04..9e528a730077c990feb59b97161b9f0c548fb313 100644 --- a/lib_dec/ivas_reverb_utils.c +++ b/lib_dec/ivas_reverb_utils.c @@ -94,8 +94,12 @@ void ivas_reverb_prepare_cldfb_params( float avg_pwr_left[CLDFB_NO_CHANNELS_MAX]; float avg_pwr_right[CLDFB_NO_CHANNELS_MAX]; +#ifndef REFACTOR_REVERB_INIT_ADD_CFG_CHECKS float delay_diff; float ln_1e6_inverted; +#else /* REFACTOR_REVERB_INIT_ADD_CFG_CHECKS */ + float delay_diff, ln_1e6_inverted, exp_argument; +#endif /* REFACTOR_REVERB_INIT_ADD_CFG_CHECKS */ const float dist = DEFAULT_SRC_DIST; const float dmx_gain_2 = 4.0f * EVS_PI * dist * dist / 0.001f; @@ -111,8 +115,16 @@ void ivas_reverb_prepare_cldfb_params( ln_1e6_inverted = 1.0f / logf( 1e06f ); for ( idx = 0; idx < CLDFB_NO_CHANNELS_MAX; idx++ ) { +#ifndef REFACTOR_REVERB_INIT_ADD_CFG_CHECKS const float tau = pOutput_t60[idx] * ln_1e6_inverted; pOutput_ene[idx] *= expf( delay_diff / tau ); +#else /* REFACTOR_REVERB_INIT_ADD_CFG_CHECKS */ + exp_argument = delay_diff / ( pOutput_t60[idx] * ln_1e6_inverted ); + /* Limit exponent to approx +/-100 dB in case of incoherent value of delay_diff, to prevent overflow */ + exp_argument = min( exp_argument, 23.0f ); + exp_argument = max( exp_argument, -23.0f ); + pOutput_ene[idx] *= expf( exp_argument ); +#endif /* REFACTOR_REVERB_INIT_ADD_CFG_CHECKS */ } ivas_reverb_get_fastconv_hrtf_set_energies( renderer_config, use_brir, output_Fs, avg_pwr_left, avg_pwr_right ); diff --git a/lib_dec/lib_dec.c b/lib_dec/lib_dec.c index 26a0bb0c925cc9112b2d34e06426407d67d84e5f..35fca2dfad497025d72faf9f67618bcba8aa4b7f 100644 --- a/lib_dec/lib_dec.c +++ b/lib_dec/lib_dec.c @@ -256,6 +256,13 @@ void IVAS_DEC_Close( ( *phIvasDec )->st_ivas->hHeadTrackData = NULL; } +#ifdef REFACTOR_REVERB_INIT_ADD_CFG_CHECKS + if ( ( *phIvasDec )->st_ivas->hRenderConfig != NULL ) + { + ivas_render_config_close( &( ( *phIvasDec )->st_ivas->hRenderConfig ) ); + } +#endif /* REFACTOR_REVERB_INIT_ADD_CFG_CHECKS */ + ivas_HRTF_binary_close( &( *phIvasDec )->st_ivas->hHrtfTD ); count_free( ( *phIvasDec )->st_ivas ); diff --git a/lib_util/render_config_reader.c b/lib_util/render_config_reader.c index a9027bf92adc41e7fc6d33ef6e401f86835d416a..4e61f7319adc55bf81df61b5161cf8e4a626c5cd 100644 --- a/lib_util/render_config_reader.c +++ b/lib_util/render_config_reader.c @@ -48,6 +48,24 @@ #define MAX_ITEM_LENGTH ( 64 ) #define N_REVERB_VECTORS ( 3 ) +#ifdef REFACTOR_REVERB_INIT_ADD_CFG_CHECKS +#define SHORTEST_REV_DEL_LINE ( 0.015f ) +#define N_BANDS_MIN ( 2 ) +#define N_BANDS_MAX ( 60 ) +#define FC_INPUT_MIN ( 0.0f ) +#define FC_INPUT_MAX ( 1.0e+5f ) +#define ACOUSTIC_RT60_MIN ( 1.0e-3f ) +#define ACOUSTIC_RT60_MAX ( 1.0e+2f ) +#define ACOUSTIC_DSR_MIN ( 0.0f ) +#define ACOUSTIC_DSR_MAX ( 1.0e+2f ) +#define ACOUSTIC_DSR_EPSILON ( 1.0e-15f ) +#define ACOUSTICPREDELAY_JOTREV_MIN ( SHORTEST_REV_DEL_LINE ) +#define ACOUSTICPREDELAY_JOTREV_MAX ( SHORTEST_REV_DEL_LINE + 1.0f / ( float )FRAMES_PER_SEC ) +#define ACOUSTICPREDELAY_FDREV_MIN ( 1.0f / ( float )( 16 * FRAMES_PER_SEC ) ) +#define ACOUSTICPREDELAY_FDREV_MAX ( ( float )( REVERB_PREDELAY_MAX ) / ( float )( 16 * FRAMES_PER_SEC ) ) +#define INPUTPREDELAY_MIN ( 0.0f ) +#define INPUTPREDELAY_MAX ( 1.0e+2f ) +#endif /* REFACTOR_REVERB_INIT_ADD_CFG_CHECKS */ /*------------------------------------------------------------------------------------------* * Type definitions @@ -205,6 +223,98 @@ static int32_t errorHandler( return numErrors; } +#ifdef REFACTOR_REVERB_INIT_ADD_CFG_CHECKS +/*------------------------------------------------------------------------------------------* + * RenderConfigReader_checkValues() + * + * Verifies if the configuration parameters lie within acceptable limits + *------------------------------------------------------------------------------------------*/ +static ivas_error RenderConfigReader_checkValues( + IVAS_RENDER_CONFIG_HANDLE hRenderConfig /* o : Renderer configuration handle */ +) +{ + int16_t band_idx, tab_value_err_count; + IVAS_ROOM_ACOUSTICS_CONFIG_DATA * pRoom_acoustics; + pRoom_acoustics = &hRenderConfig->room_acoustics; + tab_value_err_count = 0; + + /* Verify the number of frequency bands in the config input data */ + if ( ( pRoom_acoustics->nBands > N_BANDS_MAX ) || ( pRoom_acoustics->nBands < N_BANDS_MIN ) ) + { + return IVAS_ERR_WRONG_PARAMS; + } + + /* Verify input pre-delay value */ + if ( ( pRoom_acoustics->inputPreDelay > INPUTPREDELAY_MAX ) || ( pRoom_acoustics->inputPreDelay < INPUTPREDELAY_MIN ) ) + { + return IVAS_ERR_WRONG_PARAMS; + } + + /* Verify data per band in the acoustic properties table */ + for ( band_idx = 0; band_idx < pRoom_acoustics->nBands; band_idx++ ) + { + /* Verify if the frequencies are in the ascending order (required for interpolation) */ + if( band_idx != 0 ) + { + if( pRoom_acoustics->pFc_input[band_idx] <= pRoom_acoustics->pFc_input[band_idx - 1] ) + { + tab_value_err_count++; + } + } + + /* Check the input frequencies */ + if ( ( pRoom_acoustics->pFc_input[band_idx] > FC_INPUT_MAX ) || ( pRoom_acoustics->pFc_input[band_idx] < FC_INPUT_MIN ) ) + { + tab_value_err_count++; + } + + /* Check the input RT60 values */ + if ( ( pRoom_acoustics->pAcoustic_rt60[band_idx] > ACOUSTIC_RT60_MAX ) || ( pRoom_acoustics->pAcoustic_rt60[band_idx] < ACOUSTIC_RT60_MIN ) ) + { + tab_value_err_count++; + } + + /* Check the input DSR values */ + if ( ( pRoom_acoustics->pAcoustic_dsr[band_idx] > ACOUSTIC_DSR_MAX ) || ( pRoom_acoustics->pAcoustic_dsr[band_idx] < ACOUSTIC_DSR_MIN ) ) + { + tab_value_err_count++; + } + + /* Replace zero DSR values with very small positive values, to avoid issues with coloration filter design */ + if ( pRoom_acoustics->pAcoustic_dsr[band_idx] <= 0.0f ) + { + pRoom_acoustics->pAcoustic_dsr[band_idx] = ACOUSTIC_DSR_EPSILON; + } + } + + if ( tab_value_err_count != 0 ) + { + return IVAS_ERR_WRONG_PARAMS; + } + +#ifdef DEBUGGING + /* Specific limits for Jot reverb */ + if ( hRenderConfig->renderer_type_override == RENDER_TYPE_OVERRIDE_CREND ) + { + if ( ( pRoom_acoustics->acousticPreDelay > ACOUSTICPREDELAY_JOTREV_MAX ) || ( pRoom_acoustics->acousticPreDelay < ACOUSTICPREDELAY_JOTREV_MIN ) ) + { + return IVAS_ERR_WRONG_PARAMS; + } + } + + /* Specific limits for frequency-domain reverb */ + if ( hRenderConfig->renderer_type_override == RENDER_TYPE_OVERRIDE_FASTCONV ) + { + if ( ( pRoom_acoustics->acousticPreDelay > ACOUSTICPREDELAY_FDREV_MAX ) || ( pRoom_acoustics->acousticPreDelay < ACOUSTICPREDELAY_FDREV_MIN ) ) + { + return IVAS_ERR_WRONG_PARAMS; + } + } +#endif /* DEBUGGING */ + + return IVAS_ERR_OK; +} +#endif /* REFACTOR_REVERB_INIT_ADD_CFG_CHECKS */ /*------------------------------------------------------------------------------------------* * RenderConfigReader_open() @@ -452,7 +562,11 @@ ivas_error RenderConfigReader_read( return IVAS_ERR_FAILED_FILE_PARSE; } +#ifndef REFACTOR_REVERB_INIT_ADD_CFG_CHECKS return IVAS_ERR_OK; +#else /* REFACTOR_REVERB_INIT_ADD_CFG_CHECKS */ + return RenderConfigReader_checkValues( hRenderConfig ); +#endif /* REFACTOR_REVERB_INIT_ADD_CFG_CHECKS */ }