Revert 4837 "Add an extended filter mode to AEC."
> Add an extended filter mode to AEC. > > This mode extends the filter length from the current 48 ms to 128 ms. > It is runtime selectable which allows it to be enabled through > experiment. We reuse the DelayCorrection infrastructure to avoid having > to replumb everything up to libjingle. > > Increases AEC complexity by ~50% on modern x86 CPUs. > Measurements (in percent of usage on one core): > > Machine/CPU Normal Extended > MacBook Retina (Early 2013), > Core i7 Ivy Bridge (2.7 GHz, hyperthreaded) 0.6% 0.9% > > MacBook Air (Late 2010), Core 2 Duo (2.13 GHz) 1.4% 2.7% > > Chromebook Pixel, Core i5 Ivy Bridge (1.8 GHz) 0.6% 1.0% > > Samsung ARM Chromebook, > Samsung Exynos 5 Dual (1.7 GHz) 3.2% 5.6% > > The relative value is large of course but the absolute should be > acceptable in order to have a working AEC on some platforms. > > Detailed changes to the algorithm: > - The filter length is changed from 48 to 128 ms. This comes with tuning > of several parameters: i) filter adaptation stepsize and error > threshold; ii) non-linear processing smoothing and overdrive. > - Option to ignore the reported delays on platforms which we deem > sufficiently unreliable. Currently this will be enabled in Chromium for > Mac. > - Faster startup times by removing the excessive "startup phase" > processing of reported delays. > - Much more conservative adjustments to the far-end read pointer. We > smooth the delay difference more heavily, and back off from the > difference more. Adjustments force a readaptation of the filter, so they > should be avoided except when really necessary. > > Corresponds to these changes: > https://chromereviews.googleplex.com/9412014 > https://chromereviews.googleplex.com/9514013 > https://chromereviews.googleplex.com/9960013 > > BUG=454,827,1261 > R=bjornv@webrtc.org > > Review URL: https://webrtc-codereview.appspot.com/2151007 TBR=andrew@webrtc.org Review URL: https://webrtc-codereview.appspot.com/2296005 git-svn-id: http://webrtc.googlecode.com/svn/trunk@4839 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
2f240b43f5
commit
ce014d97cd
@ -109,17 +109,7 @@ const float WebRtcAec_overDriveCurve[65] = {
|
|||||||
// Target suppression levels for nlp modes.
|
// Target suppression levels for nlp modes.
|
||||||
// log{0.001, 0.00001, 0.00000001}
|
// log{0.001, 0.00001, 0.00000001}
|
||||||
static const float kTargetSupp[3] = { -6.9f, -11.5f, -18.4f };
|
static const float kTargetSupp[3] = { -6.9f, -11.5f, -18.4f };
|
||||||
|
static const float kMinOverDrive[3] = { 1.0f, 2.0f, 5.0f };
|
||||||
// Two sets of parameters, one for the extended filter mode.
|
|
||||||
static const float kExtendedMinOverDrive[3] = { 3.0f, 6.0f, 15.0f };
|
|
||||||
static const float kNormalMinOverDrive[3] = { 1.0f, 2.0f, 5.0f };
|
|
||||||
static const float kExtendedSmoothingCoefficients[2][2] =
|
|
||||||
{ { 0.9f, 0.1f }, { 0.92f, 0.08f } };
|
|
||||||
static const float kNormalSmoothingCoefficients[2][2] =
|
|
||||||
{ { 0.9f, 0.1f }, { 0.93f, 0.07f } };
|
|
||||||
|
|
||||||
// Number of partitions forming the NLP's "preferred" bands.
|
|
||||||
enum { kPrefBandSize = 24 };
|
|
||||||
|
|
||||||
#ifdef WEBRTC_AEC_DEBUG_DUMP
|
#ifdef WEBRTC_AEC_DEBUG_DUMP
|
||||||
extern int webrtc_aec_instance_count;
|
extern int webrtc_aec_instance_count;
|
||||||
@ -291,13 +281,13 @@ int WebRtcAec_FreeAec(AecCore* aec)
|
|||||||
static void FilterFar(AecCore* aec, float yf[2][PART_LEN1])
|
static void FilterFar(AecCore* aec, float yf[2][PART_LEN1])
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < aec->num_partitions; i++) {
|
for (i = 0; i < NR_PART; i++) {
|
||||||
int j;
|
int j;
|
||||||
int xPos = (i + aec->xfBufBlockPos) * PART_LEN1;
|
int xPos = (i + aec->xfBufBlockPos) * PART_LEN1;
|
||||||
int pos = i * PART_LEN1;
|
int pos = i * PART_LEN1;
|
||||||
// Check for wrap
|
// Check for wrap
|
||||||
if (i + aec->xfBufBlockPos >= aec->num_partitions) {
|
if (i + aec->xfBufBlockPos >= NR_PART) {
|
||||||
xPos -= aec->num_partitions*(PART_LEN1);
|
xPos -= NR_PART*(PART_LEN1);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (j = 0; j < PART_LEN1; j++) {
|
for (j = 0; j < PART_LEN1; j++) {
|
||||||
@ -311,25 +301,22 @@ static void FilterFar(AecCore* aec, float yf[2][PART_LEN1])
|
|||||||
|
|
||||||
static void ScaleErrorSignal(AecCore* aec, float ef[2][PART_LEN1])
|
static void ScaleErrorSignal(AecCore* aec, float ef[2][PART_LEN1])
|
||||||
{
|
{
|
||||||
const float mu = aec->extended_filter_enabled ? kExtendedMu : aec->normal_mu;
|
|
||||||
const float error_threshold = aec->extended_filter_enabled ?
|
|
||||||
kExtendedErrorThreshold : aec->normal_error_threshold;
|
|
||||||
int i;
|
int i;
|
||||||
float abs_ef;
|
float absEf;
|
||||||
for (i = 0; i < (PART_LEN1); i++) {
|
for (i = 0; i < (PART_LEN1); i++) {
|
||||||
ef[0][i] /= (aec->xPow[i] + 1e-10f);
|
ef[0][i] /= (aec->xPow[i] + 1e-10f);
|
||||||
ef[1][i] /= (aec->xPow[i] + 1e-10f);
|
ef[1][i] /= (aec->xPow[i] + 1e-10f);
|
||||||
abs_ef = sqrtf(ef[0][i] * ef[0][i] + ef[1][i] * ef[1][i]);
|
absEf = sqrtf(ef[0][i] * ef[0][i] + ef[1][i] * ef[1][i]);
|
||||||
|
|
||||||
if (abs_ef > error_threshold) {
|
if (absEf > aec->errThresh) {
|
||||||
abs_ef = error_threshold / (abs_ef + 1e-10f);
|
absEf = aec->errThresh / (absEf + 1e-10f);
|
||||||
ef[0][i] *= abs_ef;
|
ef[0][i] *= absEf;
|
||||||
ef[1][i] *= abs_ef;
|
ef[1][i] *= absEf;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stepsize factor
|
// Stepsize factor
|
||||||
ef[0][i] *= mu;
|
ef[0][i] *= aec->mu;
|
||||||
ef[1][i] *= mu;
|
ef[1][i] *= aec->mu;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,35 +325,35 @@ static void ScaleErrorSignal(AecCore* aec, float ef[2][PART_LEN1])
|
|||||||
//static void FilterAdaptationUnconstrained(AecCore* aec, float *fft,
|
//static void FilterAdaptationUnconstrained(AecCore* aec, float *fft,
|
||||||
// float ef[2][PART_LEN1]) {
|
// float ef[2][PART_LEN1]) {
|
||||||
// int i, j;
|
// int i, j;
|
||||||
// for (i = 0; i < aec->num_partitions; i++) {
|
// for (i = 0; i < NR_PART; i++) {
|
||||||
// int xPos = (i + aec->xfBufBlockPos)*(PART_LEN1);
|
// int xPos = (i + aec->xfBufBlockPos)*(PART_LEN1);
|
||||||
// int pos;
|
// int pos;
|
||||||
// // Check for wrap
|
// // Check for wrap
|
||||||
// if (i + aec->xfBufBlockPos >= aec->num_partitions) {
|
// if (i + aec->xfBufBlockPos >= NR_PART) {
|
||||||
// xPos -= aec->num_partitions * PART_LEN1;
|
// xPos -= NR_PART * PART_LEN1;
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// pos = i * PART_LEN1;
|
// pos = i * PART_LEN1;
|
||||||
//
|
//
|
||||||
// for (j = 0; j < PART_LEN1; j++) {
|
// for (j = 0; j < PART_LEN1; j++) {
|
||||||
// aec->wfBuf[0][pos + j] += MulRe(aec->xfBuf[0][xPos + j],
|
// aec->wfBuf[pos + j][0] += MulRe(aec->xfBuf[xPos + j][0],
|
||||||
// -aec->xfBuf[1][xPos + j],
|
// -aec->xfBuf[xPos + j][1],
|
||||||
// ef[0][j], ef[1][j]);
|
// ef[j][0], ef[j][1]);
|
||||||
// aec->wfBuf[1][pos + j] += MulIm(aec->xfBuf[0][xPos + j],
|
// aec->wfBuf[pos + j][1] += MulIm(aec->xfBuf[xPos + j][0],
|
||||||
// -aec->xfBuf[1][xPos + j],
|
// -aec->xfBuf[xPos + j][1],
|
||||||
// ef[0][j], ef[1][j]);
|
// ef[j][0], ef[j][1]);
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
//}
|
//}
|
||||||
|
|
||||||
static void FilterAdaptation(AecCore* aec, float *fft, float ef[2][PART_LEN1]) {
|
static void FilterAdaptation(AecCore* aec, float *fft, float ef[2][PART_LEN1]) {
|
||||||
int i, j;
|
int i, j;
|
||||||
for (i = 0; i < aec->num_partitions; i++) {
|
for (i = 0; i < NR_PART; i++) {
|
||||||
int xPos = (i + aec->xfBufBlockPos)*(PART_LEN1);
|
int xPos = (i + aec->xfBufBlockPos)*(PART_LEN1);
|
||||||
int pos;
|
int pos;
|
||||||
// Check for wrap
|
// Check for wrap
|
||||||
if (i + aec->xfBufBlockPos >= aec->num_partitions) {
|
if (i + aec->xfBufBlockPos >= NR_PART) {
|
||||||
xPos -= aec->num_partitions * PART_LEN1;
|
xPos -= NR_PART * PART_LEN1;
|
||||||
}
|
}
|
||||||
|
|
||||||
pos = i * PART_LEN1;
|
pos = i * PART_LEN1;
|
||||||
@ -440,12 +427,12 @@ int WebRtcAec_InitAec(AecCore* aec, int sampFreq)
|
|||||||
aec->sampFreq = sampFreq;
|
aec->sampFreq = sampFreq;
|
||||||
|
|
||||||
if (sampFreq == 8000) {
|
if (sampFreq == 8000) {
|
||||||
aec->normal_mu = 0.6f;
|
aec->mu = 0.6f;
|
||||||
aec->normal_error_threshold = 2e-6f;
|
aec->errThresh = 2e-6f;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
aec->normal_mu = 0.5f;
|
aec->mu = 0.5f;
|
||||||
aec->normal_error_threshold = 1.5e-6f;
|
aec->errThresh = 1.5e-6f;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (WebRtc_InitBuffer(aec->nearFrBuf) == -1) {
|
if (WebRtc_InitBuffer(aec->nearFrBuf) == -1) {
|
||||||
@ -487,9 +474,6 @@ int WebRtcAec_InitAec(AecCore* aec, int sampFreq)
|
|||||||
aec->delay_logging_enabled = 0;
|
aec->delay_logging_enabled = 0;
|
||||||
memset(aec->delay_histogram, 0, sizeof(aec->delay_histogram));
|
memset(aec->delay_histogram, 0, sizeof(aec->delay_histogram));
|
||||||
|
|
||||||
aec->extended_filter_enabled = 0;
|
|
||||||
aec->num_partitions = kNormalNumPartitions;
|
|
||||||
|
|
||||||
// Default target suppression mode.
|
// Default target suppression mode.
|
||||||
aec->nlp_mode = 1;
|
aec->nlp_mode = 1;
|
||||||
|
|
||||||
@ -499,7 +483,7 @@ int WebRtcAec_InitAec(AecCore* aec, int sampFreq)
|
|||||||
aec->mult = (short)aec->sampFreq / 16000;
|
aec->mult = (short)aec->sampFreq / 16000;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
aec->mult = (short)aec->sampFreq / 8000;
|
aec->mult = (short)aec->sampFreq / 8000;
|
||||||
}
|
}
|
||||||
|
|
||||||
aec->farBufWritePos = 0;
|
aec->farBufWritePos = 0;
|
||||||
@ -530,14 +514,11 @@ int WebRtcAec_InitAec(AecCore* aec, int sampFreq)
|
|||||||
aec->xfBufBlockPos = 0;
|
aec->xfBufBlockPos = 0;
|
||||||
// TODO: Investigate need for these initializations. Deleting them doesn't
|
// TODO: Investigate need for these initializations. Deleting them doesn't
|
||||||
// change the output at all and yields 0.4% overall speedup.
|
// change the output at all and yields 0.4% overall speedup.
|
||||||
memset(aec->xfBuf, 0, sizeof(complex_t) * kExtendedNumPartitions *
|
memset(aec->xfBuf, 0, sizeof(complex_t) * NR_PART * PART_LEN1);
|
||||||
PART_LEN1);
|
memset(aec->wfBuf, 0, sizeof(complex_t) * NR_PART * PART_LEN1);
|
||||||
memset(aec->wfBuf, 0, sizeof(complex_t) * kExtendedNumPartitions *
|
|
||||||
PART_LEN1);
|
|
||||||
memset(aec->sde, 0, sizeof(complex_t) * PART_LEN1);
|
memset(aec->sde, 0, sizeof(complex_t) * PART_LEN1);
|
||||||
memset(aec->sxd, 0, sizeof(complex_t) * PART_LEN1);
|
memset(aec->sxd, 0, sizeof(complex_t) * PART_LEN1);
|
||||||
memset(aec->xfwBuf, 0, sizeof(complex_t) * kExtendedNumPartitions *
|
memset(aec->xfwBuf, 0, sizeof(complex_t) * NR_PART * PART_LEN1);
|
||||||
PART_LEN1);
|
|
||||||
memset(aec->se, 0, sizeof(float) * PART_LEN1);
|
memset(aec->se, 0, sizeof(float) * PART_LEN1);
|
||||||
|
|
||||||
// To prevent numerical instability in the first block.
|
// To prevent numerical instability in the first block.
|
||||||
@ -753,11 +734,13 @@ int WebRtcAec_GetDelayMetricsCore(AecCore* self, int* median, int* std) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int WebRtcAec_echo_state(AecCore* self) {
|
int WebRtcAec_echo_state(AecCore* self) {
|
||||||
|
assert(self != NULL);
|
||||||
return self->echoState;
|
return self->echoState;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebRtcAec_GetEchoStats(AecCore* self, Stats* erl, Stats* erle,
|
void WebRtcAec_GetEchoStats(AecCore* self, Stats* erl, Stats* erle,
|
||||||
Stats* a_nlp) {
|
Stats* a_nlp) {
|
||||||
|
assert(self != NULL);
|
||||||
assert(erl != NULL);
|
assert(erl != NULL);
|
||||||
assert(erle != NULL);
|
assert(erle != NULL);
|
||||||
assert(a_nlp != NULL);
|
assert(a_nlp != NULL);
|
||||||
@ -768,12 +751,14 @@ void WebRtcAec_GetEchoStats(AecCore* self, Stats* erl, Stats* erle,
|
|||||||
|
|
||||||
#ifdef WEBRTC_AEC_DEBUG_DUMP
|
#ifdef WEBRTC_AEC_DEBUG_DUMP
|
||||||
void* WebRtcAec_far_time_buf(AecCore* self) {
|
void* WebRtcAec_far_time_buf(AecCore* self) {
|
||||||
|
assert(self != NULL);
|
||||||
return self->far_time_buf;
|
return self->far_time_buf;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void WebRtcAec_SetConfigCore(AecCore* self, int nlp_mode, int metrics_mode,
|
void WebRtcAec_SetConfigCore(AecCore* self, int nlp_mode, int metrics_mode,
|
||||||
int delay_logging) {
|
int delay_logging) {
|
||||||
|
assert(self != NULL);
|
||||||
assert(nlp_mode >= 0 && nlp_mode < 3);
|
assert(nlp_mode >= 0 && nlp_mode < 3);
|
||||||
self->nlp_mode = nlp_mode;
|
self->nlp_mode = nlp_mode;
|
||||||
self->metricsMode = metrics_mode;
|
self->metricsMode = metrics_mode;
|
||||||
@ -786,20 +771,13 @@ void WebRtcAec_SetConfigCore(AecCore* self, int nlp_mode, int metrics_mode,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebRtcAec_enable_delay_correction(AecCore* self, int enable) {
|
|
||||||
self->extended_filter_enabled = enable;
|
|
||||||
self->num_partitions = enable ? kExtendedNumPartitions : kNormalNumPartitions;
|
|
||||||
}
|
|
||||||
|
|
||||||
int WebRtcAec_delay_correction_enabled(AecCore* self) {
|
|
||||||
return self->extended_filter_enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
int WebRtcAec_system_delay(AecCore* self) {
|
int WebRtcAec_system_delay(AecCore* self) {
|
||||||
|
assert(self != NULL);
|
||||||
return self->system_delay;
|
return self->system_delay;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebRtcAec_SetSystemDelay(AecCore* self, int delay) {
|
void WebRtcAec_SetSystemDelay(AecCore* self, int delay) {
|
||||||
|
assert(self != NULL);
|
||||||
assert(delay >= 0);
|
assert(delay >= 0);
|
||||||
self->system_delay = delay;
|
self->system_delay = delay;
|
||||||
}
|
}
|
||||||
@ -875,8 +853,7 @@ static void ProcessBlock(AecCore* aec) {
|
|||||||
for (i = 0; i < PART_LEN1; i++) {
|
for (i = 0; i < PART_LEN1; i++) {
|
||||||
far_spectrum = (xf_ptr[i] * xf_ptr[i]) +
|
far_spectrum = (xf_ptr[i] * xf_ptr[i]) +
|
||||||
(xf_ptr[PART_LEN1 + i] * xf_ptr[PART_LEN1 + i]);
|
(xf_ptr[PART_LEN1 + i] * xf_ptr[PART_LEN1 + i]);
|
||||||
aec->xPow[i] = gPow[0] * aec->xPow[i] + gPow[1] * aec->num_partitions *
|
aec->xPow[i] = gPow[0] * aec->xPow[i] + gPow[1] * NR_PART * far_spectrum;
|
||||||
far_spectrum;
|
|
||||||
// Calculate absolute spectra
|
// Calculate absolute spectra
|
||||||
abs_far_spectrum[i] = sqrtf(far_spectrum);
|
abs_far_spectrum[i] = sqrtf(far_spectrum);
|
||||||
|
|
||||||
@ -936,7 +913,7 @@ static void ProcessBlock(AecCore* aec) {
|
|||||||
// Update the xfBuf block position.
|
// Update the xfBuf block position.
|
||||||
aec->xfBufBlockPos--;
|
aec->xfBufBlockPos--;
|
||||||
if (aec->xfBufBlockPos == -1) {
|
if (aec->xfBufBlockPos == -1) {
|
||||||
aec->xfBufBlockPos = aec->num_partitions - 1;
|
aec->xfBufBlockPos = NR_PART - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Buffer xf
|
// Buffer xf
|
||||||
@ -1037,21 +1014,18 @@ static void NonLinearProcessing(AecCore* aec, short *output, short *outputH)
|
|||||||
float cohde[PART_LEN1], cohxd[PART_LEN1];
|
float cohde[PART_LEN1], cohxd[PART_LEN1];
|
||||||
float hNlDeAvg, hNlXdAvg;
|
float hNlDeAvg, hNlXdAvg;
|
||||||
float hNl[PART_LEN1];
|
float hNl[PART_LEN1];
|
||||||
float hNlPref[kPrefBandSize];
|
float hNlPref[PREF_BAND_SIZE];
|
||||||
float hNlFb = 0, hNlFbLow = 0;
|
float hNlFb = 0, hNlFbLow = 0;
|
||||||
const float prefBandQuant = 0.75f, prefBandQuantLow = 0.5f;
|
const float prefBandQuant = 0.75f, prefBandQuantLow = 0.5f;
|
||||||
const int prefBandSize = kPrefBandSize / aec->mult;
|
const int prefBandSize = PREF_BAND_SIZE / aec->mult;
|
||||||
const int minPrefBand = 4 / aec->mult;
|
const int minPrefBand = 4 / aec->mult;
|
||||||
|
|
||||||
// Near and error power sums
|
// Near and error power sums
|
||||||
float sdSum = 0, seSum = 0;
|
float sdSum = 0, seSum = 0;
|
||||||
|
|
||||||
// Power estimate smoothing coefficients.
|
// Power estimate smoothing coefficients
|
||||||
const float *ptrGCoh = aec->extended_filter_enabled ?
|
const float gCoh[2][2] = {{0.9f, 0.1f}, {0.93f, 0.07f}};
|
||||||
kExtendedSmoothingCoefficients[aec->mult - 1] :
|
const float *ptrGCoh = gCoh[aec->mult - 1];
|
||||||
kNormalSmoothingCoefficients[aec->mult - 1];
|
|
||||||
const float* min_overdrive = aec->extended_filter_enabled ?
|
|
||||||
kExtendedMinOverDrive : kNormalMinOverDrive;
|
|
||||||
|
|
||||||
// Filter energy
|
// Filter energy
|
||||||
float wfEnMax = 0, wfEn = 0;
|
float wfEnMax = 0, wfEn = 0;
|
||||||
@ -1074,7 +1048,7 @@ static void NonLinearProcessing(AecCore* aec, short *output, short *outputH)
|
|||||||
if (aec->delayEstCtr == 0) {
|
if (aec->delayEstCtr == 0) {
|
||||||
wfEnMax = 0;
|
wfEnMax = 0;
|
||||||
aec->delayIdx = 0;
|
aec->delayIdx = 0;
|
||||||
for (i = 0; i < aec->num_partitions; i++) {
|
for (i = 0; i < NR_PART; i++) {
|
||||||
pos = i * PART_LEN1;
|
pos = i * PART_LEN1;
|
||||||
wfEn = 0;
|
wfEn = 0;
|
||||||
for (j = 0; j < PART_LEN1; j++) {
|
for (j = 0; j < PART_LEN1; j++) {
|
||||||
@ -1215,7 +1189,7 @@ static void NonLinearProcessing(AecCore* aec, short *output, short *outputH)
|
|||||||
|
|
||||||
if (aec->hNlXdAvgMin == 1) {
|
if (aec->hNlXdAvgMin == 1) {
|
||||||
aec->echoState = 0;
|
aec->echoState = 0;
|
||||||
aec->overDrive = min_overdrive[aec->nlp_mode];
|
aec->overDrive = kMinOverDrive[aec->nlp_mode];
|
||||||
|
|
||||||
if (aec->stNearState == 1) {
|
if (aec->stNearState == 1) {
|
||||||
memcpy(hNl, cohde, sizeof(hNl));
|
memcpy(hNl, cohde, sizeof(hNl));
|
||||||
@ -1271,7 +1245,7 @@ static void NonLinearProcessing(AecCore* aec, short *output, short *outputH)
|
|||||||
aec->hNlMinCtr = 0;
|
aec->hNlMinCtr = 0;
|
||||||
aec->overDrive = WEBRTC_SPL_MAX(kTargetSupp[aec->nlp_mode] /
|
aec->overDrive = WEBRTC_SPL_MAX(kTargetSupp[aec->nlp_mode] /
|
||||||
((float)log(aec->hNlFbMin + 1e-10f) + 1e-10f),
|
((float)log(aec->hNlFbMin + 1e-10f) + 1e-10f),
|
||||||
min_overdrive[aec->nlp_mode]);
|
kMinOverDrive[aec->nlp_mode]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Smooth the overdrive.
|
// Smooth the overdrive.
|
||||||
@ -1491,6 +1465,7 @@ static void InitStats(Stats* stats) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void InitMetrics(AecCore* self) {
|
static void InitMetrics(AecCore* self) {
|
||||||
|
assert(self != NULL);
|
||||||
self->stateCounter = 0;
|
self->stateCounter = 0;
|
||||||
InitLevel(&self->farlevel);
|
InitLevel(&self->farlevel);
|
||||||
InitLevel(&self->nearlevel);
|
InitLevel(&self->nearlevel);
|
||||||
@ -1712,4 +1687,3 @@ static void TimeToFrequency(float time_data[PART_LEN2],
|
|||||||
freq_data[1][i] = time_data[2 * i + 1];
|
freq_data[1][i] = time_data[2 * i + 1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,38 +70,23 @@ void WebRtcAec_ProcessFrame(AecCore* aec,
|
|||||||
// Returns the number of elements moved, and adjusts |system_delay| by the
|
// Returns the number of elements moved, and adjusts |system_delay| by the
|
||||||
// corresponding amount in ms.
|
// corresponding amount in ms.
|
||||||
int WebRtcAec_MoveFarReadPtr(AecCore* aec, int elements);
|
int WebRtcAec_MoveFarReadPtr(AecCore* aec, int elements);
|
||||||
|
|
||||||
// Calculates the median and standard deviation among the delay estimates
|
// Calculates the median and standard deviation among the delay estimates
|
||||||
// collected since the last call to this function.
|
// collected since the last call to this function.
|
||||||
int WebRtcAec_GetDelayMetricsCore(AecCore* self, int* median, int* std);
|
int WebRtcAec_GetDelayMetricsCore(AecCore* self, int* median, int* std);
|
||||||
|
|
||||||
// Returns the echo state (1: echo, 0: no echo).
|
// Returns the echo state (1: echo, 0: no echo).
|
||||||
int WebRtcAec_echo_state(AecCore* self);
|
int WebRtcAec_echo_state(AecCore* self);
|
||||||
|
|
||||||
// Gets statistics of the echo metrics ERL, ERLE, A_NLP.
|
// Gets statistics of the echo metrics ERL, ERLE, A_NLP.
|
||||||
void WebRtcAec_GetEchoStats(AecCore* self, Stats* erl, Stats* erle,
|
void WebRtcAec_GetEchoStats(AecCore* self, Stats* erl, Stats* erle,
|
||||||
Stats* a_nlp);
|
Stats* a_nlp);
|
||||||
#ifdef WEBRTC_AEC_DEBUG_DUMP
|
#ifdef WEBRTC_AEC_DEBUG_DUMP
|
||||||
void* WebRtcAec_far_time_buf(AecCore* self);
|
void* WebRtcAec_far_time_buf(AecCore* self);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Sets local configuration modes.
|
// Sets local configuration modes.
|
||||||
void WebRtcAec_SetConfigCore(AecCore* self, int nlp_mode, int metrics_mode,
|
void WebRtcAec_SetConfigCore(AecCore* self, int nlp_mode, int metrics_mode,
|
||||||
int delay_logging);
|
int delay_logging);
|
||||||
|
|
||||||
// We now interpret delay correction to mean an extended filter length feature.
|
|
||||||
// We reuse the delay correction infrastructure to avoid changes through to
|
|
||||||
// libjingle. See details along with |DelayCorrection| in
|
|
||||||
// echo_cancellation_impl.h. Non-zero enables, zero disables.
|
|
||||||
void WebRtcAec_enable_delay_correction(AecCore* self, int enable);
|
|
||||||
|
|
||||||
// Returns non-zero if delay correction is enabled and zero if disabled.
|
|
||||||
int WebRtcAec_delay_correction_enabled(AecCore* self);
|
|
||||||
|
|
||||||
// Returns the current |system_delay|, i.e., the buffered difference between
|
// Returns the current |system_delay|, i.e., the buffered difference between
|
||||||
// far-end and near-end.
|
// far-end and near-end.
|
||||||
int WebRtcAec_system_delay(AecCore* self);
|
int WebRtcAec_system_delay(AecCore* self);
|
||||||
|
|
||||||
// Sets the |system_delay| to |value|. Note that if the value is changed
|
// Sets the |system_delay| to |value|. Note that if the value is changed
|
||||||
// improperly, there can be a performance regression. So it should be used with
|
// improperly, there can be a performance regression. So it should be used with
|
||||||
// care.
|
// care.
|
||||||
|
@ -19,15 +19,8 @@
|
|||||||
#include "webrtc/modules/audio_processing/utility/ring_buffer.h"
|
#include "webrtc/modules/audio_processing/utility/ring_buffer.h"
|
||||||
#include "webrtc/typedefs.h"
|
#include "webrtc/typedefs.h"
|
||||||
|
|
||||||
// Number of partitions for the extended filter mode. The first one is an enum
|
#define NR_PART 12 // Number of partitions in filter.
|
||||||
// to be used in array declarations, as it represents the maximum filter length.
|
#define PREF_BAND_SIZE 24
|
||||||
enum { kExtendedNumPartitions = 32 };
|
|
||||||
static const int kNormalNumPartitions = 12;
|
|
||||||
|
|
||||||
// Extended filter adaptation parameters.
|
|
||||||
// TODO(ajm): No narrowband tuning yet.
|
|
||||||
static const float kExtendedMu = 0.4f;
|
|
||||||
static const float kExtendedErrorThreshold = 1.0e-6f;
|
|
||||||
|
|
||||||
typedef struct PowerLevel {
|
typedef struct PowerLevel {
|
||||||
float sfrsum;
|
float sfrsum;
|
||||||
@ -63,12 +56,11 @@ struct AecCore {
|
|||||||
float dInitMinPow[PART_LEN1];
|
float dInitMinPow[PART_LEN1];
|
||||||
float *noisePow;
|
float *noisePow;
|
||||||
|
|
||||||
float xfBuf[2][kExtendedNumPartitions * PART_LEN1]; // farend fft buffer
|
float xfBuf[2][NR_PART * PART_LEN1]; // farend fft buffer
|
||||||
float wfBuf[2][kExtendedNumPartitions * PART_LEN1]; // filter fft
|
float wfBuf[2][NR_PART * PART_LEN1]; // filter fft
|
||||||
complex_t sde[PART_LEN1]; // cross-psd of nearend and error
|
complex_t sde[PART_LEN1]; // cross-psd of nearend and error
|
||||||
complex_t sxd[PART_LEN1]; // cross-psd of farend and nearend
|
complex_t sxd[PART_LEN1]; // cross-psd of farend and nearend
|
||||||
// Farend windowed fft buffer.
|
complex_t xfwBuf[NR_PART * PART_LEN1]; // farend windowed fft buffer
|
||||||
complex_t xfwBuf[kExtendedNumPartitions * PART_LEN1];
|
|
||||||
|
|
||||||
float sx[PART_LEN1], sd[PART_LEN1], se[PART_LEN1]; // far, near, error psd
|
float sx[PART_LEN1], sd[PART_LEN1], se[PART_LEN1]; // far, near, error psd
|
||||||
float hNs[PART_LEN1];
|
float hNs[PART_LEN1];
|
||||||
@ -93,8 +85,8 @@ struct AecCore {
|
|||||||
int sampFreq;
|
int sampFreq;
|
||||||
uint32_t seed;
|
uint32_t seed;
|
||||||
|
|
||||||
float normal_mu; // stepsize
|
float mu; // stepsize
|
||||||
float normal_error_threshold; // error threshold
|
float errThresh; // error threshold
|
||||||
|
|
||||||
int noiseEstCtr;
|
int noiseEstCtr;
|
||||||
|
|
||||||
@ -120,11 +112,6 @@ struct AecCore {
|
|||||||
void* delay_estimator_farend;
|
void* delay_estimator_farend;
|
||||||
void* delay_estimator;
|
void* delay_estimator;
|
||||||
|
|
||||||
// 1 = extended filter mode enabled, 0 = disabled.
|
|
||||||
int extended_filter_enabled;
|
|
||||||
// Runtime selection of number of filter partitions.
|
|
||||||
int num_partitions;
|
|
||||||
|
|
||||||
#ifdef WEBRTC_AEC_DEBUG_DUMP
|
#ifdef WEBRTC_AEC_DEBUG_DUMP
|
||||||
RingBuffer* far_time_buf;
|
RingBuffer* far_time_buf;
|
||||||
FILE *farFile;
|
FILE *farFile;
|
||||||
|
@ -34,14 +34,13 @@ __inline static float MulIm(float aRe, float aIm, float bRe, float bIm)
|
|||||||
static void FilterFarSSE2(AecCore* aec, float yf[2][PART_LEN1])
|
static void FilterFarSSE2(AecCore* aec, float yf[2][PART_LEN1])
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
const int num_partitions = aec->num_partitions;
|
for (i = 0; i < NR_PART; i++) {
|
||||||
for (i = 0; i < num_partitions; i++) {
|
|
||||||
int j;
|
int j;
|
||||||
int xPos = (i + aec->xfBufBlockPos) * PART_LEN1;
|
int xPos = (i + aec->xfBufBlockPos) * PART_LEN1;
|
||||||
int pos = i * PART_LEN1;
|
int pos = i * PART_LEN1;
|
||||||
// Check for wrap
|
// Check for wrap
|
||||||
if (i + aec->xfBufBlockPos >= num_partitions) {
|
if (i + aec->xfBufBlockPos >= NR_PART) {
|
||||||
xPos -= num_partitions*(PART_LEN1);
|
xPos -= NR_PART*(PART_LEN1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// vectorized code (four at once)
|
// vectorized code (four at once)
|
||||||
@ -76,11 +75,8 @@ static void FilterFarSSE2(AecCore* aec, float yf[2][PART_LEN1])
|
|||||||
static void ScaleErrorSignalSSE2(AecCore* aec, float ef[2][PART_LEN1])
|
static void ScaleErrorSignalSSE2(AecCore* aec, float ef[2][PART_LEN1])
|
||||||
{
|
{
|
||||||
const __m128 k1e_10f = _mm_set1_ps(1e-10f);
|
const __m128 k1e_10f = _mm_set1_ps(1e-10f);
|
||||||
const __m128 kMu = aec->extended_filter_enabled ?
|
const __m128 kThresh = _mm_set1_ps(aec->errThresh);
|
||||||
_mm_set1_ps(kExtendedMu) : _mm_set1_ps(aec->normal_mu);
|
const __m128 kMu = _mm_set1_ps(aec->mu);
|
||||||
const __m128 kThresh = aec->extended_filter_enabled ?
|
|
||||||
_mm_set1_ps(kExtendedErrorThreshold) :
|
|
||||||
_mm_set1_ps(aec->normal_error_threshold);
|
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
// vectorized code (four at once)
|
// vectorized code (four at once)
|
||||||
@ -114,39 +110,32 @@ static void ScaleErrorSignalSSE2(AecCore* aec, float ef[2][PART_LEN1])
|
|||||||
_mm_storeu_ps(&ef[1][i], ef_im);
|
_mm_storeu_ps(&ef[1][i], ef_im);
|
||||||
}
|
}
|
||||||
// scalar code for the remaining items.
|
// scalar code for the remaining items.
|
||||||
{
|
for (; i < (PART_LEN1); i++) {
|
||||||
const float mu = aec->extended_filter_enabled ?
|
float absEf;
|
||||||
kExtendedMu : aec->normal_mu;
|
ef[0][i] /= (aec->xPow[i] + 1e-10f);
|
||||||
const float error_threshold = aec->extended_filter_enabled ?
|
ef[1][i] /= (aec->xPow[i] + 1e-10f);
|
||||||
kExtendedErrorThreshold : aec->normal_error_threshold;
|
absEf = sqrtf(ef[0][i] * ef[0][i] + ef[1][i] * ef[1][i]);
|
||||||
for (; i < (PART_LEN1); i++) {
|
|
||||||
float abs_ef;
|
|
||||||
ef[0][i] /= (aec->xPow[i] + 1e-10f);
|
|
||||||
ef[1][i] /= (aec->xPow[i] + 1e-10f);
|
|
||||||
abs_ef = sqrtf(ef[0][i] * ef[0][i] + ef[1][i] * ef[1][i]);
|
|
||||||
|
|
||||||
if (abs_ef > error_threshold) {
|
if (absEf > aec->errThresh) {
|
||||||
abs_ef = error_threshold / (abs_ef + 1e-10f);
|
absEf = aec->errThresh / (absEf + 1e-10f);
|
||||||
ef[0][i] *= abs_ef;
|
ef[0][i] *= absEf;
|
||||||
ef[1][i] *= abs_ef;
|
ef[1][i] *= absEf;
|
||||||
}
|
|
||||||
|
|
||||||
// Stepsize factor
|
|
||||||
ef[0][i] *= mu;
|
|
||||||
ef[1][i] *= mu;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stepsize factor
|
||||||
|
ef[0][i] *= aec->mu;
|
||||||
|
ef[1][i] *= aec->mu;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void FilterAdaptationSSE2(AecCore* aec, float *fft, float ef[2][PART_LEN1]) {
|
static void FilterAdaptationSSE2(AecCore* aec, float *fft, float ef[2][PART_LEN1]) {
|
||||||
int i, j;
|
int i, j;
|
||||||
const int num_partitions = aec->num_partitions;
|
for (i = 0; i < NR_PART; i++) {
|
||||||
for (i = 0; i < num_partitions; i++) {
|
|
||||||
int xPos = (i + aec->xfBufBlockPos)*(PART_LEN1);
|
int xPos = (i + aec->xfBufBlockPos)*(PART_LEN1);
|
||||||
int pos = i * PART_LEN1;
|
int pos = i * PART_LEN1;
|
||||||
// Check for wrap
|
// Check for wrap
|
||||||
if (i + aec->xfBufBlockPos >= num_partitions) {
|
if (i + aec->xfBufBlockPos >= NR_PART) {
|
||||||
xPos -= num_partitions * PART_LEN1;
|
xPos -= NR_PART * PART_LEN1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process the whole array...
|
// Process the whole array...
|
||||||
@ -424,4 +413,3 @@ void WebRtcAec_InitAec_SSE2(void) {
|
|||||||
WebRtcAec_FilterAdaptation = FilterAdaptationSSE2;
|
WebRtcAec_FilterAdaptation = FilterAdaptationSSE2;
|
||||||
WebRtcAec_OverdriveAndSuppress = OverdriveAndSuppressSSE2;
|
WebRtcAec_OverdriveAndSuppress = OverdriveAndSuppressSSE2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,61 +27,6 @@
|
|||||||
#include "webrtc/modules/audio_processing/utility/ring_buffer.h"
|
#include "webrtc/modules/audio_processing/utility/ring_buffer.h"
|
||||||
#include "webrtc/typedefs.h"
|
#include "webrtc/typedefs.h"
|
||||||
|
|
||||||
// Measured delays [ms]
|
|
||||||
// Device Chrome GTP
|
|
||||||
// MacBook Air 10
|
|
||||||
// MacBook Retina 10 100
|
|
||||||
// MacPro 30?
|
|
||||||
//
|
|
||||||
// Win7 Desktop 70 80?
|
|
||||||
// Win7 T430s 110
|
|
||||||
// Win8 T420s 70
|
|
||||||
//
|
|
||||||
// Daisy 50
|
|
||||||
// Pixel (w/ preproc?) 240
|
|
||||||
// Pixel (w/o preproc?) 110 110
|
|
||||||
|
|
||||||
// The extended filter mode gives us the flexibility to ignore the system's
|
|
||||||
// reported delays. We do this for platforms which we believe provide results
|
|
||||||
// which are incompatible with the AEC's expectations. Based on measurements
|
|
||||||
// (some provided above) we set a conservative (i.e. lower than measured)
|
|
||||||
// fixed delay.
|
|
||||||
//
|
|
||||||
// WEBRTC_UNTRUSTED_DELAY will only have an impact when |extended_filter_mode|
|
|
||||||
// is enabled. See the note along with |DelayCorrection| in
|
|
||||||
// echo_cancellation_impl.h for more details on the mode.
|
|
||||||
//
|
|
||||||
// Justification:
|
|
||||||
// Chromium/Mac: Here, the true latency is so low (~10-20 ms), that it plays
|
|
||||||
// havoc with the AEC's buffering. To avoid this, we set a fixed delay of 20 ms
|
|
||||||
// and then compensate by rewinding by 10 ms (in wideband) through
|
|
||||||
// kDelayDiffOffsetSamples. This trick does not seem to work for larger rewind
|
|
||||||
// values, but fortunately this is sufficient.
|
|
||||||
//
|
|
||||||
// Chromium/Linux(ChromeOS): The values we get on this platform don't correspond
|
|
||||||
// well to reality. The variance doesn't match the AEC's buffer changes, and the
|
|
||||||
// bulk values tend to be too low. However, the range across different hardware
|
|
||||||
// appears to be too large to choose a single value.
|
|
||||||
//
|
|
||||||
// GTP/Linux(ChromeOS): TBD, but for the moment we will trust the values.
|
|
||||||
#if defined(WEBRTC_CHROMIUM_BUILD) && defined(WEBRTC_MAC)
|
|
||||||
#define WEBRTC_UNTRUSTED_DELAY
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(WEBRTC_MAC)
|
|
||||||
static const int kFixedDelayMs = 20;
|
|
||||||
static const int kDelayDiffOffsetSamples = -160;
|
|
||||||
#elif defined(WEBRTC_WIN)
|
|
||||||
static const int kFixedDelayMs = 50;
|
|
||||||
static const int kDelayDiffOffsetSamples = 0;
|
|
||||||
#else
|
|
||||||
// Essentially ChromeOS.
|
|
||||||
static const int kFixedDelayMs = 50;
|
|
||||||
static const int kDelayDiffOffsetSamples = 0;
|
|
||||||
#endif
|
|
||||||
static const int kMinTrustedDelayMs = 20;
|
|
||||||
static const int kMaxTrustedDelayMs = 500;
|
|
||||||
|
|
||||||
// Maximum length of resampled signal. Must be an integer multiple of frames
|
// Maximum length of resampled signal. Must be an integer multiple of frames
|
||||||
// (ceil(1/(1 + MIN_SKEW)*2) + 1)*FRAME_LEN
|
// (ceil(1/(1 + MIN_SKEW)*2) + 1)*FRAME_LEN
|
||||||
// The factor of 2 handles wb, and the + 1 is as a safety margin
|
// The factor of 2 handles wb, and the + 1 is as a safety margin
|
||||||
@ -98,14 +43,7 @@ int webrtc_aec_instance_count = 0;
|
|||||||
|
|
||||||
// Estimates delay to set the position of the far-end buffer read pointer
|
// Estimates delay to set the position of the far-end buffer read pointer
|
||||||
// (controlled by knownDelay)
|
// (controlled by knownDelay)
|
||||||
static void EstBufDelayNormal(aecpc_t *aecInst);
|
static int EstBufDelay(aecpc_t *aecInst);
|
||||||
static void EstBufDelayExtended(aecpc_t *aecInst);
|
|
||||||
static int ProcessNormal(aecpc_t* self, const int16_t* near,
|
|
||||||
const int16_t* near_high, int16_t* out, int16_t* out_high,
|
|
||||||
int16_t num_samples, int16_t reported_delay_ms, int32_t skew);
|
|
||||||
static void ProcessExtended(aecpc_t* self, const int16_t* near,
|
|
||||||
const int16_t* near_high, int16_t* out, int16_t* out_high,
|
|
||||||
int16_t num_samples, int16_t reported_delay_ms, int32_t skew);
|
|
||||||
|
|
||||||
int32_t WebRtcAec_Create(void **aecInst)
|
int32_t WebRtcAec_Create(void **aecInst)
|
||||||
{
|
{
|
||||||
@ -197,6 +135,10 @@ int32_t WebRtcAec_Init(void *aecInst, int32_t sampFreq, int32_t scSampFreq)
|
|||||||
aecpc_t *aecpc = aecInst;
|
aecpc_t *aecpc = aecInst;
|
||||||
AecConfig aecConfig;
|
AecConfig aecConfig;
|
||||||
|
|
||||||
|
if (aecpc == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (sampFreq != 8000 && sampFreq != 16000 && sampFreq != 32000) {
|
if (sampFreq != 8000 && sampFreq != 16000 && sampFreq != 32000) {
|
||||||
aecpc->lastError = AEC_BAD_PARAMETER_ERROR;
|
aecpc->lastError = AEC_BAD_PARAMETER_ERROR;
|
||||||
return -1;
|
return -1;
|
||||||
@ -235,31 +177,31 @@ int32_t WebRtcAec_Init(void *aecInst, int32_t sampFreq, int32_t scSampFreq)
|
|||||||
aecpc->splitSampFreq = sampFreq;
|
aecpc->splitSampFreq = sampFreq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
aecpc->skewFrCtr = 0;
|
||||||
|
aecpc->activity = 0;
|
||||||
|
|
||||||
aecpc->delayCtr = 0;
|
aecpc->delayCtr = 0;
|
||||||
aecpc->sampFactor = (aecpc->scSampFreq * 1.0f) / aecpc->splitSampFreq;
|
|
||||||
// Sampling frequency multiplier (SWB is processed as 160 frame size).
|
|
||||||
aecpc->rate_factor = aecpc->splitSampFreq / 8000;
|
|
||||||
|
|
||||||
aecpc->sum = 0;
|
aecpc->sum = 0;
|
||||||
aecpc->counter = 0;
|
aecpc->counter = 0;
|
||||||
aecpc->checkBuffSize = 1;
|
aecpc->checkBuffSize = 1;
|
||||||
aecpc->firstVal = 0;
|
aecpc->firstVal = 0;
|
||||||
|
|
||||||
aecpc->startup_phase = 1;
|
aecpc->ECstartup = 1;
|
||||||
aecpc->bufSizeStart = 0;
|
aecpc->bufSizeStart = 0;
|
||||||
aecpc->checkBufSizeCtr = 0;
|
aecpc->checkBufSizeCtr = 0;
|
||||||
aecpc->msInSndCardBuf = 0;
|
aecpc->filtDelay = 0;
|
||||||
aecpc->filtDelay = -1; // -1 indicates an initialized state.
|
|
||||||
aecpc->timeForDelayChange = 0;
|
aecpc->timeForDelayChange = 0;
|
||||||
aecpc->knownDelay = 0;
|
aecpc->knownDelay = 0;
|
||||||
aecpc->lastDelayDiff = 0;
|
aecpc->lastDelayDiff = 0;
|
||||||
|
|
||||||
aecpc->skewFrCtr = 0;
|
aecpc->skew = 0;
|
||||||
aecpc->resample = kAecFalse;
|
aecpc->resample = kAecFalse;
|
||||||
aecpc->highSkewCtr = 0;
|
aecpc->highSkewCtr = 0;
|
||||||
aecpc->skew = 0;
|
aecpc->sampFactor = (aecpc->scSampFreq * 1.0f) / aecpc->splitSampFreq;
|
||||||
|
|
||||||
aecpc->farend_started = 0;
|
// Sampling frequency multiplier (SWB is processed as 160 frame size).
|
||||||
|
aecpc->rate_factor = aecpc->splitSampFreq / 8000;
|
||||||
|
|
||||||
// Default settings.
|
// Default settings.
|
||||||
aecConfig.nlpMode = kAecNlpModerate;
|
aecConfig.nlpMode = kAecNlpModerate;
|
||||||
@ -297,6 +239,10 @@ int32_t WebRtcAec_BufferFarend(void *aecInst, const int16_t *farend,
|
|||||||
float skew;
|
float skew;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
|
if (aecpc == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (farend == NULL) {
|
if (farend == NULL) {
|
||||||
aecpc->lastError = AEC_NULL_POINTER_ERROR;
|
aecpc->lastError = AEC_NULL_POINTER_ERROR;
|
||||||
return -1;
|
return -1;
|
||||||
@ -322,7 +268,6 @@ int32_t WebRtcAec_BufferFarend(void *aecInst, const int16_t *farend,
|
|||||||
farend_ptr = (const int16_t*) newFarend;
|
farend_ptr = (const int16_t*) newFarend;
|
||||||
}
|
}
|
||||||
|
|
||||||
aecpc->farend_started = 1;
|
|
||||||
WebRtcAec_SetSystemDelay(aecpc->aec, WebRtcAec_system_delay(aecpc->aec) +
|
WebRtcAec_SetSystemDelay(aecpc->aec, WebRtcAec_system_delay(aecpc->aec) +
|
||||||
newNrOfSamples);
|
newNrOfSamples);
|
||||||
|
|
||||||
@ -366,6 +311,17 @@ int32_t WebRtcAec_Process(void *aecInst, const int16_t *nearend,
|
|||||||
{
|
{
|
||||||
aecpc_t *aecpc = aecInst;
|
aecpc_t *aecpc = aecInst;
|
||||||
int32_t retVal = 0;
|
int32_t retVal = 0;
|
||||||
|
short i;
|
||||||
|
short nBlocks10ms;
|
||||||
|
short nFrames;
|
||||||
|
// Limit resampling to doubling/halving of signal
|
||||||
|
const float minSkewEst = -0.5f;
|
||||||
|
const float maxSkewEst = 1.0f;
|
||||||
|
|
||||||
|
if (aecpc == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (nearend == NULL) {
|
if (nearend == NULL) {
|
||||||
aecpc->lastError = AEC_NULL_POINTER_ERROR;
|
aecpc->lastError = AEC_NULL_POINTER_ERROR;
|
||||||
return -1;
|
return -1;
|
||||||
@ -398,21 +354,144 @@ int32_t WebRtcAec_Process(void *aecInst, const int16_t *nearend,
|
|||||||
aecpc->lastError = AEC_BAD_PARAMETER_WARNING;
|
aecpc->lastError = AEC_BAD_PARAMETER_WARNING;
|
||||||
retVal = -1;
|
retVal = -1;
|
||||||
}
|
}
|
||||||
else if (msInSndCardBuf > kMaxTrustedDelayMs) {
|
else if (msInSndCardBuf > 500) {
|
||||||
// The clamping is now done in ProcessExtended/Normal().
|
msInSndCardBuf = 500;
|
||||||
aecpc->lastError = AEC_BAD_PARAMETER_WARNING;
|
aecpc->lastError = AEC_BAD_PARAMETER_WARNING;
|
||||||
retVal = -1;
|
retVal = -1;
|
||||||
}
|
}
|
||||||
|
// TODO(andrew): we need to investigate if this +10 is really wanted.
|
||||||
|
msInSndCardBuf += 10;
|
||||||
|
aecpc->msInSndCardBuf = msInSndCardBuf;
|
||||||
|
|
||||||
// This returns the value of aec->extended_filter_enabled.
|
if (aecpc->skewMode == kAecTrue) {
|
||||||
if (WebRtcAec_delay_correction_enabled(aecpc->aec)) {
|
if (aecpc->skewFrCtr < 25) {
|
||||||
ProcessExtended(aecpc, nearend, nearendH, out, outH, nrOfSamples,
|
aecpc->skewFrCtr++;
|
||||||
msInSndCardBuf, skew);
|
}
|
||||||
|
else {
|
||||||
|
retVal = WebRtcAec_GetSkew(aecpc->resampler, skew, &aecpc->skew);
|
||||||
|
if (retVal == -1) {
|
||||||
|
aecpc->skew = 0;
|
||||||
|
aecpc->lastError = AEC_BAD_PARAMETER_WARNING;
|
||||||
|
}
|
||||||
|
|
||||||
|
aecpc->skew /= aecpc->sampFactor*nrOfSamples;
|
||||||
|
|
||||||
|
if (aecpc->skew < 1.0e-3 && aecpc->skew > -1.0e-3) {
|
||||||
|
aecpc->resample = kAecFalse;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
aecpc->resample = kAecTrue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aecpc->skew < minSkewEst) {
|
||||||
|
aecpc->skew = minSkewEst;
|
||||||
|
}
|
||||||
|
else if (aecpc->skew > maxSkewEst) {
|
||||||
|
aecpc->skew = maxSkewEst;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WEBRTC_AEC_DEBUG_DUMP
|
||||||
|
(void)fwrite(&aecpc->skew, sizeof(aecpc->skew), 1, aecpc->skewFile);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nFrames = nrOfSamples / FRAME_LEN;
|
||||||
|
nBlocks10ms = nFrames / aecpc->rate_factor;
|
||||||
|
|
||||||
|
if (aecpc->ECstartup) {
|
||||||
|
if (nearend != out) {
|
||||||
|
// Only needed if they don't already point to the same place.
|
||||||
|
memcpy(out, nearend, sizeof(short) * nrOfSamples);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The AEC is in the start up mode
|
||||||
|
// AEC is disabled until the system delay is OK
|
||||||
|
|
||||||
|
// Mechanism to ensure that the system delay is reasonably stable.
|
||||||
|
if (aecpc->checkBuffSize) {
|
||||||
|
aecpc->checkBufSizeCtr++;
|
||||||
|
// Before we fill up the far-end buffer we require the system delay
|
||||||
|
// to be stable (+/-8 ms) compared to the first value. This
|
||||||
|
// comparison is made during the following 6 consecutive 10 ms
|
||||||
|
// blocks. If it seems to be stable then we start to fill up the
|
||||||
|
// far-end buffer.
|
||||||
|
if (aecpc->counter == 0) {
|
||||||
|
aecpc->firstVal = aecpc->msInSndCardBuf;
|
||||||
|
aecpc->sum = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (abs(aecpc->firstVal - aecpc->msInSndCardBuf) <
|
||||||
|
WEBRTC_SPL_MAX(0.2 * aecpc->msInSndCardBuf, sampMsNb)) {
|
||||||
|
aecpc->sum += aecpc->msInSndCardBuf;
|
||||||
|
aecpc->counter++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
aecpc->counter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aecpc->counter * nBlocks10ms >= 6) {
|
||||||
|
// The far-end buffer size is determined in partitions of
|
||||||
|
// PART_LEN samples. Use 75% of the average value of the system
|
||||||
|
// delay as buffer size to start with.
|
||||||
|
aecpc->bufSizeStart = WEBRTC_SPL_MIN((3 * aecpc->sum *
|
||||||
|
aecpc->rate_factor * 8) / (4 * aecpc->counter * PART_LEN),
|
||||||
|
kMaxBufSizeStart);
|
||||||
|
// Buffer size has now been determined.
|
||||||
|
aecpc->checkBuffSize = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aecpc->checkBufSizeCtr * nBlocks10ms > 50) {
|
||||||
|
// For really bad systems, don't disable the echo canceller for
|
||||||
|
// more than 0.5 sec.
|
||||||
|
aecpc->bufSizeStart = WEBRTC_SPL_MIN((aecpc->msInSndCardBuf *
|
||||||
|
aecpc->rate_factor * 3) / 40, kMaxBufSizeStart);
|
||||||
|
aecpc->checkBuffSize = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If |checkBuffSize| changed in the if-statement above.
|
||||||
|
if (!aecpc->checkBuffSize) {
|
||||||
|
// The system delay is now reasonably stable (or has been unstable
|
||||||
|
// for too long). When the far-end buffer is filled with
|
||||||
|
// approximately the same amount of data as reported by the system
|
||||||
|
// we end the startup phase.
|
||||||
|
int overhead_elements =
|
||||||
|
WebRtcAec_system_delay(aecpc->aec) / PART_LEN -
|
||||||
|
aecpc->bufSizeStart;
|
||||||
|
if (overhead_elements == 0) {
|
||||||
|
// Enable the AEC
|
||||||
|
aecpc->ECstartup = 0;
|
||||||
|
} else if (overhead_elements > 0) {
|
||||||
|
// TODO(bjornv): Do we need a check on how much we actually
|
||||||
|
// moved the read pointer? It should always be possible to move
|
||||||
|
// the pointer |overhead_elements| since we have only added data
|
||||||
|
// to the buffer and no delay compensation nor AEC processing
|
||||||
|
// has been done.
|
||||||
|
WebRtcAec_MoveFarReadPtr(aecpc->aec, overhead_elements);
|
||||||
|
|
||||||
|
// Enable the AEC
|
||||||
|
aecpc->ECstartup = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (ProcessNormal(aecpc, nearend, nearendH, out, outH, nrOfSamples,
|
// AEC is enabled.
|
||||||
msInSndCardBuf, skew) != 0) {
|
|
||||||
retVal = -1;
|
EstBufDelay(aecpc);
|
||||||
}
|
|
||||||
|
// Note that 1 frame is supported for NB and 2 frames for WB.
|
||||||
|
for (i = 0; i < nFrames; i++) {
|
||||||
|
// Call the AEC.
|
||||||
|
WebRtcAec_ProcessFrame(aecpc->aec,
|
||||||
|
&nearend[FRAME_LEN * i],
|
||||||
|
&nearendH[FRAME_LEN * i],
|
||||||
|
aecpc->knownDelay,
|
||||||
|
&out[FRAME_LEN * i],
|
||||||
|
&outH[FRAME_LEN * i]);
|
||||||
|
// TODO(bjornv): Re-structure such that we don't have to pass
|
||||||
|
// |aecpc->knownDelay| as input. Change name to something like
|
||||||
|
// |system_buffer_diff|.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WEBRTC_AEC_DEBUG_DUMP
|
#ifdef WEBRTC_AEC_DEBUG_DUMP
|
||||||
@ -430,6 +509,11 @@ int32_t WebRtcAec_Process(void *aecInst, const int16_t *nearend,
|
|||||||
|
|
||||||
int WebRtcAec_set_config(void* handle, AecConfig config) {
|
int WebRtcAec_set_config(void* handle, AecConfig config) {
|
||||||
aecpc_t* self = (aecpc_t*)handle;
|
aecpc_t* self = (aecpc_t*)handle;
|
||||||
|
|
||||||
|
if (handle == NULL ) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (self->initFlag != initCheck) {
|
if (self->initFlag != initCheck) {
|
||||||
self->lastError = AEC_UNINITIALIZED_ERROR;
|
self->lastError = AEC_UNINITIALIZED_ERROR;
|
||||||
return -1;
|
return -1;
|
||||||
@ -464,6 +548,10 @@ int WebRtcAec_set_config(void* handle, AecConfig config) {
|
|||||||
|
|
||||||
int WebRtcAec_get_echo_status(void* handle, int* status) {
|
int WebRtcAec_get_echo_status(void* handle, int* status) {
|
||||||
aecpc_t* self = (aecpc_t*)handle;
|
aecpc_t* self = (aecpc_t*)handle;
|
||||||
|
|
||||||
|
if (handle == NULL ) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
if (status == NULL ) {
|
if (status == NULL ) {
|
||||||
self->lastError = AEC_NULL_POINTER_ERROR;
|
self->lastError = AEC_NULL_POINTER_ERROR;
|
||||||
return -1;
|
return -1;
|
||||||
@ -577,6 +665,10 @@ int WebRtcAec_GetMetrics(void* handle, AecMetrics* metrics) {
|
|||||||
|
|
||||||
int WebRtcAec_GetDelayMetrics(void* handle, int* median, int* std) {
|
int WebRtcAec_GetDelayMetrics(void* handle, int* median, int* std) {
|
||||||
aecpc_t* self = handle;
|
aecpc_t* self = handle;
|
||||||
|
|
||||||
|
if (handle == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
if (median == NULL) {
|
if (median == NULL) {
|
||||||
self->lastError = AEC_NULL_POINTER_ERROR;
|
self->lastError = AEC_NULL_POINTER_ERROR;
|
||||||
return -1;
|
return -1;
|
||||||
@ -601,6 +693,11 @@ int WebRtcAec_GetDelayMetrics(void* handle, int* median, int* std) {
|
|||||||
int32_t WebRtcAec_get_error_code(void *aecInst)
|
int32_t WebRtcAec_get_error_code(void *aecInst)
|
||||||
{
|
{
|
||||||
aecpc_t *aecpc = aecInst;
|
aecpc_t *aecpc = aecInst;
|
||||||
|
|
||||||
|
if (aecpc == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
return aecpc->lastError;
|
return aecpc->lastError;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -611,220 +708,7 @@ AecCore* WebRtcAec_aec_core(void* handle) {
|
|||||||
return ((aecpc_t*) handle)->aec;
|
return ((aecpc_t*) handle)->aec;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ProcessNormal(aecpc_t *aecpc, const int16_t *nearend,
|
static int EstBufDelay(aecpc_t* aecpc) {
|
||||||
const int16_t *nearendH, int16_t *out, int16_t *outH,
|
|
||||||
int16_t nrOfSamples, int16_t msInSndCardBuf,
|
|
||||||
int32_t skew) {
|
|
||||||
int retVal = 0;
|
|
||||||
short i;
|
|
||||||
short nBlocks10ms;
|
|
||||||
short nFrames;
|
|
||||||
// Limit resampling to doubling/halving of signal
|
|
||||||
const float minSkewEst = -0.5f;
|
|
||||||
const float maxSkewEst = 1.0f;
|
|
||||||
|
|
||||||
msInSndCardBuf = msInSndCardBuf > kMaxTrustedDelayMs ?
|
|
||||||
kMaxTrustedDelayMs : msInSndCardBuf;
|
|
||||||
// TODO(andrew): we need to investigate if this +10 is really wanted.
|
|
||||||
msInSndCardBuf += 10;
|
|
||||||
aecpc->msInSndCardBuf = msInSndCardBuf;
|
|
||||||
|
|
||||||
if (aecpc->skewMode == kAecTrue) {
|
|
||||||
if (aecpc->skewFrCtr < 25) {
|
|
||||||
aecpc->skewFrCtr++;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
retVal = WebRtcAec_GetSkew(aecpc->resampler, skew, &aecpc->skew);
|
|
||||||
if (retVal == -1) {
|
|
||||||
aecpc->skew = 0;
|
|
||||||
aecpc->lastError = AEC_BAD_PARAMETER_WARNING;
|
|
||||||
}
|
|
||||||
|
|
||||||
aecpc->skew /= aecpc->sampFactor*nrOfSamples;
|
|
||||||
|
|
||||||
if (aecpc->skew < 1.0e-3 && aecpc->skew > -1.0e-3) {
|
|
||||||
aecpc->resample = kAecFalse;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
aecpc->resample = kAecTrue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (aecpc->skew < minSkewEst) {
|
|
||||||
aecpc->skew = minSkewEst;
|
|
||||||
}
|
|
||||||
else if (aecpc->skew > maxSkewEst) {
|
|
||||||
aecpc->skew = maxSkewEst;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef WEBRTC_AEC_DEBUG_DUMP
|
|
||||||
(void)fwrite(&aecpc->skew, sizeof(aecpc->skew), 1, aecpc->skewFile);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nFrames = nrOfSamples / FRAME_LEN;
|
|
||||||
nBlocks10ms = nFrames / aecpc->rate_factor;
|
|
||||||
|
|
||||||
if (aecpc->startup_phase) {
|
|
||||||
// Only needed if they don't already point to the same place.
|
|
||||||
if (nearend != out) {
|
|
||||||
memcpy(out, nearend, sizeof(short) * nrOfSamples);
|
|
||||||
}
|
|
||||||
if (nearendH != outH) {
|
|
||||||
memcpy(outH, nearendH, sizeof(short) * nrOfSamples);
|
|
||||||
}
|
|
||||||
|
|
||||||
// The AEC is in the start up mode
|
|
||||||
// AEC is disabled until the system delay is OK
|
|
||||||
|
|
||||||
// Mechanism to ensure that the system delay is reasonably stable.
|
|
||||||
if (aecpc->checkBuffSize) {
|
|
||||||
aecpc->checkBufSizeCtr++;
|
|
||||||
// Before we fill up the far-end buffer we require the system delay
|
|
||||||
// to be stable (+/-8 ms) compared to the first value. This
|
|
||||||
// comparison is made during the following 6 consecutive 10 ms
|
|
||||||
// blocks. If it seems to be stable then we start to fill up the
|
|
||||||
// far-end buffer.
|
|
||||||
if (aecpc->counter == 0) {
|
|
||||||
aecpc->firstVal = aecpc->msInSndCardBuf;
|
|
||||||
aecpc->sum = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (abs(aecpc->firstVal - aecpc->msInSndCardBuf) <
|
|
||||||
WEBRTC_SPL_MAX(0.2 * aecpc->msInSndCardBuf, sampMsNb)) {
|
|
||||||
aecpc->sum += aecpc->msInSndCardBuf;
|
|
||||||
aecpc->counter++;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
aecpc->counter = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (aecpc->counter * nBlocks10ms >= 6) {
|
|
||||||
// The far-end buffer size is determined in partitions of
|
|
||||||
// PART_LEN samples. Use 75% of the average value of the system
|
|
||||||
// delay as buffer size to start with.
|
|
||||||
aecpc->bufSizeStart = WEBRTC_SPL_MIN((3 * aecpc->sum *
|
|
||||||
aecpc->rate_factor * 8) / (4 * aecpc->counter * PART_LEN),
|
|
||||||
kMaxBufSizeStart);
|
|
||||||
// Buffer size has now been determined.
|
|
||||||
aecpc->checkBuffSize = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (aecpc->checkBufSizeCtr * nBlocks10ms > 50) {
|
|
||||||
// For really bad systems, don't disable the echo canceller for
|
|
||||||
// more than 0.5 sec.
|
|
||||||
aecpc->bufSizeStart = WEBRTC_SPL_MIN((aecpc->msInSndCardBuf *
|
|
||||||
aecpc->rate_factor * 3) / 40, kMaxBufSizeStart);
|
|
||||||
aecpc->checkBuffSize = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If |checkBuffSize| changed in the if-statement above.
|
|
||||||
if (!aecpc->checkBuffSize) {
|
|
||||||
// The system delay is now reasonably stable (or has been unstable
|
|
||||||
// for too long). When the far-end buffer is filled with
|
|
||||||
// approximately the same amount of data as reported by the system
|
|
||||||
// we end the startup phase.
|
|
||||||
int overhead_elements =
|
|
||||||
WebRtcAec_system_delay(aecpc->aec) / PART_LEN - aecpc->bufSizeStart;
|
|
||||||
if (overhead_elements == 0) {
|
|
||||||
// Enable the AEC
|
|
||||||
aecpc->startup_phase = 0;
|
|
||||||
} else if (overhead_elements > 0) {
|
|
||||||
// TODO(bjornv): Do we need a check on how much we actually
|
|
||||||
// moved the read pointer? It should always be possible to move
|
|
||||||
// the pointer |overhead_elements| since we have only added data
|
|
||||||
// to the buffer and no delay compensation nor AEC processing
|
|
||||||
// has been done.
|
|
||||||
WebRtcAec_MoveFarReadPtr(aecpc->aec, overhead_elements);
|
|
||||||
|
|
||||||
// Enable the AEC
|
|
||||||
aecpc->startup_phase = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// AEC is enabled.
|
|
||||||
EstBufDelayNormal(aecpc);
|
|
||||||
|
|
||||||
// Note that 1 frame is supported for NB and 2 frames for WB.
|
|
||||||
for (i = 0; i < nFrames; i++) {
|
|
||||||
// Call the AEC.
|
|
||||||
WebRtcAec_ProcessFrame(aecpc->aec,
|
|
||||||
&nearend[FRAME_LEN * i],
|
|
||||||
&nearendH[FRAME_LEN * i],
|
|
||||||
aecpc->knownDelay,
|
|
||||||
&out[FRAME_LEN * i],
|
|
||||||
&outH[FRAME_LEN * i]);
|
|
||||||
// TODO(bjornv): Re-structure such that we don't have to pass
|
|
||||||
// |aecpc->knownDelay| as input. Change name to something like
|
|
||||||
// |system_buffer_diff|.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ProcessExtended(aecpc_t* self, const int16_t* near,
|
|
||||||
const int16_t* near_high, int16_t* out, int16_t* out_high,
|
|
||||||
int16_t num_samples, int16_t reported_delay_ms, int32_t skew) {
|
|
||||||
int i;
|
|
||||||
const int num_frames = num_samples / FRAME_LEN;
|
|
||||||
#if defined(WEBRTC_UNTRUSTED_DELAY)
|
|
||||||
const int delay_diff_offset = kDelayDiffOffsetSamples;
|
|
||||||
reported_delay_ms = kFixedDelayMs;
|
|
||||||
#else
|
|
||||||
// This is the usual mode where we trust the reported system delay values.
|
|
||||||
const int delay_diff_offset = 0;
|
|
||||||
// Due to the longer filter, we no longer add 10 ms to the reported delay
|
|
||||||
// to reduce chance of non-causality. Instead we apply a minimum here to avoid
|
|
||||||
// issues with the read pointer jumping around needlessly.
|
|
||||||
reported_delay_ms = reported_delay_ms < kMinTrustedDelayMs ?
|
|
||||||
kMinTrustedDelayMs : reported_delay_ms;
|
|
||||||
// If the reported delay appears to be bogus, we attempt to recover by using
|
|
||||||
// the measured fixed delay values. We use >= here because higher layers
|
|
||||||
// may already clamp to this maximum value, and we would otherwise not
|
|
||||||
// detect it here.
|
|
||||||
reported_delay_ms = reported_delay_ms >= kMaxTrustedDelayMs ?
|
|
||||||
kFixedDelayMs : reported_delay_ms;
|
|
||||||
#endif
|
|
||||||
self->msInSndCardBuf = reported_delay_ms;
|
|
||||||
|
|
||||||
if (!self->farend_started) {
|
|
||||||
// Only needed if they don't already point to the same place.
|
|
||||||
if (near != out) {
|
|
||||||
memcpy(out, near, sizeof(short) * num_samples);
|
|
||||||
}
|
|
||||||
if (near_high != out_high) {
|
|
||||||
memcpy(out_high, near_high, sizeof(short) * num_samples);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (self->startup_phase) {
|
|
||||||
// In the extended mode, there isn't a startup "phase", just a special
|
|
||||||
// action on the first frame. In the trusted delay case, we'll take the
|
|
||||||
// current reported delay, unless it's less then our conservative
|
|
||||||
// measurement.
|
|
||||||
int startup_size_ms = reported_delay_ms < kFixedDelayMs ?
|
|
||||||
kFixedDelayMs : reported_delay_ms;
|
|
||||||
int overhead_elements = (WebRtcAec_system_delay(self->aec) -
|
|
||||||
startup_size_ms / 2 * self->rate_factor * 8) / PART_LEN;
|
|
||||||
WebRtcAec_MoveFarReadPtr(self->aec, overhead_elements);
|
|
||||||
self->startup_phase = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
EstBufDelayExtended(self);
|
|
||||||
|
|
||||||
for (i = 0; i < num_frames; ++i) {
|
|
||||||
// |delay_diff_offset| gives us the option to manually rewind the delay on
|
|
||||||
// very low delay platforms which can't be expressed purely through
|
|
||||||
// |reported_delay_ms|.
|
|
||||||
WebRtcAec_ProcessFrame(self->aec, &near[FRAME_LEN * i],
|
|
||||||
&near_high[FRAME_LEN * i], self->knownDelay + delay_diff_offset,
|
|
||||||
&out[FRAME_LEN * i], &out_high[FRAME_LEN * i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void EstBufDelayNormal(aecpc_t* aecpc) {
|
|
||||||
int nSampSndCard = aecpc->msInSndCardBuf * sampMsNb * aecpc->rate_factor;
|
int nSampSndCard = aecpc->msInSndCardBuf * sampMsNb * aecpc->rate_factor;
|
||||||
int current_delay = nSampSndCard - WebRtcAec_system_delay(aecpc->aec);
|
int current_delay = nSampSndCard - WebRtcAec_system_delay(aecpc->aec);
|
||||||
int delay_difference = 0;
|
int delay_difference = 0;
|
||||||
@ -848,11 +732,8 @@ static void EstBufDelayNormal(aecpc_t* aecpc) {
|
|||||||
current_delay += WebRtcAec_MoveFarReadPtr(aecpc->aec, 1) * PART_LEN;
|
current_delay += WebRtcAec_MoveFarReadPtr(aecpc->aec, 1) * PART_LEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We use -1 to signal an initialized state in the "extended" implementation;
|
|
||||||
// compensate for that.
|
|
||||||
aecpc->filtDelay = aecpc->filtDelay < 0 ? 0 : aecpc->filtDelay;
|
|
||||||
aecpc->filtDelay = WEBRTC_SPL_MAX(0, (short) (0.8 * aecpc->filtDelay +
|
aecpc->filtDelay = WEBRTC_SPL_MAX(0, (short) (0.8 * aecpc->filtDelay +
|
||||||
0.2 * current_delay));
|
0.2 * current_delay));
|
||||||
|
|
||||||
delay_difference = aecpc->filtDelay - aecpc->knownDelay;
|
delay_difference = aecpc->filtDelay - aecpc->knownDelay;
|
||||||
if (delay_difference > 224) {
|
if (delay_difference > 224) {
|
||||||
@ -875,58 +756,6 @@ static void EstBufDelayNormal(aecpc_t* aecpc) {
|
|||||||
if (aecpc->timeForDelayChange > 25) {
|
if (aecpc->timeForDelayChange > 25) {
|
||||||
aecpc->knownDelay = WEBRTC_SPL_MAX((int) aecpc->filtDelay - 160, 0);
|
aecpc->knownDelay = WEBRTC_SPL_MAX((int) aecpc->filtDelay - 160, 0);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
return 0;
|
||||||
static void EstBufDelayExtended(aecpc_t* self) {
|
|
||||||
int reported_delay = self->msInSndCardBuf * sampMsNb * self->rate_factor;
|
|
||||||
int current_delay = reported_delay - WebRtcAec_system_delay(self->aec);
|
|
||||||
int delay_difference = 0;
|
|
||||||
|
|
||||||
// Before we proceed with the delay estimate filtering we:
|
|
||||||
// 1) Compensate for the frame that will be read.
|
|
||||||
// 2) Compensate for drift resampling.
|
|
||||||
// 3) Compensate for non-causality if needed, since the estimated delay can't
|
|
||||||
// be negative.
|
|
||||||
|
|
||||||
// 1) Compensating for the frame(s) that will be read/processed.
|
|
||||||
current_delay += FRAME_LEN * self->rate_factor;
|
|
||||||
|
|
||||||
// 2) Account for resampling frame delay.
|
|
||||||
if (self->skewMode == kAecTrue && self->resample == kAecTrue) {
|
|
||||||
current_delay -= kResamplingDelay;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3) Compensate for non-causality, if needed, by flushing two blocks.
|
|
||||||
if (current_delay < PART_LEN) {
|
|
||||||
current_delay += WebRtcAec_MoveFarReadPtr(self->aec, 2) * PART_LEN;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self->filtDelay == -1) {
|
|
||||||
self->filtDelay = WEBRTC_SPL_MAX(0, 0.5 * current_delay);
|
|
||||||
} else {
|
|
||||||
self->filtDelay = WEBRTC_SPL_MAX(0, (short) (0.95 * self->filtDelay +
|
|
||||||
0.05 * current_delay));
|
|
||||||
}
|
|
||||||
|
|
||||||
delay_difference = self->filtDelay - self->knownDelay;
|
|
||||||
if (delay_difference > 384) {
|
|
||||||
if (self->lastDelayDiff < 128) {
|
|
||||||
self->timeForDelayChange = 0;
|
|
||||||
} else {
|
|
||||||
self->timeForDelayChange++;
|
|
||||||
}
|
|
||||||
} else if (delay_difference < 128 && self->knownDelay > 0) {
|
|
||||||
if (self->lastDelayDiff > 384) {
|
|
||||||
self->timeForDelayChange = 0;
|
|
||||||
} else {
|
|
||||||
self->timeForDelayChange++;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self->timeForDelayChange = 0;
|
|
||||||
}
|
|
||||||
self->lastDelayDiff = delay_difference;
|
|
||||||
|
|
||||||
if (self->timeForDelayChange > 25) {
|
|
||||||
self->knownDelay = WEBRTC_SPL_MAX((int) self->filtDelay - 256, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,8 @@ typedef struct {
|
|||||||
int splitSampFreq;
|
int splitSampFreq;
|
||||||
int scSampFreq;
|
int scSampFreq;
|
||||||
float sampFactor; // scSampRate / sampFreq
|
float sampFactor; // scSampRate / sampFreq
|
||||||
|
short autoOnOff;
|
||||||
|
short activity;
|
||||||
short skewMode;
|
short skewMode;
|
||||||
int bufSizeStart;
|
int bufSizeStart;
|
||||||
int knownDelay;
|
int knownDelay;
|
||||||
@ -37,7 +39,7 @@ typedef struct {
|
|||||||
short msInSndCardBuf;
|
short msInSndCardBuf;
|
||||||
short filtDelay; // Filtered delay estimate.
|
short filtDelay; // Filtered delay estimate.
|
||||||
int timeForDelayChange;
|
int timeForDelayChange;
|
||||||
int startup_phase;
|
int ECstartup;
|
||||||
int checkBuffSize;
|
int checkBuffSize;
|
||||||
short lastDelayDiff;
|
short lastDelayDiff;
|
||||||
|
|
||||||
@ -60,8 +62,6 @@ typedef struct {
|
|||||||
|
|
||||||
int lastError;
|
int lastError;
|
||||||
|
|
||||||
int farend_started;
|
|
||||||
|
|
||||||
AecCore* aec;
|
AecCore* aec;
|
||||||
} aecpc_t;
|
} aecpc_t;
|
||||||
|
|
||||||
|
@ -128,7 +128,7 @@ void SystemDelayTest::RunStableStartup() {
|
|||||||
for (; process_time_ms < kStableConvergenceMs; process_time_ms += 10) {
|
for (; process_time_ms < kStableConvergenceMs; process_time_ms += 10) {
|
||||||
RenderAndCapture(kDeviceBufMs);
|
RenderAndCapture(kDeviceBufMs);
|
||||||
buffer_size += samples_per_frame_;
|
buffer_size += samples_per_frame_;
|
||||||
if (self_->startup_phase == 0) {
|
if (self_->ECstartup == 0) {
|
||||||
// We have left the startup phase.
|
// We have left the startup phase.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -222,7 +222,7 @@ TEST_F(SystemDelayTest, CorrectDelayAfterUnstableStartup) {
|
|||||||
RenderAndCapture(reported_delay_ms);
|
RenderAndCapture(reported_delay_ms);
|
||||||
buffer_size += samples_per_frame_;
|
buffer_size += samples_per_frame_;
|
||||||
buffer_offset_ms = -buffer_offset_ms;
|
buffer_offset_ms = -buffer_offset_ms;
|
||||||
if (self_->startup_phase == 0) {
|
if (self_->ECstartup == 0) {
|
||||||
// We have left the startup phase.
|
// We have left the startup phase.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -268,7 +268,7 @@ TEST_F(SystemDelayTest, CorrectDelayAfterStableBufferBuildUp) {
|
|||||||
for (; process_time_ms <= kMaxConvergenceMs; process_time_ms += 10) {
|
for (; process_time_ms <= kMaxConvergenceMs; process_time_ms += 10) {
|
||||||
RenderAndCapture(kDeviceBufMs);
|
RenderAndCapture(kDeviceBufMs);
|
||||||
buffer_size += samples_per_frame_;
|
buffer_size += samples_per_frame_;
|
||||||
if (self_->startup_phase == 0) {
|
if (self_->ECstartup == 0) {
|
||||||
// We have left the startup phase.
|
// We have left the startup phase.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -13,14 +13,12 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
#include "webrtc/modules/audio_processing/aec/aec_core.h"
|
|
||||||
}
|
|
||||||
#include "webrtc/modules/audio_processing/aec/include/echo_cancellation.h"
|
|
||||||
#include "webrtc/modules/audio_processing/audio_buffer.h"
|
#include "webrtc/modules/audio_processing/audio_buffer.h"
|
||||||
#include "webrtc/modules/audio_processing/audio_processing_impl.h"
|
#include "webrtc/modules/audio_processing/audio_processing_impl.h"
|
||||||
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
|
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
|
||||||
|
|
||||||
|
#include "webrtc/modules/audio_processing/aec/include/echo_cancellation.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
typedef void Handle;
|
typedef void Handle;
|
||||||
@ -71,8 +69,7 @@ EchoCancellationImpl::EchoCancellationImpl(const AudioProcessingImpl* apm)
|
|||||||
stream_drift_samples_(0),
|
stream_drift_samples_(0),
|
||||||
was_stream_drift_set_(false),
|
was_stream_drift_set_(false),
|
||||||
stream_has_echo_(false),
|
stream_has_echo_(false),
|
||||||
delay_logging_enabled_(false),
|
delay_logging_enabled_(false) {}
|
||||||
delay_correction_enabled_(false) {}
|
|
||||||
|
|
||||||
EchoCancellationImpl::~EchoCancellationImpl() {}
|
EchoCancellationImpl::~EchoCancellationImpl() {}
|
||||||
|
|
||||||
@ -341,11 +338,6 @@ int EchoCancellationImpl::Initialize() {
|
|||||||
return apm_->kNoError;
|
return apm_->kNoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EchoCancellationImpl::SetExtraOptions(const Config& config) {
|
|
||||||
delay_correction_enabled_ = config.Get<DelayCorrection>().enabled;
|
|
||||||
Configure();
|
|
||||||
}
|
|
||||||
|
|
||||||
void* EchoCancellationImpl::CreateHandle() const {
|
void* EchoCancellationImpl::CreateHandle() const {
|
||||||
Handle* handle = NULL;
|
Handle* handle = NULL;
|
||||||
if (WebRtcAec_Create(&handle) != apm_->kNoError) {
|
if (WebRtcAec_Create(&handle) != apm_->kNoError) {
|
||||||
@ -377,8 +369,6 @@ int EchoCancellationImpl::ConfigureHandle(void* handle) const {
|
|||||||
config.skewMode = drift_compensation_enabled_;
|
config.skewMode = drift_compensation_enabled_;
|
||||||
config.delay_logging = delay_logging_enabled_;
|
config.delay_logging = delay_logging_enabled_;
|
||||||
|
|
||||||
WebRtcAec_enable_delay_correction(WebRtcAec_aec_core(
|
|
||||||
static_cast<Handle*>(handle)), delay_correction_enabled_ ? 1 : 0);
|
|
||||||
return WebRtcAec_set_config(static_cast<Handle*>(handle), config);
|
return WebRtcAec_set_config(static_cast<Handle*>(handle), config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,30 +15,6 @@
|
|||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
// Use to enable the delay correction feature. This now engages an extended
|
|
||||||
// filter mode in the AEC, along with robustness measures around the reported
|
|
||||||
// system delays. It comes with a significant increase in AEC complexity, but is
|
|
||||||
// much more robust to unreliable reported delays.
|
|
||||||
//
|
|
||||||
// Detailed changes to the algorithm:
|
|
||||||
// - The filter length is changed from 48 to 128 ms. This comes with tuning of
|
|
||||||
// several parameters: i) filter adaptation stepsize and error threshold;
|
|
||||||
// ii) non-linear processing smoothing and overdrive.
|
|
||||||
// - Option to ignore the reported delays on platforms which we deem
|
|
||||||
// sufficiently unreliable. See WEBRTC_UNTRUSTED_DELAY in echo_cancellation.c.
|
|
||||||
// - Faster startup times by removing the excessive "startup phase" processing
|
|
||||||
// of reported delays.
|
|
||||||
// - Much more conservative adjustments to the far-end read pointer. We smooth
|
|
||||||
// the delay difference more heavily, and back off from the difference more.
|
|
||||||
// Adjustments force a readaptation of the filter, so they should be avoided
|
|
||||||
// except when really necessary.
|
|
||||||
struct DelayCorrection {
|
|
||||||
DelayCorrection() : enabled(false) {}
|
|
||||||
DelayCorrection(bool enabled) : enabled(enabled) {}
|
|
||||||
|
|
||||||
bool enabled;
|
|
||||||
};
|
|
||||||
|
|
||||||
class AudioProcessingImpl;
|
class AudioProcessingImpl;
|
||||||
class AudioBuffer;
|
class AudioBuffer;
|
||||||
|
|
||||||
@ -58,7 +34,6 @@ class EchoCancellationImpl : public EchoCancellationImplWrapper {
|
|||||||
|
|
||||||
// ProcessingComponent implementation.
|
// ProcessingComponent implementation.
|
||||||
virtual int Initialize() OVERRIDE;
|
virtual int Initialize() OVERRIDE;
|
||||||
virtual void SetExtraOptions(const Config& config) OVERRIDE;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// EchoCancellation implementation.
|
// EchoCancellation implementation.
|
||||||
@ -95,7 +70,6 @@ class EchoCancellationImpl : public EchoCancellationImplWrapper {
|
|||||||
bool was_stream_drift_set_;
|
bool was_stream_drift_set_;
|
||||||
bool stream_has_echo_;
|
bool stream_has_echo_;
|
||||||
bool delay_logging_enabled_;
|
bool delay_logging_enabled_;
|
||||||
bool delay_correction_enabled_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
@ -1,51 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by a BSD-style license
|
|
||||||
* that can be found in the LICENSE file in the root of the source
|
|
||||||
* tree. An additional intellectual property rights grant can be found
|
|
||||||
* in the file PATENTS. All contributing project authors may
|
|
||||||
* be found in the AUTHORS file in the root of the source tree.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "testing/gtest/include/gtest/gtest.h"
|
|
||||||
extern "C" {
|
|
||||||
#include "webrtc/modules/audio_processing/aec/aec_core.h"
|
|
||||||
}
|
|
||||||
#include "webrtc/modules/audio_processing/echo_cancellation_impl.h"
|
|
||||||
#include "webrtc/modules/audio_processing/include/audio_processing.h"
|
|
||||||
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
|
|
||||||
|
|
||||||
namespace webrtc {
|
|
||||||
|
|
||||||
TEST(EchoCancellationInternalTest, DelayCorrection) {
|
|
||||||
scoped_ptr<AudioProcessing> ap(AudioProcessing::Create(0));
|
|
||||||
EXPECT_TRUE(ap->echo_cancellation()->aec_core() == NULL);
|
|
||||||
|
|
||||||
EXPECT_EQ(ap->kNoError, ap->echo_cancellation()->Enable(true));
|
|
||||||
EXPECT_TRUE(ap->echo_cancellation()->is_enabled());
|
|
||||||
|
|
||||||
AecCore* aec_core = ap->echo_cancellation()->aec_core();
|
|
||||||
ASSERT_TRUE(aec_core != NULL);
|
|
||||||
// Disabled by default.
|
|
||||||
EXPECT_EQ(0, WebRtcAec_delay_correction_enabled(aec_core));
|
|
||||||
|
|
||||||
Config config;
|
|
||||||
config.Set<DelayCorrection>(new DelayCorrection(true));
|
|
||||||
ap->SetExtraOptions(config);
|
|
||||||
EXPECT_EQ(1, WebRtcAec_delay_correction_enabled(aec_core));
|
|
||||||
|
|
||||||
// Retains setting after initialization.
|
|
||||||
EXPECT_EQ(ap->kNoError, ap->Initialize());
|
|
||||||
EXPECT_EQ(1, WebRtcAec_delay_correction_enabled(aec_core));
|
|
||||||
|
|
||||||
config.Set<DelayCorrection>(new DelayCorrection(false));
|
|
||||||
ap->SetExtraOptions(config);
|
|
||||||
EXPECT_EQ(0, WebRtcAec_delay_correction_enabled(aec_core));
|
|
||||||
|
|
||||||
// Retains setting after initialization.
|
|
||||||
EXPECT_EQ(ap->kNoError, ap->Initialize());
|
|
||||||
EXPECT_EQ(0, WebRtcAec_delay_correction_enabled(aec_core));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace webrtc
|
|
@ -145,7 +145,6 @@
|
|||||||
'audio_coding/neteq4/mock/mock_payload_splitter.h',
|
'audio_coding/neteq4/mock/mock_payload_splitter.h',
|
||||||
'audio_processing/aec/system_delay_unittest.cc',
|
'audio_processing/aec/system_delay_unittest.cc',
|
||||||
'audio_processing/aec/echo_cancellation_unittest.cc',
|
'audio_processing/aec/echo_cancellation_unittest.cc',
|
||||||
'audio_processing/echo_cancellation_impl_unittest.cc',
|
|
||||||
'audio_processing/test/audio_processing_unittest.cc',
|
'audio_processing/test/audio_processing_unittest.cc',
|
||||||
'audio_processing/utility/delay_estimator_unittest.cc',
|
'audio_processing/utility/delay_estimator_unittest.cc',
|
||||||
'audio_processing/utility/ring_buffer_unittest.cc',
|
'audio_processing/utility/ring_buffer_unittest.cc',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user