/*
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
/* Resamples a signal to an arbitrary rate. Used by the AEC to compensate for clock
* skew by resampling the farend signal.
*/
#include "aec_resampler.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "aec_core.h"
enum { kEstimateLengthFrames = 400 };
typedef struct {
short buffer[kResamplerBufferSize];
float position;
int deviceSampleRateHz;
int skewData[kEstimateLengthFrames];
int skewDataIndex;
float skewEstimate;
} resampler_t;
static int EstimateSkew(const int* rawSkew,
int size,
int absLimit,
float *skewEst);
int WebRtcAec_CreateResampler(void **resampInst)
{
resampler_t *obj = malloc(sizeof(resampler_t));
*resampInst = obj;
if (obj == NULL) {
return -1;
}
return 0;
}
int WebRtcAec_InitResampler(void *resampInst, int deviceSampleRateHz)
{
resampler_t *obj = (resampler_t*) resampInst;
memset(obj->buffer, 0, sizeof(obj->buffer));
obj->position = 0.0;
obj->deviceSampleRateHz = deviceSampleRateHz;
memset(obj->skewData, 0, sizeof(obj->skewData));
obj->skewDataIndex = 0;
obj->skewEstimate = 0.0;
return 0;
}
int WebRtcAec_FreeResampler(void *resampInst)
{
resampler_t *obj = (resampler_t*) resampInst;
free(obj);
return 0;
}
int WebRtcAec_ResampleLinear(void *resampInst,
const short *inspeech,
int size,
float skew,
short *outspeech)
{
resampler_t *obj = (resampler_t*) resampInst;
short *y;
float be, tnew, interp;
int tn, outsize, mm;
if (size < 0 || size > 2 * FRAME_LEN) {
return -1;
}
// Add new frame data in lookahead
memcpy(&obj->buffer[FRAME_LEN + kResamplingDelay],
inspeech,
size * sizeof(short));
// Sample rate ratio
be = 1 + skew;
// Loop over input frame
mm = 0;
y = &obj->buffer[FRAME_LEN]; // Point at current frame
tnew = be * mm + obj->position;
tn = (int) tnew;
while (tn < size) {
// Interpolation
interp = y[tn] + (tnew - tn) * (y[tn+1] - y[tn]);
if (interp > 32767) {
interp = 32767;
}
else if (interp < -32768) {
interp = -32768;
}
outspeech[mm] = (short) interp;
mm++;
tnew = be * mm + obj->position;
tn = (int) tnew;
}
outsize = mm;
obj->position += outsize * be - size;
// Shift buffer
memmove(obj->buffer,
&obj->buffer[size],
(kResamplerBufferSize - size) * sizeof(short));
return outsize;
}
int WebRtcAec_GetSkew(void *resampInst, int rawSkew, float *skewEst)
{
resampler_t *obj = (resampler_t*)resampInst;
int err = 0;
if (obj->skewDataIndex < kEstimateLengthFrames) {
obj->skewData[obj->skewDataIndex] = rawSkew;
obj->skewDataIndex++;
}
else if (obj->skewDataIndex == kEstimateLengthFrames) {
err = EstimateSkew(obj->skewData,
kEstimateLengthFrames,
obj->deviceSampleRateHz,
skewEst);
obj->skewEstimate = *skewEst;
obj->skewDataIndex++;
}
else {
*skewEst = obj->skewEstimate;
}
return err;
}
int EstimateSkew(const int* rawSkew,
int size,
int deviceSampleRateHz,
float *skewEst)
{
const int absLimitOuter = (int)(0.04f * deviceSampleRateHz);
const int absLimitInner = (int)(0.0025f * deviceSampleRateHz);
int i = 0;
int n = 0;
float rawAvg = 0;
float err = 0;
float rawAbsDev = 0;
int upperLimit = 0;
int lowerLimit = 0;
float cumSum = 0;
float x = 0;
float x2 = 0;
float y = 0;
float xy = 0;
float xAvg = 0;
float denom = 0;
float skew = 0;
*skewEst = 0; // Set in case of error below.
for (i = 0; i < size; i++) {
if ((rawSkew[i] < absLimitOuter && rawSkew[i] > -absLimitOuter)) {
n++;
rawAvg += rawSkew[i];
}
}
if (n == 0) {
return -1;
}
assert(n > 0);
rawAvg /= n;
for (i = 0; i < size; i++) {
if ((rawSkew[i] < absLimitOuter && rawSkew[i] > -absLimitOuter)) {
err = rawSkew[i] - rawAvg;
rawAbsDev += err >= 0 ? err : -err;
}
}
assert(n > 0);
rawAbsDev /= n;
upperLimit = (int)(rawAvg + 5 * rawAbsDev + 1); // +1 for ceiling.
lowerLimit = (int)(rawAvg - 5 * rawAbsDev - 1); // -1 for floor.
n = 0;
for (i = 0; i < size; i++) {
if ((rawSkew[i] < absLimitInner && rawSkew[i] > -absLimitInner) ||
(rawSkew[i] < upperLimit && rawSkew[i] > lowerLimit)) {
n++;
cumSum += rawSkew[i];
x += n;
x2 += n*n;
y += cumSum;
xy += n * cumSum;
}
}
if (n == 0) {
return -1;
}
assert(n > 0);
xAvg = x / n;
denom = x2 - xAvg*x;
if (denom != 0) {
skew = (xy - xAvg*y) / denom;
}
*skewEst = skew;
return 0;
}