Commit 4168f1d2 authored by lefort's avatar lefort
Browse files

Branch for contribution 48 - Enhanced stereo downmix.

parent f3b4ed31
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -1677,6 +1677,26 @@ typedef enum
#define IVAS_LIMITER_THRESHOLD                  32729           /* -0.01 dBFS */
#define IVAS_LIMITER_ATTACK_SECONDS             0.005f

#ifdef ENHANCED_STEREO_DMX
/*----------------------------------------------------------------------------------*
 * Stereo downmix EVS constants
 *----------------------------------------------------------------------------------*/

typedef enum
{
    STEREO_DMX_EVS_PHA_IPD,
    STEREO_DMX_EVS_PHA_IPD2,
    STEREO_DMX_EVS_NO_PHA

} STEREO_DMX_EVS_PHA;

typedef enum
{
    STEREO_DMX_EVS_PRC_POC,
    STEREO_DMX_EVS_PRC_PHA,

} STEREO_DMX_EVS_PRC;
#endif

#endif
/* clang-format on */
+1 −0
Original line number Diff line number Diff line
@@ -169,6 +169,7 @@

#define ISSUE_24_CLEANUP_MCT_LFE                        /* Issue 24: Cleanup LFE path withing MCT */ 

#define ENHANCED_STEREO_DMX                             /* Orange : Contribution 48 - Enhanced stereo downmix. */

/* ################## End DEVELOPMENT switches ######################### */
/* clang-format on */
+34 −0
Original line number Diff line number Diff line
@@ -982,9 +982,43 @@ typedef struct stereo_dmx_evs_phase_only_correlation_structure

} STEREO_DMX_EVS_POC_DATA, *STEREO_DMX_EVS_POC_HANDLE;

#ifdef ENHANCED_STEREO_DMX
typedef struct stereo_dmx_evs_correlation_filter_structure
{
    float ipd_ff[L_FRAME48k / 2 + 1];
    float Pr[L_FRAME48k / 2 + 1];
    float Pi[L_FRAME48k / 2 + 1];

    int32_t pha_len;
    int32_t fad_len;

    float win[L_FRAME48k];
    float fad_g[L_FRAME48k];
    float *p_prev_taps[CPE_CHANNELS], prev_taps[CPE_CHANNELS][L_FRAME48k];
    float *p_curr_taps[CPE_CHANNELS], curr_taps[CPE_CHANNELS][L_FRAME48k];

    float data_mem[CPE_CHANNELS][L_FRAME48k*2];

    STEREO_DMX_EVS_PHA curr_pha;
    STEREO_DMX_EVS_PHA prev_pha;
    int16_t pha_hys_cnt;

    int16_t prc_thres;
    STEREO_DMX_EVS_PRC curr_prc;
    STEREO_DMX_EVS_PRC prev_prc;
    int16_t prc_hys_cnt;
    float fad_g_prc[L_FRAME48k];
    int32_t fad_len_prc;

} STEREO_DMX_EVS_PHA_DATA, *STEREO_DMX_EVS_PHA_HANDLE;

