mirror of
https://github.com/signalwire/freeswitch.git
synced 2025-08-14 09:58:17 +00:00
inital checkin of spandsp from http://www.soft-switch.org/downloads/snapshots/spandsp/spandsp-20080829.tar.gz
git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@9425 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
612
libs/spandsp/src/echo.c
Normal file
612
libs/spandsp/src/echo.c
Normal file
@@ -0,0 +1,612 @@
|
||||
/*
|
||||
* SpanDSP - a series of DSP components for telephony
|
||||
*
|
||||
* echo.c - An echo cancellor, suitable for electrical and acoustic
|
||||
* cancellation. This code does not currently comply with
|
||||
* any relevant standards (e.g. G.164/5/7/8). One day....
|
||||
*
|
||||
* Written by Steve Underwood <steveu@coppice.org>
|
||||
*
|
||||
* Copyright (C) 2001, 2003 Steve Underwood
|
||||
*
|
||||
* Based on a bit from here, a bit from there, eye of toad,
|
||||
* ear of bat, etc - plus, of course, my own 2 cents.
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Id: echo.c,v 1.27 2008/08/29 09:28:13 steveu Exp $
|
||||
*/
|
||||
|
||||
/*! \file */
|
||||
|
||||
/* TODO:
|
||||
Finish the echo suppressor option, however nasty suppression may be.
|
||||
Add an option to reintroduce side tone at -24dB under appropriate conditions.
|
||||
Improve double talk detector (iterative!)
|
||||
*/
|
||||
|
||||
/* We need to differentiate between transmitted energy which will train the echo
|
||||
canceller well (voice, white noise, and other broadband sources) and energy
|
||||
which will train it badly (supervisory tones, DTMF, whistles, and other
|
||||
narrowband sources). There are many ways this might be done. This canceller uses
|
||||
a method based on the autocorrelation qualities of the transmitted signal. A rather
|
||||
peaky autocorrelation function is a clear sign of a narrowband signal. We only need
|
||||
perform the autocorrelation at well spaced intervals, so the compute load is not too
|
||||
great. Multiple successive autocorrelation functions with a similar peaky shape are a
|
||||
clear indication of a stationary narrowband signal. Using TKEO, it should be possible to
|
||||
greatly reduce the compute requirement for narrowband detection. */
|
||||
|
||||
/* The FIR taps must be adapted as 32 bit values, to get the necessary finesse
|
||||
in the adaption process. However, they are applied as 16 bit values (bits 30-15
|
||||
of the 32 bit values) in the FIR. For the working 16 bit values, we need 4 sets.
|
||||
|
||||
3 of the 16 bit sets are used on a rotating basis. Normally the canceller steps
|
||||
round these 3 sets at regular intervals. Any time we detect double talk, we can go
|
||||
back to the set from two steps ago with reasonable assurance it is a well adapted
|
||||
set. We cannot just go back one step, as we may have rotated the sets just before
|
||||
double talk or tone was detected, and that set may already be somewhat corrupted.
|
||||
|
||||
When narrowband energy is detected we need to continue adapting to it, to echo
|
||||
cancel it. However, the adaption will almost certainly be going astray. Broadband
|
||||
(or even complex sequences of narrowband) energy will normally lead to a well
|
||||
trained cancellor, with taps matching the impulse response of the channel.
|
||||
For stationary narrowband energy, there is usually has an infinite number of
|
||||
alternative tap sets which will cancel it well. A previously well trained set of
|
||||
taps will tend to drift amongst the alternatives. When broadband energy resumes, the
|
||||
taps may be a total mismatch for the signal, and could even amplify rather than
|
||||
attenuate the echo. The solution is to use a fourth set of 16 bit taps. When we first
|
||||
detect the narrowband energy we save the oldest of the group of three sets, but do
|
||||
not change back to an older set. We let the canceller cancel, and it adaption drift
|
||||
while the narrowband energy is present. When we detect the narrowband energy has ceased,
|
||||
we switch to using the fourth set of taps which was saved.
|
||||
|
||||
When we revert to an older set of taps, we must replace both the 16 bit and 32 bit
|
||||
working tap sets. The saved 16 bit values are good enough to also be used as a replacement
|
||||
for the 32 bit values. We loose the fractions, but they should soon settle down in a
|
||||
reasonable way. */
|
||||
|
||||
#if defined(HAVE_CONFIG_H)
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#include "floating_fudge.h"
|
||||
#if defined(HAVE_TGMATH_H)
|
||||
#include <tgmath.h>
|
||||
#endif
|
||||
#if defined(HAVE_MATH_H)
|
||||
#include <math.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "spandsp/telephony.h"
|
||||
#include "spandsp/logging.h"
|
||||
#include "spandsp/dc_restore.h"
|
||||
#include "spandsp/bit_operations.h"
|
||||
#include "spandsp/echo.h"
|
||||
|
||||
#if !defined(NULL)
|
||||
#define NULL (void *) 0
|
||||
#endif
|
||||
#if !defined(FALSE)
|
||||
#define FALSE 0
|
||||
#endif
|
||||
#if !defined(TRUE)
|
||||
#define TRUE (!FALSE)
|
||||
#endif
|
||||
|
||||
#define NONUPDATE_DWELL_TIME 600 /* 600 samples, or 75ms */
|
||||
|
||||
#define MIN_TX_POWER_FOR_ADAPTION 64*64
|
||||
#define MIN_RX_POWER_FOR_ADAPTION 64*64
|
||||
|
||||
static int narrowband_detect(echo_can_state_t *ec)
|
||||
{
|
||||
int k;
|
||||
int i;
|
||||
float temp;
|
||||
float scale;
|
||||
float sf[128];
|
||||
float f_acf[128];
|
||||
int32_t acf[28];
|
||||
int score;
|
||||
int len = 32;
|
||||
int alen = 9;
|
||||
|
||||
k = ec->curr_pos;
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
sf[i] = ec->fir_state.history[k++];
|
||||
if (k >= 256)
|
||||
k = 0;
|
||||
}
|
||||
for (k = 0; k < alen; k++)
|
||||
{
|
||||
temp = 0;
|
||||
for (i = k; i < len; i++)
|
||||
temp += sf[i]*sf[i - k];
|
||||
f_acf[k] = temp;
|
||||
}
|
||||
scale = 0x1FFFFFFF/f_acf[0];
|
||||
for (k = 0; k < alen; k++)
|
||||
acf[k] = (int32_t) (f_acf[k]*scale);
|
||||
score = 0;
|
||||
for (i = 0; i < 9; i++)
|
||||
{
|
||||
if (ec->last_acf[i] >= 0 && acf[i] >= 0)
|
||||
{
|
||||
if ((ec->last_acf[i] >> 1) < acf[i] && acf[i] < (ec->last_acf[i] << 1))
|
||||
score++;
|
||||
}
|
||||
else if (ec->last_acf[i] < 0 && acf[i] < 0)
|
||||
{
|
||||
if ((ec->last_acf[i] >> 1) > acf[i] && acf[i] > (ec->last_acf[i] << 1))
|
||||
score++;
|
||||
}
|
||||
}
|
||||
memcpy(ec->last_acf, acf, alen*sizeof(ec->last_acf[0]));
|
||||
return score;
|
||||
}
|
||||
|
||||
static __inline__ void lms_adapt(echo_can_state_t *ec, int factor)
|
||||
{
|
||||
int i;
|
||||
|
||||
#if 0
|
||||
mmx_t *mmx_taps;
|
||||
mmx_t *mmx_coeffs;
|
||||
mmx_t *mmx_hist;
|
||||
mmx_t mmx;
|
||||
|
||||
mmx.w[0] =
|
||||
mmx.w[1] =
|
||||
mmx.w[2] =
|
||||
mmx.w[3] = factor;
|
||||
mmx_hist = (mmx_t *) &fir->history[fir->curr_pos];
|
||||
mmx_taps = (mmx_t *) &fir->taps;
|
||||
mmx_coeffs = (mmx_t *) fir->coeffs;
|
||||
i = fir->taps;
|
||||
movq_m2r(mmx, mm0);
|
||||
while (i > 0)
|
||||
{
|
||||
movq_m2r(mmx_hist[0], mm1);
|
||||
movq_m2r(mmx_taps[0], mm0);
|
||||
movq_m2r(mmx_taps[1], mm1);
|
||||
movq_r2r(mm1, mm2);
|
||||
pmulhw(mm0, mm1);
|
||||
pmullw(mm0, mm2);
|
||||
|
||||
pmaddwd_r2r(mm1, mm0);
|
||||
pmaddwd_r2r(mm3, mm2);
|
||||
paddd_r2r(mm0, mm4);
|
||||
paddd_r2r(mm2, mm4);
|
||||
movq_r2m(mm0, mmx_taps[0]);
|
||||
movq_r2m(mm1, mmx_taps[0]);
|
||||
movq_r2m(mm2, mmx_coeffs[0]);
|
||||
mmx_taps += 2;
|
||||
mmx_coeffs += 1;
|
||||
mmx_hist += 1;
|
||||
i -= 4;
|
||||
)
|
||||
emms();
|
||||
#elif 0
|
||||
/* Update the FIR taps */
|
||||
for (i = ec->taps - 1; i >= 0; i--)
|
||||
{
|
||||
/* Leak to avoid the coefficients drifting beyond the ability of the
|
||||
adaption process to bring them back under control. */
|
||||
ec->fir_taps32[i] -= (ec->fir_taps32[i] >> 23);
|
||||
ec->fir_taps32[i] += (ec->fir_state.history[i + ec->curr_pos]*factor);
|
||||
ec->latest_correction = (ec->fir_state.history[i + ec->curr_pos]*factor);
|
||||
ec->fir_taps16[ec->tap_set][i] = ec->fir_taps32[i] >> 15;
|
||||
}
|
||||
#else
|
||||
int offset1;
|
||||
int offset2;
|
||||
|
||||
/* Update the FIR taps */
|
||||
offset2 = ec->curr_pos;
|
||||
offset1 = ec->taps - offset2;
|
||||
for (i = ec->taps - 1; i >= offset1; i--)
|
||||
{
|
||||
ec->fir_taps32[i] += (ec->fir_state.history[i - offset1]*factor);
|
||||
ec->fir_taps16[ec->tap_set][i] = (int16_t) (ec->fir_taps32[i] >> 15);
|
||||
}
|
||||
for ( ; i >= 0; i--)
|
||||
{
|
||||
ec->fir_taps32[i] += (ec->fir_state.history[i + offset2]*factor);
|
||||
ec->fir_taps16[ec->tap_set][i] = (int16_t) (ec->fir_taps32[i] >> 15);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
echo_can_state_t *echo_can_create(int len, int adaption_mode)
|
||||
{
|
||||
echo_can_state_t *ec;
|
||||
int i;
|
||||
int j;
|
||||
|
||||
if ((ec = (echo_can_state_t *) malloc(sizeof(*ec))) == NULL)
|
||||
return NULL;
|
||||
memset(ec, 0, sizeof(*ec));
|
||||
ec->taps = len;
|
||||
ec->curr_pos = ec->taps - 1;
|
||||
ec->tap_mask = ec->taps - 1;
|
||||
if ((ec->fir_taps32 = (int32_t *) malloc(ec->taps*sizeof(int32_t))) == NULL)
|
||||
{
|
||||
free(ec);
|
||||
return NULL;
|
||||
}
|
||||
memset(ec->fir_taps32, 0, ec->taps*sizeof(int32_t));
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
if ((ec->fir_taps16[i] = (int16_t *) malloc(ec->taps*sizeof(int16_t))) == NULL)
|
||||
{
|
||||
for (j = 0; j < i; j++)
|
||||
free(ec->fir_taps16[j]);
|
||||
free(ec->fir_taps32);
|
||||
free(ec);
|
||||
return NULL;
|
||||
}
|
||||
memset(ec->fir_taps16[i], 0, ec->taps*sizeof(int16_t));
|
||||
}
|
||||
fir16_create(&ec->fir_state,
|
||||
ec->fir_taps16[0],
|
||||
ec->taps);
|
||||
ec->rx_power_threshold = 10000000;
|
||||
ec->geigel_max = 0;
|
||||
ec->geigel_lag = 0;
|
||||
ec->dtd_onset = FALSE;
|
||||
ec->tap_set = 0;
|
||||
ec->tap_rotate_counter = 1600;
|
||||
ec->cng_level = 1000;
|
||||
echo_can_adaption_mode(ec, adaption_mode);
|
||||
return ec;
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
void echo_can_free(echo_can_state_t *ec)
|
||||
{
|
||||
int i;
|
||||
|
||||
fir16_free(&ec->fir_state);
|
||||
free(ec->fir_taps32);
|
||||
for (i = 0; i < 4; i++)
|
||||
free(ec->fir_taps16[i]);
|
||||
free(ec);
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
void echo_can_adaption_mode(echo_can_state_t *ec, int adaption_mode)
|
||||
{
|
||||
ec->adaption_mode = adaption_mode;
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
void echo_can_flush(echo_can_state_t *ec)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
ec->tx_power[i] = 0;
|
||||
for (i = 0; i < 3; i++)
|
||||
ec->rx_power[i] = 0;
|
||||
ec->clean_rx_power = 0;
|
||||
ec->nonupdate_dwell = 0;
|
||||
|
||||
fir16_flush(&ec->fir_state);
|
||||
ec->fir_state.curr_pos = ec->taps - 1;
|
||||
memset(ec->fir_taps32, 0, ec->taps*sizeof(int32_t));
|
||||
for (i = 0; i < 4; i++)
|
||||
memset(ec->fir_taps16[i], 0, ec->taps*sizeof(int16_t));
|
||||
|
||||
ec->curr_pos = ec->taps - 1;
|
||||
|
||||
ec->supp_test1 = 0;
|
||||
ec->supp_test2 = 0;
|
||||
ec->supp1 = 0;
|
||||
ec->supp2 = 0;
|
||||
ec->vad = 0;
|
||||
ec->cng_level = 1000;
|
||||
ec->cng_filter = 0;
|
||||
|
||||
ec->geigel_max = 0;
|
||||
ec->geigel_lag = 0;
|
||||
ec->dtd_onset = FALSE;
|
||||
ec->tap_set = 0;
|
||||
ec->tap_rotate_counter = 1600;
|
||||
|
||||
ec->latest_correction = 0;
|
||||
|
||||
memset(ec->last_acf, 0, sizeof(ec->last_acf));
|
||||
ec->narrowband_count = 0;
|
||||
ec->narrowband_score = 0;
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
int sample_no = 0;
|
||||
|
||||
void echo_can_snapshot(echo_can_state_t *ec)
|
||||
{
|
||||
memcpy(ec->snapshot, ec->fir_taps16[0], ec->taps*sizeof(int16_t));
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
static __inline__ int16_t echo_can_hpf(int32_t coeff[2], int16_t amp)
|
||||
{
|
||||
int32_t z;
|
||||
|
||||
/*
|
||||
Filter DC, 3dB point is 160Hz (I think), note 32 bit precision required
|
||||
otherwise values do not track down to 0. Zero at DC, Pole at (1-Beta)
|
||||
only real axis. Some chip sets (like Si labs) don't need
|
||||
this, but something like a $10 X100P card does. Any DC really slows
|
||||
down convergence.
|
||||
|
||||
Note: removes some low frequency from the signal, this reduces
|
||||
the speech quality when listening to samples through headphones
|
||||
but may not be obvious through a telephone handset.
|
||||
|
||||
Note that the 3dB frequency in radians is approx Beta, e.g. for
|
||||
Beta = 2^(-3) = 0.125, 3dB freq is 0.125 rads = 159Hz.
|
||||
|
||||
This is one of the classic DC removal filters, adjusted to provide sufficient
|
||||
bass rolloff to meet the above requirement to protect hybrids from things that
|
||||
upset them. The difference between successive samples produces a lousy HPF, and
|
||||
then a suitably placed pole flattens things out. The final result is a nicely
|
||||
rolled off bass end. The filtering is implemented with extended fractional
|
||||
precision, which noise shapes things, giving very clean DC removal.
|
||||
|
||||
Make sure the gain of the HPF is 1.0. The first can still saturate a little under
|
||||
impulse conditions, and it might roll to 32768 and need clipping on sustained peak
|
||||
level signals. However, the scale of such clipping is small, and the error due to
|
||||
any saturation should not markedly affect the downstream processing. */
|
||||
z = amp << 15;
|
||||
z -= (z >> 4);
|
||||
coeff[0] += z - (coeff[0] >> 3) - coeff[1];
|
||||
coeff[1] = z;
|
||||
z = coeff[0] >> 15;
|
||||
|
||||
return saturate(z);
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
int16_t echo_can_update(echo_can_state_t *ec, int16_t tx, int16_t rx)
|
||||
{
|
||||
int32_t echo_value;
|
||||
int clean_rx;
|
||||
int nsuppr;
|
||||
int score;
|
||||
int i;
|
||||
|
||||
sample_no++;
|
||||
if (ec->adaption_mode & ECHO_CAN_USE_RX_HPF)
|
||||
rx = echo_can_hpf(ec->rx_hpf, rx);
|
||||
|
||||
ec->latest_correction = 0;
|
||||
/* Evaluate the echo - i.e. apply the FIR filter */
|
||||
/* Assume the gain of the FIR does not exceed unity. Exceeding unity
|
||||
would seem like a rather poor thing for an echo cancellor to do :)
|
||||
This means we can compute the result with a total disregard for
|
||||
overflows. 16bits x 16bits -> 31bits, so no overflow can occur in
|
||||
any multiply. While accumulating we may overflow and underflow the
|
||||
32 bit scale often. However, if the gain does not exceed unity,
|
||||
everything should work itself out, and the final result will be
|
||||
OK, without any saturation logic. */
|
||||
/* Overflow is very much possible here, and we do nothing about it because
|
||||
of the compute costs */
|
||||
/* 16 bit coeffs for the LMS give lousy results (maths good, actual sound
|
||||
bad!), but 32 bit coeffs require some shifting. On balance 32 bit seems
|
||||
best */
|
||||
echo_value = fir16(&ec->fir_state, tx);
|
||||
|
||||
/* And the answer is..... */
|
||||
clean_rx = rx - echo_value;
|
||||
printf("echo is %" PRId32 "\n", echo_value);
|
||||
/* That was the easy part. Now we need to adapt! */
|
||||
if (ec->nonupdate_dwell > 0)
|
||||
ec->nonupdate_dwell--;
|
||||
|
||||
/* Calculate short term power levels using very simple single pole IIRs */
|
||||
/* TODO: Is the nasty modulus approach the fastest, or would a real
|
||||
tx*tx power calculation actually be faster? Using the squares
|
||||
makes the numbers grow a lot! */
|
||||
ec->tx_power[3] += ((abs(tx) - ec->tx_power[3]) >> 5);
|
||||
ec->tx_power[2] += ((tx*tx - ec->tx_power[2]) >> 8);
|
||||
ec->tx_power[1] += ((tx*tx - ec->tx_power[1]) >> 5);
|
||||
ec->tx_power[0] += ((tx*tx - ec->tx_power[0]) >> 3);
|
||||
ec->rx_power[1] += ((rx*rx - ec->rx_power[1]) >> 6);
|
||||
ec->rx_power[0] += ((rx*rx - ec->rx_power[0]) >> 3);
|
||||
ec->clean_rx_power += ((clean_rx*clean_rx - ec->clean_rx_power) >> 6);
|
||||
|
||||
score = 0;
|
||||
/* If there is very little being transmitted, any attempt to train is
|
||||
futile. We would either be training on the far end's noise or signal,
|
||||
the channel's own noise, or our noise. Either way, this is hardly good
|
||||
training, so don't do it (avoid trouble). */
|
||||
if (ec->tx_power[0] > MIN_TX_POWER_FOR_ADAPTION)
|
||||
{
|
||||
/* If the received power is very low, either we are sending very little or
|
||||
we are already well adapted. There is little point in trying to improve
|
||||
the adaption under these circumstances, so don't do it (reduce the
|
||||
compute load). */
|
||||
if (ec->tx_power[1] > ec->rx_power[0])
|
||||
{
|
||||
/* There is no (or little) far-end speech. */
|
||||
if (ec->nonupdate_dwell == 0)
|
||||
{
|
||||
if (++ec->narrowband_count >= 160)
|
||||
{
|
||||
ec->narrowband_count = 0;
|
||||
score = narrowband_detect(ec);
|
||||
printf("Do the narrowband test %d at %d\n", score, ec->curr_pos);
|
||||
if (score > 6)
|
||||
{
|
||||
if (ec->narrowband_score == 0)
|
||||
memcpy(ec->fir_taps16[3], ec->fir_taps16[(ec->tap_set + 1)%3], ec->taps*sizeof(int16_t));
|
||||
ec->narrowband_score += score;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ec->narrowband_score > 200)
|
||||
{
|
||||
printf("Revert to %d at %d\n", (ec->tap_set + 1)%3, sample_no);
|
||||
memcpy(ec->fir_taps16[ec->tap_set], ec->fir_taps16[3], ec->taps*sizeof(int16_t));
|
||||
memcpy(ec->fir_taps16[(ec->tap_set - 1)%3], ec->fir_taps16[3], ec->taps*sizeof(int16_t));
|
||||
for (i = 0; i < ec->taps; i++)
|
||||
ec->fir_taps32[i] = ec->fir_taps16[3][i] << 15;
|
||||
ec->tap_rotate_counter = 1600;
|
||||
}
|
||||
ec->narrowband_score = 0;
|
||||
}
|
||||
}
|
||||
ec->dtd_onset = FALSE;
|
||||
if (--ec->tap_rotate_counter <= 0)
|
||||
{
|
||||
printf("Rotate to %d at %d\n", ec->tap_set, sample_no);
|
||||
ec->tap_rotate_counter = 1600;
|
||||
ec->tap_set++;
|
||||
if (ec->tap_set > 2)
|
||||
ec->tap_set = 0;
|
||||
ec->fir_state.coeffs = ec->fir_taps16[ec->tap_set];
|
||||
}
|
||||
/* ... and we are not in the dwell time from previous speech. */
|
||||
if ((ec->adaption_mode & ECHO_CAN_USE_ADAPTION) && ec->narrowband_score == 0)
|
||||
{
|
||||
//nsuppr = saturate((clean_rx << 16)/ec->tx_power[1]);
|
||||
//nsuppr = clean_rx/ec->tx_power[1];
|
||||
/* If a sudden surge in signal level (e.g. the onset of a tone
|
||||
burst) cause an abnormally high instantaneous to average
|
||||
signal power ratio, we could kick the adaption badly in the
|
||||
wrong direction. This is because the tx_power takes too long
|
||||
to react and rise. We need to stop too rapid adaption to the
|
||||
new signal. We normalise to a value derived from the
|
||||
instantaneous signal if it exceeds the peak by too much. */
|
||||
nsuppr = clean_rx;
|
||||
/* Divide isn't very quick, but the "where is the top bit" and shift
|
||||
instructions are single cycle. */
|
||||
if (tx > 4*ec->tx_power[3])
|
||||
i = top_bit(tx) - 8;
|
||||
else
|
||||
i = top_bit(ec->tx_power[3]) - 8;
|
||||
if (i > 0)
|
||||
nsuppr >>= i;
|
||||
lms_adapt(ec, nsuppr);
|
||||
}
|
||||
}
|
||||
//printf("%10d %10d %10d %10d %10d\n", rx, clean_rx, nsuppr, ec->tx_power[1], ec->rx_power[1]);
|
||||
//printf("%.4f\n", (float) ec->rx_power[1]/(float) ec->clean_rx_power);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ec->dtd_onset)
|
||||
{
|
||||
printf("Revert to %d at %d\n", (ec->tap_set + 1)%3, sample_no);
|
||||
memcpy(ec->fir_taps16[ec->tap_set], ec->fir_taps16[(ec->tap_set + 1)%3], ec->taps*sizeof(int16_t));
|
||||
memcpy(ec->fir_taps16[(ec->tap_set - 1)%3], ec->fir_taps16[(ec->tap_set + 1)%3], ec->taps*sizeof(int16_t));
|
||||
for (i = 0; i < ec->taps; i++)
|
||||
ec->fir_taps32[i] = ec->fir_taps16[(ec->tap_set + 1)%3][i] << 15;
|
||||
ec->tap_rotate_counter = 1600;
|
||||
ec->dtd_onset = TRUE;
|
||||
}
|
||||
ec->nonupdate_dwell = NONUPDATE_DWELL_TIME;
|
||||
}
|
||||
}
|
||||
|
||||
if (ec->rx_power[1])
|
||||
ec->vad = (8000*ec->clean_rx_power)/ec->rx_power[1];
|
||||
else
|
||||
ec->vad = 0;
|
||||
if (ec->rx_power[1] > 2048*2048 && ec->clean_rx_power > 4*ec->rx_power[1])
|
||||
{
|
||||
/* The EC seems to be making things worse, instead of better. Zap it! */
|
||||
memset(ec->fir_taps32, 0, ec->taps*sizeof(int32_t));
|
||||
for (i = 0; i < 4; i++)
|
||||
memset(ec->fir_taps16[i], 0, ec->taps*sizeof(int16_t));
|
||||
}
|
||||
|
||||
#if defined(XYZZY)
|
||||
if ((ec->adaption_mode & ECHO_CAN_USE_SUPPRESSOR))
|
||||
{
|
||||
ec->supp_test1 += (ec->fir_state.history[ec->curr_pos] - ec->fir_state.history[(ec->curr_pos - 7) & ec->tap_mask]);
|
||||
ec->supp_test2 += (ec->fir_state.history[(ec->curr_pos - 24) & ec->tap_mask] - ec->fir_state.history[(ec->curr_pos - 31) & ec->tap_mask]);
|
||||
if (ec->supp_test1 > 42 && ec->supp_test2 > 42)
|
||||
supp_change = 25;
|
||||
else
|
||||
supp_change = 50;
|
||||
supp = supp_change + k1*ec->supp1 + k2*ec->supp2;
|
||||
ec->supp2 = ec->supp1;
|
||||
ec->supp1 = supp;
|
||||
clean_rx *= (1 - supp);
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((ec->adaption_mode & ECHO_CAN_USE_NLP))
|
||||
{
|
||||
/* Non-linear processor - a fancy way to say "zap small signals, to avoid
|
||||
residual echo due to (uLaw/ALaw) non-linearity in the channel.". */
|
||||
if (ec->rx_power[1] < 30000000)
|
||||
{
|
||||
if (!ec->cng)
|
||||
{
|
||||
ec->cng_level = ec->clean_rx_power;
|
||||
ec->cng = TRUE;
|
||||
}
|
||||
if ((ec->adaption_mode & ECHO_CAN_USE_CNG))
|
||||
{
|
||||
/* Very elementary comfort noise generation */
|
||||
/* Just random numbers rolled off very vaguely Hoth-like */
|
||||
ec->cng_rndnum = 1664525U*ec->cng_rndnum + 1013904223U;
|
||||
ec->cng_filter = ((ec->cng_rndnum & 0xFFFF) - 32768 + 5*ec->cng_filter) >> 3;
|
||||
clean_rx = (ec->cng_filter*ec->cng_level) >> 17;
|
||||
/* TODO: A better CNG, with more accurate (tracking) spectral shaping! */
|
||||
}
|
||||
else
|
||||
{
|
||||
clean_rx = 0;
|
||||
}
|
||||
//clean_rx = -16000;
|
||||
}
|
||||
else
|
||||
{
|
||||
ec->cng = FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ec->cng = FALSE;
|
||||
}
|
||||
|
||||
printf("Narrowband score %4d %5d at %d\n", ec->narrowband_score, score, sample_no);
|
||||
/* Roll around the rolling buffer */
|
||||
if (ec->curr_pos <= 0)
|
||||
ec->curr_pos = ec->taps;
|
||||
ec->curr_pos--;
|
||||
return (int16_t) clean_rx;
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
int16_t echo_can_hpf_tx(echo_can_state_t *ec, int16_t tx)
|
||||
{
|
||||
if (ec->adaption_mode & ECHO_CAN_USE_TX_HPF)
|
||||
tx = echo_can_hpf(ec->tx_hpf, tx);
|
||||
return tx;
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
/*- End of file ------------------------------------------------------------*/
|
Reference in New Issue
Block a user