LCOV - code coverage report
Current view: top level - lib_rend - ivas_reverb_filter_design.c (source / functions) Hit Total Coverage
Test: Coverage on main @ 6baab0c613aa6c7100498ed7b93676aa8198a493 Lines: 183 190 96.3 %
Date: 2025-05-29 08:28:55 Functions: 9 9 100.0 %

          Line data    Source code
       1             : /******************************************************************************************************
       2             : 
       3             :    (C) 2022-2025 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB,
       4             :    Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
       5             :    Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
       6             :    Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
       7             :    contributors to this repository. All Rights Reserved.
       8             : 
       9             :    This software is protected by copyright law and by international treaties.
      10             :    The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB,
      11             :    Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
      12             :    Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
      13             :    Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
      14             :    contributors to this repository retain full ownership rights in their respective contributions in
      15             :    the software. This notice grants no license of any kind, including but not limited to patent
      16             :    license, nor is any license granted by implication, estoppel or otherwise.
      17             : 
      18             :    Contributors are required to enter into the IVAS codec Public Collaboration agreement before making
      19             :    contributions.
      20             : 
      21             :    This software is provided "AS IS", without any express or implied warranties. The software is in the
      22             :    development stage. It is intended exclusively for experts who have experience with such software and
      23             :    solely for the purpose of inspection. All implied warranties of non-infringement, merchantability
      24             :    and fitness for a particular purpose are hereby disclaimed and excluded.
      25             : 
      26             :    Any dispute, controversy or claim arising under or in relation to providing this software shall be
      27             :    submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in
      28             :    accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and
      29             :    the United Nations Convention on Contracts on the International Sales of Goods.
      30             : 
      31             : *******************************************************************************************************/
      32             : 
      33             : #include <stdint.h>
      34             : #include "options.h"
      35             : #include "prot.h"
      36             : #include "ivas_prot_rend.h"
      37             : #ifdef DEBUGGING
      38             : #include "debug.h"
      39             : #endif
      40             : #include <assert.h>
      41             : #include <math.h>
      42             : #include <stdint.h>
      43             : #include "wmc_auto.h"
      44             : 
      45             : 
      46             : /*------------------------------------------------------------------------------------------*
      47             :  * Local constants
      48             :  *------------------------------------------------------------------------------------------*/
      49             : 
      50             : #define STEP_LIMIT_PIVOT_FREQ  ( 1000.0f ) /* Pivot (initial reference) frequency for response gradient limit    */
      51             : #define RESPONSE_STEP_LIMIT_LF ( 4.0f )    /* Maximum step in dB per bin at low frequencies (< pivot frequency)  */
      52             : #define RESPONSE_STEP_LIMIT_HF ( 1.5f )    /* Maximum step in dB per bin at high frequencies (> pivot frequency) */
      53             : #define EPS                    ( 1e-30f )
      54             : 
      55             : 
      56             : /*------------------------------------------------------------------------------------------*
      57             :  * Function definitions
      58             :  *------------------------------------------------------------------------------------------*/
      59             : 
      60             : /*-------------------------------------------------------------------*
      61             :  * calc_min_phase()
      62             :  *
      63             :  * Compute the minimum phase spectrum that can be derived
      64             :  * from the amplitude spectrum of the input.
      65             :  *-------------------------------------------------------------------*/
      66             : 
      67       19132 : static void calc_min_phase(
      68             :     rv_fftwf_type_complex *pSpectrum,
      69             :     const int16_t fft_size,
      70             :     float *pMinPhase )
      71             : {
      72             :     int16_t idx;
      73       19132 :     const int16_t half_fft_size = fft_size >> 1;
      74       19132 :     const int16_t log2_fft_size = int_log2( fft_size );
      75       19132 :     const int16_t spectrum_size = half_fft_size + 1;
      76       19132 :     const int16_t cepstrum_smoothing_extent = fft_size >> 3;
      77       19132 :     const float scale_factor = 1.0f / (float) fft_size;
      78             : 
      79             :     float pCepstrum[RV_FILTER_MAX_FFT_SIZE];
      80             :     float pFolded_cepstrum_re[RV_FILTER_MAX_FFT_SIZE];
      81             :     float pFolded_cepstrum_im[RV_FILTER_MAX_FFT_SIZE];
      82             :     float pFolded_cepstrum_smoothing_win[RV_FILTER_MAX_FFT_SIZE];
      83             :     float angle_increment;
      84             :     float initial_angle;
      85             : 
      86             : 
      87             :     /* Prepare the smoothing window. Portion of the pCepstrum that will be smoothed out by a window to prevent aliasing.    */
      88             : 
      89             :     /* The first portion of the pCepstrum is kept unchanged */
      90     3034684 :     for ( idx = 0; idx < half_fft_size - cepstrum_smoothing_extent; idx++ )
      91             :     {
      92     3015552 :         pFolded_cepstrum_smoothing_win[idx] = 1.0f;
      93             :     }
      94             : 
      95             :     /* Adding Hann half-window for smooth transition */
      96       19132 :     angle_increment = EVS_PI / (float) ( 2 * cepstrum_smoothing_extent - 1 );
      97             : 
      98             :     /* Initial angle set to match Hann window alignment in Matlab */
      99       19132 :     initial_angle = 0.5f * ( EVS_PI + angle_increment );
     100             : 
     101     1024316 :     for ( idx = 0; idx < cepstrum_smoothing_extent; idx++ )
     102             :     {
     103     1005184 :         const float sine_value = sinf( initial_angle + angle_increment * idx );
     104             : 
     105     1005184 :         pFolded_cepstrum_smoothing_win[idx + half_fft_size - cepstrum_smoothing_extent] = sine_value * sine_value;
     106             :     }
     107             : 
     108             :     /* Padding the rest with zeros */
     109     4039868 :     for ( idx = half_fft_size; idx < fft_size; idx++ )
     110             :     {
     111     4020736 :         pFolded_cepstrum_smoothing_win[idx] = 0.0f;
     112             :     }
     113             : 
     114             :     /* Compute the log amplitude spectrum */
     115     4059000 :     for ( idx = 0; idx < spectrum_size; idx++ )
     116             :     {
     117     4039868 :         pCepstrum[idx] = 0.5f * logf( ( pSpectrum[idx][0] * pSpectrum[idx][0] ) + ( pSpectrum[idx][1] * pSpectrum[idx][1] ) + EPS );
     118             :     }
     119             : 
     120             :     /* Extending one-sided spectrum to double-sided one */
     121     4020736 :     for ( idx = spectrum_size; idx < fft_size; idx++ )
     122             :     {
     123     4001604 :         pCepstrum[idx] = pCepstrum[fft_size - idx];
     124             :     }
     125             : 
     126             :     /* Compute the real pCepstrum of the log amplitude spectrum */
     127       19132 :     fft_rel( pCepstrum, fft_size, log2_fft_size );
     128             :     /* Note: using real-input fft rather than cmplx ifft to ensure support for 1024 size */
     129             :     /* Extra scaling is though required on the real/img parts, and img part is later inverted */
     130             : 
     131     8060604 :     for ( idx = 0; idx < fft_size; idx++ )
     132             :     {
     133     8041472 :         pCepstrum[idx] *= scale_factor;
     134             :     }
     135             : 
     136             :     /* Fold the pCepstrum to ensure that zeros outside the unit circle move inside it, making it minimum phase. */
     137       19132 :     pFolded_cepstrum_re[0] = pCepstrum[0];
     138       19132 :     pFolded_cepstrum_im[0] = 0.0f;
     139             : 
     140     4020736 :     for ( idx = 1; idx < half_fft_size; idx++ )
     141             :     {
     142     4001604 :         pFolded_cepstrum_re[idx] = 2.0f * pCepstrum[idx];
     143     4001604 :         pFolded_cepstrum_im[idx] = -2.0f * pCepstrum[fft_size - idx];
     144             :         /* Note: sign inverted because of fft rather than ifft used before */
     145             :     }
     146       19132 :     pFolded_cepstrum_re[half_fft_size] = pCepstrum[half_fft_size];
     147       19132 :     pFolded_cepstrum_im[half_fft_size] = 0.0f;
     148             : 
     149     4020736 :     for ( idx = spectrum_size; idx < fft_size; idx++ )
     150             :     {
     151     4001604 :         pFolded_cepstrum_re[idx] = 0.0f;
     152     4001604 :         pFolded_cepstrum_im[idx] = 0.0f;
     153             :     }
     154             : 
     155             :     /* Smoothing out the folded pCepstrum, to remove discontinuity at Ns,
     156             :     hoping to significantly reduce the secondary response in the minimum-phase IR */
     157     8060604 :     for ( idx = 0; idx < fft_size; idx++ )
     158             :     {
     159     8041472 :         pFolded_cepstrum_re[idx] *= pFolded_cepstrum_smoothing_win[idx];
     160     8041472 :         pFolded_cepstrum_im[idx] *= pFolded_cepstrum_smoothing_win[idx];
     161             :     }
     162             : 
     163             :     /* Convert back and isolate the phase. */
     164       19132 :     if ( fft_size <= 512 ) /* for size <= 512 using complex-value FFT (more effecient, but available only up to 512 size) */
     165             :     {
     166       19132 :         DoRTFTn( pFolded_cepstrum_re, pFolded_cepstrum_im, fft_size );
     167             : 
     168             :         /* Copying the img part into the output */
     169     4020736 :         for ( idx = 1; idx < half_fft_size; idx++ )
     170             :         {
     171     4001604 :             pMinPhase[idx] = pFolded_cepstrum_im[idx];
     172             :         }
     173             :     }
     174             :     else /* for fft_size > 512 using real-values FFT twice (for real + imag parts) */
     175             :     {
     176             :         /* Real part */
     177           0 :         fft_rel( pFolded_cepstrum_re, fft_size, log2_fft_size );
     178             :         /* Imag part */
     179           0 :         fft_rel( pFolded_cepstrum_im, fft_size, log2_fft_size );
     180             : 
     181             :         /* Copying the img part into the output */
     182           0 :         for ( idx = 1; idx < half_fft_size; idx++ )
     183             :         {
     184           0 :             pMinPhase[idx] = pFolded_cepstrum_re[fft_size - idx] + pFolded_cepstrum_im[idx];
     185             :         }
     186             :     }
     187       19132 :     pMinPhase[0] = 0.0f;
     188       19132 :     pMinPhase[half_fft_size] = 0.0f;
     189             : 
     190       19132 :     return;
     191             : }
     192             : 
     193             : 
     194             : /*-------------------------------------------------------------------*
     195             :  * calc_min_phase_filter()
     196             :  *
     197             :  * Convert FFT-domain pFilter pH_flt into a minimum-phase pFilter.
     198             :  * This function expects only the positive frequency bins up to Nyquist/2
     199             :  *-------------------------------------------------------------------*/
     200             : 
     201       19132 : static void calc_min_phase_filter(
     202             :     rv_fftwf_type_complex *pH_flt,
     203             :     const int16_t fft_size,
     204             :     float delay )
     205             : {
     206       19132 :     const int16_t spectrum_size = ( fft_size >> 1 ) + 1;
     207             :     int16_t idx;
     208             :     float phase_increment;
     209             :     float pMin_phase[RV_FILTER_MAX_FFT_SIZE];
     210             : 
     211       19132 :     phase_increment = -PI2 * delay / (float) fft_size;
     212             : 
     213       19132 :     calc_min_phase( pH_flt, fft_size, pMin_phase );
     214             : 
     215     4059000 :     for ( idx = 0; idx < spectrum_size; idx++ )
     216             :     {
     217             :         /* Cancel out initial phase by computing amplitude */
     218             :         /* Note: slightly different (but mathematically equivalent) approach used for better efficiency */
     219     4039868 :         const float current_ampl = sqrtf( pH_flt[idx][0] * pH_flt[idx][0] + pH_flt[idx][1] * pH_flt[idx][1] );
     220             : 
     221             :         /* Using the phase computed by calc_min_phase() + additional delay */
     222     4039868 :         const float current_phase = pMin_phase[idx] + phase_increment * idx;
     223             : 
     224             :         /* Apply the computed phase */
     225     4039868 :         pH_flt[idx][0] = current_ampl * cosf( current_phase );
     226     4039868 :         pH_flt[idx][1] = current_ampl * sinf( current_phase );
     227             :     }
     228             : 
     229       19132 :     return;
     230             : }
     231             : 
     232             : 
     233             : /*-------------------------------------------------------------------*
     234             :  * apply_window_fft()
     235             :  *
     236             :  * Apply the smoothing (anti-aliasing) window in the time domain
     237             :  *-------------------------------------------------------------------*/
     238             : 
     239       19132 : static void apply_window_fft(
     240             :     rv_fftwf_type_complex *pH_flt,
     241             :     const float *pWindow,
     242             :     const int16_t fft_size )
     243             : {
     244             :     int16_t idx;
     245             :     float pFilter[RV_FILTER_MAX_FFT_SIZE];
     246       19132 :     const int16_t half_fft_size = fft_size >> 1;
     247       19132 :     const int16_t log2_fft_size = int_log2( fft_size );
     248             : 
     249             :     /* Converting pFilter to ifft function input format */
     250             :     /* Real parts */
     251     4059000 :     for ( idx = 0; idx <= half_fft_size; idx++ )
     252             :     {
     253     4039868 :         pFilter[idx] = pH_flt[idx][0];
     254             :     }
     255             :     /* Img parts */
     256     4020736 :     for ( idx = 1; idx < half_fft_size; idx++ )
     257             :     {
     258     4001604 :         pFilter[fft_size - idx] = pH_flt[idx][1];
     259             :     }
     260             : 
     261             :     /* Do inverse fft to go to the time domain */
     262       19132 :     ifft_rel( pFilter, fft_size, log2_fft_size );
     263             : 
     264             :     /* Apply the window in the time domain */
     265     8060604 :     for ( idx = 0; idx < fft_size; idx++ )
     266             :     {
     267     8041472 :         pFilter[idx] *= pWindow[idx];
     268             :     }
     269             : 
     270             :     /* Convert back to the frequency domain */
     271       19132 :     fft_rel( pFilter, fft_size, log2_fft_size );
     272             : 
     273             :     /* Copy data to the output with format conversion */
     274       19132 :     pH_flt[0][0] = pFilter[0];
     275       19132 :     pH_flt[half_fft_size][0] = pFilter[half_fft_size];
     276       19132 :     pH_flt[0][1] = 0.0f;
     277       19132 :     pH_flt[half_fft_size][1] = 0.0f;
     278     4020736 :     for ( idx = 1; idx < half_fft_size; idx++ )
     279             :     {
     280     4001604 :         pH_flt[idx][0] = pFilter[idx];
     281     4001604 :         pH_flt[idx][1] = pFilter[fft_size - idx];
     282             :     }
     283             : 
     284       19132 :     return;
     285             : }
     286             : 
     287             : 
     288             : /*-------------------------------------------------------------------*
     289             :  * response_step_limit()
     290             :  *
     291             :  * Limit the gain vs frequency slope to T db per bin
     292             :  *-------------------------------------------------------------------*/
     293             : 
     294        9566 : static void response_step_limit(
     295             :     float *X,
     296             :     const int16_t dim_x,
     297             :     const float step_limit_lf_dB,
     298             :     const float step_limit_hf_dB,
     299             :     const int16_t pivot_bin_idx )
     300             : {
     301             :     int16_t i;
     302        9566 :     const float positive_step_limit_lf = powf( 10.0f, 0.05f * step_limit_lf_dB );
     303        9566 :     const float negative_step_limit_lf = 1.0f / positive_step_limit_lf;
     304        9566 :     const float positive_step_limit_hf = powf( 10.0f, 0.05f * step_limit_hf_dB );
     305        9566 :     const float negative_step_limit_hf = 1.0f / positive_step_limit_hf;
     306             : 
     307             :     /* Go up from pivot frequency and limit the slope to the maximum given by T. */
     308     1882078 :     for ( i = pivot_bin_idx + 1; i < dim_x; i++ )
     309             :     {
     310     1872512 :         float desiredChange = X[i] / X[i - 1];
     311     1872512 :         float change = fabsf( desiredChange );
     312     1872512 :         if ( desiredChange >= 1 )
     313             :         {
     314      276614 :             if ( change > positive_step_limit_hf )
     315             :             {
     316           0 :                 change = positive_step_limit_hf;
     317             :             }
     318             :         }
     319             :         else
     320             :         {
     321     1595898 :             if ( change < negative_step_limit_hf )
     322             :             {
     323      124654 :                 change = negative_step_limit_hf;
     324             :             }
     325             :         }
     326     1872512 :         X[i] = X[i - 1] * change;
     327             :     }
     328             : 
     329             :     /* Go down from pivot frequency and limit the slope to the maximum given by T. */
     330      147422 :     for ( i = pivot_bin_idx - 1; i >= 0; i-- )
     331             :     {
     332      137856 :         float desiredChange = X[i] / X[i + 1];
     333      137856 :         float change = fabsf( desiredChange );
     334             : 
     335      137856 :         if ( desiredChange >= 1 )
     336             :         {
     337       51354 :             if ( change > positive_step_limit_lf )
     338             :             {
     339           8 :                 change = positive_step_limit_lf;
     340             :             }
     341             :         }
     342             :         else
     343             :         {
     344       86502 :             if ( change < negative_step_limit_lf )
     345             :             {
     346       25456 :                 change = negative_step_limit_lf;
     347             :             }
     348             :         }
     349      137856 :         X[i] = X[i + 1] * change;
     350             :     }
     351             : 
     352        9566 :     return;
     353             : }
     354             : 
     355             : 
     356             : /*-------------------------------------------------------------------*
     357             :  * ivas_reverb_define_window_fft()
     358             :  *
     359             :  * Compute a smoothing window used later to avoid aliasing in FFT filters
     360             :  *-------------------------------------------------------------------*/
     361             : 
     362        4783 : void ivas_reverb_define_window_fft(
     363             :     float *pWindow,
     364             :     const int16_t transitionStart,
     365             :     const int16_t transitionLength,
     366             :     const int16_t spectrumLength )
     367             : {
     368             :     int16_t idx, fftLength;
     369             :     float angle_increment;
     370             :     float initial_angle;
     371             : 
     372        4783 :     fftLength = ( spectrumLength - 1 ) * 2;
     373             : 
     374             :     /* The first portion of the sequence is kept unchanged, window == 1 */
     375      508923 :     for ( idx = 0; idx < transitionStart; idx++ )
     376             :     {
     377      504140 :         pWindow[idx] = 1.0f;
     378             :     }
     379             : 
     380             :     /* Adding Hann half-window for smooth transition */
     381        4783 :     angle_increment = EVS_PI / ( 2.0f * transitionLength - 1 );
     382             : 
     383             :     /* Initial angle set to match Hann window alignment in Matlab */
     384        4783 :     initial_angle = 0.5f * ( EVS_PI + angle_increment );
     385      193791 :     for ( idx = 0; idx < transitionLength; idx++ )
     386             :     {
     387      189008 :         const float sine_value = sinf( initial_angle + angle_increment * idx );
     388             : 
     389      189008 :         pWindow[idx + transitionStart] = sine_value * sine_value;
     390             :     }
     391             : 
     392             :     /* Padding the rest with zeros */
     393     1322003 :     for ( idx = transitionStart + transitionLength; idx < fftLength; idx++ )
     394             :     {
     395     1317220 :         pWindow[idx] = 0.0f;
     396             :     }
     397             : 
     398        4783 :     return;
     399             : }
     400             : 
     401             : /*-------------------------------------------------------------------*
     402             :  * apply_window_fft()
     403             :  *
     404             :  * Applies the smoothing (anti-aliasing) window in the time domain
     405             :  *-------------------------------------------------------------------*/
     406             : 
     407             : /* Computes colorations filters for the target frequency responses */
     408        4783 : int16_t ivas_reverb_calc_color_filters(
     409             :     const float *pTargetL,
     410             :     const float *pTargetR,
     411             :     const float *pWindow,
     412             :     const int16_t fft_size,
     413             :     const float delay,
     414             :     rv_fftwf_type_complex *pBeqL,
     415             :     rv_fftwf_type_complex *pBeqR )
     416             : {
     417             :     int16_t idx, half_fft_size;
     418             : 
     419        4783 :     half_fft_size = ( fft_size >> 1 );
     420             : 
     421     1014750 :     for ( idx = 0; idx <= half_fft_size; idx++ )
     422             :     {
     423     1009967 :         pBeqL[idx][0] = pTargetL[idx];
     424     1009967 :         pBeqL[idx][1] = 0.0f;
     425     1009967 :         pBeqR[idx][0] = pTargetR[idx];
     426     1009967 :         pBeqR[idx][1] = 0.0f;
     427             :     }
     428             : 
     429             :     /* Make the response minimum phase. Does make the spectrum complex, but
     430             :        will avoid aliasing when applied in the FFT domain. */
     431        4783 :     calc_min_phase_filter( pBeqL, fft_size, delay );
     432        4783 :     calc_min_phase_filter( pBeqR, fft_size, delay );
     433        4783 :     apply_window_fft( pBeqL, pWindow, fft_size );
     434        4783 :     apply_window_fft( pBeqR, pWindow, fft_size );
     435             : 
     436     1014750 :     for ( idx = 0; idx <= half_fft_size; idx++ )
     437             :     {
     438     1009967 :         if ( ( pTargetL[idx] < 0.0f ) || ( pTargetR[idx] < 0.0f ) ) /* Shouldn't have negative gains. */
     439             :         {
     440           0 :             return 1;
     441             :         }
     442             :     }
     443             : 
     444        4783 :     return 0;
     445             : }
     446             : 
     447             : 
     448             : /*-------------------------------------------------------------------*
     449             :  * ivas_reverb_calc_correl_filters()
     450             :  *
     451             :  * Compute correlation filters for the target frequency response
     452             :  *-------------------------------------------------------------------*/
     453             : 
     454        4783 : int16_t ivas_reverb_calc_correl_filters(
     455             :     const float *pTargetICC,
     456             :     const float *pWindow,
     457             :     const int16_t fft_size,
     458             :     const float delay,
     459             :     rv_fftwf_type_complex *pU,
     460             :     rv_fftwf_type_complex *pV )
     461             : {
     462             :     int16_t idx, half_fft_size;
     463             : 
     464        4783 :     half_fft_size = fft_size >> 1;
     465             : 
     466     1014750 :     for ( idx = 0; idx <= half_fft_size; idx++ )
     467             :     {
     468     1009967 :         if ( ( 1.0f + pTargetICC[idx] < 0.0f ) || ( 1.0f - pTargetICC[idx] < 0.0f ) ) /* Shouldn't have negative gains. */
     469             :         {
     470           0 :             return 1;
     471             :         }
     472             :     }
     473             : 
     474     1014750 :     for ( idx = 0; idx <= half_fft_size; idx++ )
     475             :     {
     476     1009967 :         pU[idx][0] = sqrtf( ( 1.0f + pTargetICC[idx] ) * 0.5f );
     477     1009967 :         pU[idx][1] = 0.0f;
     478     1009967 :         pV[idx][0] = sqrtf( ( 1.0f - pTargetICC[idx] ) * 0.5f );
     479     1009967 :         pV[idx][1] = 0.0f;
     480             :     }
     481             : 
     482             :     /* Make the response minimum phase. Does make the spectrum complex, but
     483             :        will avoid aliasing when applied in the FFT domain. */
     484        4783 :     calc_min_phase_filter( pU, fft_size, delay );
     485        4783 :     calc_min_phase_filter( pV, fft_size, delay );
     486        4783 :     apply_window_fft( pU, pWindow, fft_size );
     487        4783 :     apply_window_fft( pV, pWindow, fft_size );
     488             : 
     489        4783 :     return 0;
     490             : }
     491             : 
     492             : 
     493             : /*-------------------------------------------------------------------*
     494             :  * ivas_reverb_calc_color_levels()
     495             :  *
     496             :  * Compute the target levels (gains) for the coloration filters
     497             :  *-------------------------------------------------------------------*/
     498             : 
     499        4783 : void ivas_reverb_calc_color_levels(
     500             :     const int32_t output_Fs,
     501             :     const int16_t freq_count,
     502             :     const int16_t loop_count,
     503             :     const float *pFc,
     504             :     const float *pAcoustic_dsr,
     505             :     const float *pHrtf_avg_pwr_L,
     506             :     const float *pHrtf_avg_pwr_R,
     507             :     const int16_t *pLoop_delays,
     508             :     const float *pT60_filter_coeff,
     509             :     float *pTarget_color_L,
     510             :     float *pTarget_color_R )
     511             : {
     512             :     int16_t i, freq_idx, idx_pivot, nrcoefs, loop_idx;
     513             :     float t60[RV_LENGTH_NR_FC];
     514             : 
     515             :     /* Pre-computing inverse values for optimisation (to avoid divisions in inner loops) */
     516        4783 :     const float fs_inverted = 1.0f / (float) output_Fs;
     517        4783 :     const float loop_count_inverted = 1.0f / (float) loop_count;
     518             : 
     519        4783 :     const float log__0_001 = logf( 0.001f );
     520             :     /* Pre-computed for better efficiency */
     521             : 
     522        4783 :     const float revEnergyFactor = 1.0f;
     523             :     /* revEnergyFactor == 1 for IVAS, as the pDsr values are already scaled previously for predelay delta */
     524             : 
     525             :     /* Computing minimum delays in a simpler way, as they are already provided in descending order */
     526        4783 :     const int16_t minDelay = pLoop_delays[loop_count - 1];
     527        4783 :     const int16_t minDelayDiff = pLoop_delays[loop_count - 2] - pLoop_delays[loop_count - 1];
     528        4783 :     const float freq_step = 0.5f * output_Fs / (float) ( freq_count - 1 );
     529             : 
     530     1014750 :     for ( freq_idx = 0; freq_idx < freq_count; freq_idx++ )
     531             :     {
     532     1009967 :         t60[freq_idx] = 0.0f;
     533             :     }
     534             : 
     535       43047 :     for ( loop_idx = 0; loop_idx < loop_count; loop_idx++ )
     536             :     {
     537             :         float coefA[2];
     538             :         float coefB[2];
     539             : 
     540       38264 :         nrcoefs = 2;
     541             :         /* for pFilter order == 1 */
     542             : 
     543             :         /* Obtaining T60 filters coefficients for the current loop */
     544      114792 :         for ( i = 0; i < nrcoefs; i++ )
     545             :         {
     546       76528 :             coefA[i] = pT60_filter_coeff[2 * nrcoefs * loop_idx + i + nrcoefs];
     547       76528 :             coefB[i] = pT60_filter_coeff[2 * nrcoefs * loop_idx + i];
     548             :         }
     549             : 
     550             :         /* Loop over frequencies */
     551     8118000 :         for ( freq_idx = 0; freq_idx < freq_count; freq_idx++ )
     552             :         {
     553             :             /* Compute T60 pFilter gain at the given frequency */
     554             :             float cos_w;
     555             :             float H_filter;
     556             :             float T60_est;
     557             : 
     558     8079736 :             cos_w = cosf( PI2 * pFc[freq_idx] * fs_inverted );
     559     8079736 :             H_filter = coefB[0] * coefB[0] + coefB[1] * coefB[1] + 2.0f * coefB[0] * coefB[1] * cos_w;
     560     8079736 :             H_filter /= 1.0f + coefA[1] * coefA[1] + 2.0f * coefA[1] * cos_w;
     561     8079736 :             H_filter = sqrtf( H_filter );
     562             : 
     563             :             /* Compute the T60 value on the basis of the pFilter gain */
     564     8079736 :             T60_est = -3.0f * pLoop_delays[loop_idx] / ( log10f( H_filter ) * output_Fs );
     565             : 
     566     8079736 :             t60[freq_idx] += T60_est;
     567             :         }
     568             :     }
     569             : 
     570             :     /* Dividing by the number of loops to compute the average T60 estimate */
     571     1014750 :     for ( freq_idx = 0; freq_idx < freq_count; freq_idx++ )
     572             :     {
     573     1009967 :         t60[freq_idx] *= loop_count_inverted;
     574             :     }
     575             : 
     576             :     /* Compute target gains on the basis of the estimated T60 values */
     577     1014750 :     for ( freq_idx = 0; freq_idx < freq_count; freq_idx++ )
     578             :     {
     579     1009967 :         const float A0_square_est = powf( 10.0f, -6.0f * minDelay / ( t60[freq_idx] * output_Fs ) );
     580     1009967 :         const float alpha = -log__0_001 / t60[freq_idx];
     581     1009967 :         const float revPredNormEnergy = ( A0_square_est / ( 2.0f * alpha ) ) * output_Fs / ( 0.8776f * minDelayDiff + 26.7741f );
     582             : 
     583     1009967 :         pTarget_color_L[freq_idx] = sqrtf( pAcoustic_dsr[freq_idx] * revEnergyFactor * pHrtf_avg_pwr_L[freq_idx] ) / max( sqrtf( revPredNormEnergy ), EPS );
     584             : 
     585     1009967 :         pTarget_color_R[freq_idx] = sqrtf( pAcoustic_dsr[freq_idx] * revEnergyFactor * pHrtf_avg_pwr_R[freq_idx] ) / max( sqrtf( revPredNormEnergy ), EPS );
     586             :     }
     587             : 
     588             :     /* Limiting the frequency response gradients
     589             :        Find frequency band closest to chosen pivot frequency. */
     590        4783 :     idx_pivot = (int16_t) roundf( STEP_LIMIT_PIVOT_FREQ / freq_step );
     591             : 
     592             :     /* Perform step limiting */
     593        4783 :     response_step_limit( pTarget_color_L, freq_count, RESPONSE_STEP_LIMIT_LF, RESPONSE_STEP_LIMIT_HF, idx_pivot );
     594        4783 :     response_step_limit( pTarget_color_R, freq_count, RESPONSE_STEP_LIMIT_LF, RESPONSE_STEP_LIMIT_HF, idx_pivot );
     595             : 
     596        4783 :     return;
     597             : }
     598             : 
     599             : 
     600             : /*-------------------------------------------------------------------*
     601             :  * ivas_reverb_interpolate_acoustic_data()
     602             :  *
     603             :  * Interpolates data from the input T60 and DSR tables to the FFT pFilter uniform grid
     604             :  * Note: the fc frequencies both for the input and the output must be in the ascending order
     605             :  *-------------------------------------------------------------------*/
     606             : 
     607       23597 : void ivas_reverb_interpolate_acoustic_data(
     608             :     const int16_t input_table_size,
     609             :     const float *pInput_fc,
     610             :     const float *pInput_t60,
     611             :     const float *pInput_dsr,
     612             :     const int16_t output_table_size,
     613             :     const float *pOutput_fc,
     614             :     float *pOutput_t60,
     615             :     float *pOutput_dsr )
     616             : {
     617             :     int16_t input_idx, output_idx;
     618             :     float rel_offset;
     619             : 
     620       23597 :     input_idx = 0;
     621             : 
     622     2162404 :     for ( output_idx = 0; output_idx < output_table_size; output_idx++ )
     623             :     {
     624             :         /* if the bin frequency is lower than the 1st frequency point in the input table, take this 1st point */
     625     2138807 :         if ( pOutput_fc[output_idx] < pInput_fc[0] )
     626             :         {
     627        4783 :             input_idx = 0;
     628        4783 :             rel_offset = 0.0f;
     629             :         }
     630             :         else
     631             :         {
     632             :             /* if the bin frequency is higher than the last frequency point in the input table, take this last point */
     633     2134024 :             if ( pOutput_fc[output_idx] > pInput_fc[input_table_size - 1] )
     634             :             {
     635      365162 :                 input_idx = input_table_size - 2;
     636      365162 :                 rel_offset = 1.0f;
     637             :             }
     638             :             /* otherwise use linear interpolation between 2 consecutive points in the input table */
     639             :             else
     640             :             {
     641     4113867 :                 while ( pOutput_fc[output_idx] > pInput_fc[input_idx + 1] )
     642             :                 {
     643     2345005 :                     input_idx++;
     644             :                 }
     645     1768862 :                 rel_offset = ( pOutput_fc[output_idx] - pInput_fc[input_idx] ) / ( pInput_fc[input_idx + 1] - pInput_fc[input_idx] );
     646             :             }
     647             :         }
     648             : 
     649     2138807 :         pOutput_t60[output_idx] = pInput_t60[input_idx] + rel_offset * ( pInput_t60[input_idx + 1] - pInput_t60[input_idx] );
     650             : 
     651     2138807 :         pOutput_dsr[output_idx] = pInput_dsr[input_idx] + rel_offset * ( pInput_dsr[input_idx + 1] - pInput_dsr[input_idx] );
     652             :     }
     653             : 
     654       23597 :     return;
     655             : }

Generated by: LCOV version 1.14