LCOV - code coverage report
Current view: top level - lib_dec - post_dec.c (source / functions) Hit Total Coverage
Test: Coverage on main -- short test vectors @ 6c9ddc4024a9c0e1ecb8f643f114a84a0e26ec6b Lines: 109 177 61.6 %
Date: 2025-05-23 08:37:30 Functions: 2 3 66.7 %

          Line data    Source code
       1             : /******************************************************************************************************
       2             : 
       3             :    (C) 2022-2025 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB,
       4             :    Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
       5             :    Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
       6             :    Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
       7             :    contributors to this repository. All Rights Reserved.
       8             : 
       9             :    This software is protected by copyright law and by international treaties.
      10             :    The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB,
      11             :    Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
      12             :    Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
      13             :    Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
      14             :    contributors to this repository retain full ownership rights in their respective contributions in
      15             :    the software. This notice grants no license of any kind, including but not limited to patent
      16             :    license, nor is any license granted by implication, estoppel or otherwise.
      17             : 
      18             :    Contributors are required to enter into the IVAS codec Public Collaboration agreement before making
      19             :    contributions.
      20             : 
      21             :    This software is provided "AS IS", without any express or implied warranties. The software is in the
      22             :    development stage. It is intended exclusively for experts who have experience with such software and
      23             :    solely for the purpose of inspection. All implied warranties of non-infringement, merchantability
      24             :    and fitness for a particular purpose are hereby disclaimed and excluded.
      25             : 
      26             :    Any dispute, controversy or claim arising under or in relation to providing this software shall be
      27             :    submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in
      28             :    accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and
      29             :    the United Nations Convention on Contracts on the International Sales of Goods.
      30             : 
      31             : *******************************************************************************************************/
      32             : 
      33             : /*====================================================================================
      34             :     EVS Codec 3GPP TS26.443 Nov 04, 2021. Version 12.14.0 / 13.10.0 / 14.6.0 / 15.4.0 / 16.3.0
      35             :   ====================================================================================*/
      36             : 
      37             : #include <stdint.h>
      38             : #include "options.h"
      39             : #ifdef DEBUGGING
      40             : #include "debug.h"
      41             : #endif
      42             : #include <math.h>
      43             : #include "prot.h"
      44             : #include "rom_com.h"
      45             : #include "wmc_auto.h"
      46             : 
      47             : /*---------------------------------------------------------------------*
      48             :  * Function prototypes
      49             :  *---------------------------------------------------------------------*/
      50             : 
      51             : static void bass_pf_1sf_delay( float *syn, const int16_t *T_sf, const float *gainT_sf, const int16_t l_frame, const int16_t l_subfr, float *bpf_noise_buf, int16_t *gain_factor_param, const int16_t disable_bpf, float *mem_deemph_err, float *lp_ener );
      52             : 
      53             : 
      54             : /*---------------------------------------------------------------------*
      55             :  * post_decoder()
      56             :  *
      57             :  * Perform post-processing
      58             :  *---------------------------------------------------------------------*/
      59             : 
      60     2824374 : void post_decoder(
      61             :     Decoder_State *st, /* i/o: decoder memory state pointer    */
      62             :     float synth_buf[],
      63             :     const float pit_gain[],
      64             :     const int16_t pitch[],
      65             :     float signal_out[],
      66             :     float *bpf_noise_buf )
      67             : {
      68             :     int16_t L_frame, nb_subfr, i;
      69             :     float *synth2;
      70             :     float *synth;
      71             :     int16_t pfstat_on_previous;
      72             :     int16_t pitch_gain_adjust[NB_SUBFR16k];
      73             :     float synth_buf2[NBPSF_PIT_MAX + L_FRAME_MAX + M];
      74             :     int32_t brate;
      75             :     float A[M + 1];
      76             :     float pitch_buf[NB_SUBFR16k];
      77             :     float tmp;
      78             :     int16_t L_subfr;
      79             : 
      80     2824374 :     L_frame = st->L_frame;
      81     2824374 :     nb_subfr = st->nb_subfr;
      82     2824374 :     brate = ( st->core_brate > SID_2k40 ) ? st->total_brate : st->last_active_brate;
      83     2824374 :     pfstat_on_previous = 0;
      84     2824374 :     if ( st->hPFstat != NULL )
      85             :     {
      86      707832 :         pfstat_on_previous = st->hPFstat->on;
      87      707832 :         st->hPFstat->on = 0;
      88             :     }
      89     2824374 :     set_s( pitch_gain_adjust, st->bpf_gain_param, nb_subfr );
      90     2824374 :     synth = synth_buf + st->hTcxDec->old_synth_len;
      91     2824374 :     synth2 = synth_buf2 + NBPSF_PIT_MAX;
      92     2824374 :     if ( st->hBPF != NULL )
      93             :     {
      94      707832 :         mvr2r( st->hBPF->pst_old_syn, synth_buf2, NBPSF_PIT_MAX );
      95             :     }
      96             : 
      97     2824374 :     if ( st->tcxonly )
      98             :     {
      99             :         /* High bitrates (48kbps and above), high sampling rates (25.6kHz and above) */
     100             : 
     101     2437284 :         mvr2r( synth, synth2, L_frame );
     102             : 
     103     2437284 :         if ( pfstat_on_previous )
     104             :         {
     105             :             /* Past frame was low-bitrate with formant post-filter */
     106         720 :             lsp2a_stab( st->lsp_old, A, M );
     107         720 :             mvr2r( st->hPFstat->mem_pf_in + L_SYN_MEM - M, synth - M, M );
     108         720 :             L_subfr = st->L_frame / st->nb_subfr;
     109         720 :             residu( A, M, synth, synth_buf, L_subfr );
     110         720 :             syn_filt( A, M, synth_buf, synth2, L_subfr, st->hPFstat->mem_stp + L_SYN_MEM - M, 0 );
     111         720 :             scale_st( synth, synth2, &st->hPFstat->gain_prec, L_subfr, -1 );
     112         720 :             blend_subfr2( synth2 + L_subfr / 2, synth + L_subfr / 2, synth2 + L_subfr / 2 );
     113             :         }
     114             :     }
     115             :     else
     116             :     {
     117             :         /* Low bitrates (32kbps and below), low sampling rates (12.8kHz and 16kHz) */
     118      387090 :         if ( st->last_bwidth == NB )
     119             :         {
     120             :             /* NB Post-filter (pitch+formant post-filter) */
     121         354 :             mvr2r( synth, synth_buf, L_frame );
     122         354 :             tmp = synth[-1];
     123         354 :             preemph( synth_buf, st->preemph_fac, L_frame, &tmp );
     124             : 
     125         354 :             tmp = 0.0f;
     126        1947 :             for ( i = 0; i < nb_subfr; i++ )
     127             :             {
     128        1593 :                 pitch_buf[i] = pitch[i];
     129             :             }
     130             : 
     131         354 :             if ( pfstat_on_previous == 0 )
     132             :             {
     133         354 :                 st->hPFstat->reset = 1;
     134             :             }
     135             : 
     136         354 :             if ( st->bwidth == NB )
     137             :             {
     138           0 :                 st->hPFstat->on = 1;
     139           0 :                 nb_post_filt( L_frame, L_SUBFR, st->hPFstat, &tmp, 0, synth_buf, st->mem_Aq, pitch_buf, GENERIC, st->BER_detect, st->lp_noise > LP_NOISE_THRESH ? 1 : ( ( st->core != ACELP_CORE ) || ( st->coder_type == UNVOICED ) ) );
     140             :             }
     141             :             else
     142             :             {
     143         354 :                 st->hPFstat->on = 0;
     144         354 :                 nb_post_filt( L_frame, L_SUBFR, st->hPFstat, &tmp, 0, synth_buf, st->mem_Aq, pitch_buf, AUDIO, st->BER_detect, st->lp_noise > LP_NOISE_THRESH ? 1 : ( ( st->core != ACELP_CORE ) || ( st->coder_type == UNVOICED ) ) );
     145             :             }
     146             : 
     147             : 
     148         354 :             mvr2r( synth_buf, synth2, L_frame );
     149         354 :             tmp = synth2[-1];
     150         354 :             deemph( synth2, st->preemph_fac, L_frame, &tmp );
     151             :         }
     152             :         else
     153             :         {
     154             :             /* Formant Post-filter */
     155      386736 :             if ( pfstat_on_previous == 0 )
     156             :             {
     157      325578 :                 st->hPFstat->reset = 1;
     158             :             }
     159             : 
     160      386736 :             if ( st->bwidth >= WB )
     161             :             {
     162      386736 :                 st->hPFstat->on = 1;
     163      386736 :                 formant_post_filt( st->hPFstat, synth, st->mem_Aq, synth2, L_frame, L_SUBFR, st->lp_noise, brate, 0 );
     164             :             }
     165             :             else
     166             :             {
     167           0 :                 st->hPFstat->on = 0;
     168           0 :                 formant_post_filt( st->hPFstat, synth, st->mem_Aq, synth2, L_frame, L_SUBFR, st->lp_noise, brate, 1 );
     169             :             }
     170             :         }
     171             : 
     172             :         /*Bass Post-filter */
     173      387090 :         bass_pf_1sf_delay( synth2, pitch, pit_gain, L_frame, L_SUBFR, bpf_noise_buf, pitch_gain_adjust, ( st->lp_noise > LP_NOISE_THRESH && st->narrowBand ) ? 1 : 0, &( st->hBPF->pst_mem_deemp_err ), &( st->hBPF->pst_lp_ener ) );
     174             :     }
     175             : 
     176             :     /* Output */
     177     2824374 :     mvr2r( synth2, signal_out, L_frame );
     178             : 
     179             :     /* Update synth2 memory */
     180     2824374 :     if ( st->hBPF != NULL )
     181             :     {
     182      707832 :         mvr2r( synth_buf2 + L_frame, st->hBPF->pst_old_syn, NBPSF_PIT_MAX );
     183             :     }
     184             : 
     185     2824374 :     return;
     186             : }
     187             : 
     188             : 
     189             : /*---------------------------------------------------------------------*
     190             :  * bass_pf_1sf_delay()
     191             :  *
     192             :  * Perform low-frequency postfiltering
     193             :  *---------------------------------------------------------------------*/
     194             : 
     195      387090 : static void bass_pf_1sf_delay(
     196             :     float *syn,                 /* i  : synthesis to postfilter                     */
     197             :     const int16_t *T_sf,        /* i  : Pitch period for all subframes (T_sf[4])    */
     198             :     const float *gainT_sf,      /* i  : Pitch gain for all subframes (gainT_sf[4])  */
     199             :     const int16_t L_frame,      /* i  : frame length (multiple of l_subfr)          */
     200             :     const int16_t L_subfr_in,   /* i  : sub-frame length (80/64)                    */
     201             :     float *bpf_noise_buf,       /* i  : harmoninc filtered signal                   */
     202             :     int16_t *gain_factor_param, /* i  : gain factor param 0-> minimum BPF, 3-> full BPF  */
     203             :     const int16_t disable_bpf,  /* i  : flag to disable BPF                         */
     204             :     float *mem_deemph_err,      /* i/o: Error deemphasis memory                     */
     205             :     float *lp_ener              /* i/o: long_term error signal energy               */
     206             : )
     207             : {
     208             :     int16_t i, sf, i_subfr, T, lg, L_subfr;
     209             :     float tmp, corr, ener, gain;
     210             :     float noise_buf[( 2 * L_SUBFR )], *noise_in;
     211             :     float error[L_SUBFR];
     212             :     float ener2;
     213             : 
     214      387090 :     noise_in = noise_buf;
     215             : 
     216      387090 :     sf = 0;
     217      387090 :     L_subfr = L_subfr_in;
     218             : 
     219     2160492 :     for ( i_subfr = 0; i_subfr < L_frame; i_subfr += L_subfr, sf++ )
     220             :     {
     221     1773402 :         if ( i_subfr == 0 )
     222             :         {
     223      387090 :             L_subfr = L_subfr_in;
     224             :         }
     225     1386312 :         else if ( i_subfr == L_frame )
     226             :         {
     227           0 :             L_subfr = 0;
     228             :         }
     229             :         else
     230             :         {
     231     1386312 :             L_subfr = L_subfr_in;
     232             :         }
     233             : 
     234     1773402 :         T = T_sf[sf];
     235     1773402 :         gain = gainT_sf[sf];
     236             : 
     237     1773402 :         if ( gain > 1.0f )
     238             :         {
     239        1227 :             gain = 1.0f;
     240             :         }
     241     1773402 :         if ( gain < 0.0f )
     242             :         {
     243           0 :             gain = 0.0f;
     244             :         }
     245             : 
     246     1773402 :         lg = L_frame - T - i_subfr;
     247     1773402 :         if ( lg < 0 )
     248             :         {
     249        1686 :             lg = 0;
     250             :         }
     251     1773402 :         if ( lg > L_subfr )
     252             :         {
     253      999273 :             lg = L_subfr;
     254             :         }
     255             : 
     256     1773402 :         if ( !disable_bpf && gain > 0 )
     257             :         {
     258        9051 :             corr = 0.01f;
     259        9051 :             ener = 0.01f;
     260             : 
     261      423642 :             for ( i = 0; i < lg; i++ )
     262             :             {
     263      414591 :                 corr += syn[i + i_subfr] * ( 0.5f * syn[i + i_subfr - T] + 0.5f * syn[i + i_subfr + T] );
     264      414591 :                 ener += ( 0.5f * syn[i + i_subfr - T] + 0.5f * syn[i + i_subfr + T] ) * ( 0.5f * syn[i + i_subfr - T] + 0.5f * syn[i + i_subfr + T] );
     265             :             }
     266             : 
     267      173724 :             for ( i = lg; i < L_subfr; i++ )
     268             :             {
     269      164673 :                 corr += syn[i + i_subfr] * syn[i + i_subfr - T];
     270      164673 :                 ener += syn[i + i_subfr - T] * syn[i + i_subfr - T];
     271             :             }
     272        9051 :             gain = corr / ener;
     273             : 
     274        9051 :             if ( gain > 1.f )
     275             :             {
     276        3396 :                 gain = 1.0f;
     277             :             }
     278        5655 :             else if ( gain < 0.f )
     279             :             {
     280         258 :                 gain = 0.f;
     281             :             }
     282             : 
     283        9051 :             ener2 = 0.01f;
     284      423642 :             for ( i = 0; i < lg; i++ )
     285             :             {
     286      414591 :                 error[i] = gain * ( syn[i + i_subfr] - 0.5f * syn[i + i_subfr - T] - 0.5f * syn[i + i_subfr + T] );
     287      414591 :                 error[i] = error[i] + 0.9f * *mem_deemph_err;
     288      414591 :                 *mem_deemph_err = error[i];
     289      414591 :                 ener2 += error[i] * error[i];
     290             :             }
     291             : 
     292      173724 :             for ( i = lg; i < L_subfr; i++ )
     293             :             {
     294      164673 :                 error[i] = 0.5f * gain * ( syn[i + i_subfr] - syn[i + i_subfr - T] );
     295      164673 :                 error[i] = error[i] + 0.9f * *mem_deemph_err;
     296      164673 :                 *mem_deemph_err = error[i];
     297      164673 :                 ener2 += error[i] * error[i];
     298             :             }
     299             : 
     300        9051 :             ener2 = (float) ( 10.f * log10( ener2 ) );
     301        9051 :             *lp_ener = (float) ( 0.99f * *lp_ener + 0.01f * ener2 );
     302        9051 :             ener2 = (float) pow( 10.f, 0.1f * *lp_ener );
     303        9051 :             tmp = 0.5f * corr / ( ener + ener2 );
     304             : 
     305        9051 :             if ( tmp > 0.5f )
     306             :             {
     307         471 :                 tmp = 0.5f;
     308             :             }
     309        8580 :             else if ( tmp < 0.f )
     310             :             {
     311         258 :                 tmp = 0.0f;
     312             :             }
     313             : 
     314             :             /*Adjust gain*/
     315             :             /* full gain = gainLTP*0.5*/
     316             :             /* adaptive gain = gainLTP*0.5*gain_factor*0.5*/
     317        9051 :             tmp *= max( 0.5f * gain_factor_param[sf], 0.125f );
     318             : 
     319             :             /* calculate noise based on voiced pitch */
     320      423642 :             for ( i = 0; i < lg; i++ )
     321             :             {
     322      414591 :                 noise_in[i] = tmp * ( syn[i + i_subfr] - 0.5f * syn[i + i_subfr - T] - 0.5f * syn[i + i_subfr + T] );
     323             :             }
     324             : 
     325      173724 :             for ( i = lg; i < L_subfr; i++ )
     326             :             {
     327      164673 :                 noise_in[i] = tmp * ( syn[i + i_subfr] - syn[i + i_subfr - T] );
     328             :                 /*It simulates an extrapolation of the buffer syn: syn[i+i_subfr+T]=syn[i+i_subfr]
     329             :                  * -> reduce nrg of noise_in and avoid too much post-filtering*/
     330             :                 /*noise_in[i] = tmp * (syn[i+i_subfr] - 0.5f*syn[i+i_subfr-T] - 0.5f*syn[i+i_subfr]);*/
     331             :                 /*->noise_in[i] = tmp * 0.5f * (syn[i+i_subfr] - syn[i+i_subfr-T]);*/
     332      164673 :                 noise_in[i] *= 0.5f;
     333             :             }
     334             :         }
     335             :         else
     336             :         {
     337     1764351 :             set_zero( noise_in, L_subfr );
     338             :         }
     339             : 
     340             :         /* copy bpf noise signal to buffer */
     341     1773402 :         mvr2r( noise_in, bpf_noise_buf + i_subfr, L_subfr );
     342             :     }
     343             : 
     344      387090 :     return;
     345             : }
     346             : /*---------------------------------------------------------------------*
     347             :  * cldfb_synth_set_bandsToZero()
     348             :  *
     349             :  *
     350             :  *---------------------------------------------------------------------*/
     351             : 
     352           0 : void cldfb_synth_set_bandsToZero(
     353             :     Decoder_State *st,
     354             :     float **rAnalysis,
     355             :     float **iAnalysis,
     356             :     const int16_t nTimeSlots )
     357             : {
     358             :     float nrg_bwddec, nrg_band[CLDFB_NO_CHANNELS_MAX], thr_bwddwc, max_nrg, realQ1, imagQ1;
     359             :     int16_t flag, offset, WBcnt, i, k, update_perc;
     360             :     float perc_detect, perc_miss;
     361             : 
     362           0 :     realQ1 = 0.0f;
     363           0 :     imagQ1 = 0.0f;
     364             : 
     365           0 :     set_f( nrg_band, 0.0f, CLDFB_NO_CHANNELS_MAX );
     366           0 :     max_nrg = 0.0f;
     367             : 
     368           0 :     offset = 250;
     369           0 :     WBcnt = 20;
     370           0 :     perc_miss = 0.83f;
     371           0 :     perc_detect = 0.93f;
     372             : 
     373           0 :     if ( st->VAD == 1 )
     374             :     {
     375           0 :         st->active_frame_cnt_bwddec++;
     376           0 :         st->total_frame_cnt_bwddec++;
     377           0 :         if ( st->active_frame_cnt_bwddec > 99 )
     378             :         {
     379           0 :             st->active_frame_cnt_bwddec = 100;
     380             :         }
     381           0 :         if ( st->total_frame_cnt_bwddec > 500 )
     382             :         {
     383           0 :             st->total_frame_cnt_bwddec = 500;
     384             :         }
     385             : 
     386           0 :         for ( i = 0; i < ( st->cldfbSyn->no_channels - st->cldfbSyn->bandsToZero ); i++ )
     387             :         {
     388           0 :             nrg_bwddec = 0.0f;
     389           0 :             for ( k = 0; k < nTimeSlots; k++ )
     390             :             {
     391           0 :                 realQ1 = rAnalysis[k][i];
     392           0 :                 imagQ1 = iAnalysis[k][i];
     393           0 :                 nrg_bwddec += ( realQ1 * realQ1 );
     394           0 :                 nrg_bwddec += ( imagQ1 * imagQ1 );
     395             :             }
     396           0 :             nrg_band[i] = ( nrg_bwddec );
     397           0 :             if ( ( nrg_band[i] > max_nrg ) && ( i > 11 ) )
     398             :             {
     399           0 :                 max_nrg = nrg_band[i];
     400             :             }
     401             :         }
     402           0 :         for ( ; i < st->cldfbSyn->no_channels; i++ )
     403             :         {
     404           0 :             nrg_band[i] = 0;
     405             :         }
     406             : 
     407           0 :         nrg_bwddec = 0;
     408           0 :         for ( i = 2; i < 9; i++ )
     409             :         {
     410           0 :             nrg_bwddec += ( nrg_band[i] / 7.0f );
     411             :         }
     412             : 
     413           0 :         thr_bwddwc = ( nrg_bwddec / 512.0f );
     414             : 
     415           0 :         st->avg_nrg_LT = 0.98999f * st->avg_nrg_LT + 0.009979f * thr_bwddwc;
     416           0 :         update_perc = 1;
     417           0 :         if ( st->ini_frame >= 25 && thr_bwddwc < st->avg_nrg_LT * 0.005f )
     418             :         {
     419           0 :             update_perc = 0;
     420             :         }
     421             : 
     422           0 :         flag = 1;
     423           0 :         if ( max_nrg >= thr_bwddwc )
     424             :         {
     425           0 :             flag = 0;
     426             :         }
     427             : 
     428           0 :         for ( i = 0; i < WBcnt - 1; i++ )
     429             :         {
     430           0 :             st->flag_buffer[i] = st->flag_buffer[i + 1];
     431             :         }
     432           0 :         st->flag_buffer[WBcnt - 1] = flag;
     433             : 
     434             :         /*long term percentage*/
     435           0 :         if ( update_perc == 1 )
     436             :         {
     437           0 :             st->perc_bwddec += ( flag - st->perc_bwddec ) / st->active_frame_cnt_bwddec;
     438             :         }
     439           0 :         if ( ( st->total_frame_cnt_bwddec > offset ) && ( st->active_frame_cnt_bwddec > 50 ) )
     440             :         {
     441           0 :             if ( ( st->perc_bwddec >= perc_detect || ( st->perc_bwddec >= perc_miss && st->last_flag_filter_NB ) ) && ( sum_s( st->flag_buffer, WBcnt ) != 0 ) ) /* decision hysterysis */
     442             :             {
     443           0 :                 st->cldfbSyn->bandsToZero = ( st->cldfbSyn->no_channels - 10 );
     444           0 :                 st->last_flag_filter_NB = 1; /* VAD processing must be dependent on hysterysis, as if hysterysis fails, but threshold passes, we dont want next VAD frames to have NB only */
     445             :             }
     446             :             else
     447             :             {
     448           0 :                 st->last_flag_filter_NB = 0;
     449             :             }
     450             :         }
     451             :         else
     452             :         {
     453           0 :             st->last_flag_filter_NB = 0;
     454             :         }
     455           0 :         if ( sum_s( st->flag_buffer, WBcnt ) == 0 )
     456             :         {
     457           0 :             st->perc_bwddec = 0.0f;
     458           0 :             st->active_frame_cnt_bwddec = 0;
     459           0 :             st->total_frame_cnt_bwddec = 0;
     460           0 :             st->last_flag_filter_NB = 0;
     461             :         }
     462             :     }
     463             :     else
     464             :     {
     465           0 :         if ( st->last_flag_filter_NB == 1 )
     466             :         {
     467           0 :             st->cldfbSyn->bandsToZero = st->last_active_bandsToZero_bwdec;
     468             :         }
     469           0 :         st->total_frame_cnt_bwddec++;
     470           0 :         if ( st->total_frame_cnt_bwddec > 500 )
     471             :         {
     472           0 :             st->total_frame_cnt_bwddec = 500;
     473             :         }
     474             :     }
     475             : 
     476           0 :     st->last_active_bandsToZero_bwdec = st->cldfbSyn->bandsToZero;
     477             : 
     478           0 :     return;
     479             : }

Generated by: LCOV version 1.14