| Index: webrtc/modules/audio_processing/aec/aec_core.c
|
| diff --git a/webrtc/modules/audio_processing/aec/aec_core.c b/webrtc/modules/audio_processing/aec/aec_core.c
|
| index 91b55b6acd3159ee64cf3b79036d09c43aea8787..da5c26b68395eab344f7f5de650e3a394d478347 100644
|
| --- a/webrtc/modules/audio_processing/aec/aec_core.c
|
| +++ b/webrtc/modules/audio_processing/aec/aec_core.c
|
| @@ -151,30 +151,31 @@ static int CmpFloat(const void* a, const void* b) {
|
| return (*da > *db) - (*da < *db);
|
| }
|
|
|
| -static void FilterFar(int num_partitions,
|
| - int x_fft_buffer_block_pos,
|
| - float x_fft_buffer[2][kExtendedNumPartitions * PART_LEN1],
|
| - float h_fft_buffer[2][kExtendedNumPartitions * PART_LEN1],
|
| - float y_fft[2][PART_LEN1]) {
|
| +static void FilterFar(
|
| + int num_partitions,
|
| + int x_fft_buf_block_pos,
|
| + float x_fft_buf[2][kExtendedNumPartitions * PART_LEN1],
|
| + float h_fft_buf[2][kExtendedNumPartitions * PART_LEN1],
|
| + float y_fft[2][PART_LEN1]) {
|
| int i;
|
| for (i = 0; i < num_partitions; i++) {
|
| int j;
|
| - int x_pos = (i + x_fft_buffer_block_pos) * PART_LEN1;
|
| + int xPos = (i + x_fft_buf_block_pos) * PART_LEN1;
|
| int pos = i * PART_LEN1;
|
| - // Check for wrapped buffer.
|
| - if (i + x_fft_buffer_block_pos >= num_partitions) {
|
| - x_pos -= num_partitions * (PART_LEN1);
|
| + // Check for wrap
|
| + if (i + x_fft_buf_block_pos >= num_partitions) {
|
| + xPos -= num_partitions * (PART_LEN1);
|
| }
|
|
|
| for (j = 0; j < PART_LEN1; j++) {
|
| - y_fft[0][j] += MulRe(x_fft_buffer[0][x_pos + j],
|
| - x_fft_buffer[1][x_pos + j],
|
| - h_fft_buffer[0][pos + j],
|
| - h_fft_buffer[1][pos + j]);
|
| - y_fft[1][j] += MulIm(x_fft_buffer[0][x_pos + j],
|
| - x_fft_buffer[1][x_pos + j],
|
| - h_fft_buffer[0][pos + j],
|
| - h_fft_buffer[1][pos + j]);
|
| + y_fft[0][j] += MulRe(x_fft_buf[0][xPos + j],
|
| + x_fft_buf[1][xPos + j],
|
| + h_fft_buf[0][pos + j],
|
| + h_fft_buf[1][pos + j]);
|
| + y_fft[1][j] += MulIm(x_fft_buf[0][xPos + j],
|
| + x_fft_buf[1][xPos + j],
|
| + h_fft_buf[0][pos + j],
|
| + h_fft_buf[1][pos + j]);
|
| }
|
| }
|
| }
|
| @@ -182,7 +183,7 @@ static void FilterFar(int num_partitions,
|
| static void ScaleErrorSignal(int extended_filter_enabled,
|
| float normal_mu,
|
| float normal_error_threshold,
|
| - float *x_pow,
|
| + float x_pow[PART_LEN1],
|
| float ef[2][PART_LEN1]) {
|
| const float mu = extended_filter_enabled ? kExtendedMu : normal_mu;
|
| const float error_threshold = extended_filter_enabled
|
| @@ -207,59 +208,40 @@ static void ScaleErrorSignal(int extended_filter_enabled,
|
| }
|
| }
|
|
|
| -// Time-unconstrined filter adaptation.
|
| -// TODO(andrew): consider for a low-complexity mode.
|
| -// static void FilterAdaptationUnconstrained(AecCore* aec, float *fft,
|
| -// float ef[2][PART_LEN1]) {
|
| -// int i, j;
|
| -// for (i = 0; i < aec->num_partitions; i++) {
|
| -// int xPos = (i + aec->xfBufBlockPos)*(PART_LEN1);
|
| -// int pos;
|
| -// // Check for wrap
|
| -// if (i + aec->xfBufBlockPos >= aec->num_partitions) {
|
| -// xPos -= aec->num_partitions * PART_LEN1;
|
| -// }
|
| -//
|
| -// pos = i * PART_LEN1;
|
| -//
|
| -// for (j = 0; j < PART_LEN1; j++) {
|
| -// aec->wfBuf[0][pos + j] += MulRe(aec->xfBuf[0][xPos + j],
|
| -// -aec->xfBuf[1][xPos + j],
|
| -// ef[0][j], ef[1][j]);
|
| -// aec->wfBuf[1][pos + j] += MulIm(aec->xfBuf[0][xPos + j],
|
| -// -aec->xfBuf[1][xPos + j],
|
| -// ef[0][j], ef[1][j]);
|
| -// }
|
| -// }
|
| -//}
|
| -
|
| -static void FilterAdaptation(AecCore* aec, float* fft, float ef[2][PART_LEN1]) {
|
| +
|
| +static void FilterAdaptation(
|
| + int num_partitions,
|
| + int x_fft_buf_block_pos,
|
| + float x_fft_buf[2][kExtendedNumPartitions * PART_LEN1],
|
| + float e_fft[2][PART_LEN1],
|
| + float h_fft_buf[2][kExtendedNumPartitions * PART_LEN1]) {
|
| int i, j;
|
| - for (i = 0; i < aec->num_partitions; i++) {
|
| - int xPos = (i + aec->xfBufBlockPos) * (PART_LEN1);
|
| + float fft[PART_LEN2];
|
| + for (i = 0; i < num_partitions; i++) {
|
| + int xPos = (i + x_fft_buf_block_pos) * (PART_LEN1);
|
| int pos;
|
| // Check for wrap
|
| - if (i + aec->xfBufBlockPos >= aec->num_partitions) {
|
| - xPos -= aec->num_partitions * PART_LEN1;
|
| + if (i + x_fft_buf_block_pos >= num_partitions) {
|
| + xPos -= num_partitions * PART_LEN1;
|
| }
|
|
|
| pos = i * PART_LEN1;
|
|
|
| for (j = 0; j < PART_LEN; j++) {
|
|
|
| - fft[2 * j] = MulRe(aec->xfBuf[0][xPos + j],
|
| - -aec->xfBuf[1][xPos + j],
|
| - ef[0][j],
|
| - ef[1][j]);
|
| - fft[2 * j + 1] = MulIm(aec->xfBuf[0][xPos + j],
|
| - -aec->xfBuf[1][xPos + j],
|
| - ef[0][j],
|
| - ef[1][j]);
|
| + fft[2 * j] = MulRe(x_fft_buf[0][xPos + j],
|
| + -x_fft_buf[1][xPos + j],
|
| + e_fft[0][j],
|
| + e_fft[1][j]);
|
| + fft[2 * j + 1] = MulIm(x_fft_buf[0][xPos + j],
|
| + -x_fft_buf[1][xPos + j],
|
| + e_fft[0][j],
|
| + e_fft[1][j]);
|
| }
|
| - fft[1] = MulRe(aec->xfBuf[0][xPos + PART_LEN],
|
| - -aec->xfBuf[1][xPos + PART_LEN],
|
| - ef[0][PART_LEN],
|
| - ef[1][PART_LEN]);
|
| + fft[1] = MulRe(x_fft_buf[0][xPos + PART_LEN],
|
| + -x_fft_buf[1][xPos + PART_LEN],
|
| + e_fft[0][PART_LEN],
|
| + e_fft[1][PART_LEN]);
|
|
|
| aec_rdft_inverse_128(fft);
|
| memset(fft + PART_LEN, 0, sizeof(float) * PART_LEN);
|
| @@ -273,12 +255,12 @@ static void FilterAdaptation(AecCore* aec, float* fft, float ef[2][PART_LEN1]) {
|
| }
|
| aec_rdft_forward_128(fft);
|
|
|
| - aec->wfBuf[0][pos] += fft[0];
|
| - aec->wfBuf[0][pos + PART_LEN] += fft[1];
|
| + h_fft_buf[0][pos] += fft[0];
|
| + h_fft_buf[0][pos + PART_LEN] += fft[1];
|
|
|
| for (j = 1; j < PART_LEN; j++) {
|
| - aec->wfBuf[0][pos + j] += fft[2 * j];
|
| - aec->wfBuf[1][pos + j] += fft[2 * j + 1];
|
| + h_fft_buf[0][pos + j] += fft[2 * j];
|
| + h_fft_buf[1][pos + j] += fft[2 * j + 1];
|
| }
|
| }
|
| }
|
| @@ -845,34 +827,26 @@ static void UpdateDelayMetrics(AecCore* self) {
|
| return;
|
| }
|
|
|
| -static void FrequencyToTime(float freq_data[2][PART_LEN1],
|
| - float time_data[PART_LEN2]) {
|
| +static void InverseFft(float freq_data[2][PART_LEN1],
|
| + float time_data[PART_LEN2]) {
|
| int i;
|
| - time_data[0] = freq_data[0][0];
|
| - time_data[1] = freq_data[0][PART_LEN];
|
| + const float scale = 1.0f / PART_LEN2;
|
| + time_data[0] = freq_data[0][0] * scale;
|
| + time_data[1] = freq_data[0][PART_LEN] * scale;
|
| for (i = 1; i < PART_LEN; i++) {
|
| - time_data[2 * i] = freq_data[0][i];
|
| - time_data[2 * i + 1] = freq_data[1][i];
|
| + time_data[2 * i] = freq_data[0][i] * scale;
|
| + time_data[2 * i + 1] = freq_data[1][i] * scale;
|
| }
|
| aec_rdft_inverse_128(time_data);
|
| }
|
|
|
|
|
| -static void TimeToFrequency(float time_data[PART_LEN2],
|
| - float freq_data[2][PART_LEN1],
|
| - int window) {
|
| - int i = 0;
|
| -
|
| - // TODO(bjornv): Should we have a different function/wrapper for windowed FFT?
|
| - if (window) {
|
| - for (i = 0; i < PART_LEN; i++) {
|
| - time_data[i] *= WebRtcAec_sqrtHanning[i];
|
| - time_data[PART_LEN + i] *= WebRtcAec_sqrtHanning[PART_LEN - i];
|
| - }
|
| - }
|
| -
|
| +static void Fft(float time_data[PART_LEN2],
|
| + float freq_data[2][PART_LEN1]) {
|
| + int i;
|
| aec_rdft_forward_128(time_data);
|
| - // Reorder.
|
| +
|
| + // Reorder fft output data.
|
| freq_data[1][0] = 0;
|
| freq_data[1][PART_LEN] = 0;
|
| freq_data[0][0] = time_data[0];
|
| @@ -963,61 +937,76 @@ static int SignalBasedDelayCorrection(AecCore* self) {
|
| return delay_correction;
|
| }
|
|
|
| -static void EchoSubtraction(AecCore* aec,
|
| - float* nearend_ptr) {
|
| - float yf[2][PART_LEN1];
|
| - float fft[PART_LEN2];
|
| - float y[PART_LEN];
|
| +static void EchoSubtraction(
|
| + AecCore* aec,
|
| + int num_partitions,
|
| + int x_fft_buf_block_pos,
|
| + int metrics_mode,
|
| + int extended_filter_enabled,
|
| + float normal_mu,
|
| + float normal_error_threshold,
|
| + float x_fft_buf[2][kExtendedNumPartitions * PART_LEN1],
|
| + float* const y,
|
| + float x_pow[PART_LEN1],
|
| + float h_fft_buf[2][kExtendedNumPartitions * PART_LEN1],
|
| + PowerLevel* linout_level,
|
| + float echo_subtractor_output[PART_LEN]) {
|
| + float s_fft[2][PART_LEN1];
|
| + float e_extended[PART_LEN2];
|
| + float s_extended[PART_LEN2];
|
| + float *s;
|
| float e[PART_LEN];
|
| - float ef[2][PART_LEN1];
|
| - float scale;
|
| + float e_fft[2][PART_LEN1];
|
| int i;
|
| - memset(yf, 0, sizeof(yf));
|
| -
|
| - // Produce frequency domain echo estimate.
|
| - WebRtcAec_FilterFar(aec->num_partitions,
|
| - aec->xfBufBlockPos,
|
| - aec->xfBuf,
|
| - aec->wfBuf,
|
| - yf);
|
| -
|
| - // Inverse fft to obtain echo estimate and error.
|
| - FrequencyToTime(yf, fft);
|
| + memset(s_fft, 0, sizeof(s_fft));
|
| +
|
| + // Produce echo estimate s_fft.
|
| + WebRtcAec_FilterFar(num_partitions,
|
| + x_fft_buf_block_pos,
|
| + x_fft_buf,
|
| + h_fft_buf,
|
| + s_fft);
|
| +
|
| + // Compute the time-domain echo estimate s.
|
| + InverseFft(s_fft, s_extended);
|
| + s = &s_extended[PART_LEN];
|
| + for (i = 0; i < PART_LEN; ++i) {
|
| + s[i] *= 2.0f;
|
| + }
|
|
|
| - // Extract the output signal and compute the time-domain error.
|
| - scale = 2.0f / PART_LEN2;
|
| + // Compute the time-domain echo prediction error.
|
| for (i = 0; i < PART_LEN; ++i) {
|
| - y[i] = fft[PART_LEN + i] * scale; // fft scaling.
|
| - e[i] = nearend_ptr[i] - y[i];
|
| + e[i] = y[i] - s[i];
|
| }
|
|
|
| - // Error fft
|
| - memcpy(aec->eBuf + PART_LEN, e, sizeof(float) * PART_LEN);
|
| - memset(fft, 0, sizeof(float) * PART_LEN);
|
| - memcpy(fft + PART_LEN, e, sizeof(float) * PART_LEN);
|
| - TimeToFrequency(fft, ef, 0);
|
| + // Compute the frequency domain echo prediction error.
|
| + memset(e_extended, 0, sizeof(float) * PART_LEN);
|
| + memcpy(e_extended + PART_LEN, e, sizeof(float) * PART_LEN);
|
| + Fft(e_extended, e_fft);
|
|
|
| RTC_AEC_DEBUG_RAW_WRITE(aec->e_fft_file,
|
| - &ef[0][0],
|
| - sizeof(ef[0][0]) * PART_LEN1 * 2);
|
| + &e_fft[0][0],
|
| + sizeof(e_fft[0][0]) * PART_LEN1 * 2);
|
|
|
| - if (aec->metricsMode == 1) {
|
| + if (metrics_mode == 1) {
|
| // Note that the first PART_LEN samples in fft (before transformation) are
|
| // zero. Hence, the scaling by two in UpdateLevel() should not be
|
| // performed. That scaling is taken care of in UpdateMetrics() instead.
|
| - UpdateLevel(&aec->linoutlevel, ef);
|
| + UpdateLevel(linout_level, e_fft);
|
| }
|
|
|
| // Scale error signal inversely with far power.
|
| - WebRtcAec_ScaleErrorSignal(aec->extended_filter_enabled,
|
| - aec->normal_mu,
|
| - aec->normal_error_threshold,
|
| - aec->xPow,
|
| - ef);
|
| - WebRtcAec_FilterAdaptation(aec, fft, ef);
|
| -
|
| -
|
| - RTC_AEC_DEBUG_WAV_WRITE(aec->outLinearFile, e, PART_LEN);
|
| + WebRtcAec_ScaleErrorSignal(extended_filter_enabled,
|
| + normal_mu,
|
| + normal_error_threshold,
|
| + x_pow,
|
| + e_fft);
|
| + WebRtcAec_FilterAdaptation(num_partitions,
|
| + x_fft_buf_block_pos,
|
| + x_fft_buf,
|
| + e_fft,
|
| + h_fft_buf);
|
| + memcpy(echo_subtractor_output, e, sizeof(float) * PART_LEN);
|
| }
|
|
|
|
|
| @@ -1274,6 +1263,7 @@ static void ProcessBlock(AecCore* aec) {
|
| const float gInitNoise[2] = {0.999f, 0.001f};
|
|
|
| float nearend[PART_LEN];
|
| + float echo_subtractor_output[PART_LEN];
|
| float* nearend_ptr = NULL;
|
| float output[PART_LEN];
|
| float outputH[NUM_HIGH_BANDS_MAX][PART_LEN];
|
| @@ -1313,7 +1303,7 @@ static void ProcessBlock(AecCore* aec) {
|
|
|
| // Near fft
|
| memcpy(fft, aec->dBuf, sizeof(float) * PART_LEN2);
|
| - TimeToFrequency(fft, df, 0);
|
| + Fft(fft, df);
|
|
|
| // Power smoothing
|
| for (i = 0; i < PART_LEN1; i++) {
|
| @@ -1392,9 +1382,26 @@ static void ProcessBlock(AecCore* aec) {
|
| sizeof(float) * PART_LEN1);
|
|
|
| // Perform echo subtraction.
|
| - EchoSubtraction(aec, nearend_ptr);
|
| + EchoSubtraction(aec,
|
| + aec->num_partitions,
|
| + aec->xfBufBlockPos,
|
| + aec->metricsMode,
|
| + aec->extended_filter_enabled,
|
| + aec->normal_mu,
|
| + aec->normal_error_threshold,
|
| + aec->xfBuf,
|
| + nearend_ptr,
|
| + aec->xPow,
|
| + aec->wfBuf,
|
| + &aec->linoutlevel,
|
| + echo_subtractor_output);
|
| +
|
| + RTC_AEC_DEBUG_WAV_WRITE(aec->outLinearFile, echo_subtractor_output, PART_LEN);
|
|
|
| // Perform echo suppression.
|
| + memcpy(aec->eBuf + PART_LEN,
|
| + echo_subtractor_output,
|
| + sizeof(float) * PART_LEN);
|
| EchoSuppression(aec, output, outputH_ptr);
|
|
|
| if (aec->metricsMode == 1) {
|
| @@ -1737,12 +1744,12 @@ void WebRtcAec_BufferFarendPartition(AecCore* aec, const float* farend) {
|
| }
|
| // Convert far-end partition to the frequency domain without windowing.
|
| memcpy(fft, farend, sizeof(float) * PART_LEN2);
|
| - TimeToFrequency(fft, xf, 0);
|
| + Fft(fft, xf);
|
| WebRtc_WriteBuffer(aec->far_buf, &xf[0][0], 1);
|
|
|
| // Convert far-end partition to the frequency domain with windowing.
|
| - memcpy(fft, farend, sizeof(float) * PART_LEN2);
|
| - TimeToFrequency(fft, xf, 1);
|
| + WindowData(fft, farend);
|
| + Fft(fft, xf);
|
| WebRtc_WriteBuffer(aec->far_buf_windowed, &xf[0][0], 1);
|
| }
|
|
|
|
|