FreeCalypso > hg > gsm-codec-lib
changeset 110:913fe3c11890
libgsmefr/vad.c: initial import from ETSI code
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Tue, 29 Nov 2022 03:01:38 +0000 |
parents | 2361a7d8c1eb |
children | 756605c4850f |
files | libgsmefr/vad.c |
diffstat | 1 files changed, 1223 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgsmefr/vad.c Tue Nov 29 03:01:38 2022 +0000 @@ -0,0 +1,1223 @@ +/*************************************************************************** + * + * File Name: vad.c + * + * Purpose: Contains all functions for voice activity detection, as + * described in the high level specification of VAD. + * + * Below is a listing of all the functions appearing in the file. + * The functions are arranged according to their purpose. Under + * each heading, the ordering is hierarchical. + * + * Resetting of static variables of VAD: + * reset_vad() + * + * Main routine of VAD (called by the speech encoder): + * vad_computation() + * Adaptive filtering and energy computation: + * energy_computation() + * Averaging of autocorrelation function values: + * acf_averaging() + * Computation of predictor values: + * predictor_values() + * schur_recursion() + * step_up() + * compute_rav1() + * Spectral comparison: + * spectral_comparison() + * Information tone detection: + * tone_detection() + * step_up() + * Threshold adaptation: + * threshold_adaptation() + * VAD decision: + * vad_decision() + * VAD hangover addition: + * vad_hangover() + * + * Periodicity detection routine (called by the speech encoder): + * periodicity_detection() + * + **************************************************************************/ + +#include "typedef.h" +#include "cnst.h" +#include "basic_op.h" +#include "oper_32b.h" +#include "count.h" +#include "vad.h" + +/* Constants of VAD hangover addition */ + +#define HANGCONST 10 +#define BURSTCONST 3 + +/* Constant of spectral comparison */ + +#define STAT_THRESH 3670L /* 0.056 */ + +/* Constants of periodicity detection */ + +#define LTHRESH 2 +#define NTHRESH 4 + +/* Pseudo floating point representations of constants + for threshold adaptation */ + +#define M_PTH 32500 /*** 130000.0 ***/ +#define E_PTH 17 +#define M_PLEV 21667 /*** 346666.7 ***/ +#define E_PLEV 19 +#define M_MARGIN 16927 /*** 69333340 ***/ +#define E_MARGIN 27 + +#define FAC 17203 /* 2.1 */ + +/* Constants of tone detection */ + +#define FREQTH 3189 +#define PREDTH 1464 + +/* Static variables of VAD */ + +static Word16 rvad[9], scal_rvad; +static Pfloat thvad; +static Word32 L_sacf[27]; +static Word32 L_sav0[36]; +static Word16 pt_sacf, pt_sav0; +static Word32 L_lastdm; +static Word16 adaptcount; +static Word16 burstcount, hangcount; +static Word16 oldlagcount, veryoldlagcount, oldlag; + +Word16 ptch; + +/************************************************************************* + * + * FUNCTION NAME: vad_reset + * + * PURPOSE: Resets the static variables of the VAD to their + * initial values + * + *************************************************************************/ + +void vad_reset () +{ + Word16 i; + + /* Initialize rvad variables */ + rvad[0] = 0x6000; + for (i = 1; i < 9; i++) + { + rvad[i] = 0; + } + scal_rvad = 7; + + /* Initialize threshold level */ + thvad.e = 20; /*** exponent ***/ + thvad.m = 27083; /*** mantissa ***/ + + /* Initialize ACF averaging variables */ + for (i = 0; i < 27; i++) + { + L_sacf[i] = 0L; + } + for (i = 0; i < 36; i++) + { + L_sav0[i] = 0L; + } + pt_sacf = 0; + pt_sav0 = 0; + + /* Initialize spectral comparison variable */ + L_lastdm = 0L; + + /* Initialize threshold adaptation variable */ + adaptcount = 0; + + /* Initialize VAD hangover addition variables */ + burstcount = 0; + hangcount = -1; + + /* Initialize periodicity detection variables */ + oldlagcount = 0; + veryoldlagcount = 0; + oldlag = 18; + + ptch = 1; + + return; +} + +/**************************************************************************** + * + * FUNCTION: vad_computation + * + * PURPOSE: Returns a decision as to whether the current frame being + * processed by the speech encoder contains speech or not. + * + * INPUTS: r_h[0..8] autocorrelation of input signal frame (msb) + * r_l[0..8] autocorrelation of input signal frame (lsb) + * scal_acf scaling factor for the autocorrelations + * rc[0..3] speech encoder reflection coefficients + * ptch flag to indicate a periodic signal component + * + * OUTPUTS: none + * + * RETURN VALUE: vad decision + * + ***************************************************************************/ + +Word16 vad_computation ( + Word16 r_h[], + Word16 r_l[], + Word16 scal_acf, + Word16 rc[], + Word16 ptch +) +{ + Word32 L_av0[9], L_av1[9]; + Word16 vad, vvad, rav1[9], scal_rav1, stat, tone; + Pfloat acf0, pvad; + + energy_computation (r_h, scal_acf, rvad, scal_rvad, &acf0, &pvad); + acf_averaging (r_h, r_l, scal_acf, L_av0, L_av1); + predictor_values (L_av1, rav1, &scal_rav1); + stat = spectral_comparison (rav1, scal_rav1, L_av0); move16 (); + tone_detection (rc, &tone); + threshold_adaptation (stat, ptch, tone, rav1, scal_rav1, pvad, acf0, + rvad, &scal_rvad, &thvad); + vvad = vad_decision (pvad, thvad); move16 (); + vad = vad_hangover (vvad); move16 (); + + return vad; +} + +/**************************************************************************** + * + * FUNCTION: energy_computation + * + * PURPOSE: Computes the input and residual energies of the adaptive + * filter in a floating point representation. + * + * INPUTS: r_h[0..8] autocorrelation of input signal frame (msb) + * scal_acf scaling factor for the autocorrelations + * rvad[0..8] autocorrelated adaptive filter coefficients + * scal_rvad scaling factor for rvad[] + * + * OUTPUTS: *acf0 signal frame energy (mantissa+exponent) + * *pvad filtered signal energy (mantissa+exponent) + * + * RETURN VALUE: none + * + ***************************************************************************/ + +void energy_computation ( + Word16 r_h[], + Word16 scal_acf, + Word16 rvad[], + Word16 scal_rvad, + Pfloat * acf0, + Pfloat * pvad +) +{ + Word16 i, temp, norm_prod; + Word32 L_temp; + + /* r[0] is always greater than zero (no need to test for r[0] == 0) */ + + /* Computation of acf0 (exponent and mantissa) */ + + acf0->e = sub (32, scal_acf); move16 (); + acf0->m = r_h[0] & 0x7ff8; move16 (); logic16 (); + + /* Computation of pvad (exponent and mantissa) */ + + pvad->e = add (acf0->e, 14); move16 (); + pvad->e = sub (pvad->e, scal_rvad); move16 (); + + L_temp = 0L; move32 (); + + for (i = 1; i <= 8; i++) + { + temp = shr (r_h[i], 3); + L_temp = L_mac (L_temp, temp, rvad[i]); + } + + temp = shr (r_h[0], 3); + L_temp = L_add (L_temp, L_shr (L_mult (temp, rvad[0]), 1)); + + test (); + if (L_temp <= 0L) + { + L_temp = 1L; move32 (); + } + norm_prod = norm_l (L_temp); + pvad->e = sub (pvad->e, norm_prod); move16 (); + pvad->m = extract_h (L_shl (L_temp, norm_prod)); + move16 (); + + return; +} + +/**************************************************************************** + * + * FUNCTION: acf_averaging + * + * PURPOSE: Computes the arrays L_av0[0..8] and L_av1[0..8]. + * + * INPUTS: r_h[0..8] autocorrelation of input signal frame (msb) + * r_l[0..8] autocorrelation of input signal frame (lsb) + * scal_acf scaling factor for the autocorrelations + * + * OUTPUTS: L_av0[0..8] ACF averaged over last four frames + * L_av1[0..8] ACF averaged over previous four frames + * + * RETURN VALUE: none + * + ***************************************************************************/ + +void acf_averaging ( + Word16 r_h[], + Word16 r_l[], + Word16 scal_acf, + Word32 L_av0[], + Word32 L_av1[] +) +{ + Word32 L_temp; + Word16 scale; + Word16 i; + + scale = add (9, scal_acf); + + for (i = 0; i <= 8; i++) + { + L_temp = L_shr (L_Comp (r_h[i], r_l[i]), scale); + L_av0[i] = L_add (L_sacf[i], L_temp); move32 (); + L_av0[i] = L_add (L_sacf[i + 9], L_av0[i]); move32 (); + L_av0[i] = L_add (L_sacf[i + 18], L_av0[i]); move32 (); + L_sacf[pt_sacf + i] = L_temp; move32 (); + L_av1[i] = L_sav0[pt_sav0 + i]; move32 (); + L_sav0[pt_sav0 + i] = L_av0[i]; move32 (); + } + + /* Update the array pointers */ + + test (); + if (sub (pt_sacf, 18) == 0) + { + pt_sacf = 0; move16 (); + } + else + { + pt_sacf = add (pt_sacf, 9); + } + + test (); + if (sub (pt_sav0, 27) == 0) + { + pt_sav0 = 0; move16 (); + } + else + { + pt_sav0 = add (pt_sav0, 9); + } + + return; +} + +/**************************************************************************** + * + * FUNCTION: predictor_values + * + * PURPOSE: Computes the array rav[0..8] needed for the spectral + * comparison and the threshold adaptation. + * + * INPUTS: L_av1[0..8] ACF averaged over previous four frames + * + * OUTPUTS: rav1[0..8] ACF obtained from L_av1 + * *scal_rav1 rav1[] scaling factor + * + * RETURN VALUE: none + * + ***************************************************************************/ + +void predictor_values ( + Word32 L_av1[], + Word16 rav1[], + Word16 *scal_rav1 +) +{ + Word16 vpar[8], aav1[9]; + + schur_recursion (L_av1, vpar); + step_up (8, vpar, aav1); + compute_rav1 (aav1, rav1, scal_rav1); + + return; +} + +/**************************************************************************** + * + * FUNCTION: schur_recursion + * + * PURPOSE: Uses the Schur recursion to compute adaptive filter + * reflection coefficients from an autorrelation function. + * + * INPUTS: L_av1[0..8] autocorrelation function + * + * OUTPUTS: vpar[0..7] reflection coefficients + * + * RETURN VALUE: none + * + ***************************************************************************/ + +void schur_recursion ( + Word32 L_av1[], + Word16 vpar[] +) +{ + Word16 acf[9], pp[9], kk[9], temp; + Word16 i, k, m, n; + + /*** Schur recursion with 16-bit arithmetic ***/ + + test (); move32 (); + if (L_av1[0] == 0) + { + for (i = 0; i < 8; i++) + { + vpar[i] = 0; move16 (); + } + return; + } + temp = norm_l (L_av1[0]); + + for (k = 0; k <= 8; k++) + { + acf[k] = extract_h (L_shl (L_av1[k], temp)); move16 (); + } + + /*** Initialize arrays pp[..] and kk[..] for the recursion: ***/ + + for (i = 1; i <= 7; i++) + { + kk[9 - i] = acf[i]; move16 (); + } + + for (i = 0; i <= 8; i++) + { + pp[i] = acf[i]; move16 (); + } + + /*** Compute Parcor coefficients: ***/ + + for (n = 0; n < 8; n++) + { + test (); + if ((pp[0] == 0) || + (sub (pp[0], abs_s (pp[1])) < 0)) + { + for (i = n; i < 8; i++) + { + vpar[i] = 0; move16 (); + } + return; + } + vpar[n] = div_s (abs_s (pp[1]), pp[0]); move16 (); + + test (); move16 (); + if (pp[1] > 0) + { + vpar[n] = negate (vpar[n]); move16 (); + } + test (); + if (sub (n, 7) == 0) + { + return; + } + /*** Schur recursion: ***/ + + pp[0] = add (pp[0], mult_r (pp[1], vpar[n])); move16 (); + + for (m = 1; m <= 7 - n; m++) + { + pp[m] = add (pp[1 + m], mult_r (kk[9 - m], vpar[n])); + move16 (); + kk[9 - m] = add (kk[9 - m], mult_r (pp[1 + m], vpar[n])); + move16 (); + } + } + + return; +} + +/**************************************************************************** + * + * FUNCTION: step_up + * + * PURPOSE: Computes the transversal filter coefficients from the + * reflection coefficients. + * + * INPUTS: np filter order (2..8) + * vpar[0..np-1] reflection coefficients + * + * OUTPUTS: aav1[0..np] transversal filter coefficients + * + * RETURN VALUE: none + * + ***************************************************************************/ + +void step_up ( + Word16 np, + Word16 vpar[], + Word16 aav1[] +) +{ + Word32 L_coef[9], L_work[9]; + Word16 temp; + Word16 i, m; + + /*** Initialization of the step-up recursion ***/ + + L_coef[0] = 0x20000000L; move32 (); + L_coef[1] = L_shl (L_deposit_l (vpar[0]), 14); move32 (); + + /*** Loop on the LPC analysis order: ***/ + + for (m = 2; m <= np; m++) + { + for (i = 1; i < m; i++) + { + temp = extract_h (L_coef[m - i]); + L_work[i] = L_mac (L_coef[i], vpar[m - 1], temp); move32 (); + } + + for (i = 1; i < m; i++) + { + L_coef[i] = L_work[i]; move32 (); + } + + L_coef[m] = L_shl (L_deposit_l (vpar[m - 1]), 14); move32 (); + } + + /*** Keep the aav1[0..np] in 15 bits ***/ + + for (i = 0; i <= np; i++) + { + aav1[i] = extract_h (L_shr (L_coef[i], 3)); move32 (); + } + + return; +} + +/**************************************************************************** + * + * FUNCTION: compute_rav1 + * + * PURPOSE: Computes the autocorrelation function of the adaptive + * filter coefficients. + * + * INPUTS: aav1[0..8] adaptive filter coefficients + * + * OUTPUTS: rav1[0..8] ACF of aav1 + * *scal_rav1 rav1[] scaling factor + * + * RETURN VALUE: none + * + ***************************************************************************/ + +void compute_rav1 ( + Word16 aav1[], + Word16 rav1[], + Word16 *scal_rav1 +) +{ + Word32 L_work[9]; + Word16 i, k; + + /*** Computation of the rav1[0..8] ***/ + + for (i = 0; i <= 8; i++) + { + L_work[i] = 0L; move32 (); + + for (k = 0; k <= 8 - i; k++) + { + L_work[i] = L_mac (L_work[i], aav1[k], aav1[k + i]); move32 (); + } + } + + test (); move32 (); + if (L_work[0] == 0L) + { + *scal_rav1 = 0; move16 (); + } + else + { + *scal_rav1 = norm_l (L_work[0]); + } + + for (i = 0; i <= 8; i++) + { + rav1[i] = extract_h (L_shl (L_work[i], *scal_rav1)); move16 (); + } + + return; +} + +/**************************************************************************** + * + * FUNCTION: spectral_comparison + * + * PURPOSE: Computes the stat flag needed for the threshold + * adaptation decision. + * + * INPUTS: rav1[0..8] ACF obtained from L_av1 + * *scal_rav1 rav1[] scaling factor + * L_av0[0..8] ACF averaged over last four frames + * + * OUTPUTS: none + * + * RETURN VALUE: flag to indicate spectral stationarity + * + ***************************************************************************/ + +Word16 spectral_comparison ( + Word16 rav1[], + Word16 scal_rav1, + Word32 L_av0[] +) +{ + Word32 L_dm, L_sump, L_temp; + Word16 stat, sav0[9], shift, divshift, temp; + Word16 i; + + /*** Re-normalize L_av0[0..8] ***/ + + test (); move32 (); + if (L_av0[0] == 0L) + { + for (i = 0; i <= 8; i++) + { + sav0[i] = 0x0fff; /* 4095 */ move16 (); + } + } + else + { + shift = sub (norm_l (L_av0[0]), 3); + for (i = 0; i <= 8; i++) + { + sav0[i] = extract_h (L_shl (L_av0[i], shift)); move16 (); + } + } + + /*** Compute partial sum of dm ***/ + + L_sump = 0L; move32 (); + for (i = 1; i <= 8; i++) + { + L_sump = L_mac (L_sump, rav1[i], sav0[i]); + } + + /*** Compute the division of the partial sum by sav0[0] ***/ + + test (); + if (L_sump < 0L) + { + L_temp = L_negate (L_sump); + } + else + { + L_temp = L_sump; move32 (); + } + + test (); + if (L_temp == 0L) + { + L_dm = 0L; move32 (); + shift = 0; move16 (); + } + else + { + sav0[0] = shl (sav0[0], 3); move16 (); + shift = norm_l (L_temp); + temp = extract_h (L_shl (L_temp, shift)); + + test (); + if (sub (sav0[0], temp) >= 0) + { + divshift = 0; move16 (); + temp = div_s (temp, sav0[0]); + } + else + { + divshift = 1; move16 (); + temp = sub (temp, sav0[0]); + temp = div_s (temp, sav0[0]); + } + + test (); + if (sub (divshift, 1) == 0) + { + L_dm = 0x8000L; move32 (); + } + else + { + L_dm = 0L; move32 (); + } + + L_dm = L_shl (L_add (L_dm, L_deposit_l (temp)), 1); + + test (); + if (L_sump < 0L) + { + L_dm = L_negate (L_dm); + } + } + + /*** Re-normalization and final computation of L_dm ***/ + + L_dm = L_shl (L_dm, 14); + L_dm = L_shr (L_dm, shift); + L_dm = L_add (L_dm, L_shl (L_deposit_l (rav1[0]), 11)); + L_dm = L_shr (L_dm, scal_rav1); + + /*** Compute the difference and save L_dm ***/ + + L_temp = L_sub (L_dm, L_lastdm); + L_lastdm = L_dm; move32 (); + + test (); + if (L_temp < 0L) + { + L_temp = L_negate (L_temp); + } + /*** Evaluation of the stat flag ***/ + + L_temp = L_sub (L_temp, STAT_THRESH); + + test (); + if (L_temp < 0L) + { + stat = 1; move16 (); + } + else + { + stat = 0; move16 (); + } + + return stat; +} + +/**************************************************************************** + * + * FUNCTION: threshold_adaptation + * + * PURPOSE: Evaluates the secondary VAD decision. If speech is not + * present then the noise model rvad and adaptive threshold + * thvad are updated. + * + * INPUTS: stat flag to indicate spectral stationarity + * ptch flag to indicate a periodic signal component + * tone flag to indicate a tone signal component + * rav1[0..8] ACF obtained from L_av1 + * scal_rav1 rav1[] scaling factor + * pvad filtered signal energy (mantissa+exponent) + * acf0 signal frame energy (mantissa+exponent) + * + * OUTPUTS: rvad[0..8] autocorrelated adaptive filter coefficients + * *scal_rvad rvad[] scaling factor + * *thvad decision threshold (mantissa+exponent) + * + * RETURN VALUE: none + * + ***************************************************************************/ + +void threshold_adaptation ( + Word16 stat, + Word16 ptch, + Word16 tone, + Word16 rav1[], + Word16 scal_rav1, + Pfloat pvad, + Pfloat acf0, + Word16 rvad[], + Word16 *scal_rvad, + Pfloat * thvad +) +{ + Word16 comp, comp2; + Word32 L_temp; + Word16 temp; + Pfloat p_temp; + Word16 i; + + comp = 0; move16 (); + + /*** Test if acf0 < pth; if yes set thvad to plev ***/ + + test (); + if (sub (acf0.e, E_PTH) < 0) + { + comp = 1; move16 (); + } + test (); test (); + if ((sub (acf0.e, E_PTH) == 0) && (sub (acf0.m, M_PTH) < 0)) + { + comp = 1; move16 (); + } + test (); + if (sub (comp, 1) == 0) + { + thvad->e = E_PLEV; move16 (); + thvad->m = M_PLEV; move16 (); + + return; + } + /*** Test if an adaption is required ***/ + + test (); + if (sub (ptch, 1) == 0) + { + comp = 1; move16 (); + } + test (); + if (stat == 0) + { + comp = 1; move16 (); + } + test (); + if (sub (tone, 1) == 0) + { + comp = 1; move16 (); + } + test (); + if (sub (comp, 1) == 0) + { + adaptcount = 0; move16 (); + return; + } + /*** Increment adaptcount ***/ + + adaptcount = add (adaptcount, 1); + test (); + if (sub (adaptcount, 8) <= 0) + { + return; + } + /*** computation of thvad-(thvad/dec) ***/ + + thvad->m = sub (thvad->m, shr (thvad->m, 5)); move16 (); + + test (); + if (sub (thvad->m, 0x4000) < 0) + { + thvad->m = shl (thvad->m, 1); move16 (); + thvad->e = sub (thvad->e, 1); move16 (); + } + /*** computation of pvad*fac ***/ + + L_temp = L_mult (pvad.m, FAC); + L_temp = L_shr (L_temp, 15); + p_temp.e = add (pvad.e, 1); move16 (); + + test (); + if (L_temp > 0x7fffL) + { + L_temp = L_shr (L_temp, 1); + p_temp.e = add (p_temp.e, 1); move16 (); + } + p_temp.m = extract_l (L_temp); move16 (); + + /*** test if thvad < pvad*fac ***/ + + test (); + if (sub (thvad->e, p_temp.e) < 0) + { + comp = 1; move16 (); + } + test (); test (); + if ((sub (thvad->e, p_temp.e) == 0) && + (sub (thvad->m, p_temp.m) < 0)) + { + comp = 1; move16 (); + } + /*** compute minimum(thvad+(thvad/inc), pvad*fac) when comp = 1 ***/ + + test (); + if (sub (comp, 1) == 0) + { + /*** compute thvad + (thvad/inc) ***/ + + L_temp = L_add (L_deposit_l (thvad->m), + L_deposit_l (shr (thvad->m, 4))); + + test (); + if (L_sub (L_temp, 0x7fffL) > 0) + { + thvad->m = extract_l (L_shr (L_temp, 1)); move16 (); + thvad->e = add (thvad->e, 1); move16 (); + } + else + { + thvad->m = extract_l (L_temp); move16 (); + } + + comp2 = 0; move16 (); + + test (); + if (sub (p_temp.e, thvad->e) < 0) + { + comp2 = 1; move16 (); + } + test (); test (); + if ((sub (p_temp.e, thvad->e) == 0) && + (sub (p_temp.m, thvad->m) < 0)) + { + comp2 = 1; move16 (); + } + test (); + if (sub (comp2, 1) == 0) + { + thvad->e = p_temp.e;move16 (); + thvad->m = p_temp.m;move16 (); + } + } + /*** compute pvad + margin ***/ + + test (); + if (sub (pvad.e, E_MARGIN) == 0) + { + L_temp = L_add (L_deposit_l (pvad.m), L_deposit_l (M_MARGIN)); + p_temp.m = extract_l (L_shr (L_temp, 1)); move16 (); + p_temp.e = add (pvad.e, 1); move16 (); + } + else + { + test (); + if (sub (pvad.e, E_MARGIN) > 0) + { + temp = sub (pvad.e, E_MARGIN); + temp = shr (M_MARGIN, temp); + L_temp = L_add (L_deposit_l (pvad.m), L_deposit_l (temp)); + + test (); + if (L_sub (L_temp, 0x7fffL) > 0) + { + p_temp.e = add (pvad.e, 1); move16 (); + p_temp.m = extract_l (L_shr (L_temp, 1)); + move16 (); + } + else + { + p_temp.e = pvad.e; move16 (); + p_temp.m = extract_l (L_temp); move16 (); + } + } + else + { + temp = sub (E_MARGIN, pvad.e); + temp = shr (pvad.m, temp); + L_temp = L_add (L_deposit_l (M_MARGIN), L_deposit_l (temp)); + + test (); + if (L_sub (L_temp, 0x7fffL) > 0) + { + p_temp.e = add (E_MARGIN, 1); move16 (); + p_temp.m = extract_l (L_shr (L_temp, 1)); + move16 (); + } + else + { + p_temp.e = E_MARGIN; move16 (); + p_temp.m = extract_l (L_temp); move16 (); + } + } + } + + /*** Test if thvad > pvad + margin ***/ + + comp = 0; move16 (); + + test (); + if (sub (thvad->e, p_temp.e) > 0) + { + comp = 1; move16 (); + } + test (); test (); + if ((sub (thvad->e, p_temp.e) == 0) && + (sub (thvad->m, p_temp.m) > 0)) + { + comp = 1; move16 (); + } + test (); + if (sub (comp, 1) == 0) + { + thvad->e = p_temp.e; move16 (); + thvad->m = p_temp.m; move16 (); + } + /*** Normalise and retain rvad[0..8] in memory ***/ + + *scal_rvad = scal_rav1; move16 (); + + for (i = 0; i <= 8; i++) + { + rvad[i] = rav1[i]; move16 (); + } + + /*** Set adaptcount to adp + 1 ***/ + + adaptcount = 9; move16 (); + + return; +} + +/**************************************************************************** + * + * FUNCTION: tone_detection + * + * PURPOSE: Computes the tone flag needed for the threshold + * adaptation decision. + * + * INPUTS: rc[0..3] reflection coefficients calculated in the + * speech encoder short term predictor + * + * OUTPUTS: *tone flag to indicate a periodic signal component + * + * RETURN VALUE: none + * + ***************************************************************************/ + +void tone_detection ( + Word16 rc[], + Word16 *tone +) +{ + Word32 L_num, L_den, L_temp; + Word16 temp, prederr, a[3]; + Word16 i; + + *tone = 0; move16 (); + + /*** Calculate filter coefficients ***/ + + step_up (2, rc, a); + + /*** Calculate ( a[1] * a[1] ) ***/ + + temp = shl (a[1], 3); + L_den = L_mult (temp, temp); + + /*** Calculate ( 4*a[2] - a[1]*a[1] ) ***/ + + L_temp = L_shl (L_deposit_h (a[2]), 3); + L_num = L_sub (L_temp, L_den); + + /*** Check if pole frequency is less than 385 Hz ***/ + + test (); + if (L_num <= 0) + { + return; + } + test (); move16 (); + if (a[1] < 0) + { + temp = extract_h (L_den); + L_den = L_mult (temp, FREQTH); + + L_temp = L_sub (L_num, L_den); + + test (); + if (L_temp < 0) + { + return; + } + } + /*** Calculate normalised prediction error ***/ + + prederr = 0x7fff; move16 (); + + for (i = 0; i < 4; i++) + { + temp = mult (rc[i], rc[i]); + temp = sub (0x7fff, temp); + prederr = mult (prederr, temp); + } + + /*** Test if prediction error is smaller than threshold ***/ + + temp = sub (prederr, PREDTH); + + test (); + if (temp < 0) + { + *tone = 1; move16 (); + } + return; +} + +/**************************************************************************** + * + * FUNCTION: vad_decision + * + * PURPOSE: Computes the VAD decision based on the comparison of the + * floating point representations of pvad and thvad. + * + * INPUTS: pvad filtered signal energy (mantissa+exponent) + * thvad decision threshold (mantissa+exponent) + * + * OUTPUTS: none + * + * RETURN VALUE: vad decision before hangover is added + * + ***************************************************************************/ + +Word16 vad_decision ( + Pfloat pvad, + Pfloat thvad +) +{ + Word16 vvad; + + test (); test (); test (); + if (sub (pvad.e, thvad.e) > 0) + { + vvad = 1; move16 (); + } + else if ((sub (pvad.e, thvad.e) == 0) && + (sub (pvad.m, thvad.m) > 0)) + { + vvad = 1; move16 (); + } + else + { + vvad = 0; move16 (); + } + + return vvad; +} + +/**************************************************************************** + * + * FUNCTION: vad_hangover + * + * PURPOSE: Computes the final VAD decision for the current frame + * being processed. + * + * INPUTS: vvad vad decision before hangover is added + * + * OUTPUTS: none + * + * RETURN VALUE: vad decision after hangover is added + * + ***************************************************************************/ + +Word16 vad_hangover ( + Word16 vvad +) +{ + test (); + if (sub (vvad, 1) == 0) + { + burstcount = add (burstcount, 1); + } + else + { + burstcount = 0; move16 (); + } + + test (); + if (sub (burstcount, BURSTCONST) >= 0) + { + hangcount = HANGCONST; move16 (); + burstcount = BURSTCONST;move16 (); + } + test (); + if (hangcount >= 0) + { + hangcount = sub (hangcount, 1); + return 1; /* vad = 1 */ + } + return vvad; /* vad = vvad */ +} + +/**************************************************************************** + * + * FUNCTION: periodicity_update + * + * PURPOSE: Computes the ptch flag needed for the threshold + * adaptation decision for the next frame. + * + * INPUTS: lags[0..1] speech encoder long term predictor lags + * + * OUTPUTS: *ptch Boolean voiced / unvoiced decision + * + * RETURN VALUE: none + * + ***************************************************************************/ + +void periodicity_update ( + Word16 lags[], + Word16 *ptch +) +{ + Word16 minlag, maxlag, lagcount, temp; + Word16 i; + + /*** Run loop for the two halves in the frame ***/ + + lagcount = 0; move16 (); + + for (i = 0; i <= 1; i++) + { + /*** Search the maximum and minimum of consecutive lags ***/ + + test (); + if (sub (oldlag, lags[i]) > 0) + { + minlag = lags[i]; move16 (); + maxlag = oldlag; move16 (); + } + else + { + minlag = oldlag; move16 (); + maxlag = lags[i]; move16 (); + } + + temp = sub (maxlag, minlag); + + test (); + if (sub (temp, LTHRESH) < 0) + { + lagcount = add (lagcount, 1); + } + /*** Save the current LTP lag ***/ + + oldlag = lags[i]; move16 (); + } + + /*** Update the veryoldlagcount and oldlagcount ***/ + + veryoldlagcount = oldlagcount; + move16 (); + oldlagcount = lagcount; move16 (); + + /*** Make ptch decision ready for next frame ***/ + + temp = add (oldlagcount, veryoldlagcount); + + test (); + if (sub (temp, NTHRESH) >= 0) + { + *ptch = 1; move16 (); + } + else + { + *ptch = 0; move16 (); + } + + return; +}