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 "aec_core.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <stddef.h> // size_t
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
@ -22,6 +24,10 @@
|
|||||||
#include "delay_estimator_wrapper.h"
|
#include "delay_estimator_wrapper.h"
|
||||||
#include "ring_buffer.h"
|
#include "ring_buffer.h"
|
||||||
#include "system_wrappers/interface/cpu_features_wrapper.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
|
// Noise suppression
|
||||||
static const int converged = 250;
|
static const int converged = 250;
|
||||||
@ -99,12 +105,7 @@ const float WebRtcAec_overDriveCurve[65] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// "Private" function prototypes.
|
// "Private" function prototypes.
|
||||||
static void ProcessBlock(aec_t *aec, const short *farend,
|
static void ProcessBlock(aec_t* aec);
|
||||||
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 NonLinearProcessing(aec_t *aec, short *output, short *outputH);
|
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_InitLevel(power_level_t *level);
|
||||||
static void WebRtcAec_InitStats(stats_t *stats);
|
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);
|
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)
|
__inline static float MulRe(float aRe, float aIm, float bRe, float bIm)
|
||||||
{
|
{
|
||||||
@ -146,36 +152,59 @@ int WebRtcAec_CreateAec(aec_t **aecInst)
|
|||||||
return -1;
|
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);
|
WebRtcAec_FreeAec(aec);
|
||||||
aec = NULL;
|
aec = NULL;
|
||||||
return -1;
|
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);
|
WebRtcAec_FreeAec(aec);
|
||||||
aec = NULL;
|
aec = NULL;
|
||||||
return -1;
|
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);
|
WebRtcAec_FreeAec(aec);
|
||||||
aec = NULL;
|
aec = NULL;
|
||||||
return -1;
|
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);
|
WebRtcAec_FreeAec(aec);
|
||||||
aec = NULL;
|
aec = NULL;
|
||||||
return -1;
|
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);
|
WebRtcAec_FreeAec(aec);
|
||||||
aec = NULL;
|
aec = NULL;
|
||||||
return -1;
|
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,
|
if (WebRtc_CreateDelayEstimator(&aec->delay_estimator,
|
||||||
PART_LEN1,
|
PART_LEN1,
|
||||||
kMaxDelayBlocks,
|
kMaxDelayBlocks,
|
||||||
@ -194,13 +223,17 @@ int WebRtcAec_FreeAec(aec_t *aec)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
WebRtcApm_FreeBuffer(aec->farFrBuf);
|
WebRtc_FreeBuffer(aec->nearFrBuf);
|
||||||
WebRtcApm_FreeBuffer(aec->nearFrBuf);
|
WebRtc_FreeBuffer(aec->outFrBuf);
|
||||||
WebRtcApm_FreeBuffer(aec->outFrBuf);
|
|
||||||
|
|
||||||
WebRtcApm_FreeBuffer(aec->nearFrBufH);
|
WebRtc_FreeBuffer(aec->nearFrBufH);
|
||||||
WebRtcApm_FreeBuffer(aec->outFrBufH);
|
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);
|
WebRtc_FreeDelayEstimator(aec->delay_estimator);
|
||||||
|
|
||||||
free(aec);
|
free(aec);
|
||||||
@ -364,25 +397,35 @@ int WebRtcAec_InitAec(aec_t *aec, int sampFreq)
|
|||||||
aec->errThresh = 1.5e-6f;
|
aec->errThresh = 1.5e-6f;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (WebRtcApm_InitBuffer(aec->farFrBuf) == -1) {
|
if (WebRtc_InitBuffer(aec->nearFrBuf) == -1) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (WebRtcApm_InitBuffer(aec->nearFrBuf) == -1) {
|
if (WebRtc_InitBuffer(aec->outFrBuf) == -1) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (WebRtcApm_InitBuffer(aec->outFrBuf) == -1) {
|
if (WebRtc_InitBuffer(aec->nearFrBufH) == -1) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (WebRtcApm_InitBuffer(aec->nearFrBufH) == -1) {
|
if (WebRtc_InitBuffer(aec->outFrBufH) == -1) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (WebRtcApm_InitBuffer(aec->outFrBufH) == -1) {
|
// Initialize far-end buffers.
|
||||||
|
if (WebRtc_InitBuffer(aec->far_buf) == -1) {
|
||||||
return -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) {
|
if (WebRtc_InitDelayEstimator(aec->delay_estimator) != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
@ -411,8 +454,6 @@ int WebRtcAec_InitAec(aec_t *aec, int sampFreq)
|
|||||||
aec->knownDelay = 0;
|
aec->knownDelay = 0;
|
||||||
|
|
||||||
// Initialize buffers
|
// 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->dBuf, 0, sizeof(aec->dBuf));
|
||||||
memset(aec->eBuf, 0, sizeof(aec->eBuf));
|
memset(aec->eBuf, 0, sizeof(aec->eBuf));
|
||||||
// For H band
|
// For H band
|
||||||
@ -500,86 +541,104 @@ void WebRtcAec_InitMetrics(aec_t *aec)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void WebRtcAec_ProcessFrame(aec_t *aec, const short *farend,
|
void WebRtcAec_BufferFarendPartition(aec_t *aec, const float* farend) {
|
||||||
const short *nearend, const short *nearendH,
|
float fft[PART_LEN2];
|
||||||
short *out, short *outH,
|
float xf[2][PART_LEN1];
|
||||||
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];
|
|
||||||
|
|
||||||
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
|
// Convert far-end partition to the frequency domain with windowing.
|
||||||
memset(nearBlH, 0, sizeof(nearBlH));
|
memcpy(fft, farend, sizeof(float) * PART_LEN2);
|
||||||
memset(outBlH, 0, sizeof(outBlH));
|
TimeToFrequency(fft, xf, 1);
|
||||||
|
WebRtc_WriteBuffer(aec->far_buf_windowed, &xf[0][0], 1);
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ProcessBlock(aec_t *aec, const short *farend,
|
void WebRtcAec_ProcessFrame(aec_t *aec,
|
||||||
const short *nearend, const short *nearendH,
|
const short *nearend,
|
||||||
short *output, short *outputH)
|
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;
|
int i;
|
||||||
float d[PART_LEN], y[PART_LEN], e[PART_LEN], dH[PART_LEN];
|
float d[PART_LEN], y[PART_LEN], e[PART_LEN], dH[PART_LEN];
|
||||||
short eInt16[PART_LEN];
|
|
||||||
float scale;
|
float scale;
|
||||||
|
|
||||||
float fft[PART_LEN2];
|
float fft[PART_LEN2];
|
||||||
float xf[2][PART_LEN1], yf[2][PART_LEN1], ef[2][PART_LEN1];
|
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 far_spectrum = 0.0f;
|
||||||
float near_spectrum = 0.0f;
|
float near_spectrum = 0.0f;
|
||||||
float abs_far_spectrum[PART_LEN1];
|
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 ramp = 1.0002f;
|
||||||
const float gInitNoise[2] = {0.999f, 0.001f};
|
const float gInitNoise[2] = {0.999f, 0.001f};
|
||||||
|
|
||||||
#ifdef WEBRTC_AEC_DEBUG_DUMP
|
int16_t nearend[PART_LEN];
|
||||||
fwrite(farend, sizeof(int16_t), PART_LEN, aec->farFile);
|
int16_t* nearend_ptr = NULL;
|
||||||
fwrite(nearend, sizeof(int16_t), PART_LEN, aec->nearFile);
|
int16_t output[PART_LEN];
|
||||||
#endif
|
int16_t outputH[PART_LEN];
|
||||||
|
|
||||||
|
float* xf_ptr = NULL;
|
||||||
|
|
||||||
memset(dH, 0, sizeof(dH));
|
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 ----------
|
// ---------- Ooura fft ----------
|
||||||
// Concatenate old and new farend blocks.
|
// Concatenate old and new nearend blocks.
|
||||||
for (i = 0; i < PART_LEN; i++) {
|
for (i = 0; i < PART_LEN; i++) {
|
||||||
aec->xBuf[i + PART_LEN] = (float)farend[i];
|
d[i] = (float) (nearend_ptr[i]);
|
||||||
d[i] = (float)nearend[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);
|
memcpy(aec->dBuf + PART_LEN, d, sizeof(float) * PART_LEN);
|
||||||
// For H band
|
|
||||||
if (aec->sampFreq == 32000) {
|
#ifdef WEBRTC_AEC_DEBUG_DUMP
|
||||||
memcpy(aec->dBufH + PART_LEN, dH, sizeof(float) * PART_LEN);
|
{
|
||||||
|
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);
|
// We should always have at least one element stored in |far_buf|.
|
||||||
|
assert(WebRtc_available_read(aec->far_buf) > 0);
|
||||||
// Far fft
|
WebRtc_ReadBuffer(aec->far_buf, (void**) &xf_ptr, &xf[0][0], 1);
|
||||||
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];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Near fft
|
// Near fft
|
||||||
memcpy(fft, aec->dBuf, sizeof(float) * PART_LEN2);
|
memcpy(fft, aec->dBuf, sizeof(float) * PART_LEN2);
|
||||||
aec_rdft_forward_128(fft);
|
TimeToFrequency(fft, df, 0);
|
||||||
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];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Power smoothing
|
// Power smoothing
|
||||||
for (i = 0; i < PART_LEN1; i++) {
|
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;
|
aec->xPow[i] = gPow[0] * aec->xPow[i] + gPow[1] * NR_PART * far_spectrum;
|
||||||
// Calculate absolute spectra
|
// Calculate absolute spectra
|
||||||
abs_far_spectrum[i] = sqrtf(far_spectrum);
|
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;
|
aec->dPow[i] = gPow[0] * aec->dPow[i] + gPow[1] * near_spectrum;
|
||||||
// Calculate absolute spectra
|
// Calculate absolute spectra
|
||||||
abs_near_spectrum[i] = sqrtf(near_spectrum);
|
abs_near_spectrum[i] = sqrtf(near_spectrum);
|
||||||
@ -713,9 +766,9 @@ static void ProcessBlock(aec_t *aec, const short *farend,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Buffer xf
|
// 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);
|
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);
|
sizeof(float) * PART_LEN1);
|
||||||
|
|
||||||
memset(yf[0], 0, sizeof(float) * (PART_LEN1 * 2));
|
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);
|
memcpy(aec->eBuf + PART_LEN, e, sizeof(float) * PART_LEN);
|
||||||
memset(fft, 0, sizeof(float) * PART_LEN);
|
memset(fft, 0, sizeof(float) * PART_LEN);
|
||||||
memcpy(fft + PART_LEN, e, sizeof(float) * PART_LEN);
|
memcpy(fft + PART_LEN, e, sizeof(float) * PART_LEN);
|
||||||
|
// TODO(bjornv): Change to use TimeToFrequency().
|
||||||
aec_rdft_forward_128(fft);
|
aec_rdft_forward_128(fft);
|
||||||
|
|
||||||
ef[1][0] = 0;
|
ef[1][0] = 0;
|
||||||
@ -756,40 +810,49 @@ static void ProcessBlock(aec_t *aec, const short *farend,
|
|||||||
ef[1][i] = fft[2 * i + 1];
|
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.
|
// Scale error signal inversely with far power.
|
||||||
WebRtcAec_ScaleErrorSignal(aec, ef);
|
WebRtcAec_ScaleErrorSignal(aec, ef);
|
||||||
WebRtcAec_FilterAdaptation(aec, fft, ef);
|
WebRtcAec_FilterAdaptation(aec, fft, ef);
|
||||||
NonLinearProcessing(aec, output, outputH);
|
NonLinearProcessing(aec, output, outputH);
|
||||||
|
|
||||||
if (aec->metricsMode == 1) {
|
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
|
// Update power levels and echo metrics
|
||||||
UpdateLevel(&aec->farlevel, farend);
|
UpdateLevel(&aec->farlevel, (float (*)[PART_LEN1]) xf_ptr);
|
||||||
UpdateLevel(&aec->nearlevel, nearend);
|
UpdateLevel(&aec->nearlevel, df);
|
||||||
UpdateLevel(&aec->linoutlevel, eInt16);
|
|
||||||
UpdateLevel(&aec->nlpoutlevel, output);
|
|
||||||
UpdateMetrics(aec);
|
UpdateMetrics(aec);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WEBRTC_AEC_DEBUG_DUMP
|
// Store the output block.
|
||||||
for (i = 0; i < PART_LEN; i++) {
|
WebRtc_WriteBuffer(aec->outFrBuf, output, PART_LEN);
|
||||||
eInt16[i] = (int16_t)WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX, e[i],
|
// For H band
|
||||||
WEBRTC_SPL_WORD16_MIN);
|
if (aec->sampFreq == 32000) {
|
||||||
|
WebRtc_WriteBuffer(aec->outFrBufH, outputH, PART_LEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
fwrite(eInt16, sizeof(int16_t), PART_LEN, aec->outLinearFile);
|
#ifdef WEBRTC_AEC_DEBUG_DUMP
|
||||||
fwrite(output, sizeof(int16_t), PART_LEN, aec->outFile);
|
{
|
||||||
|
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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void NonLinearProcessing(aec_t *aec, short *output, short *outputH)
|
static void NonLinearProcessing(aec_t *aec, short *output, short *outputH)
|
||||||
{
|
{
|
||||||
float efw[2][PART_LEN1], dfw[2][PART_LEN1];
|
float efw[2][PART_LEN1], dfw[2][PART_LEN1], xfw[2][PART_LEN1];
|
||||||
complex_t xfw[PART_LEN1];
|
|
||||||
complex_t comfortNoiseHband[PART_LEN1];
|
complex_t comfortNoiseHband[PART_LEN1];
|
||||||
float fft[PART_LEN2];
|
float fft[PART_LEN2];
|
||||||
float scale, dtmp;
|
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 gCoh[2][2] = {{0.9f, 0.1f}, {0.93f, 0.07f}};
|
||||||
const float *ptrGCoh = gCoh[aec->mult - 1];
|
const float *ptrGCoh = gCoh[aec->mult - 1];
|
||||||
|
|
||||||
// Filter energey
|
// Filter energy
|
||||||
float wfEnMax = 0, wfEn = 0;
|
float wfEnMax = 0, wfEn = 0;
|
||||||
const int delayEstInterval = 10 * aec->mult;
|
const int delayEstInterval = 10 * aec->mult;
|
||||||
|
|
||||||
|
float* xfw_ptr = NULL;
|
||||||
|
|
||||||
aec->delayEstCtr++;
|
aec->delayEstCtr++;
|
||||||
if (aec->delayEstCtr == delayEstInterval) {
|
if (aec->delayEstCtr == delayEstInterval) {
|
||||||
aec->delayEstCtr = 0;
|
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
|
// NLP
|
||||||
// Windowed far fft
|
WebRtc_ReadBuffer(aec->far_buf_windowed, (void**) &xfw_ptr, &xfw[0][0], 1);
|
||||||
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];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// TODO(bjornv): Investigate if we can reuse |far_buf_windowed| instead of
|
||||||
|
// |xfwBuf|.
|
||||||
// Buffer far.
|
// Buffer far.
|
||||||
memcpy(aec->xfwBuf, xfw, sizeof(xfw));
|
memcpy(aec->xfwBuf, xfw_ptr, sizeof(float) * 2 * PART_LEN1);
|
||||||
|
|
||||||
// Use delayed far.
|
// Use delayed far.
|
||||||
memcpy(xfw, aec->xfwBuf + aec->delayIdx * PART_LEN1, sizeof(xfw));
|
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.
|
// adverse interaction with the algorithm's tuning.
|
||||||
// TODO: investigate further why this is so sensitive.
|
// TODO: investigate further why this is so sensitive.
|
||||||
aec->sx[i] = ptrGCoh[0] * aec->sx[i] + ptrGCoh[1] *
|
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] *
|
aec->sde[i][0] = ptrGCoh[0] * aec->sde[i][0] + ptrGCoh[1] *
|
||||||
(dfw[0][i] * efw[0][i] + dfw[1][i] * efw[1][i]);
|
(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]);
|
(dfw[0][i] * efw[1][i] - dfw[1][i] * efw[0][i]);
|
||||||
|
|
||||||
aec->sxd[i][0] = ptrGCoh[0] * aec->sxd[i][0] + ptrGCoh[1] *
|
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] *
|
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];
|
sdSum += aec->sd[i];
|
||||||
seSum += aec->se[i];
|
seSum += aec->se[i];
|
||||||
@ -1054,6 +1109,15 @@ static void NonLinearProcessing(aec_t *aec, short *output, short *outputH)
|
|||||||
// Add comfort noise.
|
// Add comfort noise.
|
||||||
ComfortNoise(aec, efw, comfortNoiseHband, aec->noisePow, hNl);
|
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.
|
// Inverse error fft.
|
||||||
fft[0] = efw[0][0];
|
fft[0] = efw[0][0];
|
||||||
fft[1] = efw[0][PART_LEN];
|
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.
|
// 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->dBuf, aec->dBuf + PART_LEN, sizeof(float) * PART_LEN);
|
||||||
memcpy(aec->eBuf, aec->eBuf + 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)
|
static void WebRtcAec_InitLevel(power_level_t *level)
|
||||||
{
|
{
|
||||||
const float bigFloat = 1E17f;
|
const float bigFloat = 1E17f;
|
||||||
@ -1305,37 +1314,63 @@ static void WebRtcAec_InitStats(stats_t *stats)
|
|||||||
stats->hicounter = 0;
|
stats->hicounter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void UpdateLevel(power_level_t *level, const short *in)
|
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
|
||||||
int k;
|
// 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++) {
|
// TODO(bjornv): Investigate reusing energy calculations performed at other
|
||||||
level->sfrsum += in[k] * in[k];
|
// 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++;
|
level->frcounter++;
|
||||||
|
level->frsum += level->framelevel;
|
||||||
if (level->sfrcounter > subCountLen) {
|
if (level->frcounter > countLen) {
|
||||||
level->framelevel = level->sfrsum / (subCountLen * PART_LEN);
|
level->averagelevel = level->frsum / countLen;
|
||||||
level->sfrsum = 0;
|
level->frsum = 0;
|
||||||
level->sfrcounter = 0;
|
level->frcounter = 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void UpdateMetrics(aec_t *aec)
|
static void UpdateMetrics(aec_t *aec)
|
||||||
@ -1354,7 +1389,7 @@ static void UpdateMetrics(aec_t *aec)
|
|||||||
aec->stateCounter++;
|
aec->stateCounter++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aec->farlevel.frcounter == countLen) {
|
if (aec->farlevel.frcounter == 0) {
|
||||||
|
|
||||||
if (aec->farlevel.minlevel < noisyPower) {
|
if (aec->farlevel.minlevel < noisyPower) {
|
||||||
actThreshold = actThresholdClean;
|
actThreshold = actThresholdClean;
|
||||||
@ -1400,10 +1435,11 @@ static void UpdateMetrics(aec_t *aec)
|
|||||||
|
|
||||||
// A_NLP
|
// A_NLP
|
||||||
dtmp = 10 * (float)log10(aec->nearlevel.averagelevel /
|
dtmp = 10 * (float)log10(aec->nearlevel.averagelevel /
|
||||||
aec->linoutlevel.averagelevel + 1e-10f);
|
(2 * aec->linoutlevel.averagelevel) + 1e-10f);
|
||||||
|
|
||||||
// subtract noise power
|
// 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);
|
dtmp2 = 10 * (float)log10(echo / suppressedEcho + 1e-10f);
|
||||||
|
|
||||||
@ -1430,10 +1466,11 @@ static void UpdateMetrics(aec_t *aec)
|
|||||||
// ERLE
|
// ERLE
|
||||||
|
|
||||||
// subtract noise power
|
// 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 /
|
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);
|
dtmp2 = 10 * (float)log10(echo / suppressedEcho + 1e-10f);
|
||||||
|
|
||||||
dtmp = dtmp2;
|
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_LEN 64 // Length of partition
|
||||||
#define PART_LEN1 (PART_LEN + 1) // Unique fft coefficients
|
#define PART_LEN1 (PART_LEN + 1) // Unique fft coefficients
|
||||||
#define PART_LEN2 (PART_LEN * 2) // Length of partition * 2
|
#define PART_LEN2 (PART_LEN * 2) // Length of partition * 2
|
||||||
#define NR_PART 12 // Number of partitions
|
#define NR_PART 12 // Number of partitions in filter.
|
||||||
#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 PREF_BAND_SIZE 24
|
#define PREF_BAND_SIZE 24
|
||||||
|
|
||||||
// Delay estimator constants, used for logging.
|
// Delay estimator constants, used for logging.
|
||||||
@ -76,12 +73,11 @@ typedef struct {
|
|||||||
int inSamples, outSamples;
|
int inSamples, outSamples;
|
||||||
int delayEstCtr;
|
int delayEstCtr;
|
||||||
|
|
||||||
void *farFrBuf, *nearFrBuf, *outFrBuf;
|
void *nearFrBuf, *outFrBuf;
|
||||||
|
|
||||||
void *nearFrBufH;
|
void *nearFrBufH;
|
||||||
void *outFrBufH;
|
void *outFrBufH;
|
||||||
|
|
||||||
float xBuf[PART_LEN2]; // farend
|
|
||||||
float dBuf[PART_LEN2]; // nearend
|
float dBuf[PART_LEN2]; // nearend
|
||||||
float eBuf[PART_LEN2]; // error
|
float eBuf[PART_LEN2]; // error
|
||||||
|
|
||||||
@ -114,9 +110,11 @@ typedef struct {
|
|||||||
|
|
||||||
int xfBufBlockPos;
|
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;
|
int sampFreq;
|
||||||
WebRtc_UWord32 seed;
|
WebRtc_UWord32 seed;
|
||||||
|
|
||||||
@ -147,6 +145,7 @@ typedef struct {
|
|||||||
void* delay_estimator;
|
void* delay_estimator;
|
||||||
|
|
||||||
#ifdef WEBRTC_AEC_DEBUG_DUMP
|
#ifdef WEBRTC_AEC_DEBUG_DUMP
|
||||||
|
void* far_time_buf;
|
||||||
FILE *farFile;
|
FILE *farFile;
|
||||||
FILE *nearFile;
|
FILE *nearFile;
|
||||||
FILE *outFile;
|
FILE *outFile;
|
||||||
@ -171,10 +170,10 @@ int WebRtcAec_InitAec(aec_t *aec, int sampFreq);
|
|||||||
void WebRtcAec_InitAec_SSE2(void);
|
void WebRtcAec_InitAec_SSE2(void);
|
||||||
|
|
||||||
void WebRtcAec_InitMetrics(aec_t *aec);
|
void WebRtcAec_InitMetrics(aec_t *aec);
|
||||||
void WebRtcAec_ProcessFrame(aec_t *aec, const short *farend,
|
void WebRtcAec_BufferFarendPartition(aec_t *aec, const float* farend);
|
||||||
const short *nearend, const short *nearendH,
|
void WebRtcAec_ProcessFrame(aec_t* aec,
|
||||||
short *out, short *outH,
|
const short *nearend,
|
||||||
int knownDelay);
|
const short *nearendH,
|
||||||
|
int knownDelay);
|
||||||
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AEC_MAIN_SOURCE_AEC_CORE_H_
|
|
||||||
|
|
||||||
|
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AEC_MAIN_SOURCE_AEC_CORE_H_
|
||||||
|
@ -21,11 +21,10 @@
|
|||||||
|
|
||||||
#include "aec_core.h"
|
#include "aec_core.h"
|
||||||
|
|
||||||
enum { kFrameBufferSize = FRAME_LEN * 4 };
|
|
||||||
enum { kEstimateLengthFrames = 400 };
|
enum { kEstimateLengthFrames = 400 };
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
short buffer[kFrameBufferSize];
|
short buffer[kResamplerBufferSize];
|
||||||
float position;
|
float position;
|
||||||
|
|
||||||
int deviceSampleRateHz;
|
int deviceSampleRateHz;
|
||||||
@ -128,7 +127,7 @@ int WebRtcAec_ResampleLinear(void *resampInst,
|
|||||||
// Shift buffer
|
// Shift buffer
|
||||||
memmove(obj->buffer,
|
memmove(obj->buffer,
|
||||||
&obj->buffer[size],
|
&obj->buffer[size],
|
||||||
(kFrameBufferSize - size) * sizeof(short));
|
(kResamplerBufferSize - size) * sizeof(short));
|
||||||
|
|
||||||
return outsize;
|
return outsize;
|
||||||
}
|
}
|
||||||
|
@ -8,10 +8,13 @@
|
|||||||
* be found in the AUTHORS file in the root of the source tree.
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AEC_MAIN_SOURCE_RESAMPLER_H_
|
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_RESAMPLER_H_
|
||||||
#define WEBRTC_MODULES_AUDIO_PROCESSING_AEC_MAIN_SOURCE_RESAMPLER_H_
|
#define WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_RESAMPLER_H_
|
||||||
|
|
||||||
|
#include "aec_core.h"
|
||||||
|
|
||||||
enum { kResamplingDelay = 1 };
|
enum { kResamplingDelay = 1 };
|
||||||
|
enum { kResamplerBufferSize = FRAME_LEN * 4 };
|
||||||
|
|
||||||
// Unless otherwise specified, functions return 0 on success and -1 on error
|
// Unless otherwise specified, functions return 0 on success and -1 on error
|
||||||
int WebRtcAec_CreateResampler(void **resampInst);
|
int WebRtcAec_CreateResampler(void **resampInst);
|
||||||
@ -29,4 +32,4 @@ int WebRtcAec_ResampleLinear(void *resampInst,
|
|||||||
float skew,
|
float skew,
|
||||||
short *outspeech);
|
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_core.h"
|
||||||
#include "aec_resampler.h"
|
#include "aec_resampler.h"
|
||||||
#include "ring_buffer.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
|
// Maximum length of resampled signal. Must be an integer multiple of frames
|
||||||
// (ceil(1/(1 + MIN_SKEW)*2) + 1)*FRAME_LEN
|
// (ceil(1/(1 + MIN_SKEW)*2) + 1)*FRAME_LEN
|
||||||
// The factor of 2 handles wb, and the + 1 is as a safety margin
|
// The factor of 2 handles wb, and the + 1 is as a safety margin
|
||||||
|
// TODO(bjornv): Replace with kResamplerBufferSize
|
||||||
#define MAX_RESAMP_LEN (5 * FRAME_LEN)
|
#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
|
static const int sampMsNb = 8; // samples per ms in nb
|
||||||
// Target suppression levels for nlp modes
|
// Target suppression levels for nlp modes
|
||||||
// log{0.001, 0.00001, 0.00000001}
|
// log{0.001, 0.00001, 0.00000001}
|
||||||
@ -52,37 +53,34 @@ typedef struct {
|
|||||||
short autoOnOff;
|
short autoOnOff;
|
||||||
short activity;
|
short activity;
|
||||||
short skewMode;
|
short skewMode;
|
||||||
short bufSizeStart;
|
int bufSizeStart;
|
||||||
//short bufResetCtr; // counts number of noncausal frames
|
//short bufResetCtr; // counts number of noncausal frames
|
||||||
int knownDelay;
|
int knownDelay;
|
||||||
|
|
||||||
// Stores the last frame added to the farend buffer
|
|
||||||
short farendOld[2][FRAME_LEN];
|
|
||||||
short initFlag; // indicates if AEC has been initialized
|
short initFlag; // indicates if AEC has been initialized
|
||||||
|
|
||||||
// Variables used for averaging far end buffer size
|
// Variables used for averaging far end buffer size
|
||||||
short counter;
|
short counter;
|
||||||
short sum;
|
int sum;
|
||||||
short firstVal;
|
short firstVal;
|
||||||
short checkBufSizeCtr;
|
short checkBufSizeCtr;
|
||||||
|
|
||||||
// Variables used for delay shifts
|
// Variables used for delay shifts
|
||||||
short msInSndCardBuf;
|
short msInSndCardBuf;
|
||||||
short filtDelay;
|
short filtDelay; // Filtered delay estimate.
|
||||||
int timeForDelayChange;
|
int timeForDelayChange;
|
||||||
int ECstartup;
|
int ECstartup;
|
||||||
int checkBuffSize;
|
int checkBuffSize;
|
||||||
int delayChange;
|
|
||||||
short lastDelayDiff;
|
short lastDelayDiff;
|
||||||
|
|
||||||
#ifdef WEBRTC_AEC_DEBUG_DUMP
|
#ifdef WEBRTC_AEC_DEBUG_DUMP
|
||||||
|
void* far_pre_buf_s16; // Time domain far-end pre-buffer in int16_t.
|
||||||
FILE *bufFile;
|
FILE *bufFile;
|
||||||
FILE *delayFile;
|
FILE *delayFile;
|
||||||
FILE *skewFile;
|
FILE *skewFile;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Structures
|
// Structures
|
||||||
void *farendBuf;
|
|
||||||
void *resampler;
|
void *resampler;
|
||||||
|
|
||||||
int skewFrCtr;
|
int skewFrCtr;
|
||||||
@ -90,17 +88,16 @@ typedef struct {
|
|||||||
int highSkewCtr;
|
int highSkewCtr;
|
||||||
float skew;
|
float skew;
|
||||||
|
|
||||||
|
void* far_pre_buf; // Time domain far-end pre-buffer.
|
||||||
|
|
||||||
int lastError;
|
int lastError;
|
||||||
|
|
||||||
aec_t *aec;
|
aec_t *aec;
|
||||||
} aecpc_t;
|
} 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)
|
// (controlled by knownDelay)
|
||||||
static int EstBufDelay(aecpc_t *aecInst, short msInSndCardBuf);
|
static int EstBufDelay(aecpc_t *aecInst);
|
||||||
|
|
||||||
// Stuffs the farend buffer if the estimated delay is too large
|
|
||||||
static int DelayComp(aecpc_t *aecInst);
|
|
||||||
|
|
||||||
WebRtc_Word32 WebRtcAec_Create(void **aecInst)
|
WebRtc_Word32 WebRtcAec_Create(void **aecInst)
|
||||||
{
|
{
|
||||||
@ -121,13 +118,17 @@ WebRtc_Word32 WebRtcAec_Create(void **aecInst)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (WebRtcApm_CreateBuffer(&aecpc->farendBuf, bufSizeSamp) == -1) {
|
if (WebRtcAec_CreateResampler(&aecpc->resampler) == -1) {
|
||||||
WebRtcAec_Free(aecpc);
|
WebRtcAec_Free(aecpc);
|
||||||
aecpc = NULL;
|
aecpc = NULL;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
// Create far-end pre-buffer. The buffer size has to be large enough for
|
||||||
if (WebRtcAec_CreateResampler(&aecpc->resampler) == -1) {
|
// 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);
|
WebRtcAec_Free(aecpc);
|
||||||
aecpc = NULL;
|
aecpc = NULL;
|
||||||
return -1;
|
return -1;
|
||||||
@ -137,6 +138,13 @@ WebRtc_Word32 WebRtcAec_Create(void **aecInst)
|
|||||||
aecpc->lastError = 0;
|
aecpc->lastError = 0;
|
||||||
|
|
||||||
#ifdef WEBRTC_AEC_DEBUG_DUMP
|
#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];
|
char filename[64];
|
||||||
sprintf(filename, "aec_far%d.pcm", instance_count);
|
sprintf(filename, "aec_far%d.pcm", instance_count);
|
||||||
@ -168,7 +176,10 @@ WebRtc_Word32 WebRtcAec_Free(void *aecInst)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WebRtc_FreeBuffer(aecpc->far_pre_buf);
|
||||||
|
|
||||||
#ifdef WEBRTC_AEC_DEBUG_DUMP
|
#ifdef WEBRTC_AEC_DEBUG_DUMP
|
||||||
|
WebRtc_FreeBuffer(aecpc->far_pre_buf_s16);
|
||||||
fclose(aecpc->aec->farFile);
|
fclose(aecpc->aec->farFile);
|
||||||
fclose(aecpc->aec->nearFile);
|
fclose(aecpc->aec->nearFile);
|
||||||
fclose(aecpc->aec->outFile);
|
fclose(aecpc->aec->outFile);
|
||||||
@ -179,7 +190,6 @@ WebRtc_Word32 WebRtcAec_Free(void *aecInst)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
WebRtcAec_FreeAec(aecpc->aec);
|
WebRtcAec_FreeAec(aecpc->aec);
|
||||||
WebRtcApm_FreeBuffer(aecpc->farendBuf);
|
|
||||||
WebRtcAec_FreeResampler(aecpc->resampler);
|
WebRtcAec_FreeResampler(aecpc->resampler);
|
||||||
free(aecpc);
|
free(aecpc);
|
||||||
|
|
||||||
@ -213,17 +223,17 @@ WebRtc_Word32 WebRtcAec_Init(void *aecInst, WebRtc_Word32 sampFreq, WebRtc_Word3
|
|||||||
return -1;
|
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) {
|
if (WebRtcAec_InitResampler(aecpc->resampler, aecpc->scSampFreq) == -1) {
|
||||||
aecpc->lastError = AEC_UNSPECIFIED_ERROR;
|
aecpc->lastError = AEC_UNSPECIFIED_ERROR;
|
||||||
return -1;
|
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
|
aecpc->initFlag = initCheck; // indicates that initialization has been done
|
||||||
|
|
||||||
if (aecpc->sampFreq == 32000) {
|
if (aecpc->sampFreq == 32000) {
|
||||||
@ -236,7 +246,6 @@ WebRtc_Word32 WebRtcAec_Init(void *aecInst, WebRtc_Word32 sampFreq, WebRtc_Word3
|
|||||||
aecpc->skewFrCtr = 0;
|
aecpc->skewFrCtr = 0;
|
||||||
aecpc->activity = 0;
|
aecpc->activity = 0;
|
||||||
|
|
||||||
aecpc->delayChange = 1;
|
|
||||||
aecpc->delayCtr = 0;
|
aecpc->delayCtr = 0;
|
||||||
|
|
||||||
aecpc->sum = 0;
|
aecpc->sum = 0;
|
||||||
@ -248,7 +257,7 @@ WebRtc_Word32 WebRtcAec_Init(void *aecInst, WebRtc_Word32 sampFreq, WebRtc_Word3
|
|||||||
aecpc->bufSizeStart = 0;
|
aecpc->bufSizeStart = 0;
|
||||||
aecpc->checkBufSizeCtr = 0;
|
aecpc->checkBufSizeCtr = 0;
|
||||||
aecpc->filtDelay = 0;
|
aecpc->filtDelay = 0;
|
||||||
aecpc->timeForDelayChange =0;
|
aecpc->timeForDelayChange = 0;
|
||||||
aecpc->knownDelay = 0;
|
aecpc->knownDelay = 0;
|
||||||
aecpc->lastDelayDiff = 0;
|
aecpc->lastDelayDiff = 0;
|
||||||
|
|
||||||
@ -257,8 +266,6 @@ WebRtc_Word32 WebRtcAec_Init(void *aecInst, WebRtc_Word32 sampFreq, WebRtc_Word3
|
|||||||
aecpc->highSkewCtr = 0;
|
aecpc->highSkewCtr = 0;
|
||||||
aecpc->sampFactor = (aecpc->scSampFreq * 1.0f) / aecpc->splitSampFreq;
|
aecpc->sampFactor = (aecpc->scSampFreq * 1.0f) / aecpc->splitSampFreq;
|
||||||
|
|
||||||
memset(&aecpc->farendOld[0][0], 0, 160);
|
|
||||||
|
|
||||||
// Default settings.
|
// Default settings.
|
||||||
aecConfig.nlpMode = kAecNlpModerate;
|
aecConfig.nlpMode = kAecNlpModerate;
|
||||||
aecConfig.skewMode = kAecFalse;
|
aecConfig.skewMode = kAecFalse;
|
||||||
@ -270,6 +277,14 @@ WebRtc_Word32 WebRtcAec_Init(void *aecInst, WebRtc_Word32 sampFreq, WebRtc_Word3
|
|||||||
return -1;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,9 +294,13 @@ WebRtc_Word32 WebRtcAec_BufferFarend(void *aecInst, const WebRtc_Word16 *farend,
|
|||||||
{
|
{
|
||||||
aecpc_t *aecpc = aecInst;
|
aecpc_t *aecpc = aecInst;
|
||||||
WebRtc_Word32 retVal = 0;
|
WebRtc_Word32 retVal = 0;
|
||||||
short newNrOfSamples;
|
int newNrOfSamples = (int) nrOfSamples;
|
||||||
short newFarend[MAX_RESAMP_LEN];
|
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;
|
float skew;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
if (aecpc == NULL) {
|
if (aecpc == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
@ -305,11 +324,6 @@ WebRtc_Word32 WebRtcAec_BufferFarend(void *aecInst, const WebRtc_Word16 *farend,
|
|||||||
|
|
||||||
skew = aecpc->skew;
|
skew = aecpc->skew;
|
||||||
|
|
||||||
// TODO: Is this really a good idea?
|
|
||||||
if (!aecpc->ECstartup) {
|
|
||||||
DelayComp(aecpc);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (aecpc->skewMode == kAecTrue && aecpc->resample == kAecTrue) {
|
if (aecpc->skewMode == kAecTrue && aecpc->resample == kAecTrue) {
|
||||||
// Resample and get a new number of samples
|
// Resample and get a new number of samples
|
||||||
newNrOfSamples = WebRtcAec_ResampleLinear(aecpc->resampler,
|
newNrOfSamples = WebRtcAec_ResampleLinear(aecpc->resampler,
|
||||||
@ -317,10 +331,38 @@ WebRtc_Word32 WebRtcAec_BufferFarend(void *aecInst, const WebRtc_Word16 *farend,
|
|||||||
nrOfSamples,
|
nrOfSamples,
|
||||||
skew,
|
skew,
|
||||||
newFarend);
|
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;
|
return retVal;
|
||||||
@ -333,8 +375,6 @@ WebRtc_Word32 WebRtcAec_Process(void *aecInst, const WebRtc_Word16 *nearend,
|
|||||||
aecpc_t *aecpc = aecInst;
|
aecpc_t *aecpc = aecInst;
|
||||||
WebRtc_Word32 retVal = 0;
|
WebRtc_Word32 retVal = 0;
|
||||||
short i;
|
short i;
|
||||||
short farend[FRAME_LEN];
|
|
||||||
short nmbrOfFilledBuffers;
|
|
||||||
short nBlocks10ms;
|
short nBlocks10ms;
|
||||||
short nFrames;
|
short nFrames;
|
||||||
// Limit resampling to doubling/halving of signal
|
// 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.
|
// Only needed if they don't already point to the same place.
|
||||||
memcpy(out, nearend, sizeof(short) * nrOfSamples);
|
memcpy(out, nearend, sizeof(short) * nrOfSamples);
|
||||||
}
|
}
|
||||||
nmbrOfFilledBuffers = WebRtcApm_get_buffer_size(aecpc->farendBuf) / FRAME_LEN;
|
|
||||||
|
|
||||||
// The AEC is in the start up mode
|
// 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) {
|
if (aecpc->checkBuffSize) {
|
||||||
|
|
||||||
aecpc->checkBufSizeCtr++;
|
aecpc->checkBufSizeCtr++;
|
||||||
// Before we fill up the far end buffer we require the amount of data on the
|
// Before we fill up the far-end buffer we require the system delay
|
||||||
// sound card to be stable (+/-8 ms) compared to the first value. This
|
// to be stable (+/-8 ms) compared to the first value. This
|
||||||
// comparison is made during the following 4 consecutive frames. If it seems
|
// comparison is made during the following 6 consecutive 10 ms
|
||||||
// to be stable then we start to fill up the far end buffer.
|
// blocks. If it seems to be stable then we start to fill up the
|
||||||
|
// far-end buffer.
|
||||||
if (aecpc->counter == 0) {
|
if (aecpc->counter == 0) {
|
||||||
aecpc->firstVal = aecpc->msInSndCardBuf;
|
aecpc->firstVal = aecpc->msInSndCardBuf;
|
||||||
aecpc->sum = 0;
|
aecpc->sum = 0;
|
||||||
@ -455,79 +493,107 @@ WebRtc_Word32 WebRtcAec_Process(void *aecInst, const WebRtc_Word16 *nearend,
|
|||||||
aecpc->counter = 0;
|
aecpc->counter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aecpc->counter*nBlocks10ms >= 6) {
|
if (aecpc->counter * nBlocks10ms >= 6) {
|
||||||
// The farend buffer size is determined in blocks of 80 samples
|
// The far-end buffer size is determined in partitions of
|
||||||
// Use 75% of the average value of the soundcard buffer
|
// PART_LEN samples. Use 75% of the average value of the system
|
||||||
aecpc->bufSizeStart = WEBRTC_SPL_MIN((int) (0.75 * (aecpc->sum *
|
// delay as buffer size to start with.
|
||||||
aecpc->aec->mult) / (aecpc->counter * 10)), BUF_SIZE_FRAMES);
|
aecpc->bufSizeStart = WEBRTC_SPL_MIN((3 * aecpc->sum *
|
||||||
// buffersize has now been determined
|
aecpc->aec->mult * 8) / (4 * aecpc->counter * PART_LEN),
|
||||||
|
kMaxBufSizeStart);
|
||||||
|
// Buffer size has now been determined.
|
||||||
aecpc->checkBuffSize = 0;
|
aecpc->checkBuffSize = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aecpc->checkBufSizeCtr * nBlocks10ms > 50) {
|
if (aecpc->checkBufSizeCtr * nBlocks10ms > 50) {
|
||||||
// for really bad sound cards, don't disable echocanceller for more than 0.5 sec
|
// For really bad systems, don't disable the echo canceller for
|
||||||
aecpc->bufSizeStart = WEBRTC_SPL_MIN((int) (0.75 * (aecpc->msInSndCardBuf *
|
// more than 0.5 sec.
|
||||||
aecpc->aec->mult) / 10), BUF_SIZE_FRAMES);
|
aecpc->bufSizeStart = WEBRTC_SPL_MIN((aecpc->msInSndCardBuf *
|
||||||
|
aecpc->aec->mult * 3) / 40, kMaxBufSizeStart);
|
||||||
aecpc->checkBuffSize = 0;
|
aecpc->checkBuffSize = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if checkBuffSize changed in the if-statement above
|
// If |checkBuffSize| changed in the if-statement above.
|
||||||
if (!aecpc->checkBuffSize) {
|
if (!aecpc->checkBuffSize) {
|
||||||
// soundcard buffer is now reasonably stable
|
// The system delay is now reasonably stable (or has been unstable
|
||||||
// When the far end buffer is filled with approximately the same amount of
|
// for too long). When the far-end buffer is filled with
|
||||||
// data as the amount on the sound card we end the start up phase and start
|
// approximately the same amount of data as reported by the system
|
||||||
// to cancel echoes.
|
// 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) {
|
// Enable the AEC
|
||||||
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);
|
|
||||||
aecpc->ECstartup = 0;
|
aecpc->ECstartup = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// AEC is enabled.
|
||||||
|
|
||||||
}
|
int out_elements = 0;
|
||||||
else {
|
|
||||||
// AEC is enabled
|
|
||||||
|
|
||||||
// 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++) {
|
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
|
// Call the AEC.
|
||||||
if (nmbrOfFilledBuffers > 0) {
|
WebRtcAec_ProcessFrame(aecpc->aec,
|
||||||
// Get the next 80 samples from the farend buffer
|
&nearend[FRAME_LEN * i],
|
||||||
WebRtcApm_ReadBuffer(aecpc->farendBuf, farend, FRAME_LEN);
|
&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
|
// Stuff the out buffer if we have less than a frame to output.
|
||||||
memcpy(&(aecpc->farendOld[i][0]), farend, FRAME_LEN * sizeof(short));
|
// This should only happen for the first frame.
|
||||||
}
|
out_elements = (int) WebRtc_available_read(aecpc->aec->outFrBuf);
|
||||||
else {
|
if (out_elements < FRAME_LEN) {
|
||||||
// We have no data so we use the last played frame
|
WebRtc_MoveReadPtr(aecpc->aec->outFrBuf,
|
||||||
memcpy(farend, &(aecpc->farendOld[i][0]), FRAME_LEN * sizeof(short));
|
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,
|
// Obtain an output frame.
|
||||||
// i.e. i = 0 for NB and i = 1 for WB or SWB
|
WebRtc_ReadBuffer(aecpc->aec->outFrBuf, (void**) &out_ptr,
|
||||||
if ((i == 0 && aecpc->splitSampFreq == 8000) ||
|
out_tmp, FRAME_LEN);
|
||||||
(i == 1 && (aecpc->splitSampFreq == 16000))) {
|
memcpy(&out[FRAME_LEN * i], out_ptr, sizeof(int16_t) * FRAME_LEN);
|
||||||
EstBufDelay(aecpc, aecpc->msInSndCardBuf);
|
// 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
|
#ifdef WEBRTC_AEC_DEBUG_DUMP
|
||||||
{
|
{
|
||||||
short msInAECBuf = WebRtcApm_get_buffer_size(aecpc->farendBuf) /
|
int16_t far_buf_size_ms = (int16_t) (aecpc->aec->system_delay /
|
||||||
(sampMsNb*aecpc->aec->mult);
|
(sampMsNb * aecpc->aec->mult));
|
||||||
fwrite(&msInAECBuf, 2, 1, aecpc->bufFile);
|
fwrite(&far_buf_size_ms, 2, 1, aecpc->bufFile);
|
||||||
fwrite(&(aecpc->knownDelay), sizeof(aecpc->knownDelay), 1, aecpc->delayFile);
|
fwrite(&(aecpc->knownDelay), sizeof(aecpc->knownDelay), 1, aecpc->delayFile);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -828,80 +894,47 @@ WebRtc_Word32 WebRtcAec_get_error_code(void *aecInst)
|
|||||||
return aecpc->lastError;
|
return aecpc->lastError;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int EstBufDelay(aecpc_t *aecpc, short msInSndCardBuf)
|
static int EstBufDelay(aecpc_t* aecpc) {
|
||||||
{
|
int nSampSndCard = aecpc->msInSndCardBuf * sampMsNb * aecpc->aec->mult;
|
||||||
short delayNew, nSampFar, nSampSndCard;
|
int current_delay = nSampSndCard - aecpc->aec->system_delay;
|
||||||
short diff;
|
int delay_difference = 0;
|
||||||
|
|
||||||
nSampFar = WebRtcApm_get_buffer_size(aecpc->farendBuf);
|
// Before we proceed with the delay estimate filtering we:
|
||||||
nSampSndCard = msInSndCardBuf * sampMsNb * aecpc->aec->mult;
|
// 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
|
// 2) Account for resampling frame delay.
|
||||||
if (aecpc->skewMode == kAecTrue && aecpc->resample == kAecTrue) {
|
if (aecpc->skewMode == kAecTrue && aecpc->resample == kAecTrue) {
|
||||||
delayNew -= kResamplingDelay;
|
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) {
|
if (aecpc->timeForDelayChange > 25) {
|
||||||
WebRtcApm_FlushBuffer(aecpc->farendBuf, FRAME_LEN);
|
aecpc->knownDelay = WEBRTC_SPL_MAX((int) aecpc->filtDelay - 160, 0);
|
||||||
delayNew += FRAME_LEN;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
aecpc->filtDelay = WEBRTC_SPL_MAX(0, (short)(0.8*aecpc->filtDelay + 0.2*delayNew));
|
return 0;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
@ -8,232 +8,469 @@
|
|||||||
* be found in the AUTHORS file in the root of the source tree.
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
// A ring buffer to hold arbitrary data. Provides no thread safety. Unless
|
||||||
* Provides a generic ring buffer that can be written to and read from with
|
// otherwise specified, functions return 0 on success and -1 on error.
|
||||||
* arbitrarily sized blocks. The AEC uses this for several different tasks.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "ring_buffer.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 {
|
typedef struct {
|
||||||
int readPos;
|
int readPos;
|
||||||
int writePos;
|
int writePos;
|
||||||
int size;
|
int size;
|
||||||
|
int element_size;
|
||||||
char rwWrap;
|
char rwWrap;
|
||||||
bufdata_t *data;
|
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;
|
} buf_t;
|
||||||
|
|
||||||
enum {SAME_WRAP, DIFF_WRAP};
|
enum { SAME_WRAP, DIFF_WRAP };
|
||||||
|
|
||||||
int WebRtcApm_CreateBuffer(void **bufInst, int size)
|
// Get address of region(s) from which we can read data.
|
||||||
{
|
// If the region is contiguous, |data_ptr_bytes_2| will be zero.
|
||||||
buf_t *buf = NULL;
|
// 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) {
|
const size_t readable_elements = WebRtc_available_read(buf);
|
||||||
return -1;
|
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));
|
// Check to see if read is not contiguous.
|
||||||
*bufInst = buf;
|
if (read_elements > margin) {
|
||||||
if (buf == NULL) {
|
// Write data in two blocks that wrap the buffer.
|
||||||
return -1;
|
*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));
|
return read_elements;
|
||||||
if (buf->data == NULL) {
|
|
||||||
free(buf);
|
|
||||||
buf = NULL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
buf->size = size;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int WebRtcApm_InitBuffer(void *bufInst)
|
int WebRtcApm_CreateBuffer(void **bufInst, int size) {
|
||||||
{
|
tmp_buf_t *buf = NULL;
|
||||||
buf_t *buf = (buf_t*)bufInst;
|
|
||||||
|
|
||||||
buf->readPos = 0;
|
if (size < 0) {
|
||||||
buf->writePos = 0;
|
return -1;
|
||||||
buf->rwWrap = SAME_WRAP;
|
}
|
||||||
|
|
||||||
// Initialize buffer to zeros
|
buf = malloc(sizeof(tmp_buf_t));
|
||||||
memset(buf->data, 0, sizeof(bufdata_t)*buf->size);
|
*bufInst = buf;
|
||||||
|
if (buf == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
buf->data = malloc(size * sizeof(bufdata_t));
|
||||||
}
|
if (buf->data == NULL) {
|
||||||
|
|
||||||
int WebRtcApm_FreeBuffer(void *bufInst)
|
|
||||||
{
|
|
||||||
buf_t *buf = (buf_t*)bufInst;
|
|
||||||
|
|
||||||
if (buf == NULL) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(buf->data);
|
|
||||||
free(buf);
|
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;
|
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)
|
size_t WebRtc_WriteBuffer(void* handle,
|
||||||
{
|
const void* data,
|
||||||
buf_t *buf = (buf_t*)bufInst;
|
size_t element_count) {
|
||||||
int n = 0, margin = 0;
|
|
||||||
|
|
||||||
if (size <= 0 || size > buf->size) {
|
buf_t* self = (buf_t*) handle;
|
||||||
return -1;
|
|
||||||
|
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;
|
return write_elements;
|
||||||
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)
|
int WebRtc_MoveReadPtr(void* handle, int element_count) {
|
||||||
{
|
|
||||||
buf_t *buf = (buf_t*)bufInst;
|
|
||||||
int n = 0, margin = 0;
|
|
||||||
|
|
||||||
if (size < 0 || size > buf->size) {
|
buf_t* self = (buf_t*) handle;
|
||||||
return -1;
|
|
||||||
|
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;
|
read_pos += element_count;
|
||||||
if (buf->rwWrap == SAME_WRAP) {
|
if (read_pos > (int) self->element_count) {
|
||||||
margin = buf->size - buf->writePos;
|
// Buffer wrap around. Restart read position and wrap indicator.
|
||||||
if (n > margin) {
|
read_pos -= (int) self->element_count;
|
||||||
buf->rwWrap = DIFF_WRAP;
|
self->rw_wrap = SAME_WRAP;
|
||||||
memcpy(buf->data + buf->writePos, data,
|
}
|
||||||
sizeof(bufdata_t)*margin);
|
if (read_pos < 0) {
|
||||||
buf->writePos = 0;
|
// Buffer wrap around. Restart read position and wrap indicator.
|
||||||
n = size - margin;
|
read_pos += (int) self->element_count;
|
||||||
}
|
self->rw_wrap = DIFF_WRAP;
|
||||||
else {
|
|
||||||
memcpy(buf->data + buf->writePos, data,
|
|
||||||
sizeof(bufdata_t)*n);
|
|
||||||
buf->writePos += n;
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buf->rwWrap == DIFF_WRAP) {
|
self->read_pos = (size_t) read_pos;
|
||||||
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;
|
return element_count;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int WebRtcApm_FlushBuffer(void *bufInst, int size)
|
size_t WebRtc_available_read(const void* handle) {
|
||||||
{
|
const buf_t* self = (buf_t*) handle;
|
||||||
buf_t *buf = (buf_t*)bufInst;
|
|
||||||
int n = 0, margin = 0;
|
|
||||||
|
|
||||||
if (size <= 0 || size > buf->size) {
|
if (self == NULL) {
|
||||||
return -1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
n = size;
|
if (self->rw_wrap == SAME_WRAP) {
|
||||||
if (buf->rwWrap == DIFF_WRAP) {
|
return self->write_pos - self->read_pos;
|
||||||
margin = buf->size - buf->readPos;
|
} else {
|
||||||
if (n > margin) {
|
return self->element_count - self->read_pos + self->write_pos;
|
||||||
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)
|
size_t WebRtc_available_write(const void* handle) {
|
||||||
{
|
const buf_t* self = (buf_t*) handle;
|
||||||
buf_t *buf = (buf_t*)bufInst;
|
|
||||||
int n = 0, margin = 0;
|
|
||||||
|
|
||||||
if (size <= 0 || size > buf->size) {
|
if (self == NULL) {
|
||||||
return -1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
n = size;
|
return self->element_count - WebRtc_available_read(handle);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
@ -8,17 +8,23 @@
|
|||||||
* be found in the AUTHORS file in the root of the source tree.
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
// A ring buffer to hold arbitrary data. Provides no thread safety. Unless
|
||||||
* Specifies the interface for the AEC generic buffer.
|
// otherwise specified, functions return 0 on success and -1 on error.
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_UTILITY_RING_BUFFER_H_
|
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_UTILITY_RING_BUFFER_H_
|
||||||
#define WEBRTC_MODULES_AUDIO_PROCESSING_UTILITY_RING_BUFFER_H_
|
#define WEBRTC_MODULES_AUDIO_PROCESSING_UTILITY_RING_BUFFER_H_
|
||||||
|
|
||||||
// Determines buffer datatype
|
#include <stddef.h> // size_t
|
||||||
typedef short bufdata_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_CreateBuffer(void **bufInst, int size);
|
||||||
int WebRtcApm_InitBuffer(void *bufInst);
|
int WebRtcApm_InitBuffer(void *bufInst);
|
||||||
int WebRtcApm_FreeBuffer(void *bufInst);
|
int WebRtcApm_FreeBuffer(void *bufInst);
|
||||||
@ -38,4 +44,39 @@ int WebRtcApm_StuffBuffer(void *bufInst, int size);
|
|||||||
// Returns number of samples in buffer
|
// Returns number of samples in buffer
|
||||||
int WebRtcApm_get_buffer_size(const void *bufInst);
|
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_
|
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_UTILITY_RING_BUFFER_H_
|
||||||
|
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user