aboutsummaryrefslogtreecommitdiff
path: root/src/modules/audio_processing/aec/aec_resampler.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules/audio_processing/aec/aec_resampler.c')
-rw-r--r--src/modules/audio_processing/aec/aec_resampler.c233
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;
+}