This includes changes like:
* Attempt to break lines at better positions
* Use "override" in more places, don't use "virtual" with it
* Use {} where the body is more than one line
* Make declaration and definition arg names match
* Eliminate unused code
* EXPECT_EQ(expected, actual) (but use (actual, expected) for e.g. _GT)
* Correct #include order
* Use anonymous namespaces in preference to "static" for file-scoping
* Eliminate unnecessary casts
* Update reference code in comments of ARM assembly sources to match actual current C code
* Fix indenting to be more style-guide compliant
* Use arraysize() in more places
* Use bool instead of int for "boolean" values (0/1)
* Shorten and simplify code
* Spaces around operators
* 80 column limit
* Use const more consistently
* Space goes after '*' in type name, not before
* Remove unnecessary return values
* Use "(var == const)", not "(const == var)"
* Spelling
* Prefer true, typed constants to "enum hack" constants
* Avoid "virtual" on non-overridden functions
* ASSERT(x == y) -> ASSERT_EQ(y, x)
BUG=none
R=andrew@webrtc.org, asapersson@webrtc.org, henrika@webrtc.org, juberti@webrtc.org, kjellander@webrtc.org, kwiberg@webrtc.org
Review URL: https://codereview.webrtc.org/1172163004
Cr-Commit-Position: refs/heads/master@{#9420}
211 lines
5.0 KiB
C
211 lines
5.0 KiB
C
/*
|
|
* Copyright (c) 2012 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 "webrtc/modules/audio_processing/aec/aec_resampler.h"
|
|
|
|
#include <assert.h>
|
|
#include <math.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "webrtc/modules/audio_processing/aec/aec_core.h"
|
|
|
|
enum {
|
|
kEstimateLengthFrames = 400
|
|
};
|
|
|
|
typedef struct {
|
|
float buffer[kResamplerBufferSize];
|
|
float position;
|
|
|
|
int deviceSampleRateHz;
|
|
int skewData[kEstimateLengthFrames];
|
|
int skewDataIndex;
|
|
float skewEstimate;
|
|
} AecResampler;
|
|
|
|
static int EstimateSkew(const int* rawSkew,
|
|
int size,
|
|
int absLimit,
|
|
float* skewEst);
|
|
|
|
void* WebRtcAec_CreateResampler() {
|
|
return malloc(sizeof(AecResampler));
|
|
}
|
|
|
|
int WebRtcAec_InitResampler(void* resampInst, int deviceSampleRateHz) {
|
|
AecResampler* obj = (AecResampler*)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;
|
|
}
|
|
|
|
void WebRtcAec_FreeResampler(void* resampInst) {
|
|
AecResampler* obj = (AecResampler*)resampInst;
|
|
free(obj);
|
|
}
|
|
|
|
void WebRtcAec_ResampleLinear(void* resampInst,
|
|
const float* inspeech,
|
|
int size,
|
|
float skew,
|
|
float* outspeech,
|
|
int* size_out) {
|
|
AecResampler* obj = (AecResampler*)resampInst;
|
|
|
|
float* y;
|
|
float be, tnew;
|
|
int tn, mm;
|
|
|
|
assert(size >= 0);
|
|
assert(size <= 2 * FRAME_LEN);
|
|
assert(resampInst != NULL);
|
|
assert(inspeech != NULL);
|
|
assert(outspeech != NULL);
|
|
assert(size_out != NULL);
|
|
|
|
// Add new frame data in lookahead
|
|
memcpy(&obj->buffer[FRAME_LEN + kResamplingDelay],
|
|
inspeech,
|
|
size * sizeof(inspeech[0]));
|
|
|
|
// 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
|
|
outspeech[mm] = y[tn] + (tnew - tn) * (y[tn + 1] - y[tn]);
|
|
mm++;
|
|
|
|
tnew = be * mm + obj->position;
|
|
tn = (int)tnew;
|
|
}
|
|
|
|
*size_out = mm;
|
|
obj->position += (*size_out) * be - size;
|
|
|
|
// Shift buffer
|
|
memmove(obj->buffer,
|
|
&obj->buffer[size],
|
|
(kResamplerBufferSize - size) * sizeof(obj->buffer[0]));
|
|
}
|
|
|
|
int WebRtcAec_GetSkew(void* resampInst, int rawSkew, float* skewEst) {
|
|
AecResampler* obj = (AecResampler*)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;
|
|
}
|