#endif 
typedef struct stereo_dmx_evs_enc_data_structure
{
    STEREO_DMX_EVS_POC_HANDLE hPOC;
#ifdef ENHANCED_STEREO_DMX
    STEREO_DMX_EVS_PHA_HANDLE hPHA;
#endif 

    float itd;

+526 −0
Original line number Diff line number Diff line
@@ -65,13 +65,37 @@

#define Q_BAND 0.25f

#ifdef ENHANCED_STEREO_DMX

#define STEREO_DMX_EVS_PHA_LEN_16 20.0f
#define STEREO_DMX_EVS_FAD_LEN_16 20.0f
#define STEREO_DMX_EVS_PHA_LEN_32 10.0f
#define STEREO_DMX_EVS_FAD_LEN_32 10.0f
#define STEREO_DMX_EVS_PHA_LEN_48 5.0f
#define STEREO_DMX_EVS_FAD_LEN_48 5.0f

#define STEREO_DMX_EVS_ISD_THRES       1.3f
#define STEREO_DMX_EVS_ISD_DIST_THRES  0.42f
#define STEREO_DMX_EVS_SWTCH_HYS_THRES 1

#define STEREO_DMX_EVS_SWTCH_PRC_THRES     0.6f
#define STEREO_DMX_EVS_SWTCH_PRC_HYS_THRES 1
#define STEREO_DMX_EVS_FADE_LEN_PRC        20.0f

#endif

/*-----------------------------------------------------------------------*
 * Local function prototypes
 *-----------------------------------------------------------------------*/

static void estimate_itd_wnd_fft( const float *input, float *specr, float *speci, const float *rfft_coef, const float *wnd, const int16_t input_frame );
#ifdef ENHANCED_STEREO_DMX
static void calc_poc( STEREO_DMX_EVS_POC_HANDLE hPOC, STEREO_DMX_EVS_PHA_HANDLE hPHA, const float wnd[], const float rfft_coef[], const float specLr[], const float specLi[], const float specRr[], const float specRi[], const int16_t input_frame );
static ivas_error estimate_itd( float *corr, STEREO_DMX_EVS_POC_HANDLE hPOC, STEREO_DMX_EVS_PHA_HANDLE hPHA, const float srcL[], const float srcR[], float itd[], const int16_t input_frame );
# else 
static void calc_poc( STEREO_DMX_EVS_POC_HANDLE hPOC, const float wnd[], const float rfft_coef[], const float specLr[], const float specLi[], const float specRr[], const float specRi[], const int16_t input_frame );
static ivas_error estimate_itd( float *corr, STEREO_DMX_EVS_POC_HANDLE hPOC, const float srcL[], const float srcR[], float itd[], const int16_t input_frame );
#endif 
static void weighted_ave( const float src1[], const float src2[], float dst[], const float gain, const float old_gain, const int16_t input_frame, const float wnd[] );
static void adapt_gain( const float src[], float dst[], const float gain, const float old_gain, const int16_t input_frame, const float wnd[] );
static void create_M_signal( const float srcL[], const float srcR[], float dmx[], const float w_curr, const int16_t input_frame, const float wnd[], float *w_prev, float *dmx_energy, float *src_energy );
@@ -141,6 +165,9 @@ void estimate_itd_wnd_fft(

static void calc_poc(
    STEREO_DMX_EVS_POC_HANDLE hPOC, /* i/o: phase only correlation structure */
#ifdef ENHANCED_STEREO_DMX
    STEREO_DMX_EVS_PHA_HANDLE hPHA, /* i/o : correlation filter structure */
#endif 
    const float wnd[],              /* i  : window coef                      */
    const float rfft_coef[],        /* i  : RFFT coef                        */
    const float specLr[],           /* i  : Lch real-part spectra            */
@@ -167,9 +194,23 @@ static void calc_poc(
    int16_t cos_step, cos_max;
    float eps_cos, eps_sin, EPS;

#ifdef ENHANCED_STEREO_DMX
    float Nr, Ni, Dr, Di, ISD, isd_rate;
    int16_t isd_cnt, n, end8;
    float *Pr, *Pi, *ipd_ff;
    float tPr, tPi, Pn;
    float *p_curr_taps, *p_curr_taps_l2r;
    double energy;
#endif 

    /* Initialization */
    iN = 1.0f / (float) input_frame;

#ifdef ENHANCED_STEREO_DMX
    Pr = hPHA->Pr;
    Pi = hPHA->Pi;
#endif

    s = hPOC->sin;
    P = hPOC->P;
    n0 = input_frame / 2;
@@ -337,6 +378,183 @@ static void calc_poc(
    }
    specPOr[n0] = sign( specLr[n0] * specRr[n0] ) * wnd[i * step + bias] * gamma;

#ifdef ENHANCED_STEREO_DMX

    end8 = 160;

    // Memorize the filters N-1
    for ( n = 0; n < CPE_CHANNELS; n++ )
    {
        if (hPHA->p_curr_taps[n])
        {
            hPHA->p_prev_taps[n] = hPHA->prev_taps[n];
            mvr2r( hPHA->p_curr_taps[n], hPHA->p_prev_taps[n], hPHA->pha_len );
        }
        else
        {
            hPHA->p_prev_taps[n] = NULL;
        }

        hPHA->p_curr_taps[n] = NULL;
    }

    // ISD
    isd_cnt = 0;
    for ( int i = 1; i <= end8 ; i++ )
    {
        Nr = ( specLr[i] - specRr[i] );
        Ni = ( specLi[i] - specRi[i] );
        Dr = ( specLr[i] + specRr[i] );
        Di = ( specLi[i] + specRi[i] );
        ISD = sqrt( (Nr*Nr + Ni*Ni) / (Dr*Dr + Di*Di) );
        if (ISD > STEREO_DMX_EVS_ISD_THRES)
        {
            isd_cnt++;
        }
    }
    isd_rate = (float)isd_cnt / (float)end8;

    if (isd_rate > STEREO_DMX_EVS_ISD_DIST_THRES)
    {
        if ( hPHA->curr_pha != STEREO_DMX_EVS_PHA_IPD )
        {
            if (hPHA->prev_pha == STEREO_DMX_EVS_PHA_IPD)
            {
                hPHA->pha_hys_cnt += 1;
            }
            else
            {
                hPHA->pha_hys_cnt = 0;
            }

            if (hPHA->pha_hys_cnt >= STEREO_DMX_EVS_SWTCH_HYS_THRES)
            {
                hPHA->curr_pha = STEREO_DMX_EVS_PHA_IPD;
            }
        }
        hPHA->prev_pha = STEREO_DMX_EVS_PHA_IPD;
    }
    else
    {
        if (hPHA->curr_pha != STEREO_DMX_EVS_PHA_IPD2)
        {
            if (hPHA->prev_pha == STEREO_DMX_EVS_PHA_IPD2)
            {
                hPHA->pha_hys_cnt += 1;
            }
            else
            {
                hPHA->pha_hys_cnt = 0;
            }

            if (hPHA->pha_hys_cnt >= STEREO_DMX_EVS_SWTCH_HYS_THRES)
            {
                hPHA->curr_pha = STEREO_DMX_EVS_PHA_IPD2;
            }
        }
        hPHA->prev_pha = STEREO_DMX_EVS_PHA_IPD2;
    }

    if (hPHA->curr_pha != STEREO_DMX_EVS_NO_PHA)
    {
        ipd_ff = hPHA->ipd_ff;
        for ( i = 0; i < ( end8 + 1 ); i++ )
        {
            tPr = ( specLr[i] * specRr[i] + specLi[i] * specRi[i] );
            tPi = ( specLi[i] * specRr[i] - specLr[i] * specRi[i] );
            Pn = sqrt( tPr * tPr + tPi * tPi );
            tPr /= (Pn+EPSILON);
            tPi /= (Pn+EPSILON);

            if (fabs(tPr*tPi) < EPSILON)
            {
                tPr = 1.;
                tPi = 0.;
            }

            Pr[i] = ipd_ff[i]*Pr[i] + (1.0 - ipd_ff[i])*tPr;
            Pi[i] = ipd_ff[i]*Pi[i] + (1.0 - ipd_ff[i])*tPi;

        }

        for ( i = end8+1; i < n0; i++ )
        {
            Pr[i] = 1.0;
            Pi[i] = 0.0;
        }

        if (hPHA->curr_pha == STEREO_DMX_EVS_PHA_IPD)
        {
            // PHA R2L
            hPHA->p_curr_taps[1] = hPHA->curr_taps[1];

            p_curr_taps = hPHA->p_curr_taps[1];

            p_curr_taps[0] = Pr[0];
            p_curr_taps[1] = Pr[n0];
            for ( i = 1; i < n0; i++ )
            {
                p_curr_taps[i * 2] = Pr[i];
                p_curr_taps[i * 2 + 1] = Pi[i];
            }
            rfft( p_curr_taps, rfft_coef, input_frame, +1 );
        }
        else
        {
            // PHA R2L
            hPHA->p_curr_taps[1] = hPHA->curr_taps[1];
            
            p_curr_taps = hPHA->p_curr_taps[1];

            for ( i = 1; i < n0; i++ )
            {
                Pn = sqrt( Pr[i] * Pr[i] + Pi[i] * Pi[i] );
                tPr = Pr[i]/(Pn+EPSILON);
                p_curr_taps[i * 2] = sqrt((1.+tPr)/2.);
                p_curr_taps[i * 2 + 1] = sqrt((1.-tPr)/2.)*sign(Pi[i]);

            }
            p_curr_taps[0] = 1;
            p_curr_taps[1] = 1;

            rfft( p_curr_taps, rfft_coef, input_frame, +1 );

            // PHA L2R
            hPHA->p_curr_taps[0] = hPHA->curr_taps[0];
            
            p_curr_taps_l2r = hPHA->p_curr_taps[0];

            p_curr_taps_l2r[0] = p_curr_taps[0];
            for ( i = 1; i < input_frame; i++ )
            {
                p_curr_taps_l2r[i] = p_curr_taps[input_frame-i];
            }
        }
    }

    for ( n = 0; n < CPE_CHANNELS; n++ )
    {
        if (hPHA->p_curr_taps[n])
        {
            for ( i = 0; i < hPHA->pha_len; i++ )
            {
                hPHA->p_curr_taps[n][i] *= hPHA->win[i];
            }

            energy = 0;
            for ( i = 0; i < hPHA->pha_len; i++ )
            {
                energy += hPHA->p_curr_taps[n][i] * hPHA->p_curr_taps[n][i];
            }
            energy = sqrtf( energy );
            for ( i = 0; i < hPHA->pha_len; i++ )
            {
                hPHA->p_curr_taps[n][i] /= energy;
            }
        }
    }

#endif 

    rfft_buf[0] = specPOr[0];
    rfft_buf[1] = specPOr[n0];
@@ -550,6 +768,9 @@ static float find_poc_peak(
static ivas_error estimate_itd(
    float *corr,                    /* o  : correlation                      */
    STEREO_DMX_EVS_POC_HANDLE hPOC, /* i/o: phase only correlation structure */
#ifdef ENHANCED_STEREO_DMX
    STEREO_DMX_EVS_PHA_HANDLE hPHA, /* i/o : correlation filter structure */
#endif 
    const float srcL[],             /* i  : Lch input signal                 */
    const float srcR[],             /* i  : Rch input signal                 */
    float itd[],                    /* o  : estimated itd                    */
@@ -598,7 +819,11 @@ static ivas_error estimate_itd(
    estimate_itd_wnd_fft( srcL, specLr, specLi, rfft_coef, hPOC->wnd, input_frame );
    estimate_itd_wnd_fft( srcR, specRr, specRi, rfft_coef, hPOC->wnd, input_frame );

#ifdef ENHANCED_STEREO_DMX
    calc_poc( hPOC, hPHA, hPOC->wnd, rfft_coef, specLr, specLi, specRr, specRi, input_frame );
#else
    calc_poc( hPOC, hPOC->wnd, rfft_coef, specLr, specLi, specRr, specRi, input_frame );
#endif
    *corr = find_poc_peak( hPOC, itd, input_frame, STEREO_DMX_EVS_POC_W_FORGETTING );

    return error;
@@ -794,7 +1019,18 @@ void stereo_dmx_evs_enc(
    int16_t n;
    float dmx_weight, corr;
    float data_f[CPE_CHANNELS][L_FRAME48k];

#ifdef ENHANCED_STEREO_DMX
    int16_t k, m;
    int32_t pha_len, fad_len;
    float mem_out_curr[CPE_CHANNELS][L_FRAME48k], mem_out_last[L_FRAME48k];
    float *p_data_mem, *p_prev_taps, *p_curr_taps, *fad_g, *p_mem_out_curr, *p_data_f;
    float dmx_itd_data[L_FRAME48k], dmx_ipd_data[L_FRAME48k], *p_dmx_data;
    STEREO_DMX_EVS_PRC curr_prc;
#else
    float dmx_data[L_FRAME48k];
#endif

    int16_t input_frame;

    input_frame = (int16_t) ( input_Fs / FRAMES_PER_SEC );
@@ -810,6 +1046,186 @@ void stereo_dmx_evs_enc(
        set_f( data_f[1] + n_samples, 0.0f, input_frame - n_samples );
    }

#ifdef ENHANCED_STEREO_DMX
    
    estimate_itd( &corr, hStereoDmxEVS->hPOC, hStereoDmxEVS->hPHA, data_f[0], data_f[1], &hStereoDmxEVS->itd, input_frame );

    pha_len = hStereoDmxEVS->hPHA->pha_len;
    fad_len = hStereoDmxEVS->hPHA->fad_len;
    fad_g = hStereoDmxEVS->hPHA->fad_g;

    for ( k = 0; k < CPE_CHANNELS; k++ )
    {
        p_prev_taps = hStereoDmxEVS->hPHA->p_prev_taps[k];
        p_curr_taps = hStereoDmxEVS->hPHA->p_curr_taps[k];
        p_mem_out_curr = mem_out_curr[k];
        p_data_f = data_f[k];

        p_data_mem = hStereoDmxEVS->hPHA->data_mem[k];
        mvr2r( &( p_data_mem[input_frame] ), p_data_mem, pha_len );
        p_data_mem = &( p_data_mem[pha_len] );
        mvr2r( p_data_f, p_data_mem, input_frame );

        if (p_prev_taps)
        {
            for (n = 0; n < fad_len; n++)
            {
                mem_out_last[n] = 0;
                for ( m = 0; m < pha_len; m++ )
                {
                    mem_out_last[n] += p_data_mem[n - m] * p_prev_taps[m];
                }
                mem_out_last[n] *= fad_g[fad_len - (1+n)];
            }
        }

        if (p_curr_taps)
        {
            if (p_prev_taps == NULL)
            {
                for (n = 0; n < fad_len; n++)
                {
                    mem_out_last[n] = fad_g[fad_len - (1+n)] * p_data_f[n];
                }
            }

            for (n = 0; n < fad_len; n++)
            {
                p_mem_out_curr[n] = 0;
                for ( m = 0; m < pha_len; m++ )
                {
                    p_mem_out_curr[n] += p_data_mem[n - m] * p_curr_taps[m];
                }
                p_mem_out_curr[n] = fad_g[n] * p_mem_out_curr[n] + mem_out_last[n];
            }
            for (; n < input_frame; n++)
            {
                p_mem_out_curr[n] = 0;
                for (m = 0; m < pha_len; m++)
                {
                    p_mem_out_curr[n] += p_data_mem[n - m] * p_curr_taps[m];
                }
            }
        }
        else
        {
            if (p_prev_taps)
            {
                for (n = 0; n < fad_len; n++)
                {
                    p_mem_out_curr[n] = fad_g[n] * p_data_f[n] + mem_out_last[n];
                } 
                mvr2r( &(p_data_f[fad_len]), &(p_mem_out_curr[fad_len]), input_frame-fad_len );
            }
            else
            {
                mvr2r( p_data_f, p_mem_out_curr, input_frame );
            }
        }
    }

    // poc

    if ( hStereoDmxEVS->itd )
    {
        dmx_weight = ( ( hStereoDmxEVS->itd > 0 ) ? ( -1 ) : 1 ) * 0.5f * corr + 0.5f;
    }
    else
    {
        dmx_weight = 0.5f;
    }

    create_M_signal( data_f[0], data_f[1], dmx_itd_data, dmx_weight, input_frame, hStereoDmxEVS->s_wnd,
                     hStereoDmxEVS->dmx_weight, hStereoDmxEVS->pre_dmx_energy, hStereoDmxEVS->aux_dmx_energy );

    // pha

    for ( n=0; n < input_frame; n++ )
    {
        dmx_ipd_data[n] = (mem_out_curr[0][n] + mem_out_curr[1][n])*0.5;
    }

    // prc switch

    curr_prc = hStereoDmxEVS->hPHA->curr_prc;
    if (abs((int16_t)hStereoDmxEVS->itd) > hStereoDmxEVS->hPHA->prc_thres)
    {
        if ( hStereoDmxEVS->hPHA->curr_prc != STEREO_DMX_EVS_PRC_POC )
        {
            if (hStereoDmxEVS->hPHA->prev_prc == STEREO_DMX_EVS_PRC_POC)
            {
                hStereoDmxEVS->hPHA->prc_hys_cnt += 1;
            }
            else
            {
                hStereoDmxEVS->hPHA->prc_hys_cnt = 0;
            }

            if (hStereoDmxEVS->hPHA->prc_hys_cnt >= STEREO_DMX_EVS_SWTCH_PRC_HYS_THRES)
            {
                hStereoDmxEVS->hPHA->curr_prc = STEREO_DMX_EVS_PRC_POC;
            }
        }
        hStereoDmxEVS->hPHA->prev_prc = STEREO_DMX_EVS_PRC_POC;
    }
    else
    {
        if ( hStereoDmxEVS->hPHA->curr_prc != STEREO_DMX_EVS_PRC_PHA )
        {
            if (hStereoDmxEVS->hPHA->prev_prc == STEREO_DMX_EVS_PRC_PHA)
            {
                hStereoDmxEVS->hPHA->prc_hys_cnt += 1;
            }
            else
            {
                hStereoDmxEVS->hPHA->prc_hys_cnt = 0;
            }

            if (hStereoDmxEVS->hPHA->prc_hys_cnt >= STEREO_DMX_EVS_SWTCH_PRC_HYS_THRES)
            {
                hStereoDmxEVS->hPHA->curr_prc = STEREO_DMX_EVS_PRC_PHA;
            }
        }
        hStereoDmxEVS->hPHA->prev_prc = STEREO_DMX_EVS_PRC_PHA;
    }
 
    if ( hStereoDmxEVS->hPHA->curr_prc == STEREO_DMX_EVS_PRC_POC )
    {
        p_dmx_data = dmx_itd_data;

        if (curr_prc != hStereoDmxEVS->hPHA->curr_prc)
        {
            fad_len = hStereoDmxEVS->hPHA->fad_len_prc;
            fad_g = hStereoDmxEVS->hPHA->fad_g_prc;

            for (n = 0; n < fad_len; n++)
            {
                p_dmx_data[n] *= fad_g[n];
                p_dmx_data[n] += (1.-fad_g[n]) * dmx_ipd_data[n];
            }
        }
    }
    else
    {
        p_dmx_data = dmx_ipd_data;

        if (curr_prc != hStereoDmxEVS->hPHA->curr_prc)
        {
            fad_len = hStereoDmxEVS->hPHA->fad_len_prc;
            fad_g = hStereoDmxEVS->hPHA->fad_g_prc;

            for (n = 0; n < fad_len; n++)
            {
                p_dmx_data[n] *= fad_g[n];
                p_dmx_data[n] += (1.-fad_g[n]) * dmx_itd_data[n];
            }
        }
    }

    mvr2s( p_dmx_data, data, n_samples );

#else

    estimate_itd( &corr, hStereoDmxEVS->hPOC, data_f[0], data_f[1], &hStereoDmxEVS->itd, input_frame );

    if ( hStereoDmxEVS->itd )
@@ -826,6 +1242,8 @@ void stereo_dmx_evs_enc(

    mvr2s( dmx_data, data, n_samples );
    
#endif

    return;
}

@@ -844,6 +1262,15 @@ ivas_error stereo_dmx_evs_init_encoder(
    STEREO_DMX_EVS_ENC_HANDLE hStereoDmxEVS;
    int16_t n, input_frame;

#ifdef ENHANCED_STEREO_DMX
    int32_t f_len, pha_len, fad_len;
    float *win, *fad_g;

    float a_min, a_max, a_step, n0, itrh;
    float *ipd_ff;
   
#endif

    input_frame = (int16_t) ( input_Fs / FRAMES_PER_SEC );

    hStereoDmxEVS = NULL;
@@ -933,6 +1360,97 @@ ivas_error stereo_dmx_evs_init_encoder(

    hStereoDmxEVS->hPOC->confidence = 0.0f;

#ifdef ENHANCED_STEREO_DMX

    hStereoDmxEVS->hPHA = NULL;
    if ( ( hStereoDmxEVS->hPHA = (STEREO_DMX_EVS_PHA_HANDLE) malloc( sizeof( STEREO_DMX_EVS_PHA_DATA ) ) ) == NULL )
    {
        return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for STEREO_DMX_EVS_CORFILT_DATA\n" ) );
    }

    for ( n = 0; n < CPE_CHANNELS; n++ )
    {
        hStereoDmxEVS->hPHA->p_curr_taps[n] = NULL;
        hStereoDmxEVS->hPHA->p_prev_taps[n] = NULL;

        set_zero( hStereoDmxEVS->hPHA->data_mem[n], L_FRAME48k * 2 );
        set_zero( hStereoDmxEVS->hPHA->curr_taps[n], L_FRAME48k );
    }

    if ( input_Fs == 16000 )
    {
        f_len = STEREO_DMX_EVS_PHA_LEN_16 * input_Fs / 1000;
        hStereoDmxEVS->hPHA->fad_len = STEREO_DMX_EVS_FAD_LEN_16 * input_Fs / 1000;
    }
    else if ( input_Fs == 32000 )
    {
        f_len = STEREO_DMX_EVS_PHA_LEN_32 * input_Fs / 1000;
        hStereoDmxEVS->hPHA->fad_len = STEREO_DMX_EVS_FAD_LEN_32 * input_Fs / 1000;
    }
    else if ( input_Fs == 48000 )
    {
        f_len = STEREO_DMX_EVS_PHA_LEN_48 * input_Fs / 1000;
        hStereoDmxEVS->hPHA->fad_len = STEREO_DMX_EVS_FAD_LEN_48 * input_Fs / 1000;
    }
    else
    {
        return IVAS_ERROR( IVAS_ERR_INTERNAL_FATAL, "invalid sampling frequency\n" );
    }

    hStereoDmxEVS->hPHA->pha_len = f_len/2;

    pha_len = hStereoDmxEVS->hPHA->pha_len;
    fad_len = hStereoDmxEVS->hPHA->fad_len;

    win = hStereoDmxEVS->hPHA->win;
    set_zero( win, L_FRAME48k );
    for ( n = 0; n < pha_len; n++ )
    {
        win[n] = 0.5f * (1.0f + cosf( ( PI2 * ( n + 1 ) ) / ( ( f_len ) + 1 ) ) );
    }

    fad_g = hStereoDmxEVS->hPHA->fad_g;
    for ( n = 0; n < fad_len; n++ )
    {
        fad_g[n] = (float) n / (float)fad_len;
    }

    hStereoDmxEVS->hPHA->curr_pha = STEREO_DMX_EVS_NO_PHA;
    hStereoDmxEVS->hPHA->prev_pha = STEREO_DMX_EVS_NO_PHA;
    hStereoDmxEVS->hPHA->pha_hys_cnt = 0;

    // Compute the forgetting factor
    a_min = 0.8576958985908941;
    a_max = 0.9440608762859234;
    itrh = 60;
    n0 = input_frame / 2;
    a_step = ( a_min - a_max ) / (n0+1-itrh);
    ipd_ff = hStereoDmxEVS->hPHA->ipd_ff;
    for ( n = 0; n < itrh; n++ )
    {
        ipd_ff[n] = a_max;
    }
    for ( ; n < ( n0 + 1 ); n++ )
    {
        ipd_ff[n] = a_max + (n-itrh) * a_step;
    }

    hStereoDmxEVS->hPHA->prc_thres = STEREO_DMX_EVS_SWTCH_PRC_THRES * input_Fs / 1000;

    hStereoDmxEVS->hPHA->curr_prc = STEREO_DMX_EVS_PRC_POC;
    hStereoDmxEVS->hPHA->prev_prc = STEREO_DMX_EVS_PRC_POC;
    hStereoDmxEVS->hPHA->prc_hys_cnt = 0;

    hStereoDmxEVS->hPHA->fad_len_prc = STEREO_DMX_EVS_FADE_LEN_PRC * input_Fs / 1000;
    fad_len = hStereoDmxEVS->hPHA->fad_len_prc;
    fad_g = hStereoDmxEVS->hPHA->fad_g_prc;
    for ( n = 0; n < fad_len; n++ )
    {
        fad_g[n] = (float) n / (float)fad_len;
    }

#endif 

    *hStereoDmxEVS_out = hStereoDmxEVS;

    return IVAS_ERR_OK;
@@ -960,6 +1478,14 @@ void stereo_dmx_evs_close_encoder(
        ( *hStereoDmxEVS )->hPOC = NULL;
    }

#ifdef ENHANCED_STEREO_DMX
    if ( ( *hStereoDmxEVS )->hPHA != NULL )
    {
        free( ( *hStereoDmxEVS )->hPHA );
        ( *hStereoDmxEVS )->hPHA = NULL;
    }
#endif 

    free( ( *hStereoDmxEVS ) );
    ( *hStereoDmxEVS ) = NULL;