diff options
Diffstat (limited to 'src/modules/audio_processing/aec/aec_resampler.c')
-rw-r--r-- | src/modules/audio_processing/aec/aec_resampler.c | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/src/modules/audio_processing/aec/aec_resampler.c b/src/modules/audio_processing/aec/aec_resampler.c new file mode 100644 index 0000000000..ea980cd96a --- /dev/null +++ b/src/modules/audio_processing/aec/aec_resampler.c @@ -0,0 +1,233 @@ +/* + * 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; +} |