Merged apm-buffer branch [r1293] back to trunk.

This CL includes a larger structural change in how we handle buffers in AEC. We now perform FFT at once and move within blocks to compensate for system delays.

TEST=audioproc_unittest(float and fix), voe_auto_test
Review URL: http://webrtc-codereview.appspot.com/335012

git-svn-id: http://webrtc.googlecode.com/svn/trunk@1299 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
bjornv@webrtc.org 2011-12-28 08:44:17 +00:00
parent e39de16fa5
commit 7270a6bcc2
8 changed files with 1011 additions and 638 deletions

View File

@ -14,7 +14,9 @@
#include "aec_core.h"
#include <assert.h>
#include <math.h>
#include <stddef.h> // size_t
#include <stdlib.h>
#include <string.h>
@ -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];
}
}

View File

@ -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_

View File

@ -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;
}

View File

@ -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_

View File

@ -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;
}

View File

@ -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 <stdlib.h>
#include <string.h>
#include "ring_buffer.h"
#include <stddef.h> // size_t
#include <stdlib.h>
#include <string.h>
// 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);
}

View File

@ -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 <stddef.h> // 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_