Commit 49860cd7 authored by Nicolas Roussin's avatar Nicolas Roussin
Browse files

Optimize ivas_filter_process_fx part 1.

parent b9bfbe38
Loading
Loading
Loading
Loading
+97 −0
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@
 *------------------------------------------------------------------------------------------*/

static void ivas_iir_2_filter_fx( ivas_filters_process_state_t *filter_state, Word32 *pIn_Out_fx, const Word16 length, const Word16 stage, Word16 *pIn_Out_e );
static void ivas_iir_2_filter_fixed_fx( ivas_filters_process_state_t *filter_state, Word32 *pIn_Out_fx, const Word16 length, const Word16 stage, Word16 *pIn_Out_e );


/*-----------------------------------------------------------------------------------------*
@@ -145,6 +146,10 @@ void ivas_filters_init_fx(
        move16();
    }

#ifdef OPT_2239_IVAS_FILTER_PROCESS
    filter_state->q_diff = 0;
#endif

    return;
}

@@ -285,3 +290,95 @@ static void ivas_iir_2_filter_fx(

    return;
}

void ivas_filter_process_fixed_fx(
    ivas_filters_process_state_t *filter_state, /* i/o: filter state handle             */
    Word32 *pIn_Out_fx,                         /* i/o: signal subject to filtering (exp[i] : pIn_out_e[i])   */
    const Word16 length,                        /* i  : filter order                    */
    Word16 *q_in_out )
{
    SWITCH( filter_state->order )
    {
        case IVAS_FILTER_ORDER_1:
            ivas_iir_2_filter_fixed_fx( filter_state, pIn_Out_fx, length, IVAS_FILTER_STAGE_0, q_in_out );
            /* Scale pIn_Out_fx back to input Q */
            BREAK;
        case IVAS_FILTER_ORDER_4:
            /* biquad-1 */
            ivas_iir_2_filter_fixed_fx( filter_state, pIn_Out_fx, length, IVAS_FILTER_STAGE_0, q_in_out );
            /* biquad-2 */
            ivas_iir_2_filter_fixed_fx( filter_state, pIn_Out_fx, length, IVAS_FILTER_STAGE_1, q_in_out );
            /* Scale pIn_Out_fx back to input Q */
            BREAK;
        default:
            BREAK;
    }

    return;
}

