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:
parent
e39de16fa5
commit
7270a6bcc2
@ -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];
|
||||
}
|
||||
}
|
||||
|
@ -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_
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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_
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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_
|
||||
|
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user