diff --git a/src/modules/audio_processing/aec/aec_core.c b/src/modules/audio_processing/aec/aec_core.c index ee85c2e8a1..b47891f3b1 100644 --- a/src/modules/audio_processing/aec/aec_core.c +++ b/src/modules/audio_processing/aec/aec_core.c @@ -14,7 +14,9 @@ #include "aec_core.h" +#include #include +#include // size_t #include #include @@ -22,6 +24,10 @@ #include "delay_estimator_wrapper.h" #include "ring_buffer.h" #include "system_wrappers/interface/cpu_features_wrapper.h" +#include "typedefs.h" + +// Buffer size (samples) +static const size_t kBufSizePartitions = 250; // 1 second of audio in 16 kHz. // Noise suppression static const int converged = 250; @@ -99,12 +105,7 @@ const float WebRtcAec_overDriveCurve[65] = { }; // "Private" function prototypes. -static void ProcessBlock(aec_t *aec, const short *farend, - const short *nearend, const short *nearendH, - short *out, short *outH); - -static void BufferFar(aec_t *aec, const short *farend, int farLen); -static void FetchFar(aec_t *aec, short *farend, int farLen, int knownDelay); +static void ProcessBlock(aec_t* aec); static void NonLinearProcessing(aec_t *aec, short *output, short *outputH); @@ -117,8 +118,13 @@ static void ComfortNoise(aec_t *aec, float efw[2][PART_LEN1], static void WebRtcAec_InitLevel(power_level_t *level); static void WebRtcAec_InitStats(stats_t *stats); -static void UpdateLevel(power_level_t *level, const short *in); +static void UpdateLevel(power_level_t* level, float in[2][PART_LEN1]); static void UpdateMetrics(aec_t *aec); +// Convert from time domain to frequency domain. Note that |time_data| are +// overwritten. +static void TimeToFrequency(float time_data[PART_LEN2], + float freq_data[2][PART_LEN1], + int window); __inline static float MulRe(float aRe, float aIm, float bRe, float bIm) { @@ -146,36 +152,59 @@ int WebRtcAec_CreateAec(aec_t **aecInst) return -1; } - if (WebRtcApm_CreateBuffer(&aec->farFrBuf, FRAME_LEN + PART_LEN) == -1) { + if (WebRtc_CreateBuffer(&aec->nearFrBuf, + FRAME_LEN + PART_LEN, + sizeof(int16_t)) == -1) { WebRtcAec_FreeAec(aec); aec = NULL; return -1; } - if (WebRtcApm_CreateBuffer(&aec->nearFrBuf, FRAME_LEN + PART_LEN) == -1) { + if (WebRtc_CreateBuffer(&aec->outFrBuf, + FRAME_LEN + PART_LEN, + sizeof(int16_t)) == -1) { WebRtcAec_FreeAec(aec); aec = NULL; return -1; } - if (WebRtcApm_CreateBuffer(&aec->outFrBuf, FRAME_LEN + PART_LEN) == -1) { + if (WebRtc_CreateBuffer(&aec->nearFrBufH, + FRAME_LEN + PART_LEN, + sizeof(int16_t)) == -1) { WebRtcAec_FreeAec(aec); aec = NULL; return -1; } - if (WebRtcApm_CreateBuffer(&aec->nearFrBufH, FRAME_LEN + PART_LEN) == -1) { + if (WebRtc_CreateBuffer(&aec->outFrBufH, + FRAME_LEN + PART_LEN, + sizeof(int16_t)) == -1) { WebRtcAec_FreeAec(aec); aec = NULL; return -1; } - if (WebRtcApm_CreateBuffer(&aec->outFrBufH, FRAME_LEN + PART_LEN) == -1) { + // Create far-end buffers. + if (WebRtc_CreateBuffer(&aec->far_buf, kBufSizePartitions, + sizeof(float) * 2 * PART_LEN1) == -1) { WebRtcAec_FreeAec(aec); aec = NULL; return -1; } - + if (WebRtc_CreateBuffer(&aec->far_buf_windowed, kBufSizePartitions, + sizeof(float) * 2 * PART_LEN1) == -1) { + WebRtcAec_FreeAec(aec); + aec = NULL; + return -1; + } +#ifdef WEBRTC_AEC_DEBUG_DUMP + if (WebRtc_CreateBuffer(&aec->far_time_buf, kBufSizePartitions, + sizeof(int16_t) * PART_LEN) == -1) { + WebRtcAec_FreeAec(aec); + aec = NULL; + return -1; + } +#endif if (WebRtc_CreateDelayEstimator(&aec->delay_estimator, PART_LEN1, kMaxDelayBlocks, @@ -194,13 +223,17 @@ int WebRtcAec_FreeAec(aec_t *aec) return -1; } - WebRtcApm_FreeBuffer(aec->farFrBuf); - WebRtcApm_FreeBuffer(aec->nearFrBuf); - WebRtcApm_FreeBuffer(aec->outFrBuf); + WebRtc_FreeBuffer(aec->nearFrBuf); + WebRtc_FreeBuffer(aec->outFrBuf); - WebRtcApm_FreeBuffer(aec->nearFrBufH); - WebRtcApm_FreeBuffer(aec->outFrBufH); + WebRtc_FreeBuffer(aec->nearFrBufH); + WebRtc_FreeBuffer(aec->outFrBufH); + WebRtc_FreeBuffer(aec->far_buf); + WebRtc_FreeBuffer(aec->far_buf_windowed); +#ifdef WEBRTC_AEC_DEBUG_DUMP + WebRtc_FreeBuffer(aec->far_time_buf); +#endif WebRtc_FreeDelayEstimator(aec->delay_estimator); free(aec); @@ -364,25 +397,35 @@ int WebRtcAec_InitAec(aec_t *aec, int sampFreq) aec->errThresh = 1.5e-6f; } - if (WebRtcApm_InitBuffer(aec->farFrBuf) == -1) { + if (WebRtc_InitBuffer(aec->nearFrBuf) == -1) { return -1; } - if (WebRtcApm_InitBuffer(aec->nearFrBuf) == -1) { + if (WebRtc_InitBuffer(aec->outFrBuf) == -1) { return -1; } - if (WebRtcApm_InitBuffer(aec->outFrBuf) == -1) { + if (WebRtc_InitBuffer(aec->nearFrBufH) == -1) { return -1; } - if (WebRtcApm_InitBuffer(aec->nearFrBufH) == -1) { + if (WebRtc_InitBuffer(aec->outFrBufH) == -1) { return -1; } - if (WebRtcApm_InitBuffer(aec->outFrBufH) == -1) { + // Initialize far-end buffers. + if (WebRtc_InitBuffer(aec->far_buf) == -1) { return -1; } + if (WebRtc_InitBuffer(aec->far_buf_windowed) == -1) { + return -1; + } +#ifdef WEBRTC_AEC_DEBUG_DUMP + if (WebRtc_InitBuffer(aec->far_time_buf) == -1) { + return -1; + } +#endif + aec->system_delay = 0; if (WebRtc_InitDelayEstimator(aec->delay_estimator) != 0) { return -1; @@ -411,8 +454,6 @@ int WebRtcAec_InitAec(aec_t *aec, int sampFreq) aec->knownDelay = 0; // Initialize buffers - memset(aec->farBuf, 0, sizeof(aec->farBuf)); - memset(aec->xBuf, 0, sizeof(aec->xBuf)); memset(aec->dBuf, 0, sizeof(aec->dBuf)); memset(aec->eBuf, 0, sizeof(aec->eBuf)); // For H band @@ -500,86 +541,104 @@ void WebRtcAec_InitMetrics(aec_t *aec) } -void WebRtcAec_ProcessFrame(aec_t *aec, const short *farend, - const short *nearend, const short *nearendH, - short *out, short *outH, - int knownDelay) -{ - short farBl[PART_LEN], nearBl[PART_LEN], outBl[PART_LEN]; - short farFr[FRAME_LEN]; - // For H band - short nearBlH[PART_LEN], outBlH[PART_LEN]; +void WebRtcAec_BufferFarendPartition(aec_t *aec, const float* farend) { + float fft[PART_LEN2]; + float xf[2][PART_LEN1]; - int size = 0; + // Check if the buffer is full, and in that case flush the oldest data. + if (WebRtc_available_write(aec->far_buf) < 1) { + WebRtc_MoveReadPtr(aec->far_buf, 1); + WebRtc_MoveReadPtr(aec->far_buf_windowed, 1); + aec->system_delay -= PART_LEN; +#ifdef WEBRTC_AEC_DEBUG_DUMP + WebRtc_MoveReadPtr(aec->far_time_buf, 1); +#endif + } + // Convert far-end partition to the frequency domain without windowing. + memcpy(fft, farend, sizeof(float) * PART_LEN2); + TimeToFrequency(fft, xf, 0); + WebRtc_WriteBuffer(aec->far_buf, &xf[0][0], 1); - // initialize: only used for SWB - memset(nearBlH, 0, sizeof(nearBlH)); - memset(outBlH, 0, sizeof(outBlH)); - - // Buffer the current frame. - // Fetch an older one corresponding to the delay. - BufferFar(aec, farend, FRAME_LEN); - FetchFar(aec, farFr, FRAME_LEN, knownDelay); - - // Buffer the synchronized far and near frames, - // to pass the smaller blocks individually. - WebRtcApm_WriteBuffer(aec->farFrBuf, farFr, FRAME_LEN); - WebRtcApm_WriteBuffer(aec->nearFrBuf, nearend, FRAME_LEN); - // For H band - if (aec->sampFreq == 32000) { - WebRtcApm_WriteBuffer(aec->nearFrBufH, nearendH, FRAME_LEN); - } - - // Process as many blocks as possible. - while (WebRtcApm_get_buffer_size(aec->farFrBuf) >= PART_LEN) { - - WebRtcApm_ReadBuffer(aec->farFrBuf, farBl, PART_LEN); - WebRtcApm_ReadBuffer(aec->nearFrBuf, nearBl, PART_LEN); - - // For H band - if (aec->sampFreq == 32000) { - WebRtcApm_ReadBuffer(aec->nearFrBufH, nearBlH, PART_LEN); - } - - ProcessBlock(aec, farBl, nearBl, nearBlH, outBl, outBlH); - - WebRtcApm_WriteBuffer(aec->outFrBuf, outBl, PART_LEN); - // For H band - if (aec->sampFreq == 32000) { - WebRtcApm_WriteBuffer(aec->outFrBufH, outBlH, PART_LEN); - } - } - - // Stuff the out buffer if we have less than a frame to output. - // This should only happen for the first frame. - size = WebRtcApm_get_buffer_size(aec->outFrBuf); - if (size < FRAME_LEN) { - WebRtcApm_StuffBuffer(aec->outFrBuf, FRAME_LEN - size); - if (aec->sampFreq == 32000) { - WebRtcApm_StuffBuffer(aec->outFrBufH, FRAME_LEN - size); - } - } - - // Obtain an output frame. - WebRtcApm_ReadBuffer(aec->outFrBuf, out, FRAME_LEN); - // For H band - if (aec->sampFreq == 32000) { - WebRtcApm_ReadBuffer(aec->outFrBufH, outH, FRAME_LEN); - } + // Convert far-end partition to the frequency domain with windowing. + memcpy(fft, farend, sizeof(float) * PART_LEN2); + TimeToFrequency(fft, xf, 1); + WebRtc_WriteBuffer(aec->far_buf_windowed, &xf[0][0], 1); } -static void ProcessBlock(aec_t *aec, const short *farend, - const short *nearend, const short *nearendH, - short *output, short *outputH) +void WebRtcAec_ProcessFrame(aec_t *aec, + const short *nearend, + const short *nearendH, + int knownDelay) { + // For each frame the process is as follows: + // 1) If the system_delay indicates on being too small for processing a + // frame we stuff the buffer with enough data for 10 ms. + // 2) Adjust the buffer to the system delay, by moving the read pointer. + // 3) If we can't move read pointer due to buffer size limitations we + // flush/stuff the buffer. + // 4) Process as many partitions as possible. + // 5) Update the |system_delay| with respect to a full frame of FRAME_LEN + // samples. Even though we will have data left to process (we work with + // partitions) we consider updating a whole frame, since that's the + // amount of data we input and output in audio_processing. + + // TODO(bjornv): Investigate how we should round the delay difference; right + // now we know that incoming |knownDelay| is underestimated when it's less + // than |aec->knownDelay|. We therefore, round (-32) in that direction. In + // the other direction, we don't have this situation, but might flush one + // partition too little. This can cause non-causality, which should be + // investigated. Maybe, allow for a non-symmetric rounding, like -16. + int move_elements = (aec->knownDelay - knownDelay - 32) / PART_LEN; + int moved_elements = 0; + + // TODO(bjornv): Change the near-end buffer handling to be the same as for + // far-end, that is, with a near_pre_buf. + // Buffer the near-end frame. + WebRtc_WriteBuffer(aec->nearFrBuf, nearend, FRAME_LEN); + // For H band + if (aec->sampFreq == 32000) { + WebRtc_WriteBuffer(aec->nearFrBufH, nearendH, FRAME_LEN); + } + + // 1) At most we process |aec->mult|+1 partitions in 10 ms. Make sure we + // have enough far-end data for that by stuffing the buffer if the + // |system_delay| indicates others. + if (aec->system_delay < FRAME_LEN) { + // We don't have enough data so we rewind 10 ms. + WebRtc_MoveReadPtr(aec->far_buf_windowed, -(aec->mult + 1)); + aec->system_delay -= WebRtc_MoveReadPtr(aec->far_buf, -(aec->mult + 1)) * + PART_LEN; +#ifdef WEBRTC_AEC_DEBUG_DUMP + WebRtc_MoveReadPtr(aec->far_time_buf, -(aec->mult + 1)); +#endif + } + + // 2) Compensate for a possible change in the system delay. + + WebRtc_MoveReadPtr(aec->far_buf_windowed, move_elements); + moved_elements = WebRtc_MoveReadPtr(aec->far_buf, move_elements); + aec->knownDelay -= moved_elements * PART_LEN; +#ifdef WEBRTC_AEC_DEBUG_DUMP + WebRtc_MoveReadPtr(aec->far_time_buf, move_elements); +#endif + + // 4) Process as many blocks as possible. + while (WebRtc_available_read(aec->nearFrBuf) >= PART_LEN) { + ProcessBlock(aec); + } + + // 5) Update system delay with respect to the entire frame. + aec->system_delay -= FRAME_LEN; +} + +static void ProcessBlock(aec_t* aec) { int i; float d[PART_LEN], y[PART_LEN], e[PART_LEN], dH[PART_LEN]; - short eInt16[PART_LEN]; float scale; float fft[PART_LEN2]; float xf[2][PART_LEN1], yf[2][PART_LEN1], ef[2][PART_LEN1]; - complex_t df[PART_LEN1]; + float df[2][PART_LEN1]; float far_spectrum = 0.0f; float near_spectrum = 0.0f; float abs_far_spectrum[PART_LEN1]; @@ -593,67 +652,61 @@ static void ProcessBlock(aec_t *aec, const short *farend, const float ramp = 1.0002f; const float gInitNoise[2] = {0.999f, 0.001f}; -#ifdef WEBRTC_AEC_DEBUG_DUMP - fwrite(farend, sizeof(int16_t), PART_LEN, aec->farFile); - fwrite(nearend, sizeof(int16_t), PART_LEN, aec->nearFile); -#endif + int16_t nearend[PART_LEN]; + int16_t* nearend_ptr = NULL; + int16_t output[PART_LEN]; + int16_t outputH[PART_LEN]; + + float* xf_ptr = NULL; memset(dH, 0, sizeof(dH)); + if (aec->sampFreq == 32000) { + // Get the upper band first so we can reuse |nearend|. + WebRtc_ReadBuffer(aec->nearFrBufH, + (void**) &nearend_ptr, + nearend, + PART_LEN); + for (i = 0; i < PART_LEN; i++) { + dH[i] = (float) (nearend_ptr[i]); + } + memcpy(aec->dBufH + PART_LEN, dH, sizeof(float) * PART_LEN); + } + WebRtc_ReadBuffer(aec->nearFrBuf, (void**) &nearend_ptr, nearend, PART_LEN); // ---------- Ooura fft ---------- - // Concatenate old and new farend blocks. + // Concatenate old and new nearend blocks. for (i = 0; i < PART_LEN; i++) { - aec->xBuf[i + PART_LEN] = (float)farend[i]; - d[i] = (float)nearend[i]; + d[i] = (float) (nearend_ptr[i]); } - - if (aec->sampFreq == 32000) { - for (i = 0; i < PART_LEN; i++) { - dH[i] = (float)nearendH[i]; - } - } - - memcpy(fft, aec->xBuf, sizeof(float) * PART_LEN2); memcpy(aec->dBuf + PART_LEN, d, sizeof(float) * PART_LEN); - // For H band - if (aec->sampFreq == 32000) { - memcpy(aec->dBufH + PART_LEN, dH, sizeof(float) * PART_LEN); + +#ifdef WEBRTC_AEC_DEBUG_DUMP + { + int16_t farend[PART_LEN]; + int16_t* farend_ptr = NULL; + WebRtc_ReadBuffer(aec->far_time_buf, (void**) &farend_ptr, farend, 1); + fwrite(farend_ptr, sizeof(int16_t), PART_LEN, aec->farFile); + fwrite(nearend_ptr, sizeof(int16_t), PART_LEN, aec->nearFile); } +#endif - aec_rdft_forward_128(fft); - - // Far fft - xf[1][0] = 0; - xf[1][PART_LEN] = 0; - xf[0][0] = fft[0]; - xf[0][PART_LEN] = fft[1]; - - for (i = 1; i < PART_LEN; i++) { - xf[0][i] = fft[2 * i]; - xf[1][i] = fft[2 * i + 1]; - } + // We should always have at least one element stored in |far_buf|. + assert(WebRtc_available_read(aec->far_buf) > 0); + WebRtc_ReadBuffer(aec->far_buf, (void**) &xf_ptr, &xf[0][0], 1); // Near fft memcpy(fft, aec->dBuf, sizeof(float) * PART_LEN2); - aec_rdft_forward_128(fft); - df[0][1] = 0; - df[PART_LEN][1] = 0; - df[0][0] = fft[0]; - df[PART_LEN][0] = fft[1]; - - for (i = 1; i < PART_LEN; i++) { - df[i][0] = fft[2 * i]; - df[i][1] = fft[2 * i + 1]; - } + TimeToFrequency(fft, df, 0); // Power smoothing for (i = 0; i < PART_LEN1; i++) { - far_spectrum = xf[0][i] * xf[0][i] + xf[1][i] * xf[1][i]; + far_spectrum = (xf_ptr[i] * xf_ptr[i]) + + (xf_ptr[PART_LEN1 + i] * xf_ptr[PART_LEN1 + i]); aec->xPow[i] = gPow[0] * aec->xPow[i] + gPow[1] * NR_PART * far_spectrum; // Calculate absolute spectra abs_far_spectrum[i] = sqrtf(far_spectrum); - near_spectrum = df[i][0] * df[i][0] + df[i][1] * df[i][1]; + near_spectrum = df[0][i] * df[0][i] + df[1][i] * df[1][i]; aec->dPow[i] = gPow[0] * aec->dPow[i] + gPow[1] * near_spectrum; // Calculate absolute spectra abs_near_spectrum[i] = sqrtf(near_spectrum); @@ -713,9 +766,9 @@ static void ProcessBlock(aec_t *aec, const short *farend, } // Buffer xf - memcpy(aec->xfBuf[0] + aec->xfBufBlockPos * PART_LEN1, xf[0], + memcpy(aec->xfBuf[0] + aec->xfBufBlockPos * PART_LEN1, xf_ptr, sizeof(float) * PART_LEN1); - memcpy(aec->xfBuf[1] + aec->xfBufBlockPos * PART_LEN1, xf[1], + memcpy(aec->xfBuf[1] + aec->xfBufBlockPos * PART_LEN1, &xf_ptr[PART_LEN1], sizeof(float) * PART_LEN1); memset(yf[0], 0, sizeof(float) * (PART_LEN1 * 2)); @@ -745,6 +798,7 @@ static void ProcessBlock(aec_t *aec, const short *farend, 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); + // TODO(bjornv): Change to use TimeToFrequency(). aec_rdft_forward_128(fft); ef[1][0] = 0; @@ -756,40 +810,49 @@ static void ProcessBlock(aec_t *aec, const short *farend, ef[1][i] = fft[2 * i + 1]; } + if (aec->metricsMode == 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); + } + // Scale error signal inversely with far power. WebRtcAec_ScaleErrorSignal(aec, ef); WebRtcAec_FilterAdaptation(aec, fft, ef); NonLinearProcessing(aec, output, outputH); if (aec->metricsMode == 1) { - for (i = 0; i < PART_LEN; i++) { - eInt16[i] = (short)WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX, e[i], - WEBRTC_SPL_WORD16_MIN); - } - // Update power levels and echo metrics - UpdateLevel(&aec->farlevel, farend); - UpdateLevel(&aec->nearlevel, nearend); - UpdateLevel(&aec->linoutlevel, eInt16); - UpdateLevel(&aec->nlpoutlevel, output); + UpdateLevel(&aec->farlevel, (float (*)[PART_LEN1]) xf_ptr); + UpdateLevel(&aec->nearlevel, df); UpdateMetrics(aec); } -#ifdef WEBRTC_AEC_DEBUG_DUMP - for (i = 0; i < PART_LEN; i++) { - eInt16[i] = (int16_t)WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX, e[i], - WEBRTC_SPL_WORD16_MIN); + // Store the output block. + WebRtc_WriteBuffer(aec->outFrBuf, output, PART_LEN); + // For H band + if (aec->sampFreq == 32000) { + WebRtc_WriteBuffer(aec->outFrBufH, outputH, PART_LEN); } - fwrite(eInt16, sizeof(int16_t), PART_LEN, aec->outLinearFile); - fwrite(output, sizeof(int16_t), PART_LEN, aec->outFile); +#ifdef WEBRTC_AEC_DEBUG_DUMP + { + int16_t eInt16[PART_LEN]; + for (i = 0; i < PART_LEN; i++) { + eInt16[i] = (int16_t)WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX, e[i], + WEBRTC_SPL_WORD16_MIN); + } + + fwrite(eInt16, sizeof(int16_t), PART_LEN, aec->outLinearFile); + fwrite(output, sizeof(int16_t), PART_LEN, aec->outFile); + } #endif } static void NonLinearProcessing(aec_t *aec, short *output, short *outputH) { - float efw[2][PART_LEN1], dfw[2][PART_LEN1]; - complex_t xfw[PART_LEN1]; + float efw[2][PART_LEN1], dfw[2][PART_LEN1], xfw[2][PART_LEN1]; complex_t comfortNoiseHband[PART_LEN1]; float fft[PART_LEN2]; float scale, dtmp; @@ -813,10 +876,12 @@ static void NonLinearProcessing(aec_t *aec, short *output, short *outputH) const float gCoh[2][2] = {{0.9f, 0.1f}, {0.93f, 0.07f}}; const float *ptrGCoh = gCoh[aec->mult - 1]; - // Filter energey + // Filter energy float wfEnMax = 0, wfEn = 0; const int delayEstInterval = 10 * aec->mult; + float* xfw_ptr = NULL; + aec->delayEstCtr++; if (aec->delayEstCtr == delayEstInterval) { aec->delayEstCtr = 0; @@ -847,25 +912,15 @@ static void NonLinearProcessing(aec_t *aec, short *output, short *outputH) } } + // We should always have at least one element stored in |far_buf|. + assert(WebRtc_available_read(aec->far_buf_windowed) > 0); // NLP - // Windowed far fft - for (i = 0; i < PART_LEN; i++) { - fft[i] = aec->xBuf[i] * sqrtHanning[i]; - fft[PART_LEN + i] = aec->xBuf[PART_LEN + i] * sqrtHanning[PART_LEN - i]; - } - aec_rdft_forward_128(fft); - - xfw[0][1] = 0; - xfw[PART_LEN][1] = 0; - xfw[0][0] = fft[0]; - xfw[PART_LEN][0] = fft[1]; - for (i = 1; i < PART_LEN; i++) { - xfw[i][0] = fft[2 * i]; - xfw[i][1] = fft[2 * i + 1]; - } + WebRtc_ReadBuffer(aec->far_buf_windowed, (void**) &xfw_ptr, &xfw[0][0], 1); + // TODO(bjornv): Investigate if we can reuse |far_buf_windowed| instead of + // |xfwBuf|. // Buffer far. - memcpy(aec->xfwBuf, xfw, sizeof(xfw)); + memcpy(aec->xfwBuf, xfw_ptr, sizeof(float) * 2 * PART_LEN1); // Use delayed far. memcpy(xfw, aec->xfwBuf + aec->delayIdx * PART_LEN1, sizeof(xfw)); @@ -912,7 +967,7 @@ static void NonLinearProcessing(aec_t *aec, short *output, short *outputH) // adverse interaction with the algorithm's tuning. // TODO: investigate further why this is so sensitive. aec->sx[i] = ptrGCoh[0] * aec->sx[i] + ptrGCoh[1] * - WEBRTC_SPL_MAX(xfw[i][0] * xfw[i][0] + xfw[i][1] * xfw[i][1], 15); + WEBRTC_SPL_MAX(xfw[0][i] * xfw[0][i] + xfw[1][i] * xfw[1][i], 15); aec->sde[i][0] = ptrGCoh[0] * aec->sde[i][0] + ptrGCoh[1] * (dfw[0][i] * efw[0][i] + dfw[1][i] * efw[1][i]); @@ -920,9 +975,9 @@ static void NonLinearProcessing(aec_t *aec, short *output, short *outputH) (dfw[0][i] * efw[1][i] - dfw[1][i] * efw[0][i]); aec->sxd[i][0] = ptrGCoh[0] * aec->sxd[i][0] + ptrGCoh[1] * - (dfw[0][i] * xfw[i][0] + dfw[1][i] * xfw[i][1]); + (dfw[0][i] * xfw[0][i] + dfw[1][i] * xfw[1][i]); aec->sxd[i][1] = ptrGCoh[0] * aec->sxd[i][1] + ptrGCoh[1] * - (dfw[0][i] * xfw[i][1] - dfw[1][i] * xfw[i][0]); + (dfw[0][i] * xfw[1][i] - dfw[1][i] * xfw[0][i]); sdSum += aec->sd[i]; seSum += aec->se[i]; @@ -1054,6 +1109,15 @@ static void NonLinearProcessing(aec_t *aec, short *output, short *outputH) // Add comfort noise. ComfortNoise(aec, efw, comfortNoiseHband, aec->noisePow, hNl); + // TODO(bjornv): Investigate how to take the windowing below into account if + // needed. + if (aec->metricsMode == 1) { + // Note that we have a scaling by two in the time domain |eBuf|. + // In addition the time domain signal is windowed before transformation, + // losing half the energy on the average. We take care of the first + // scaling only in UpdateMetrics(). + UpdateLevel(&aec->nlpoutlevel, efw); + } // Inverse error fft. fft[0] = efw[0][0]; fft[1] = efw[0][PART_LEN]; @@ -1116,7 +1180,6 @@ static void NonLinearProcessing(aec_t *aec, short *output, short *outputH) } // Copy the current block to the old position. - memcpy(aec->xBuf, aec->xBuf + PART_LEN, sizeof(float) * PART_LEN); memcpy(aec->dBuf, aec->dBuf + PART_LEN, sizeof(float) * PART_LEN); memcpy(aec->eBuf, aec->eBuf + PART_LEN, sizeof(float) * PART_LEN); @@ -1225,60 +1288,6 @@ static void ComfortNoise(aec_t *aec, float efw[2][PART_LEN1], } } -// Buffer the farend to account for knownDelay -static void BufferFar(aec_t *aec, const short *farend, int farLen) -{ - int writeLen = farLen, writePos = 0; - - // Check if the write position must be wrapped. - while (aec->farBufWritePos + writeLen > FAR_BUF_LEN) { - - // Write to remaining buffer space before wrapping. - writeLen = FAR_BUF_LEN - aec->farBufWritePos; - memcpy(aec->farBuf + aec->farBufWritePos, farend + writePos, - sizeof(short) * writeLen); - aec->farBufWritePos = 0; - writePos = writeLen; - writeLen = farLen - writeLen; - } - - memcpy(aec->farBuf + aec->farBufWritePos, farend + writePos, - sizeof(short) * writeLen); - aec->farBufWritePos += writeLen; -} - -static void FetchFar(aec_t *aec, short *farend, int farLen, int knownDelay) -{ - int readLen = farLen, readPos = 0, delayChange = knownDelay - aec->knownDelay; - - aec->farBufReadPos -= delayChange; - - // Check if delay forces a read position wrap. - while(aec->farBufReadPos < 0) { - aec->farBufReadPos += FAR_BUF_LEN; - } - while(aec->farBufReadPos > FAR_BUF_LEN - 1) { - aec->farBufReadPos -= FAR_BUF_LEN; - } - - aec->knownDelay = knownDelay; - - // Check if read position must be wrapped. - while (aec->farBufReadPos + readLen > FAR_BUF_LEN) { - - // Read from remaining buffer space before wrapping. - readLen = FAR_BUF_LEN - aec->farBufReadPos; - memcpy(farend + readPos, aec->farBuf + aec->farBufReadPos, - sizeof(short) * readLen); - aec->farBufReadPos = 0; - readPos = readLen; - readLen = farLen - readLen; - } - memcpy(farend + readPos, aec->farBuf + aec->farBufReadPos, - sizeof(short) * readLen); - aec->farBufReadPos += readLen; -} - static void WebRtcAec_InitLevel(power_level_t *level) { const float bigFloat = 1E17f; @@ -1305,37 +1314,63 @@ static void WebRtcAec_InitStats(stats_t *stats) stats->hicounter = 0; } -static void UpdateLevel(power_level_t *level, const short *in) -{ - int k; +static void UpdateLevel(power_level_t* level, float in[2][PART_LEN1]) { + // Do the energy calculation in the frequency domain. The FFT is performed on + // a segment of PART_LEN2 samples due to overlap, but we only want the energy + // of half that data (the last PART_LEN samples). Parseval's relation states + // that the energy is preserved according to + // + // \sum_{n=0}^{N-1} |x(n)|^2 = 1/N * \sum_{n=0}^{N-1} |X(n)|^2 + // = ENERGY, + // + // where N = PART_LEN2. Since we are only interested in calculating the energy + // for the last PART_LEN samples we approximate by calculating ENERGY and + // divide by 2, + // + // \sum_{n=N/2}^{N-1} |x(n)|^2 ~= ENERGY / 2 + // + // Since we deal with real valued time domain signals we only store frequency + // bins [0, PART_LEN], which is what |in| consists of. To calculate ENERGY we + // need to add the contribution from the missing part in + // [PART_LEN+1, PART_LEN2-1]. These values are, up to a phase shift, identical + // with the values in [1, PART_LEN-1], hence multiply those values by 2. This + // is the values in the for loop below, but multiplication by 2 and division + // by 2 cancel. - for (k = 0; k < PART_LEN; k++) { - level->sfrsum += in[k] * in[k]; + // TODO(bjornv): Investigate reusing energy calculations performed at other + // places in the code. + int k = 1; + // Imaginary parts are zero at end points and left out of the calculation. + float energy = (in[0][0] * in[0][0]) / 2; + energy += (in[0][PART_LEN] * in[0][PART_LEN]) / 2; + + for (k = 1; k < PART_LEN; k++) { + energy += (in[0][k] * in[0][k] + in[1][k] * in[1][k]); + } + energy /= PART_LEN2; + + level->sfrsum += energy; + level->sfrcounter++; + + if (level->sfrcounter > subCountLen) { + level->framelevel = level->sfrsum / (subCountLen * PART_LEN); + level->sfrsum = 0; + level->sfrcounter = 0; + if (level->framelevel > 0) { + if (level->framelevel < level->minlevel) { + level->minlevel = level->framelevel; // New minimum. + } else { + level->minlevel *= (1 + 0.001f); // Small increase. + } } - level->sfrcounter++; - - if (level->sfrcounter > subCountLen) { - level->framelevel = level->sfrsum / (subCountLen * PART_LEN); - level->sfrsum = 0; - level->sfrcounter = 0; - - if (level->framelevel > 0) { - if (level->framelevel < level->minlevel) { - level->minlevel = level->framelevel; // New minimum - } else { - level->minlevel *= (1 + 0.001f); // Small increase - } - } - level->frcounter++; - level->frsum += level->framelevel; - - if (level->frcounter > countLen) { - level->averagelevel = level->frsum / countLen; - level->frsum = 0; - level->frcounter = 0; - } - + level->frcounter++; + level->frsum += level->framelevel; + if (level->frcounter > countLen) { + level->averagelevel = level->frsum / countLen; + level->frsum = 0; + level->frcounter = 0; } + } } static void UpdateMetrics(aec_t *aec) @@ -1354,7 +1389,7 @@ static void UpdateMetrics(aec_t *aec) aec->stateCounter++; } - if (aec->farlevel.frcounter == countLen) { + if (aec->farlevel.frcounter == 0) { if (aec->farlevel.minlevel < noisyPower) { actThreshold = actThresholdClean; @@ -1400,10 +1435,11 @@ static void UpdateMetrics(aec_t *aec) // A_NLP dtmp = 10 * (float)log10(aec->nearlevel.averagelevel / - aec->linoutlevel.averagelevel + 1e-10f); + (2 * aec->linoutlevel.averagelevel) + 1e-10f); // subtract noise power - suppressedEcho = aec->linoutlevel.averagelevel - safety * aec->linoutlevel.minlevel; + suppressedEcho = 2 * (aec->linoutlevel.averagelevel - + safety * aec->linoutlevel.minlevel); dtmp2 = 10 * (float)log10(echo / suppressedEcho + 1e-10f); @@ -1430,10 +1466,11 @@ static void UpdateMetrics(aec_t *aec) // ERLE // subtract noise power - suppressedEcho = aec->nlpoutlevel.averagelevel - safety * aec->nlpoutlevel.minlevel; + suppressedEcho = 2 * (aec->nlpoutlevel.averagelevel - + safety * aec->nlpoutlevel.minlevel); dtmp = 10 * (float)log10(aec->nearlevel.averagelevel / - aec->nlpoutlevel.averagelevel + 1e-10f); + (2 * aec->nlpoutlevel.averagelevel) + 1e-10f); dtmp2 = 10 * (float)log10(echo / suppressedEcho + 1e-10f); dtmp = dtmp2; @@ -1462,3 +1499,27 @@ static void UpdateMetrics(aec_t *aec) } } +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] *= sqrtHanning[i]; + time_data[PART_LEN + i] *= sqrtHanning[PART_LEN - i]; + } + } + + aec_rdft_forward_128(time_data); + // Reorder. + freq_data[1][0] = 0; + freq_data[1][PART_LEN] = 0; + freq_data[0][0] = time_data[0]; + freq_data[0][PART_LEN] = time_data[1]; + for (i = 1; i < PART_LEN; i++) { + freq_data[0][i] = time_data[2 * i]; + freq_data[1][i] = time_data[2 * i + 1]; + } +} diff --git a/src/modules/audio_processing/aec/aec_core.h b/src/modules/audio_processing/aec/aec_core.h index f858a050ed..1b9828ab17 100644 --- a/src/modules/audio_processing/aec/aec_core.h +++ b/src/modules/audio_processing/aec/aec_core.h @@ -24,10 +24,7 @@ #define PART_LEN 64 // Length of partition #define PART_LEN1 (PART_LEN + 1) // Unique fft coefficients #define PART_LEN2 (PART_LEN * 2) // Length of partition * 2 -#define NR_PART 12 // Number of partitions -#define FILT_LEN (PART_LEN * NR_PART) // Filter length -#define FILT_LEN2 (FILT_LEN * 2) // Double filter length -#define FAR_BUF_LEN (FILT_LEN2 * 2) +#define NR_PART 12 // Number of partitions in filter. #define PREF_BAND_SIZE 24 // Delay estimator constants, used for logging. @@ -76,12 +73,11 @@ typedef struct { int inSamples, outSamples; int delayEstCtr; - void *farFrBuf, *nearFrBuf, *outFrBuf; + void *nearFrBuf, *outFrBuf; void *nearFrBufH; void *outFrBufH; - float xBuf[PART_LEN2]; // farend float dBuf[PART_LEN2]; // nearend float eBuf[PART_LEN2]; // error @@ -114,9 +110,11 @@ typedef struct { int xfBufBlockPos; - short farBuf[FILT_LEN2 * 2]; + void* far_buf; + void* far_buf_windowed; + int system_delay; // Current system delay buffered in AEC. - short mult; // sampling frequency multiple + int mult; // sampling frequency multiple int sampFreq; WebRtc_UWord32 seed; @@ -147,6 +145,7 @@ typedef struct { void* delay_estimator; #ifdef WEBRTC_AEC_DEBUG_DUMP + void* far_time_buf; FILE *farFile; FILE *nearFile; FILE *outFile; @@ -171,10 +170,10 @@ int WebRtcAec_InitAec(aec_t *aec, int sampFreq); void WebRtcAec_InitAec_SSE2(void); void WebRtcAec_InitMetrics(aec_t *aec); -void WebRtcAec_ProcessFrame(aec_t *aec, const short *farend, - const short *nearend, const short *nearendH, - short *out, short *outH, - int knownDelay); - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AEC_MAIN_SOURCE_AEC_CORE_H_ +void WebRtcAec_BufferFarendPartition(aec_t *aec, const float* farend); +void WebRtcAec_ProcessFrame(aec_t* aec, + const short *nearend, + const short *nearendH, + int knownDelay); +#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AEC_MAIN_SOURCE_AEC_CORE_H_ diff --git a/src/modules/audio_processing/aec/aec_resampler.c b/src/modules/audio_processing/aec/aec_resampler.c index 218599d119..ea980cd96a 100644 --- a/src/modules/audio_processing/aec/aec_resampler.c +++ b/src/modules/audio_processing/aec/aec_resampler.c @@ -21,11 +21,10 @@ #include "aec_core.h" -enum { kFrameBufferSize = FRAME_LEN * 4 }; enum { kEstimateLengthFrames = 400 }; typedef struct { - short buffer[kFrameBufferSize]; + short buffer[kResamplerBufferSize]; float position; int deviceSampleRateHz; @@ -128,7 +127,7 @@ int WebRtcAec_ResampleLinear(void *resampInst, // Shift buffer memmove(obj->buffer, &obj->buffer[size], - (kFrameBufferSize - size) * sizeof(short)); + (kResamplerBufferSize - size) * sizeof(short)); return outsize; } diff --git a/src/modules/audio_processing/aec/aec_resampler.h b/src/modules/audio_processing/aec/aec_resampler.h index 9cb2837293..ab4cc6ecf2 100644 --- a/src/modules/audio_processing/aec/aec_resampler.h +++ b/src/modules/audio_processing/aec/aec_resampler.h @@ -8,10 +8,13 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AEC_MAIN_SOURCE_RESAMPLER_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_AEC_MAIN_SOURCE_RESAMPLER_H_ +#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_RESAMPLER_H_ +#define WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_RESAMPLER_H_ + +#include "aec_core.h" enum { kResamplingDelay = 1 }; +enum { kResamplerBufferSize = FRAME_LEN * 4 }; // Unless otherwise specified, functions return 0 on success and -1 on error int WebRtcAec_CreateResampler(void **resampInst); @@ -29,4 +32,4 @@ int WebRtcAec_ResampleLinear(void *resampInst, float skew, short *outspeech); -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AEC_MAIN_SOURCE_RESAMPLER_H_ +#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_RESAMPLER_H_ diff --git a/src/modules/audio_processing/aec/echo_cancellation.c b/src/modules/audio_processing/aec/echo_cancellation.c index 67e4a645c6..66c9b979f1 100644 --- a/src/modules/audio_processing/aec/echo_cancellation.c +++ b/src/modules/audio_processing/aec/echo_cancellation.c @@ -23,14 +23,15 @@ #include "aec_core.h" #include "aec_resampler.h" #include "ring_buffer.h" +#include "typedefs.h" -#define BUF_SIZE_FRAMES 50 // buffer size (frames) // Maximum length of resampled signal. Must be an integer multiple of frames // (ceil(1/(1 + MIN_SKEW)*2) + 1)*FRAME_LEN // The factor of 2 handles wb, and the + 1 is as a safety margin +// TODO(bjornv): Replace with kResamplerBufferSize #define MAX_RESAMP_LEN (5 * FRAME_LEN) -static const int bufSizeSamp = BUF_SIZE_FRAMES * FRAME_LEN; // buffer size (samples) +static const int kMaxBufSizeStart = 62; // In partitions static const int sampMsNb = 8; // samples per ms in nb // Target suppression levels for nlp modes // log{0.001, 0.00001, 0.00000001} @@ -52,37 +53,34 @@ typedef struct { short autoOnOff; short activity; short skewMode; - short bufSizeStart; + int bufSizeStart; //short bufResetCtr; // counts number of noncausal frames int knownDelay; - // Stores the last frame added to the farend buffer - short farendOld[2][FRAME_LEN]; short initFlag; // indicates if AEC has been initialized // Variables used for averaging far end buffer size short counter; - short sum; + int sum; short firstVal; short checkBufSizeCtr; // Variables used for delay shifts short msInSndCardBuf; - short filtDelay; + short filtDelay; // Filtered delay estimate. int timeForDelayChange; int ECstartup; int checkBuffSize; - int delayChange; short lastDelayDiff; #ifdef WEBRTC_AEC_DEBUG_DUMP + void* far_pre_buf_s16; // Time domain far-end pre-buffer in int16_t. FILE *bufFile; FILE *delayFile; FILE *skewFile; #endif // Structures - void *farendBuf; void *resampler; int skewFrCtr; @@ -90,17 +88,16 @@ typedef struct { int highSkewCtr; float skew; + void* far_pre_buf; // Time domain far-end pre-buffer. + int lastError; aec_t *aec; } aecpc_t; -// Estimates delay to set the position of the farend buffer read pointer +// Estimates delay to set the position of the far-end buffer read pointer // (controlled by knownDelay) -static int EstBufDelay(aecpc_t *aecInst, short msInSndCardBuf); - -// Stuffs the farend buffer if the estimated delay is too large -static int DelayComp(aecpc_t *aecInst); +static int EstBufDelay(aecpc_t *aecInst); WebRtc_Word32 WebRtcAec_Create(void **aecInst) { @@ -121,13 +118,17 @@ WebRtc_Word32 WebRtcAec_Create(void **aecInst) return -1; } - if (WebRtcApm_CreateBuffer(&aecpc->farendBuf, bufSizeSamp) == -1) { + if (WebRtcAec_CreateResampler(&aecpc->resampler) == -1) { WebRtcAec_Free(aecpc); aecpc = NULL; return -1; } - - if (WebRtcAec_CreateResampler(&aecpc->resampler) == -1) { + // Create far-end pre-buffer. The buffer size has to be large enough for + // largest possible drift compensation (kResamplerBufferSize) + "almost" an + // FFT buffer (PART_LEN2 - 1). + if (WebRtc_CreateBuffer(&aecpc->far_pre_buf, + PART_LEN2 + kResamplerBufferSize, + sizeof(float)) == -1) { WebRtcAec_Free(aecpc); aecpc = NULL; return -1; @@ -137,6 +138,13 @@ WebRtc_Word32 WebRtcAec_Create(void **aecInst) aecpc->lastError = 0; #ifdef WEBRTC_AEC_DEBUG_DUMP + if (WebRtc_CreateBuffer(&aecpc->far_pre_buf_s16, + PART_LEN2 + kResamplerBufferSize, + sizeof(int16_t)) == -1) { + WebRtcAec_Free(aecpc); + aecpc = NULL; + return -1; + } { char filename[64]; sprintf(filename, "aec_far%d.pcm", instance_count); @@ -168,7 +176,10 @@ WebRtc_Word32 WebRtcAec_Free(void *aecInst) return -1; } + WebRtc_FreeBuffer(aecpc->far_pre_buf); + #ifdef WEBRTC_AEC_DEBUG_DUMP + WebRtc_FreeBuffer(aecpc->far_pre_buf_s16); fclose(aecpc->aec->farFile); fclose(aecpc->aec->nearFile); fclose(aecpc->aec->outFile); @@ -179,7 +190,6 @@ WebRtc_Word32 WebRtcAec_Free(void *aecInst) #endif WebRtcAec_FreeAec(aecpc->aec); - WebRtcApm_FreeBuffer(aecpc->farendBuf); WebRtcAec_FreeResampler(aecpc->resampler); free(aecpc); @@ -213,17 +223,17 @@ WebRtc_Word32 WebRtcAec_Init(void *aecInst, WebRtc_Word32 sampFreq, WebRtc_Word3 return -1; } - // Initialize farend buffer - if (WebRtcApm_InitBuffer(aecpc->farendBuf) == -1) { - aecpc->lastError = AEC_UNSPECIFIED_ERROR; - return -1; - } - if (WebRtcAec_InitResampler(aecpc->resampler, aecpc->scSampFreq) == -1) { aecpc->lastError = AEC_UNSPECIFIED_ERROR; return -1; } + if (WebRtc_InitBuffer(aecpc->far_pre_buf) == -1) { + aecpc->lastError = AEC_UNSPECIFIED_ERROR; + return -1; + } + WebRtc_MoveReadPtr(aecpc->far_pre_buf, -PART_LEN); // Start overlap. + aecpc->initFlag = initCheck; // indicates that initialization has been done if (aecpc->sampFreq == 32000) { @@ -236,7 +246,6 @@ WebRtc_Word32 WebRtcAec_Init(void *aecInst, WebRtc_Word32 sampFreq, WebRtc_Word3 aecpc->skewFrCtr = 0; aecpc->activity = 0; - aecpc->delayChange = 1; aecpc->delayCtr = 0; aecpc->sum = 0; @@ -248,7 +257,7 @@ WebRtc_Word32 WebRtcAec_Init(void *aecInst, WebRtc_Word32 sampFreq, WebRtc_Word3 aecpc->bufSizeStart = 0; aecpc->checkBufSizeCtr = 0; aecpc->filtDelay = 0; - aecpc->timeForDelayChange =0; + aecpc->timeForDelayChange = 0; aecpc->knownDelay = 0; aecpc->lastDelayDiff = 0; @@ -257,8 +266,6 @@ WebRtc_Word32 WebRtcAec_Init(void *aecInst, WebRtc_Word32 sampFreq, WebRtc_Word3 aecpc->highSkewCtr = 0; aecpc->sampFactor = (aecpc->scSampFreq * 1.0f) / aecpc->splitSampFreq; - memset(&aecpc->farendOld[0][0], 0, 160); - // Default settings. aecConfig.nlpMode = kAecNlpModerate; aecConfig.skewMode = kAecFalse; @@ -270,6 +277,14 @@ WebRtc_Word32 WebRtcAec_Init(void *aecInst, WebRtc_Word32 sampFreq, WebRtc_Word3 return -1; } +#ifdef WEBRTC_AEC_DEBUG_DUMP + if (WebRtc_InitBuffer(aecpc->far_pre_buf_s16) == -1) { + aecpc->lastError = AEC_UNSPECIFIED_ERROR; + return -1; + } + WebRtc_MoveReadPtr(aecpc->far_pre_buf_s16, -PART_LEN); // Start overlap. +#endif + return 0; } @@ -279,9 +294,13 @@ WebRtc_Word32 WebRtcAec_BufferFarend(void *aecInst, const WebRtc_Word16 *farend, { aecpc_t *aecpc = aecInst; WebRtc_Word32 retVal = 0; - short newNrOfSamples; + int newNrOfSamples = (int) nrOfSamples; short newFarend[MAX_RESAMP_LEN]; + const int16_t* farend_ptr = farend; + float tmp_farend[MAX_RESAMP_LEN]; + const float* farend_float = tmp_farend; float skew; + int i = 0; if (aecpc == NULL) { return -1; @@ -305,11 +324,6 @@ WebRtc_Word32 WebRtcAec_BufferFarend(void *aecInst, const WebRtc_Word16 *farend, skew = aecpc->skew; - // TODO: Is this really a good idea? - if (!aecpc->ECstartup) { - DelayComp(aecpc); - } - if (aecpc->skewMode == kAecTrue && aecpc->resample == kAecTrue) { // Resample and get a new number of samples newNrOfSamples = WebRtcAec_ResampleLinear(aecpc->resampler, @@ -317,10 +331,38 @@ WebRtc_Word32 WebRtcAec_BufferFarend(void *aecInst, const WebRtc_Word16 *farend, nrOfSamples, skew, newFarend); - WebRtcApm_WriteBuffer(aecpc->farendBuf, newFarend, newNrOfSamples); + farend_ptr = (const int16_t*) newFarend; } - else { - WebRtcApm_WriteBuffer(aecpc->farendBuf, farend, nrOfSamples); + + aecpc->aec->system_delay += newNrOfSamples; + +#ifdef WEBRTC_AEC_DEBUG_DUMP + WebRtc_WriteBuffer(aecpc->far_pre_buf_s16, farend_ptr, + (size_t) newNrOfSamples); +#endif + // Cast to float and write the time-domain data to |far_pre_buf|. + for (i = 0; i < newNrOfSamples; i++) { + tmp_farend[i] = (float) farend_ptr[i]; + } + WebRtc_WriteBuffer(aecpc->far_pre_buf, farend_float, + (size_t) newNrOfSamples); + + // Transform to frequency domain if we have enough data. + while (WebRtc_available_read(aecpc->far_pre_buf) >= PART_LEN2) { + // We have enough data to pass to the FFT, hence read PART_LEN2 samples. + WebRtc_ReadBuffer(aecpc->far_pre_buf, (void**) &farend_float, tmp_farend, + PART_LEN2); + + WebRtcAec_BufferFarendPartition(aecpc->aec, farend_float); + + // Rewind |far_pre_buf| PART_LEN samples for overlap before continuing. + WebRtc_MoveReadPtr(aecpc->far_pre_buf, -PART_LEN); +#ifdef WEBRTC_AEC_DEBUG_DUMP + WebRtc_ReadBuffer(aecpc->far_pre_buf_s16, (void**) &farend_ptr, newFarend, + PART_LEN2); + WebRtc_WriteBuffer(aecpc->aec->far_time_buf, &farend_ptr[PART_LEN], 1); + WebRtc_MoveReadPtr(aecpc->far_pre_buf_s16, -PART_LEN); +#endif } return retVal; @@ -333,8 +375,6 @@ WebRtc_Word32 WebRtcAec_Process(void *aecInst, const WebRtc_Word16 *nearend, aecpc_t *aecpc = aecInst; WebRtc_Word32 retVal = 0; short i; - short farend[FRAME_LEN]; - short nmbrOfFilledBuffers; short nBlocks10ms; short nFrames; // Limit resampling to doubling/halving of signal @@ -427,20 +467,18 @@ WebRtc_Word32 WebRtcAec_Process(void *aecInst, const WebRtc_Word16 *nearend, // Only needed if they don't already point to the same place. memcpy(out, nearend, sizeof(short) * nrOfSamples); } - nmbrOfFilledBuffers = WebRtcApm_get_buffer_size(aecpc->farendBuf) / FRAME_LEN; // The AEC is in the start up mode - // AEC is disabled until the soundcard buffer and farend buffers are OK + // AEC is disabled until the system delay is OK - // Mechanism to ensure that the soundcard buffer is reasonably stable. + // 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 amount of data on the - // sound card to be stable (+/-8 ms) compared to the first value. This - // comparison is made during the following 4 consecutive frames. If it seems - // to be stable then we start to fill up the far end buffer. - + // 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; @@ -455,79 +493,107 @@ WebRtc_Word32 WebRtcAec_Process(void *aecInst, const WebRtc_Word16 *nearend, aecpc->counter = 0; } - if (aecpc->counter*nBlocks10ms >= 6) { - // The farend buffer size is determined in blocks of 80 samples - // Use 75% of the average value of the soundcard buffer - aecpc->bufSizeStart = WEBRTC_SPL_MIN((int) (0.75 * (aecpc->sum * - aecpc->aec->mult) / (aecpc->counter * 10)), BUF_SIZE_FRAMES); - // buffersize has now been determined + 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->aec->mult * 8) / (4 * aecpc->counter * PART_LEN), + kMaxBufSizeStart); + // Buffer size has now been determined. aecpc->checkBuffSize = 0; } if (aecpc->checkBufSizeCtr * nBlocks10ms > 50) { - // for really bad sound cards, don't disable echocanceller for more than 0.5 sec - aecpc->bufSizeStart = WEBRTC_SPL_MIN((int) (0.75 * (aecpc->msInSndCardBuf * - aecpc->aec->mult) / 10), BUF_SIZE_FRAMES); + // For really bad systems, don't disable the echo canceller for + // more than 0.5 sec. + aecpc->bufSizeStart = WEBRTC_SPL_MIN((aecpc->msInSndCardBuf * + aecpc->aec->mult * 3) / 40, kMaxBufSizeStart); aecpc->checkBuffSize = 0; } } - // if checkBuffSize changed in the if-statement above + // If |checkBuffSize| changed in the if-statement above. if (!aecpc->checkBuffSize) { - // soundcard buffer is now reasonably stable - // When the far end buffer is filled with approximately the same amount of - // data as the amount on the sound card we end the start up phase and start - // to cancel echoes. + // 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 = aecpc->aec->system_delay / PART_LEN - + aecpc->bufSizeStart; + if (overhead_elements == 0) { + // Enable the AEC + aecpc->ECstartup = 0; + } else if (overhead_elements > 0) { + WebRtc_MoveReadPtr(aecpc->aec->far_buf_windowed, + overhead_elements); + WebRtc_MoveReadPtr(aecpc->aec->far_buf, overhead_elements); +#ifdef WEBRTC_AEC_DEBUG_DUMP + WebRtc_MoveReadPtr(aecpc->aec->far_time_buf, overhead_elements); +#endif + // 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. + aecpc->aec->system_delay -= overhead_elements * PART_LEN; - if (nmbrOfFilledBuffers == aecpc->bufSizeStart) { - aecpc->ECstartup = 0; // Enable the AEC - } - else if (nmbrOfFilledBuffers > aecpc->bufSizeStart) { - WebRtcApm_FlushBuffer(aecpc->farendBuf, WebRtcApm_get_buffer_size(aecpc->farendBuf) - - aecpc->bufSizeStart * FRAME_LEN); + // Enable the AEC aecpc->ECstartup = 0; } } + } else { + // AEC is enabled. - } - else { - // AEC is enabled + int out_elements = 0; - // Note only 1 block supported for nb and 2 blocks for wb + EstBufDelay(aecpc); + + // Note that 1 frame is supported for NB and 2 frames for WB. for (i = 0; i < nFrames; i++) { - nmbrOfFilledBuffers = WebRtcApm_get_buffer_size(aecpc->farendBuf) / FRAME_LEN; + int16_t* out_ptr = NULL; + int16_t out_tmp[FRAME_LEN]; - // Check that there is data in the far end buffer - if (nmbrOfFilledBuffers > 0) { - // Get the next 80 samples from the farend buffer - WebRtcApm_ReadBuffer(aecpc->farendBuf, farend, FRAME_LEN); + // Call the AEC. + WebRtcAec_ProcessFrame(aecpc->aec, + &nearend[FRAME_LEN * i], + &nearendH[FRAME_LEN * i], + aecpc->knownDelay); + // TODO(bjornv): Re-structure such that we don't have to pass + // |aecpc->knownDelay| as input. Change name to something like + // |system_buffer_diff|. - // Always store the last frame for use when we run out of data - memcpy(&(aecpc->farendOld[i][0]), farend, FRAME_LEN * sizeof(short)); - } - else { - // We have no data so we use the last played frame - memcpy(farend, &(aecpc->farendOld[i][0]), FRAME_LEN * sizeof(short)); + // Stuff the out buffer if we have less than a frame to output. + // This should only happen for the first frame. + out_elements = (int) WebRtc_available_read(aecpc->aec->outFrBuf); + if (out_elements < FRAME_LEN) { + WebRtc_MoveReadPtr(aecpc->aec->outFrBuf, + out_elements - FRAME_LEN); + if (aecpc->sampFreq == 32000) { + WebRtc_MoveReadPtr(aecpc->aec->outFrBufH, + out_elements - FRAME_LEN); + } } - // Call buffer delay estimator when all data is extracted, - // i.e. i = 0 for NB and i = 1 for WB or SWB - if ((i == 0 && aecpc->splitSampFreq == 8000) || - (i == 1 && (aecpc->splitSampFreq == 16000))) { - EstBufDelay(aecpc, aecpc->msInSndCardBuf); + // Obtain an output frame. + WebRtc_ReadBuffer(aecpc->aec->outFrBuf, (void**) &out_ptr, + out_tmp, FRAME_LEN); + memcpy(&out[FRAME_LEN * i], out_ptr, sizeof(int16_t) * FRAME_LEN); + // For H band + if (aecpc->sampFreq == 32000) { + WebRtc_ReadBuffer(aecpc->aec->outFrBufH, (void**) &out_ptr, + out_tmp, FRAME_LEN); + memcpy(&outH[FRAME_LEN * i], out_ptr, + sizeof(int16_t) * FRAME_LEN); } - - // Call the AEC - WebRtcAec_ProcessFrame(aecpc->aec, farend, &nearend[FRAME_LEN * i], &nearendH[FRAME_LEN * i], - &out[FRAME_LEN * i], &outH[FRAME_LEN * i], aecpc->knownDelay); } } #ifdef WEBRTC_AEC_DEBUG_DUMP { - short msInAECBuf = WebRtcApm_get_buffer_size(aecpc->farendBuf) / - (sampMsNb*aecpc->aec->mult); - fwrite(&msInAECBuf, 2, 1, aecpc->bufFile); + int16_t far_buf_size_ms = (int16_t) (aecpc->aec->system_delay / + (sampMsNb * aecpc->aec->mult)); + fwrite(&far_buf_size_ms, 2, 1, aecpc->bufFile); fwrite(&(aecpc->knownDelay), sizeof(aecpc->knownDelay), 1, aecpc->delayFile); } #endif @@ -828,80 +894,47 @@ WebRtc_Word32 WebRtcAec_get_error_code(void *aecInst) return aecpc->lastError; } -static int EstBufDelay(aecpc_t *aecpc, short msInSndCardBuf) -{ - short delayNew, nSampFar, nSampSndCard; - short diff; +static int EstBufDelay(aecpc_t* aecpc) { + int nSampSndCard = aecpc->msInSndCardBuf * sampMsNb * aecpc->aec->mult; + int current_delay = nSampSndCard - aecpc->aec->system_delay; + int delay_difference = 0; - nSampFar = WebRtcApm_get_buffer_size(aecpc->farendBuf); - nSampSndCard = msInSndCardBuf * sampMsNb * aecpc->aec->mult; + // Before we proceed with the delay estimate filtering we: + // 1) Compensate for the frame that will be read. + // 2) Compensate for drift resampling. - delayNew = nSampSndCard - nSampFar; + // 1) Compensating for the frame(s) that will be read/processed. + current_delay += FRAME_LEN * aecpc->aec->mult; - // Account for resampling frame delay - if (aecpc->skewMode == kAecTrue && aecpc->resample == kAecTrue) { - delayNew -= kResamplingDelay; + // 2) Account for resampling frame delay. + if (aecpc->skewMode == kAecTrue && aecpc->resample == kAecTrue) { + current_delay -= kResamplingDelay; + } + + aecpc->filtDelay = WEBRTC_SPL_MAX(0, (short) (0.8 * aecpc->filtDelay + + 0.2 * current_delay)); + + delay_difference = aecpc->filtDelay - aecpc->knownDelay; + if (delay_difference > 224) { + if (aecpc->lastDelayDiff < 96) { + aecpc->timeForDelayChange = 0; + } else { + aecpc->timeForDelayChange++; } + } else if (delay_difference < 96 && aecpc->knownDelay > 0) { + if (aecpc->lastDelayDiff > 224) { + aecpc->timeForDelayChange = 0; + } else { + aecpc->timeForDelayChange++; + } + } else { + aecpc->timeForDelayChange = 0; + } + aecpc->lastDelayDiff = delay_difference; - if (delayNew < FRAME_LEN) { - WebRtcApm_FlushBuffer(aecpc->farendBuf, FRAME_LEN); - delayNew += FRAME_LEN; - } + if (aecpc->timeForDelayChange > 25) { + aecpc->knownDelay = WEBRTC_SPL_MAX((int) aecpc->filtDelay - 160, 0); + } - aecpc->filtDelay = WEBRTC_SPL_MAX(0, (short)(0.8*aecpc->filtDelay + 0.2*delayNew)); - - diff = aecpc->filtDelay - aecpc->knownDelay; - if (diff > 224) { - if (aecpc->lastDelayDiff < 96) { - aecpc->timeForDelayChange = 0; - } - else { - aecpc->timeForDelayChange++; - } - } - else if (diff < 96 && aecpc->knownDelay > 0) { - if (aecpc->lastDelayDiff > 224) { - aecpc->timeForDelayChange = 0; - } - else { - aecpc->timeForDelayChange++; - } - } - else { - aecpc->timeForDelayChange = 0; - } - aecpc->lastDelayDiff = diff; - - if (aecpc->timeForDelayChange > 25) { - aecpc->knownDelay = WEBRTC_SPL_MAX((int)aecpc->filtDelay - 160, 0); - } - return 0; -} - -static int DelayComp(aecpc_t *aecpc) -{ - int nSampFar, nSampSndCard, delayNew, nSampAdd; - const int maxStuffSamp = 10 * FRAME_LEN; - - nSampFar = WebRtcApm_get_buffer_size(aecpc->farendBuf); - nSampSndCard = aecpc->msInSndCardBuf * sampMsNb * aecpc->aec->mult; - delayNew = nSampSndCard - nSampFar; - - // Account for resampling frame delay - if (aecpc->skewMode == kAecTrue && aecpc->resample == kAecTrue) { - delayNew -= kResamplingDelay; - } - - if (delayNew > FAR_BUF_LEN - FRAME_LEN*aecpc->aec->mult) { - // The difference of the buffersizes is larger than the maximum - // allowed known delay. Compensate by stuffing the buffer. - nSampAdd = (int)(WEBRTC_SPL_MAX((int)(0.5 * nSampSndCard - nSampFar), - FRAME_LEN)); - nSampAdd = WEBRTC_SPL_MIN(nSampAdd, maxStuffSamp); - - WebRtcApm_StuffBuffer(aecpc->farendBuf, nSampAdd); - aecpc->delayChange = 1; // the delay needs to be updated - } - - return 0; + return 0; } diff --git a/src/modules/audio_processing/utility/ring_buffer.c b/src/modules/audio_processing/utility/ring_buffer.c index ea2e3544be..7177ce3b13 100644 --- a/src/modules/audio_processing/utility/ring_buffer.c +++ b/src/modules/audio_processing/utility/ring_buffer.c @@ -8,232 +8,469 @@ * be found in the AUTHORS file in the root of the source tree. */ -/* - * Provides a generic ring buffer that can be written to and read from with - * arbitrarily sized blocks. The AEC uses this for several different tasks. - */ +// A ring buffer to hold arbitrary data. Provides no thread safety. Unless +// otherwise specified, functions return 0 on success and -1 on error. -#include -#include #include "ring_buffer.h" +#include // size_t +#include +#include + +// TODO(bjornv): Remove tmp_buf_t once old buffer function has been replaced in +// APM. typedef struct { int readPos; int writePos; int size; + int element_size; char rwWrap; bufdata_t *data; +} tmp_buf_t; + +typedef struct { + size_t read_pos; + size_t write_pos; + size_t element_count; + size_t element_size; + char rw_wrap; + void* data; } buf_t; -enum {SAME_WRAP, DIFF_WRAP}; +enum { SAME_WRAP, DIFF_WRAP }; -int WebRtcApm_CreateBuffer(void **bufInst, int size) -{ - buf_t *buf = NULL; +// Get address of region(s) from which we can read data. +// If the region is contiguous, |data_ptr_bytes_2| will be zero. +// If non-contiguous, |data_ptr_bytes_2| will be the size in bytes of the second +// region. Returns room available to be read or |element_count|, whichever is +// smaller. +static size_t GetBufferReadRegions(buf_t* buf, + size_t element_count, + void** data_ptr_1, + size_t* data_ptr_bytes_1, + void** data_ptr_2, + size_t* data_ptr_bytes_2) { - if (size < 0) { - return -1; - } + const size_t readable_elements = WebRtc_available_read(buf); + const size_t read_elements = (readable_elements < element_count ? + readable_elements : element_count); + const size_t margin = buf->element_count - buf->read_pos; - buf = malloc(sizeof(buf_t)); - *bufInst = buf; - if (buf == NULL) { - return -1; - } + // Check to see if read is not contiguous. + if (read_elements > margin) { + // Write data in two blocks that wrap the buffer. + *data_ptr_1 = buf->data + (buf->read_pos * buf->element_size); + *data_ptr_bytes_1 = margin * buf->element_size; + *data_ptr_2 = buf->data; + *data_ptr_bytes_2 = (read_elements - margin) * buf->element_size; + } else { + *data_ptr_1 = buf->data + (buf->read_pos * buf->element_size); + *data_ptr_bytes_1 = read_elements * buf->element_size; + *data_ptr_2 = NULL; + *data_ptr_bytes_2 = 0; + } - buf->data = malloc(size*sizeof(bufdata_t)); - if (buf->data == NULL) { - free(buf); - buf = NULL; - return -1; - } - - buf->size = size; - return 0; + return read_elements; } -int WebRtcApm_InitBuffer(void *bufInst) -{ - buf_t *buf = (buf_t*)bufInst; +int WebRtcApm_CreateBuffer(void **bufInst, int size) { + tmp_buf_t *buf = NULL; - buf->readPos = 0; - buf->writePos = 0; - buf->rwWrap = SAME_WRAP; + if (size < 0) { + return -1; + } - // Initialize buffer to zeros - memset(buf->data, 0, sizeof(bufdata_t)*buf->size); + buf = malloc(sizeof(tmp_buf_t)); + *bufInst = buf; + if (buf == NULL) { + return -1; + } - return 0; -} - -int WebRtcApm_FreeBuffer(void *bufInst) -{ - buf_t *buf = (buf_t*)bufInst; - - if (buf == NULL) { - return -1; - } - - free(buf->data); + buf->data = malloc(size * sizeof(bufdata_t)); + if (buf->data == NULL) { free(buf); + buf = NULL; + return -1; + } + buf->size = size; + buf->element_size = 1; + + return 0; +} + +int WebRtcApm_InitBuffer(void *bufInst) { + tmp_buf_t *buf = (tmp_buf_t*)bufInst; + + buf->readPos = 0; + buf->writePos = 0; + buf->rwWrap = SAME_WRAP; + + // Initialize buffer to zeros + memset(buf->data, 0, sizeof(bufdata_t) * buf->size); + + return 0; +} + +int WebRtcApm_FreeBuffer(void *bufInst) { + tmp_buf_t *buf = (tmp_buf_t*)bufInst; + + if (buf == NULL) { + return -1; + } + + free(buf->data); + free(buf); + + return 0; +} + +int WebRtcApm_ReadBuffer(void *bufInst, bufdata_t *data, int size) { + tmp_buf_t *buf = (tmp_buf_t*)bufInst; + int n = 0, margin = 0; + + if (size <= 0 || size > buf->size) { + return -1; + } + + n = size; + if (buf->rwWrap == DIFF_WRAP) { + margin = buf->size - buf->readPos; + if (n > margin) { + buf->rwWrap = SAME_WRAP; + memcpy(data, buf->data + buf->readPos, sizeof(bufdata_t) * margin); + buf->readPos = 0; + n = size - margin; + } else { + memcpy(data, buf->data + buf->readPos, sizeof(bufdata_t) * n); + buf->readPos += n; + return n; + } + } + + if (buf->rwWrap == SAME_WRAP) { + margin = buf->writePos - buf->readPos; + if (margin > n) + margin = n; + memcpy(data + size - n, buf->data + buf->readPos, + sizeof(bufdata_t) * margin); + buf->readPos += margin; + n -= margin; + } + + return size - n; +} + +int WebRtcApm_WriteBuffer(void *bufInst, const bufdata_t *data, int size) { + tmp_buf_t *buf = (tmp_buf_t*)bufInst; + int n = 0, margin = 0; + + if (size < 0 || size > buf->size) { + return -1; + } + + n = size; + if (buf->rwWrap == SAME_WRAP) { + margin = buf->size - buf->writePos; + if (n > margin) { + buf->rwWrap = DIFF_WRAP; + memcpy(buf->data + buf->writePos, data, sizeof(bufdata_t) * margin); + buf->writePos = 0; + n = size - margin; + } else { + memcpy(buf->data + buf->writePos, data, sizeof(bufdata_t) * n); + buf->writePos += n; + return n; + } + } + + if (buf->rwWrap == DIFF_WRAP) { + margin = buf->readPos - buf->writePos; + if (margin > n) + margin = n; + memcpy(buf->data + buf->writePos, data + size - n, + sizeof(bufdata_t) * margin); + buf->writePos += margin; + n -= margin; + } + + return size - n; +} + +int WebRtcApm_FlushBuffer(void *bufInst, int size) { + tmp_buf_t *buf = (tmp_buf_t*)bufInst; + int n = 0, margin = 0; + + if (size <= 0 || size > buf->size) { + return -1; + } + + n = size; + if (buf->rwWrap == DIFF_WRAP) { + margin = buf->size - buf->readPos; + if (n > margin) { + buf->rwWrap = SAME_WRAP; + buf->readPos = 0; + n = size - margin; + } else { + buf->readPos += n; + return n; + } + } + + if (buf->rwWrap == SAME_WRAP) { + margin = buf->writePos - buf->readPos; + if (margin > n) + margin = n; + buf->readPos += margin; + n -= margin; + } + + return size - n; +} + +int WebRtcApm_StuffBuffer(void *bufInst, int size) { + tmp_buf_t *buf = (tmp_buf_t*)bufInst; + int n = 0, margin = 0; + + if (size <= 0 || size > buf->size) { + return -1; + } + + n = size; + if (buf->rwWrap == SAME_WRAP) { + margin = buf->readPos; + if (n > margin) { + buf->rwWrap = DIFF_WRAP; + buf->readPos = buf->size - 1; + n -= margin + 1; + } else { + buf->readPos -= n; + return n; + } + } + + if (buf->rwWrap == DIFF_WRAP) { + margin = buf->readPos - buf->writePos; + if (margin > n) + margin = n; + buf->readPos -= margin; + n -= margin; + } + + return size - n; +} + +int WebRtcApm_get_buffer_size(const void *bufInst) { + const tmp_buf_t *buf = (tmp_buf_t*)bufInst; + + if (buf->rwWrap == SAME_WRAP) + return buf->writePos - buf->readPos; + else + return buf->size - buf->readPos + buf->writePos; +} + +int WebRtc_CreateBuffer(void** handle, + size_t element_count, + size_t element_size) { + buf_t* self = NULL; + + if (handle == NULL) { + return -1; + } + + self = malloc(sizeof(buf_t)); + if (self == NULL) { + return -1; + } + *handle = self; + + self->data = malloc(element_count * element_size); + if (self->data == NULL) { + free(self); + self = NULL; + return -1; + } + + self->element_count = element_count; + self->element_size = element_size; + + return 0; +} + +int WebRtc_InitBuffer(void* handle) { + buf_t* self = (buf_t*) handle; + + if (self == NULL) { + return -1; + } + + self->read_pos = 0; + self->write_pos = 0; + self->rw_wrap = SAME_WRAP; + + // Initialize buffer to zeros + memset(self->data, 0, self->element_count * self->element_size); + + return 0; +} + +int WebRtc_FreeBuffer(void* handle) { + buf_t* self = (buf_t*) handle; + + if (self == NULL) { + return -1; + } + + free(self->data); + free(self); + + return 0; +} + +size_t WebRtc_ReadBuffer(void* handle, + void** data_ptr, + void* data, + size_t element_count) { + + buf_t* self = (buf_t*) handle; + + if (self == NULL) { return 0; + } + if (data == NULL) { + return 0; + } + if (data_ptr == NULL) { + return 0; + } + + { + void* buf_ptr_1 = NULL; + void* buf_ptr_2 = NULL; + size_t buf_ptr_bytes_1 = 0; + size_t buf_ptr_bytes_2 = 0; + const size_t read_count = GetBufferReadRegions(self, + element_count, + &buf_ptr_1, + &buf_ptr_bytes_1, + &buf_ptr_2, + &buf_ptr_bytes_2); + + if (buf_ptr_bytes_2 > 0) { + // We have a wrap around when reading the buffer. Copy the buffer data to + // |data| and point to it. + memcpy(data, buf_ptr_1, buf_ptr_bytes_1); + memcpy(data + buf_ptr_bytes_1, buf_ptr_2, buf_ptr_bytes_2); + *data_ptr = data; + } else { + *data_ptr = buf_ptr_1; + } + + // Update read position + WebRtc_MoveReadPtr(handle, (int) read_count); + + return read_count; + } } -int WebRtcApm_ReadBuffer(void *bufInst, bufdata_t *data, int size) -{ - buf_t *buf = (buf_t*)bufInst; - int n = 0, margin = 0; +size_t WebRtc_WriteBuffer(void* handle, + const void* data, + size_t element_count) { - if (size <= 0 || size > buf->size) { - return -1; + buf_t* self = (buf_t*) handle; + + if (self == NULL) { + return 0; + } + if (data == NULL) { + return 0; + } + + { + const size_t free_elements = WebRtc_available_write(handle); + const size_t write_elements = (free_elements < element_count ? free_elements + : element_count); + size_t n = write_elements; + const size_t margin = self->element_count - self->write_pos; + + if (write_elements > margin) { + // Buffer wrap around when writing. + memcpy(self->data + (self->write_pos * self->element_size), + data, margin * self->element_size); + self->write_pos = 0; + n -= margin; + self->rw_wrap = DIFF_WRAP; } + memcpy(self->data + (self->write_pos * self->element_size), + data + ((write_elements - n) * self->element_size), + n * self->element_size); + self->write_pos += n; - n = size; - if (buf->rwWrap == DIFF_WRAP) { - margin = buf->size - buf->readPos; - if (n > margin) { - buf->rwWrap = SAME_WRAP; - memcpy(data, buf->data + buf->readPos, - sizeof(bufdata_t)*margin); - buf->readPos = 0; - n = size - margin; - } - else { - memcpy(data, buf->data + buf->readPos, - sizeof(bufdata_t)*n); - buf->readPos += n; - return n; - } - } - - if (buf->rwWrap == SAME_WRAP) { - margin = buf->writePos - buf->readPos; - if (margin > n) - margin = n; - memcpy(data + size - n, buf->data + buf->readPos, - sizeof(bufdata_t)*margin); - buf->readPos += margin; - n -= margin; - } - - return size - n; + return write_elements; + } } -int WebRtcApm_WriteBuffer(void *bufInst, const bufdata_t *data, int size) -{ - buf_t *buf = (buf_t*)bufInst; - int n = 0, margin = 0; +int WebRtc_MoveReadPtr(void* handle, int element_count) { - if (size < 0 || size > buf->size) { - return -1; + buf_t* self = (buf_t*) handle; + + if (self == NULL) { + return 0; + } + + { + // We need to be able to take care of negative changes, hence use "int" + // instead of "size_t". + const int free_elements = (int) WebRtc_available_write(handle); + const int readable_elements = (int) WebRtc_available_read(handle); + int read_pos = (int) self->read_pos; + + if (element_count > readable_elements) { + element_count = readable_elements; + } + if (element_count < -free_elements) { + element_count = -free_elements; } - n = size; - if (buf->rwWrap == SAME_WRAP) { - margin = buf->size - buf->writePos; - if (n > margin) { - buf->rwWrap = DIFF_WRAP; - memcpy(buf->data + buf->writePos, data, - sizeof(bufdata_t)*margin); - buf->writePos = 0; - n = size - margin; - } - else { - memcpy(buf->data + buf->writePos, data, - sizeof(bufdata_t)*n); - buf->writePos += n; - return n; - } + read_pos += element_count; + if (read_pos > (int) self->element_count) { + // Buffer wrap around. Restart read position and wrap indicator. + read_pos -= (int) self->element_count; + self->rw_wrap = SAME_WRAP; + } + if (read_pos < 0) { + // Buffer wrap around. Restart read position and wrap indicator. + read_pos += (int) self->element_count; + self->rw_wrap = DIFF_WRAP; } - if (buf->rwWrap == DIFF_WRAP) { - margin = buf->readPos - buf->writePos; - if (margin > n) - margin = n; - memcpy(buf->data + buf->writePos, data + size - n, - sizeof(bufdata_t)*margin); - buf->writePos += margin; - n -= margin; - } + self->read_pos = (size_t) read_pos; - return size - n; + return element_count; + } } -int WebRtcApm_FlushBuffer(void *bufInst, int size) -{ - buf_t *buf = (buf_t*)bufInst; - int n = 0, margin = 0; +size_t WebRtc_available_read(const void* handle) { + const buf_t* self = (buf_t*) handle; - if (size <= 0 || size > buf->size) { - return -1; - } + if (self == NULL) { + return 0; + } - n = size; - if (buf->rwWrap == DIFF_WRAP) { - margin = buf->size - buf->readPos; - if (n > margin) { - buf->rwWrap = SAME_WRAP; - buf->readPos = 0; - n = size - margin; - } - else { - buf->readPos += n; - return n; - } - } - - if (buf->rwWrap == SAME_WRAP) { - margin = buf->writePos - buf->readPos; - if (margin > n) - margin = n; - buf->readPos += margin; - n -= margin; - } - - return size - n; + if (self->rw_wrap == SAME_WRAP) { + return self->write_pos - self->read_pos; + } else { + return self->element_count - self->read_pos + self->write_pos; + } } -int WebRtcApm_StuffBuffer(void *bufInst, int size) -{ - buf_t *buf = (buf_t*)bufInst; - int n = 0, margin = 0; +size_t WebRtc_available_write(const void* handle) { + const buf_t* self = (buf_t*) handle; - if (size <= 0 || size > buf->size) { - return -1; - } + if (self == NULL) { + return 0; + } - n = size; - if (buf->rwWrap == SAME_WRAP) { - margin = buf->readPos; - if (n > margin) { - buf->rwWrap = DIFF_WRAP; - buf->readPos = buf->size - 1; - n -= margin + 1; - } - else { - buf->readPos -= n; - return n; - } - } - - if (buf->rwWrap == DIFF_WRAP) { - margin = buf->readPos - buf->writePos; - if (margin > n) - margin = n; - buf->readPos -= margin; - n -= margin; - } - - return size - n; -} - -int WebRtcApm_get_buffer_size(const void *bufInst) -{ - const buf_t *buf = (buf_t*)bufInst; - - if (buf->rwWrap == SAME_WRAP) - return buf->writePos - buf->readPos; - else - return buf->size - buf->readPos + buf->writePos; + return self->element_count - WebRtc_available_read(handle); } diff --git a/src/modules/audio_processing/utility/ring_buffer.h b/src/modules/audio_processing/utility/ring_buffer.h index 0fd261dfe9..3c94adf57d 100644 --- a/src/modules/audio_processing/utility/ring_buffer.h +++ b/src/modules/audio_processing/utility/ring_buffer.h @@ -8,17 +8,23 @@ * be found in the AUTHORS file in the root of the source tree. */ -/* - * Specifies the interface for the AEC generic buffer. - */ +// A ring buffer to hold arbitrary data. Provides no thread safety. Unless +// otherwise specified, functions return 0 on success and -1 on error. #ifndef WEBRTC_MODULES_AUDIO_PROCESSING_UTILITY_RING_BUFFER_H_ #define WEBRTC_MODULES_AUDIO_PROCESSING_UTILITY_RING_BUFFER_H_ -// Determines buffer datatype -typedef short bufdata_t; +#include // size_t + +// Determines buffer datatype +typedef short bufdata_t; // TODO(bjornv): Remove together with the below. + +// TODO(bjornv): Remove WebRtcApm_CreateBuffer, WebRtcApm_InitBuffer and +// WebRtcApm_FreeBuffer when replaced in APM. +// Rename WebRtcApm_FreeBuffer to WebRtc_FreeBuffer() and replace. +// Replace WebRtcApm_FlushBuffer and WebRtcApm_StuffBuffer with +// WebRtc_MoveReadPtr and Read/Write-Buffer with its new versions. -// Unless otherwise specified, functions return 0 on success and -1 on error int WebRtcApm_CreateBuffer(void **bufInst, int size); int WebRtcApm_InitBuffer(void *bufInst); int WebRtcApm_FreeBuffer(void *bufInst); @@ -38,4 +44,39 @@ int WebRtcApm_StuffBuffer(void *bufInst, int size); // Returns number of samples in buffer int WebRtcApm_get_buffer_size(const void *bufInst); +// TODO(bjornv): Below are the new functions, to replace the older ones above. +int WebRtc_CreateBuffer(void** handle, + size_t element_count, + size_t element_size); +int WebRtc_InitBuffer(void* handle); +int WebRtc_FreeBuffer(void* handle); + +// Reads data from the buffer. The |data_ptr| will point to the address where +// it is located. If all |element_count| data are feasible to read without +// buffer wrap around |data_ptr| will point to the location in the buffer. +// Otherwise, the data will be copied to |data| (memory allocation done by the +// user) and |data_ptr| points to the address of |data|. |data_ptr| is only +// guaranteed to be valid until the next call to WebRtc_WriteBuffer(). +// Returns number of elements read. +size_t WebRtc_ReadBuffer(void* handle, + void** data_ptr, + void* data, + size_t element_count); + +// Writes |data| to buffer and returns the number of elements written. +size_t WebRtc_WriteBuffer(void* handle, const void* data, size_t element_count); + +// Moves the buffer read position and returns the number of elements moved. +// Positive |element_count| moves the read position towards the write position, +// that is, flushing the buffer. Negative |element_count| moves the read +// position away from the the write position, that is, stuffing the buffer. +// Returns number of elements moved. +int WebRtc_MoveReadPtr(void* handle, int element_count); + +// Returns number of available elements to read. +size_t WebRtc_available_read(const void* handle); + +// Returns number of available elements for write. +size_t WebRtc_available_write(const void* handle); + #endif // WEBRTC_MODULES_AUDIO_PROCESSING_UTILITY_RING_BUFFER_H_ diff --git a/test/data/audio_processing/output_data_float.pb b/test/data/audio_processing/output_data_float.pb index b755c0e870..d608e5fb04 100644 Binary files a/test/data/audio_processing/output_data_float.pb and b/test/data/audio_processing/output_data_float.pb differ