static void ivas_iir_2_filter_fixed_fx(
    ivas_filters_process_state_t *filter_state,
    Word32 *pIn_Out_fx, // Q(31-*pIn_Out_e)
    const Word16 length,
    const Word16 stage,
    Word16 *q_in_out )
{
    Word16 i, j;

    Word32 *pIn_fx = pIn_Out_fx;
    Word32 *pOut_fx = pIn_Out_fx;
    Word32 tmp_pIn_buf_i_fx;
    Word16 tmp_pIn_buf_i_e;

    Word32 L_tmp_prod, L_tmp;
    Word16 L_prod_e, L_tmp_e;

    Word16 exp;
    Word16 q_in = *q_in_out;
    Word16 q_out = add( filter_state->q_diff, q_in );
    *q_in_out = q_out;

    FOR( i = 0; i < length; i++ )
    {
        tmp_pIn_buf_i_fx = pIn_fx[i];
        move32();
        tmp_pIn_buf_i_e = q_in;
        move16();

        L_tmp_prod = Mpy_32_32( filter_state->num_fx[stage][0], pIn_fx[i] ); // Q31 -(q_in+ filter_state->num_e[stage][0])
        L_prod_e = add( q_in, filter_state->num_e[stage][0] );

        pOut_fx[i] = BASOP_Util_Add_Mant32Exp( filter_state->state_fx[stage][0], filter_state->state_e[stage][0], L_tmp_prod, L_prod_e, &exp ); // Q31 - pIn_Out_e[i]
        move32();

        FOR( j = 1; j < filter_state->filt_len; j++ )
        {
            L_tmp_prod = Mpy_32_32( filter_state->num_fx[stage][j], tmp_pIn_buf_i_fx ); // Q31-L_prod_e
            L_prod_e = add( filter_state->num_e[stage][j], tmp_pIn_buf_i_e );

            L_tmp = BASOP_Util_Add_Mant32Exp( filter_state->state_fx[stage][j], filter_state->state_e[stage][j], L_tmp_prod, L_prod_e, &L_tmp_e ); // Q31 - L_tmp_e

            L_tmp_prod = Mpy_32_32( filter_state->den_fx[stage][j], pOut_fx[i] ); // Q31 - ( pIn_Out_e[i]+filter_state->den_e[stage][j] )
            L_prod_e = add( exp, filter_state->den_e[stage][j] );

            filter_state->state_fx[stage][j - 1] = BASOP_Util_Add_Mant32Exp( L_tmp, L_tmp_e, L_negate( L_tmp_prod ), L_prod_e, &filter_state->state_e[stage][j - 1] ); // Q31 - filter_state->state_e[stage][j - 1]
            move32();
            /*In case when exponent is less than -31 the value is very small and negligible hence resetting it to zero to avoid exponent overflow*/
            IF( LT_16( filter_state->state_e[stage][j - 1], -31 ) )
            {
                filter_state->state_fx[stage][j - 1] = 0;
                move32();
                filter_state->state_e[stage][j - 1] = 0;
                move16();
            }
        }


        Word16 shift = ( 31 - exp ) - q_out;
        pOut_fx[i] = L_shr_r_sat( pOut_fx[i], shift );
        move32();
    }

    return;
}
+7 −0
Original line number Diff line number Diff line
@@ -3817,6 +3817,13 @@ void ivas_filter_process_exp_fx(
    Word16 *pIn_Out_e 
);

void ivas_filter_process_fixed_fx(
    ivas_filters_process_state_t *filter_state, /* i/o: filter state handle             */
    Word32 *pIn_Out_fx,                         /* i/o: signal subject to filtering (exp[i] : pIn_out_e[i])   */
    const Word16 length,                        /* i  : filter order                    */
    Word16 *q_in_out 
);

ivas_error ivas_osba_enc_open_fx(
    Encoder_Struct *st_ivas                                     /* i/o: IVAS encoder handle                             */
);
+4 −0
Original line number Diff line number Diff line
@@ -661,6 +661,10 @@ typedef struct ivas_filters_process_state_t
    Word16 den_e[IVAS_FILTER_MAX_STAGES][IVAS_BIQUAD_FILT_LEN];
    Word16 state_e[IVAS_FILTER_MAX_STAGES][IVAS_BIQUAD_FILT_LEN];

#ifdef OPT_2239_IVAS_FILTER_PROCESS
    Word16 q_diff; // q_diff = q_out - q_in -> q_out = q_diff + q_in
#endif

} ivas_filters_process_state_t;


+76 −1
Original line number Diff line number Diff line
@@ -382,7 +382,6 @@ void ivas_td_decorr_get_ducking_gains_fx(
{
    Word16 i;
    Word32 e_fast_fx[L_FRAME48k], e_slow_fx[L_FRAME48k];
    Word16 e_fast_e[L_FRAME48k], e_slow_e[L_FRAME48k];
    Word32 in_duck_gain = hTranDet->in_duck_gain; /*Q30*/
    move32();
    Word32 out_duck_gain = hTranDet->out_duck_gain; /*Q30*/
@@ -396,6 +395,80 @@ void ivas_td_decorr_get_ducking_gains_fx(

    Copy32( pIn_pcm, e_fast_fx, frame_len ); /*Q11*/


#ifdef OPT_2239_IVAS_FILTER_PROCESS
    /* env hpf */
    Word16 q_fast = Q11;

    ivas_filter_process_fixed_fx( &hTranDet->env_hpf, e_fast_fx, frame_len, &q_fast );

    Word32 env_eps_fx = IVAS_TDET_PARM_ENV_EPS_fx;
    Word16 env_eps_q = Q31;

    Word16 q_diff = sub( q_fast, env_eps_q );
    q_fast = s_min( q_fast, env_eps_q );

    if ( q_diff >= 0 )
    {
        for ( i = 0; i < frame_len; i++ )
        {
            e_fast_fx[i] = L_add( L_abs( L_shr( e_fast_fx[i], q_diff ) ), env_eps_fx );
            move32();
            e_slow_fx[i] = e_fast_fx[i];
            move32();
        }
    }
    else
    {
        env_eps_fx = L_shl( env_eps_fx, q_diff );
        for ( i = 0; i < frame_len; i++ )
        {
            e_fast_fx[i] = L_add( L_abs( e_fast_fx[i] ), env_eps_fx );
            move32();
            e_slow_fx[i] = e_fast_fx[i];
            move32();
        }
    }

    Word16 q_slow = q_fast;

    /* env fast*/
    ivas_filter_process_fixed_fx( &hTranDet->env_fast, e_fast_fx, frame_len, &q_fast );

    /* env slow */
    ivas_filter_process_fixed_fx( &hTranDet->env_slow, e_slow_fx, frame_len, &q_slow );


    IF( tdet_flag )
    {
        FOR( i = 0; i < frame_len; i++ )
        {
            in_duck_gain = ivas_calc_duck_gain_fx( in_duck_gain, in_duck_coeff, e_slow_fx[i], 31 - q_slow, e_fast_fx[i], 31 - q_fast, duck_mult_fac ); /*Q30*/
            pIn_duck_gains[i] = in_duck_gain;                                                                                                          /*Q30*/
            move32();
        }
        hTranDet->in_duck_gain = in_duck_gain; /*Q30*/
        move32();
    }
    ELSE
    {
        FOR( i = 0; i < frame_len; i++ )
        {
            in_duck_gain = ivas_calc_duck_gain_fx( in_duck_gain, in_duck_coeff, e_slow_fx[i], 31 - q_slow, e_fast_fx[i], 31 - q_fast, duck_mult_fac ); /*Q30*/
            pIn_duck_gains[i] = in_duck_gain;                                                                                                          /*Q30*/
            move32();
            out_duck_gain = ivas_calc_duck_gain_fx( out_duck_gain, out_duck_coeff, e_fast_fx[i], 31 - q_fast, e_slow_fx[i], 31 - q_slow, duck_mult_fac ); /*Q30*/
            pOut_duck_gains[i] = out_duck_gain;                                                                                                           /*Q30*/
            move32();
        }
        hTranDet->in_duck_gain = in_duck_gain; /*Q30*/
        move32();
        hTranDet->out_duck_gain = out_duck_gain; /*Q30*/
        move32();
    }
#else
    Word16 e_fast_e[L_FRAME48k], e_slow_e[L_FRAME48k];

    set16_fx( e_fast_e, 31 - Q11, L_FRAME48k );

    /* env hpf */
@@ -446,6 +519,8 @@ void ivas_td_decorr_get_ducking_gains_fx(
        hTranDet->out_duck_gain = out_duck_gain; /*Q30*/
        move32();
    }
#endif


    return;
}
+98 −0
Original line number Diff line number Diff line
import numpy as np
from scipy import signal
import matplotlib.pyplot as plt

def generate_biquad_butterworth_coeffs(cutoff_freq, fs, order=2, ftype='lowpass', output='ba'):
    Wn = cutoff_freq
    if output == 'sos':
        sos_coeffs = signal.butter(order, Wn, btype=ftype, analog=False, output='sos', fs=fs)
        return sos_coeffs
    elif output == 'ba':
        b_coeffs, a_coeffs = signal.butter(order, Wn, btype=ftype, analog=False, output='ba', fs=fs)
        return b_coeffs, a_coeffs
    else:
        raise ValueError("Output format must be 'sos' or 'ba'")

def plot_biquad_frequency_response(b, a, fs):
    w, h = signal.freqz(b, a, fs=fs)
    magnitude_db = 20 * np.log10(np.abs(h))
    phase_degrees = np.unwrap(np.angle(h)) * 180 / np.pi

    fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True, tight_layout=True)

    ax1.plot(w, magnitude_db, 'b')
    ax1.set_ylabel('Magnitude (dB)', color='b')
    ax1.set_xlabel('Frequency (Hz)')
    ax1.grid()

    ax2.plot(w, phase_degrees, 'g')
    ax2.set_ylabel('Phase (degrees)', color='g')
    ax2.set_xlabel('Frequency (Hz)')
    ax2.grid()

    plt.show()

def max_abs_output_biquad(b, a, n_samples=10000):
    if a[0] != 1.0:
        b = np.array(b) / a[0]
        a = np.array(a) / a[0]
        a[0] = 1.0

    impulse_input = np.zeros(n_samples)
    impulse_input[0] = 1.0

    impulse_response = signal.lfilter(b, a, impulse_input)

    max_gain = np.sum(np.abs(impulse_response))
    
    return max_gain

# sampling_rate = 44100.0 # Hz
# cutoff_frequency = 5000.0 # Hz
# b_coeffs,a_coeffs = generate_biquad_butterworth_coeffs(cutoff_frequency, sampling_rate)
# plot_biquad_frequency_response(b_coeffs, a_coeffs, sampling_rate)

# # Calculate the maximum gain
# gain = max_abs_output_biquad(b_coeffs, a_coeffs)
# print(f"Numerator coefficients (b): {b_coeffs}")
# print(f"Denominator coefficients (a): {a_coeffs}")
# print(f"Maximum possible time-domain gain (L1 norm): {gain:.4f}")


IVAS_C_HPF_48k = 0.675231906655777
IVAS_C_HPF_32k = 0.554854910159853
IVAS_C_HPF_16k = 0.307863971328499

IVAS_C_FAST_48k = 0.995842001845110
IVAS_C_FAST_32k = 0.993769490623395
IVAS_C_FAST_16k = 0.987577800493881

IVAS_C_SLOW_48k = 0.999739617238810
IVAS_C_SLOW_32k = 0.999609451284012
IVAS_C_SLOW_16k = 0.999219055096324

#env_hpf
sampling_rate = 48000.0 # Hz
b_coeffs = [ +IVAS_C_HPF_48k, -IVAS_C_HPF_48k, 0.0 ]
a_coeffs = [ 1.0, -IVAS_C_HPF_48k, 0.0 ]
plot_biquad_frequency_response(b_coeffs, a_coeffs, sampling_rate)
gain = max_abs_output_biquad(b_coeffs, a_coeffs)
print(f"Maximum possible time-domain gain (L1 norm): {gain:.4f}")

#env_fast
sampling_rate = 48000.0 # Hz
b_coeffs = [ 1.0 - IVAS_C_FAST_48k, 0.0, 0.0 ]
a_coeffs = [ 1.0, -IVAS_C_FAST_48k, 0.0 ]
plot_biquad_frequency_response(b_coeffs, a_coeffs, sampling_rate)
gain = max_abs_output_biquad(b_coeffs, a_coeffs)
print(f"Maximum possible time-domain gain (L1 norm): {gain:.4f}")

#env_slow
sampling_rate = 48000.0 # Hz
b_coeffs = [ 1.0 - IVAS_C_SLOW_48k, 0.0, 0.0 ]
a_coeffs = [ 1.0, -IVAS_C_SLOW_48k, 0.0 ]
plot_biquad_frequency_response(b_coeffs, a_coeffs, sampling_rate)
gain = max_abs_output_biquad(b_coeffs, a_coeffs)
print(f"Maximum possible time-domain gain (L1 norm): {gain:.4f}")

Loading