Update common_audio

Corresponds to upstream commit 524e9b043e7e86fd72353b987c9d5f6a1ebf83e1

Update notes:

 * Moved src/ to webrtc/ to easily diff against the third_party/webrtc
   in the chromium tree

 * ARM/NEON/MIPS support is not yet hooked up

 * Tests have not been copied
This commit is contained in:
Arun Raghavan
2015-10-13 12:16:16 +05:30
parent 9413986e79
commit c4fb4e38de
230 changed files with 11201 additions and 8656 deletions

View File

@ -0,0 +1,59 @@
SUBDIRS = utility ns aec aecm agc
lib_LTLIBRARIES = libwebrtc_audio_processing.la
if NS_FIXED
COMMON_CXXFLAGS += -DWEBRTC_NS_FIXED=1
NS_LIB = libns_fix
else
COMMON_CXXFLAGS += -DWEBRTC_NS_FLOAT=1
NS_LIB = libns
endif
webrtcincludedir = $(includedir)/webrtc_audio_processing
webrtcinclude_HEADERS = $(top_srcdir)/src/typedefs.h \
$(top_srcdir)/src/modules/interface/module.h \
interface/audio_processing.h \
$(top_srcdir)/src/common_types.h \
$(top_srcdir)/src/modules/interface/module_common_types.h
libwebrtc_audio_processing_la_SOURCES = interface/audio_processing.h \
audio_buffer.cc \
audio_buffer.h \
audio_processing_impl.cc \
audio_processing_impl.h \
echo_cancellation_impl.cc \
echo_cancellation_impl.h \
echo_control_mobile_impl.cc \
echo_control_mobile_impl.h \
gain_control_impl.cc \
gain_control_impl.h \
high_pass_filter_impl.cc \
high_pass_filter_impl.h \
level_estimator_impl.cc \
level_estimator_impl.h \
noise_suppression_impl.cc \
noise_suppression_impl.h \
splitting_filter.cc \
splitting_filter.h \
processing_component.cc \
processing_component.h \
voice_detection_impl.cc \
voice_detection_impl.h
libwebrtc_audio_processing_la_CXXFLAGS = $(AM_CXXFLAGS) $(COMMON_CXXFLAGS) \
-I$(top_srcdir)/src/common_audio/signal_processing_library/main/interface \
-I$(top_srcdir)/src/common_audio/vad/main/interface \
-I$(top_srcdir)/src/system_wrappers/interface \
-I$(top_srcdir)/src/modules/audio_processing/utility \
-I$(top_srcdir)/src/modules/audio_processing/ns/interface \
-I$(top_srcdir)/src/modules/audio_processing/aec/interface \
-I$(top_srcdir)/src/modules/audio_processing/aecm/interface \
-I$(top_srcdir)/src/modules/audio_processing/agc/interface
libwebrtc_audio_processing_la_LIBADD = $(top_builddir)/src/system_wrappers/libsystem_wrappers.la \
$(top_builddir)/src/common_audio/signal_processing_library/libspl.la \
$(top_builddir)/src/common_audio/vad/libvad.la \
$(top_builddir)/src/modules/audio_processing/utility/libapm_util.la \
$(top_builddir)/src/modules/audio_processing/ns/$(NS_LIB).la \
$(top_builddir)/src/modules/audio_processing/aec/libaec.la \
$(top_builddir)/src/modules/audio_processing/aecm/libaecm.la \
$(top_builddir)/src/modules/audio_processing/agc/libagc.la
libwebrtc_audio_processing_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBWEBRTC_AUDIO_PROCESSING_VERSION_INFO)

View File

@ -0,0 +1,2 @@
andrew@webrtc.org
bjornv@webrtc.org

View File

@ -0,0 +1,16 @@
noinst_LTLIBRARIES = libaec.la
libaec_la_SOURCES = interface/echo_cancellation.h \
echo_cancellation.c \
aec_core.h \
aec_core.c \
aec_core_sse2.c \
aec_rdft.h \
aec_rdft.c \
aec_rdft_sse2.c \
resampler.h \
resampler.c
libaec_la_CFLAGS = $(AM_CFLAGS) $(COMMON_CFLAGS) \
-I$(top_srcdir)/src/common_audio/signal_processing_library/main/interface \
-I$(top_srcdir)/src/system_wrappers/interface \
-I$(top_srcdir)/src/modules/audio_processing/utility

View File

@ -0,0 +1,40 @@
# 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.
{
'targets': [
{
'target_name': 'aec',
'type': '<(library)',
'dependencies': [
'<(webrtc_root)/common_audio/common_audio.gyp:spl',
'apm_util'
],
'include_dirs': [
'interface',
],
'direct_dependent_settings': {
'include_dirs': [
'interface',
],
},
'sources': [
'interface/echo_cancellation.h',
'echo_cancellation.c',
'aec_core.h',
'aec_core.c',
'aec_core_sse2.c',
'aec_rdft.h',
'aec_rdft.c',
'aec_rdft_sse2.c',
'resampler.h',
'resampler.c',
],
},
],
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,181 @@
/*
* 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.
*/
/*
* Specifies the interface for the AEC core.
*/
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AEC_MAIN_SOURCE_AEC_CORE_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_AEC_MAIN_SOURCE_AEC_CORE_H_
#include <stdio.h>
#include "signal_processing_library.h"
#include "typedefs.h"
//#define AEC_DEBUG // for recording files
#define FRAME_LEN 80
#define PART_LEN 64 // Length of partition
#define PART_LEN1 (PART_LEN + 1) // Unique fft coefficients
#define PART_LEN2 (PART_LEN * 2) // Length of partition * 2
#define NR_PART 12 // Number of partitions
#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 BLOCKL_MAX FRAME_LEN
// Maximum delay in fixed point delay estimator, used for logging
enum {kMaxDelay = 100};
typedef float complex_t[2];
// For performance reasons, some arrays of complex numbers are replaced by twice
// as long arrays of float, all the real parts followed by all the imaginary
// ones (complex_t[SIZE] -> float[2][SIZE]). This allows SIMD optimizations and
// is better than two arrays (one for the real parts and one for the imaginary
// parts) as this other way would require two pointers instead of one and cause
// extra register spilling. This also allows the offsets to be calculated at
// compile time.
// Metrics
enum {offsetLevel = -100};
typedef struct {
float sfrsum;
int sfrcounter;
float framelevel;
float frsum;
int frcounter;
float minlevel;
float averagelevel;
} power_level_t;
typedef struct {
float instant;
float average;
float min;
float max;
float sum;
float hisum;
float himean;
int counter;
int hicounter;
} stats_t;
typedef struct {
int farBufWritePos, farBufReadPos;
int knownDelay;
int inSamples, outSamples;
int delayEstCtr;
void *farFrBuf, *nearFrBuf, *outFrBuf;
void *nearFrBufH;
void *outFrBufH;
float xBuf[PART_LEN2]; // farend
float dBuf[PART_LEN2]; // nearend
float eBuf[PART_LEN2]; // error
float dBufH[PART_LEN2]; // nearend
float xPow[PART_LEN1];
float dPow[PART_LEN1];
float dMinPow[PART_LEN1];
float dInitMinPow[PART_LEN1];
float *noisePow;
float xfBuf[2][NR_PART * PART_LEN1]; // farend fft buffer
float wfBuf[2][NR_PART * PART_LEN1]; // filter fft
complex_t sde[PART_LEN1]; // cross-psd of nearend and error
complex_t sxd[PART_LEN1]; // cross-psd of farend and nearend
complex_t xfwBuf[NR_PART * PART_LEN1]; // farend windowed fft buffer
float sx[PART_LEN1], sd[PART_LEN1], se[PART_LEN1]; // far, near and error psd
float hNs[PART_LEN1];
float hNlFbMin, hNlFbLocalMin;
float hNlXdAvgMin;
int hNlNewMin, hNlMinCtr;
float overDrive, overDriveSm;
float targetSupp, minOverDrive;
float outBuf[PART_LEN];
int delayIdx;
short stNearState, echoState;
short divergeState;
int xfBufBlockPos;
short farBuf[FILT_LEN2 * 2];
short mult; // sampling frequency multiple
int sampFreq;
WebRtc_UWord32 seed;
float mu; // stepsize
float errThresh; // error threshold
int noiseEstCtr;
power_level_t farlevel;
power_level_t nearlevel;
power_level_t linoutlevel;
power_level_t nlpoutlevel;
int metricsMode;
int stateCounter;
stats_t erl;
stats_t erle;
stats_t aNlp;
stats_t rerl;
// Quantities to control H band scaling for SWB input
int freq_avg_ic; //initial bin for averaging nlp gain
int flag_Hband_cn; //for comfort noise
float cn_scale_Hband; //scale for comfort noise in H band
int delay_histogram[kMaxDelay];
int delay_logging_enabled;
void* delay_estimator;
#ifdef AEC_DEBUG
FILE *farFile;
FILE *nearFile;
FILE *outFile;
FILE *outLpFile;
#endif
} aec_t;
typedef void (*WebRtcAec_FilterFar_t)(aec_t *aec, float yf[2][PART_LEN1]);
extern WebRtcAec_FilterFar_t WebRtcAec_FilterFar;
typedef void (*WebRtcAec_ScaleErrorSignal_t)(aec_t *aec, float ef[2][PART_LEN1]);
extern WebRtcAec_ScaleErrorSignal_t WebRtcAec_ScaleErrorSignal;
typedef void (*WebRtcAec_FilterAdaptation_t)
(aec_t *aec, float *fft, float ef[2][PART_LEN1]);
extern WebRtcAec_FilterAdaptation_t WebRtcAec_FilterAdaptation;
typedef void (*WebRtcAec_OverdriveAndSuppress_t)
(aec_t *aec, float hNl[PART_LEN1], const float hNlFb, float efw[2][PART_LEN1]);
extern WebRtcAec_OverdriveAndSuppress_t WebRtcAec_OverdriveAndSuppress;
int WebRtcAec_CreateAec(aec_t **aec);
int WebRtcAec_FreeAec(aec_t *aec);
int WebRtcAec_InitAec(aec_t *aec, int sampFreq);
void WebRtcAec_InitAec_SSE2(void);
void WebRtcAec_InitMetrics(aec_t *aec);
void WebRtcAec_ProcessFrame(aec_t *aec, const short *farend,
const short *nearend, const short *nearendH,
short *out, short *outH,
int knownDelay);
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AEC_MAIN_SOURCE_AEC_CORE_H_

View File

@ -0,0 +1,417 @@
/*
* 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.
*/
/*
* The core AEC algorithm, SSE2 version of speed-critical functions.
*/
#include "typedefs.h"
#if defined(WEBRTC_USE_SSE2)
#include <emmintrin.h>
#include <math.h>
#include "aec_core.h"
#include "aec_rdft.h"
__inline static float MulRe(float aRe, float aIm, float bRe, float bIm)
{
return aRe * bRe - aIm * bIm;
}
__inline static float MulIm(float aRe, float aIm, float bRe, float bIm)
{
return aRe * bIm + aIm * bRe;
}
static void FilterFarSSE2(aec_t *aec, float yf[2][PART_LEN1])
{
int i;
for (i = 0; i < NR_PART; i++) {
int j;
int xPos = (i + aec->xfBufBlockPos) * PART_LEN1;
int pos = i * PART_LEN1;
// Check for wrap
if (i + aec->xfBufBlockPos >= NR_PART) {
xPos -= NR_PART*(PART_LEN1);
}
// vectorized code (four at once)
for (j = 0; j + 3 < PART_LEN1; j += 4) {
const __m128 xfBuf_re = _mm_loadu_ps(&aec->xfBuf[0][xPos + j]);
const __m128 xfBuf_im = _mm_loadu_ps(&aec->xfBuf[1][xPos + j]);
const __m128 wfBuf_re = _mm_loadu_ps(&aec->wfBuf[0][pos + j]);
const __m128 wfBuf_im = _mm_loadu_ps(&aec->wfBuf[1][pos + j]);
const __m128 yf_re = _mm_loadu_ps(&yf[0][j]);
const __m128 yf_im = _mm_loadu_ps(&yf[1][j]);
const __m128 a = _mm_mul_ps(xfBuf_re, wfBuf_re);
const __m128 b = _mm_mul_ps(xfBuf_im, wfBuf_im);
const __m128 c = _mm_mul_ps(xfBuf_re, wfBuf_im);
const __m128 d = _mm_mul_ps(xfBuf_im, wfBuf_re);
const __m128 e = _mm_sub_ps(a, b);
const __m128 f = _mm_add_ps(c, d);
const __m128 g = _mm_add_ps(yf_re, e);
const __m128 h = _mm_add_ps(yf_im, f);
_mm_storeu_ps(&yf[0][j], g);
_mm_storeu_ps(&yf[1][j], h);
}
// scalar code for the remaining items.
for (; j < PART_LEN1; j++) {
yf[0][j] += MulRe(aec->xfBuf[0][xPos + j], aec->xfBuf[1][xPos + j],
aec->wfBuf[0][ pos + j], aec->wfBuf[1][ pos + j]);
yf[1][j] += MulIm(aec->xfBuf[0][xPos + j], aec->xfBuf[1][xPos + j],
aec->wfBuf[0][ pos + j], aec->wfBuf[1][ pos + j]);
}
}
}
static void ScaleErrorSignalSSE2(aec_t *aec, float ef[2][PART_LEN1])
{
const __m128 k1e_10f = _mm_set1_ps(1e-10f);
const __m128 kThresh = _mm_set1_ps(aec->errThresh);
const __m128 kMu = _mm_set1_ps(aec->mu);
int i;
// vectorized code (four at once)
for (i = 0; i + 3 < PART_LEN1; i += 4) {
const __m128 xPow = _mm_loadu_ps(&aec->xPow[i]);
const __m128 ef_re_base = _mm_loadu_ps(&ef[0][i]);
const __m128 ef_im_base = _mm_loadu_ps(&ef[1][i]);
const __m128 xPowPlus = _mm_add_ps(xPow, k1e_10f);
__m128 ef_re = _mm_div_ps(ef_re_base, xPowPlus);
__m128 ef_im = _mm_div_ps(ef_im_base, xPowPlus);
const __m128 ef_re2 = _mm_mul_ps(ef_re, ef_re);
const __m128 ef_im2 = _mm_mul_ps(ef_im, ef_im);
const __m128 ef_sum2 = _mm_add_ps(ef_re2, ef_im2);
const __m128 absEf = _mm_sqrt_ps(ef_sum2);
const __m128 bigger = _mm_cmpgt_ps(absEf, kThresh);
__m128 absEfPlus = _mm_add_ps(absEf, k1e_10f);
const __m128 absEfInv = _mm_div_ps(kThresh, absEfPlus);
__m128 ef_re_if = _mm_mul_ps(ef_re, absEfInv);
__m128 ef_im_if = _mm_mul_ps(ef_im, absEfInv);
ef_re_if = _mm_and_ps(bigger, ef_re_if);
ef_im_if = _mm_and_ps(bigger, ef_im_if);
ef_re = _mm_andnot_ps(bigger, ef_re);
ef_im = _mm_andnot_ps(bigger, ef_im);
ef_re = _mm_or_ps(ef_re, ef_re_if);
ef_im = _mm_or_ps(ef_im, ef_im_if);
ef_re = _mm_mul_ps(ef_re, kMu);
ef_im = _mm_mul_ps(ef_im, kMu);
_mm_storeu_ps(&ef[0][i], ef_re);
_mm_storeu_ps(&ef[1][i], ef_im);
}
// scalar code for the remaining items.
for (; i < (PART_LEN1); i++) {
float absEf;
ef[0][i] /= (aec->xPow[i] + 1e-10f);
ef[1][i] /= (aec->xPow[i] + 1e-10f);
absEf = sqrtf(ef[0][i] * ef[0][i] + ef[1][i] * ef[1][i]);
if (absEf > aec->errThresh) {
absEf = aec->errThresh / (absEf + 1e-10f);
ef[0][i] *= absEf;
ef[1][i] *= absEf;
}
// Stepsize factor
ef[0][i] *= aec->mu;
ef[1][i] *= aec->mu;
}
}
static void FilterAdaptationSSE2(aec_t *aec, float *fft, float ef[2][PART_LEN1]) {
int i, j;
for (i = 0; i < NR_PART; i++) {
int xPos = (i + aec->xfBufBlockPos)*(PART_LEN1);
int pos = i * PART_LEN1;
// Check for wrap
if (i + aec->xfBufBlockPos >= NR_PART) {
xPos -= NR_PART * PART_LEN1;
}
// Process the whole array...
for (j = 0; j < PART_LEN; j+= 4) {
// Load xfBuf and ef.
const __m128 xfBuf_re = _mm_loadu_ps(&aec->xfBuf[0][xPos + j]);
const __m128 xfBuf_im = _mm_loadu_ps(&aec->xfBuf[1][xPos + j]);
const __m128 ef_re = _mm_loadu_ps(&ef[0][j]);
const __m128 ef_im = _mm_loadu_ps(&ef[1][j]);
// Calculate the product of conjugate(xfBuf) by ef.
// re(conjugate(a) * b) = aRe * bRe + aIm * bIm
// im(conjugate(a) * b)= aRe * bIm - aIm * bRe
const __m128 a = _mm_mul_ps(xfBuf_re, ef_re);
const __m128 b = _mm_mul_ps(xfBuf_im, ef_im);
const __m128 c = _mm_mul_ps(xfBuf_re, ef_im);
const __m128 d = _mm_mul_ps(xfBuf_im, ef_re);
const __m128 e = _mm_add_ps(a, b);
const __m128 f = _mm_sub_ps(c, d);
// Interleave real and imaginary parts.
const __m128 g = _mm_unpacklo_ps(e, f);
const __m128 h = _mm_unpackhi_ps(e, f);
// Store
_mm_storeu_ps(&fft[2*j + 0], g);
_mm_storeu_ps(&fft[2*j + 4], h);
}
// ... and fixup the first imaginary entry.
fft[1] = MulRe(aec->xfBuf[0][xPos + PART_LEN],
-aec->xfBuf[1][xPos + PART_LEN],
ef[0][PART_LEN], ef[1][PART_LEN]);
aec_rdft_inverse_128(fft);
memset(fft + PART_LEN, 0, sizeof(float)*PART_LEN);
// fft scaling
{
float scale = 2.0f / PART_LEN2;
const __m128 scale_ps = _mm_load_ps1(&scale);
for (j = 0; j < PART_LEN; j+=4) {
const __m128 fft_ps = _mm_loadu_ps(&fft[j]);
const __m128 fft_scale = _mm_mul_ps(fft_ps, scale_ps);
_mm_storeu_ps(&fft[j], fft_scale);
}
}
aec_rdft_forward_128(fft);
{
float wt1 = aec->wfBuf[1][pos];
aec->wfBuf[0][pos + PART_LEN] += fft[1];
for (j = 0; j < PART_LEN; j+= 4) {
__m128 wtBuf_re = _mm_loadu_ps(&aec->wfBuf[0][pos + j]);
__m128 wtBuf_im = _mm_loadu_ps(&aec->wfBuf[1][pos + j]);
const __m128 fft0 = _mm_loadu_ps(&fft[2 * j + 0]);
const __m128 fft4 = _mm_loadu_ps(&fft[2 * j + 4]);
const __m128 fft_re = _mm_shuffle_ps(fft0, fft4, _MM_SHUFFLE(2, 0, 2 ,0));
const __m128 fft_im = _mm_shuffle_ps(fft0, fft4, _MM_SHUFFLE(3, 1, 3 ,1));
wtBuf_re = _mm_add_ps(wtBuf_re, fft_re);
wtBuf_im = _mm_add_ps(wtBuf_im, fft_im);
_mm_storeu_ps(&aec->wfBuf[0][pos + j], wtBuf_re);
_mm_storeu_ps(&aec->wfBuf[1][pos + j], wtBuf_im);
}
aec->wfBuf[1][pos] = wt1;
}
}
}
static __m128 mm_pow_ps(__m128 a, __m128 b)
{
// a^b = exp2(b * log2(a))
// exp2(x) and log2(x) are calculated using polynomial approximations.
__m128 log2_a, b_log2_a, a_exp_b;
// Calculate log2(x), x = a.
{
// To calculate log2(x), we decompose x like this:
// x = y * 2^n
// n is an integer
// y is in the [1.0, 2.0) range
//
// log2(x) = log2(y) + n
// n can be evaluated by playing with float representation.
// log2(y) in a small range can be approximated, this code uses an order
// five polynomial approximation. The coefficients have been
// estimated with the Remez algorithm and the resulting
// polynomial has a maximum relative error of 0.00086%.
// Compute n.
// This is done by masking the exponent, shifting it into the top bit of
// the mantissa, putting eight into the biased exponent (to shift/
// compensate the fact that the exponent has been shifted in the top/
// fractional part and finally getting rid of the implicit leading one
// from the mantissa by substracting it out.
static const ALIGN16_BEG int float_exponent_mask[4] ALIGN16_END =
{0x7F800000, 0x7F800000, 0x7F800000, 0x7F800000};
static const ALIGN16_BEG int eight_biased_exponent[4] ALIGN16_END =
{0x43800000, 0x43800000, 0x43800000, 0x43800000};
static const ALIGN16_BEG int implicit_leading_one[4] ALIGN16_END =
{0x43BF8000, 0x43BF8000, 0x43BF8000, 0x43BF8000};
static const int shift_exponent_into_top_mantissa = 8;
const __m128 two_n = _mm_and_ps(a, *((__m128 *)float_exponent_mask));
const __m128 n_1 = _mm_castsi128_ps(_mm_srli_epi32(_mm_castps_si128(two_n),
shift_exponent_into_top_mantissa));
const __m128 n_0 = _mm_or_ps(n_1, *((__m128 *)eight_biased_exponent));
const __m128 n = _mm_sub_ps(n_0, *((__m128 *)implicit_leading_one));
// Compute y.
static const ALIGN16_BEG int mantissa_mask[4] ALIGN16_END =
{0x007FFFFF, 0x007FFFFF, 0x007FFFFF, 0x007FFFFF};
static const ALIGN16_BEG int zero_biased_exponent_is_one[4] ALIGN16_END =
{0x3F800000, 0x3F800000, 0x3F800000, 0x3F800000};
const __m128 mantissa = _mm_and_ps(a, *((__m128 *)mantissa_mask));
const __m128 y = _mm_or_ps(
mantissa, *((__m128 *)zero_biased_exponent_is_one));
// Approximate log2(y) ~= (y - 1) * pol5(y).
// pol5(y) = C5 * y^5 + C4 * y^4 + C3 * y^3 + C2 * y^2 + C1 * y + C0
static const ALIGN16_BEG float ALIGN16_END C5[4] =
{-3.4436006e-2f, -3.4436006e-2f, -3.4436006e-2f, -3.4436006e-2f};
static const ALIGN16_BEG float ALIGN16_END C4[4] =
{3.1821337e-1f, 3.1821337e-1f, 3.1821337e-1f, 3.1821337e-1f};
static const ALIGN16_BEG float ALIGN16_END C3[4] =
{-1.2315303f, -1.2315303f, -1.2315303f, -1.2315303f};
static const ALIGN16_BEG float ALIGN16_END C2[4] =
{2.5988452f, 2.5988452f, 2.5988452f, 2.5988452f};
static const ALIGN16_BEG float ALIGN16_END C1[4] =
{-3.3241990f, -3.3241990f, -3.3241990f, -3.3241990f};
static const ALIGN16_BEG float ALIGN16_END C0[4] =
{3.1157899f, 3.1157899f, 3.1157899f, 3.1157899f};
const __m128 pol5_y_0 = _mm_mul_ps(y, *((__m128 *)C5));
const __m128 pol5_y_1 = _mm_add_ps(pol5_y_0, *((__m128 *)C4));
const __m128 pol5_y_2 = _mm_mul_ps(pol5_y_1, y);
const __m128 pol5_y_3 = _mm_add_ps(pol5_y_2, *((__m128 *)C3));
const __m128 pol5_y_4 = _mm_mul_ps(pol5_y_3, y);
const __m128 pol5_y_5 = _mm_add_ps(pol5_y_4, *((__m128 *)C2));
const __m128 pol5_y_6 = _mm_mul_ps(pol5_y_5, y);
const __m128 pol5_y_7 = _mm_add_ps(pol5_y_6, *((__m128 *)C1));
const __m128 pol5_y_8 = _mm_mul_ps(pol5_y_7, y);
const __m128 pol5_y = _mm_add_ps(pol5_y_8, *((__m128 *)C0));
const __m128 y_minus_one = _mm_sub_ps(
y, *((__m128 *)zero_biased_exponent_is_one));
const __m128 log2_y = _mm_mul_ps(y_minus_one , pol5_y);
// Combine parts.
log2_a = _mm_add_ps(n, log2_y);
}
// b * log2(a)
b_log2_a = _mm_mul_ps(b, log2_a);
// Calculate exp2(x), x = b * log2(a).
{
// To calculate 2^x, we decompose x like this:
// x = n + y
// n is an integer, the value of x - 0.5 rounded down, therefore
// y is in the [0.5, 1.5) range
//
// 2^x = 2^n * 2^y
// 2^n can be evaluated by playing with float representation.
// 2^y in a small range can be approximated, this code uses an order two
// polynomial approximation. The coefficients have been estimated
// with the Remez algorithm and the resulting polynomial has a
// maximum relative error of 0.17%.
// To avoid over/underflow, we reduce the range of input to ]-127, 129].
static const ALIGN16_BEG float max_input[4] ALIGN16_END =
{129.f, 129.f, 129.f, 129.f};
static const ALIGN16_BEG float min_input[4] ALIGN16_END =
{-126.99999f, -126.99999f, -126.99999f, -126.99999f};
const __m128 x_min = _mm_min_ps(b_log2_a, *((__m128 *)max_input));
const __m128 x_max = _mm_max_ps(x_min, *((__m128 *)min_input));
// Compute n.
static const ALIGN16_BEG float half[4] ALIGN16_END =
{0.5f, 0.5f, 0.5f, 0.5f};
const __m128 x_minus_half = _mm_sub_ps(x_max, *((__m128 *)half));
const __m128i x_minus_half_floor = _mm_cvtps_epi32(x_minus_half);
// Compute 2^n.
static const ALIGN16_BEG int float_exponent_bias[4] ALIGN16_END =
{127, 127, 127, 127};
static const int float_exponent_shift = 23;
const __m128i two_n_exponent = _mm_add_epi32(
x_minus_half_floor, *((__m128i *)float_exponent_bias));
const __m128 two_n = _mm_castsi128_ps(_mm_slli_epi32(
two_n_exponent, float_exponent_shift));
// Compute y.
const __m128 y = _mm_sub_ps(x_max, _mm_cvtepi32_ps(x_minus_half_floor));
// Approximate 2^y ~= C2 * y^2 + C1 * y + C0.
static const ALIGN16_BEG float C2[4] ALIGN16_END =
{3.3718944e-1f, 3.3718944e-1f, 3.3718944e-1f, 3.3718944e-1f};
static const ALIGN16_BEG float C1[4] ALIGN16_END =
{6.5763628e-1f, 6.5763628e-1f, 6.5763628e-1f, 6.5763628e-1f};
static const ALIGN16_BEG float C0[4] ALIGN16_END =
{1.0017247f, 1.0017247f, 1.0017247f, 1.0017247f};
const __m128 exp2_y_0 = _mm_mul_ps(y, *((__m128 *)C2));
const __m128 exp2_y_1 = _mm_add_ps(exp2_y_0, *((__m128 *)C1));
const __m128 exp2_y_2 = _mm_mul_ps(exp2_y_1, y);
const __m128 exp2_y = _mm_add_ps(exp2_y_2, *((__m128 *)C0));
// Combine parts.
a_exp_b = _mm_mul_ps(exp2_y, two_n);
}
return a_exp_b;
}
extern const float WebRtcAec_weightCurve[65];
extern const float WebRtcAec_overDriveCurve[65];
static void OverdriveAndSuppressSSE2(aec_t *aec, float hNl[PART_LEN1],
const float hNlFb,
float efw[2][PART_LEN1]) {
int i;
const __m128 vec_hNlFb = _mm_set1_ps(hNlFb);
const __m128 vec_one = _mm_set1_ps(1.0f);
const __m128 vec_minus_one = _mm_set1_ps(-1.0f);
const __m128 vec_overDriveSm = _mm_set1_ps(aec->overDriveSm);
// vectorized code (four at once)
for (i = 0; i + 3 < PART_LEN1; i+=4) {
// Weight subbands
__m128 vec_hNl = _mm_loadu_ps(&hNl[i]);
const __m128 vec_weightCurve = _mm_loadu_ps(&WebRtcAec_weightCurve[i]);
const __m128 bigger = _mm_cmpgt_ps(vec_hNl, vec_hNlFb);
const __m128 vec_weightCurve_hNlFb = _mm_mul_ps(
vec_weightCurve, vec_hNlFb);
const __m128 vec_one_weightCurve = _mm_sub_ps(vec_one, vec_weightCurve);
const __m128 vec_one_weightCurve_hNl = _mm_mul_ps(
vec_one_weightCurve, vec_hNl);
const __m128 vec_if0 = _mm_andnot_ps(bigger, vec_hNl);
const __m128 vec_if1 = _mm_and_ps(
bigger, _mm_add_ps(vec_weightCurve_hNlFb, vec_one_weightCurve_hNl));
vec_hNl = _mm_or_ps(vec_if0, vec_if1);
{
const __m128 vec_overDriveCurve = _mm_loadu_ps(
&WebRtcAec_overDriveCurve[i]);
const __m128 vec_overDriveSm_overDriveCurve = _mm_mul_ps(
vec_overDriveSm, vec_overDriveCurve);
vec_hNl = mm_pow_ps(vec_hNl, vec_overDriveSm_overDriveCurve);
_mm_storeu_ps(&hNl[i], vec_hNl);
}
// Suppress error signal
{
__m128 vec_efw_re = _mm_loadu_ps(&efw[0][i]);
__m128 vec_efw_im = _mm_loadu_ps(&efw[1][i]);
vec_efw_re = _mm_mul_ps(vec_efw_re, vec_hNl);
vec_efw_im = _mm_mul_ps(vec_efw_im, vec_hNl);
// Ooura fft returns incorrect sign on imaginary component. It matters
// here because we are making an additive change with comfort noise.
vec_efw_im = _mm_mul_ps(vec_efw_im, vec_minus_one);
_mm_storeu_ps(&efw[0][i], vec_efw_re);
_mm_storeu_ps(&efw[1][i], vec_efw_im);
}
}
// scalar code for the remaining items.
for (; i < PART_LEN1; i++) {
// Weight subbands
if (hNl[i] > hNlFb) {
hNl[i] = WebRtcAec_weightCurve[i] * hNlFb +
(1 - WebRtcAec_weightCurve[i]) * hNl[i];
}
hNl[i] = powf(hNl[i], aec->overDriveSm * WebRtcAec_overDriveCurve[i]);
// Suppress error signal
efw[0][i] *= hNl[i];
efw[1][i] *= hNl[i];
// Ooura fft returns incorrect sign on imaginary component. It matters
// here because we are making an additive change with comfort noise.
efw[1][i] *= -1;
}
}
void WebRtcAec_InitAec_SSE2(void) {
WebRtcAec_FilterFar = FilterFarSSE2;
WebRtcAec_ScaleErrorSignal = ScaleErrorSignalSSE2;
WebRtcAec_FilterAdaptation = FilterAdaptationSSE2;
WebRtcAec_OverdriveAndSuppress = OverdriveAndSuppressSSE2;
}
#endif // WEBRTC_USE_SSE2

View File

@ -0,0 +1,587 @@
/*
* http://www.kurims.kyoto-u.ac.jp/~ooura/fft.html
* Copyright Takuya OOURA, 1996-2001
*
* You may use, copy, modify and distribute this code for any purpose (include
* commercial use) and without fee. Please refer to this package when you modify
* this code.
*
* Changes by the WebRTC authors:
* - Trivial type modifications.
* - Minimal code subset to do rdft of length 128.
* - Optimizations because of known length.
*
* All changes are covered by the WebRTC license and IP grant:
* 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.
*/
#include "aec_rdft.h"
#include <math.h>
#include "system_wrappers/interface/cpu_features_wrapper.h"
#include "typedefs.h"
// constants shared by all paths (C, SSE2).
float rdft_w[64];
// constants used by the C path.
float rdft_wk3ri_first[32];
float rdft_wk3ri_second[32];
// constants used by SSE2 but initialized in C path.
ALIGN16_BEG float ALIGN16_END rdft_wk1r[32];
ALIGN16_BEG float ALIGN16_END rdft_wk2r[32];
ALIGN16_BEG float ALIGN16_END rdft_wk3r[32];
ALIGN16_BEG float ALIGN16_END rdft_wk1i[32];
ALIGN16_BEG float ALIGN16_END rdft_wk2i[32];
ALIGN16_BEG float ALIGN16_END rdft_wk3i[32];
ALIGN16_BEG float ALIGN16_END cftmdl_wk1r[4];
static int ip[16];
static void bitrv2_32or128(int n, int *ip, float *a) {
// n is 32 or 128
int j, j1, k, k1, m, m2;
float xr, xi, yr, yi;
ip[0] = 0;
{
int l = n;
m = 1;
while ((m << 3) < l) {
l >>= 1;
for (j = 0; j < m; j++) {
ip[m + j] = ip[j] + l;
}
m <<= 1;
}
}
m2 = 2 * m;
for (k = 0; k < m; k++) {
for (j = 0; j < k; j++) {
j1 = 2 * j + ip[k];
k1 = 2 * k + ip[j];
xr = a[j1];
xi = a[j1 + 1];
yr = a[k1];
yi = a[k1 + 1];
a[j1] = yr;
a[j1 + 1] = yi;
a[k1] = xr;
a[k1 + 1] = xi;
j1 += m2;
k1 += 2 * m2;
xr = a[j1];
xi = a[j1 + 1];
yr = a[k1];
yi = a[k1 + 1];
a[j1] = yr;
a[j1 + 1] = yi;
a[k1] = xr;
a[k1 + 1] = xi;
j1 += m2;
k1 -= m2;
xr = a[j1];
xi = a[j1 + 1];
yr = a[k1];
yi = a[k1 + 1];
a[j1] = yr;
a[j1 + 1] = yi;
a[k1] = xr;
a[k1 + 1] = xi;
j1 += m2;
k1 += 2 * m2;
xr = a[j1];
xi = a[j1 + 1];
yr = a[k1];
yi = a[k1 + 1];
a[j1] = yr;
a[j1 + 1] = yi;
a[k1] = xr;
a[k1 + 1] = xi;
}
j1 = 2 * k + m2 + ip[k];
k1 = j1 + m2;
xr = a[j1];
xi = a[j1 + 1];
yr = a[k1];
yi = a[k1 + 1];
a[j1] = yr;
a[j1 + 1] = yi;
a[k1] = xr;
a[k1 + 1] = xi;
}
}
static void makewt_32(void) {
const int nw = 32;
int j, nwh;
float delta, x, y;
ip[0] = nw;
ip[1] = 1;
nwh = nw >> 1;
delta = atanf(1.0f) / nwh;
rdft_w[0] = 1;
rdft_w[1] = 0;
rdft_w[nwh] = cosf(delta * nwh);
rdft_w[nwh + 1] = rdft_w[nwh];
for (j = 2; j < nwh; j += 2) {
x = cosf(delta * j);
y = sinf(delta * j);
rdft_w[j] = x;
rdft_w[j + 1] = y;
rdft_w[nw - j] = y;
rdft_w[nw - j + 1] = x;
}
bitrv2_32or128(nw, ip + 2, rdft_w);
// pre-calculate constants used by cft1st_128 and cftmdl_128...
cftmdl_wk1r[0] = rdft_w[2];
cftmdl_wk1r[1] = rdft_w[2];
cftmdl_wk1r[2] = rdft_w[2];
cftmdl_wk1r[3] = -rdft_w[2];
{
int k1;
for (k1 = 0, j = 0; j < 128; j += 16, k1 += 2) {
const int k2 = 2 * k1;
const float wk2r = rdft_w[k1 + 0];
const float wk2i = rdft_w[k1 + 1];
float wk1r, wk1i;
// ... scalar version.
wk1r = rdft_w[k2 + 0];
wk1i = rdft_w[k2 + 1];
rdft_wk3ri_first[k1 + 0] = wk1r - 2 * wk2i * wk1i;
rdft_wk3ri_first[k1 + 1] = 2 * wk2i * wk1r - wk1i;
wk1r = rdft_w[k2 + 2];
wk1i = rdft_w[k2 + 3];
rdft_wk3ri_second[k1 + 0] = wk1r - 2 * wk2r * wk1i;
rdft_wk3ri_second[k1 + 1] = 2 * wk2r * wk1r - wk1i;
// ... vector version.
rdft_wk1r[k2 + 0] = rdft_w[k2 + 0];
rdft_wk1r[k2 + 1] = rdft_w[k2 + 0];
rdft_wk1r[k2 + 2] = rdft_w[k2 + 2];
rdft_wk1r[k2 + 3] = rdft_w[k2 + 2];
rdft_wk2r[k2 + 0] = rdft_w[k1 + 0];
rdft_wk2r[k2 + 1] = rdft_w[k1 + 0];
rdft_wk2r[k2 + 2] = -rdft_w[k1 + 1];
rdft_wk2r[k2 + 3] = -rdft_w[k1 + 1];
rdft_wk3r[k2 + 0] = rdft_wk3ri_first[k1 + 0];
rdft_wk3r[k2 + 1] = rdft_wk3ri_first[k1 + 0];
rdft_wk3r[k2 + 2] = rdft_wk3ri_second[k1 + 0];
rdft_wk3r[k2 + 3] = rdft_wk3ri_second[k1 + 0];
rdft_wk1i[k2 + 0] = -rdft_w[k2 + 1];
rdft_wk1i[k2 + 1] = rdft_w[k2 + 1];
rdft_wk1i[k2 + 2] = -rdft_w[k2 + 3];
rdft_wk1i[k2 + 3] = rdft_w[k2 + 3];
rdft_wk2i[k2 + 0] = -rdft_w[k1 + 1];
rdft_wk2i[k2 + 1] = rdft_w[k1 + 1];
rdft_wk2i[k2 + 2] = -rdft_w[k1 + 0];
rdft_wk2i[k2 + 3] = rdft_w[k1 + 0];
rdft_wk3i[k2 + 0] = -rdft_wk3ri_first[k1 + 1];
rdft_wk3i[k2 + 1] = rdft_wk3ri_first[k1 + 1];
rdft_wk3i[k2 + 2] = -rdft_wk3ri_second[k1 + 1];
rdft_wk3i[k2 + 3] = rdft_wk3ri_second[k1 + 1];
}
}
}
static void makect_32(void) {
float *c = rdft_w + 32;
const int nc = 32;
int j, nch;
float delta;
ip[1] = nc;
nch = nc >> 1;
delta = atanf(1.0f) / nch;
c[0] = cosf(delta * nch);
c[nch] = 0.5f * c[0];
for (j = 1; j < nch; j++) {
c[j] = 0.5f * cosf(delta * j);
c[nc - j] = 0.5f * sinf(delta * j);
}
}
static void cft1st_128_C(float *a) {
const int n = 128;
int j, k1, k2;
float wk1r, wk1i, wk2r, wk2i, wk3r, wk3i;
float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;
x0r = a[0] + a[2];
x0i = a[1] + a[3];
x1r = a[0] - a[2];
x1i = a[1] - a[3];
x2r = a[4] + a[6];
x2i = a[5] + a[7];
x3r = a[4] - a[6];
x3i = a[5] - a[7];
a[0] = x0r + x2r;
a[1] = x0i + x2i;
a[4] = x0r - x2r;
a[5] = x0i - x2i;
a[2] = x1r - x3i;
a[3] = x1i + x3r;
a[6] = x1r + x3i;
a[7] = x1i - x3r;
wk1r = rdft_w[2];
x0r = a[8] + a[10];
x0i = a[9] + a[11];
x1r = a[8] - a[10];
x1i = a[9] - a[11];
x2r = a[12] + a[14];
x2i = a[13] + a[15];
x3r = a[12] - a[14];
x3i = a[13] - a[15];
a[8] = x0r + x2r;
a[9] = x0i + x2i;
a[12] = x2i - x0i;
a[13] = x0r - x2r;
x0r = x1r - x3i;
x0i = x1i + x3r;
a[10] = wk1r * (x0r - x0i);
a[11] = wk1r * (x0r + x0i);
x0r = x3i + x1r;
x0i = x3r - x1i;
a[14] = wk1r * (x0i - x0r);
a[15] = wk1r * (x0i + x0r);
k1 = 0;
for (j = 16; j < n; j += 16) {
k1 += 2;
k2 = 2 * k1;
wk2r = rdft_w[k1 + 0];
wk2i = rdft_w[k1 + 1];
wk1r = rdft_w[k2 + 0];
wk1i = rdft_w[k2 + 1];
wk3r = rdft_wk3ri_first[k1 + 0];
wk3i = rdft_wk3ri_first[k1 + 1];
x0r = a[j + 0] + a[j + 2];
x0i = a[j + 1] + a[j + 3];
x1r = a[j + 0] - a[j + 2];
x1i = a[j + 1] - a[j + 3];
x2r = a[j + 4] + a[j + 6];
x2i = a[j + 5] + a[j + 7];
x3r = a[j + 4] - a[j + 6];
x3i = a[j + 5] - a[j + 7];
a[j + 0] = x0r + x2r;
a[j + 1] = x0i + x2i;
x0r -= x2r;
x0i -= x2i;
a[j + 4] = wk2r * x0r - wk2i * x0i;
a[j + 5] = wk2r * x0i + wk2i * x0r;
x0r = x1r - x3i;
x0i = x1i + x3r;
a[j + 2] = wk1r * x0r - wk1i * x0i;
a[j + 3] = wk1r * x0i + wk1i * x0r;
x0r = x1r + x3i;
x0i = x1i - x3r;
a[j + 6] = wk3r * x0r - wk3i * x0i;
a[j + 7] = wk3r * x0i + wk3i * x0r;
wk1r = rdft_w[k2 + 2];
wk1i = rdft_w[k2 + 3];
wk3r = rdft_wk3ri_second[k1 + 0];
wk3i = rdft_wk3ri_second[k1 + 1];
x0r = a[j + 8] + a[j + 10];
x0i = a[j + 9] + a[j + 11];
x1r = a[j + 8] - a[j + 10];
x1i = a[j + 9] - a[j + 11];
x2r = a[j + 12] + a[j + 14];
x2i = a[j + 13] + a[j + 15];
x3r = a[j + 12] - a[j + 14];
x3i = a[j + 13] - a[j + 15];
a[j + 8] = x0r + x2r;
a[j + 9] = x0i + x2i;
x0r -= x2r;
x0i -= x2i;
a[j + 12] = -wk2i * x0r - wk2r * x0i;
a[j + 13] = -wk2i * x0i + wk2r * x0r;
x0r = x1r - x3i;
x0i = x1i + x3r;
a[j + 10] = wk1r * x0r - wk1i * x0i;
a[j + 11] = wk1r * x0i + wk1i * x0r;
x0r = x1r + x3i;
x0i = x1i - x3r;
a[j + 14] = wk3r * x0r - wk3i * x0i;
a[j + 15] = wk3r * x0i + wk3i * x0r;
}
}
static void cftmdl_128_C(float *a) {
const int l = 8;
const int n = 128;
const int m = 32;
int j0, j1, j2, j3, k, k1, k2, m2;
float wk1r, wk1i, wk2r, wk2i, wk3r, wk3i;
float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;
for (j0 = 0; j0 < l; j0 += 2) {
j1 = j0 + 8;
j2 = j0 + 16;
j3 = j0 + 24;
x0r = a[j0 + 0] + a[j1 + 0];
x0i = a[j0 + 1] + a[j1 + 1];
x1r = a[j0 + 0] - a[j1 + 0];
x1i = a[j0 + 1] - a[j1 + 1];
x2r = a[j2 + 0] + a[j3 + 0];
x2i = a[j2 + 1] + a[j3 + 1];
x3r = a[j2 + 0] - a[j3 + 0];
x3i = a[j2 + 1] - a[j3 + 1];
a[j0 + 0] = x0r + x2r;
a[j0 + 1] = x0i + x2i;
a[j2 + 0] = x0r - x2r;
a[j2 + 1] = x0i - x2i;
a[j1 + 0] = x1r - x3i;
a[j1 + 1] = x1i + x3r;
a[j3 + 0] = x1r + x3i;
a[j3 + 1] = x1i - x3r;
}
wk1r = rdft_w[2];
for (j0 = m; j0 < l + m; j0 += 2) {
j1 = j0 + 8;
j2 = j0 + 16;
j3 = j0 + 24;
x0r = a[j0 + 0] + a[j1 + 0];
x0i = a[j0 + 1] + a[j1 + 1];
x1r = a[j0 + 0] - a[j1 + 0];
x1i = a[j0 + 1] - a[j1 + 1];
x2r = a[j2 + 0] + a[j3 + 0];
x2i = a[j2 + 1] + a[j3 + 1];
x3r = a[j2 + 0] - a[j3 + 0];
x3i = a[j2 + 1] - a[j3 + 1];
a[j0 + 0] = x0r + x2r;
a[j0 + 1] = x0i + x2i;
a[j2 + 0] = x2i - x0i;
a[j2 + 1] = x0r - x2r;
x0r = x1r - x3i;
x0i = x1i + x3r;
a[j1 + 0] = wk1r * (x0r - x0i);
a[j1 + 1] = wk1r * (x0r + x0i);
x0r = x3i + x1r;
x0i = x3r - x1i;
a[j3 + 0] = wk1r * (x0i - x0r);
a[j3 + 1] = wk1r * (x0i + x0r);
}
k1 = 0;
m2 = 2 * m;
for (k = m2; k < n; k += m2) {
k1 += 2;
k2 = 2 * k1;
wk2r = rdft_w[k1 + 0];
wk2i = rdft_w[k1 + 1];
wk1r = rdft_w[k2 + 0];
wk1i = rdft_w[k2 + 1];
wk3r = rdft_wk3ri_first[k1 + 0];
wk3i = rdft_wk3ri_first[k1 + 1];
for (j0 = k; j0 < l + k; j0 += 2) {
j1 = j0 + 8;
j2 = j0 + 16;
j3 = j0 + 24;
x0r = a[j0 + 0] + a[j1 + 0];
x0i = a[j0 + 1] + a[j1 + 1];
x1r = a[j0 + 0] - a[j1 + 0];
x1i = a[j0 + 1] - a[j1 + 1];
x2r = a[j2 + 0] + a[j3 + 0];
x2i = a[j2 + 1] + a[j3 + 1];
x3r = a[j2 + 0] - a[j3 + 0];
x3i = a[j2 + 1] - a[j3 + 1];
a[j0 + 0] = x0r + x2r;
a[j0 + 1] = x0i + x2i;
x0r -= x2r;
x0i -= x2i;
a[j2 + 0] = wk2r * x0r - wk2i * x0i;
a[j2 + 1] = wk2r * x0i + wk2i * x0r;
x0r = x1r - x3i;
x0i = x1i + x3r;
a[j1 + 0] = wk1r * x0r - wk1i * x0i;
a[j1 + 1] = wk1r * x0i + wk1i * x0r;
x0r = x1r + x3i;
x0i = x1i - x3r;
a[j3 + 0] = wk3r * x0r - wk3i * x0i;
a[j3 + 1] = wk3r * x0i + wk3i * x0r;
}
wk1r = rdft_w[k2 + 2];
wk1i = rdft_w[k2 + 3];
wk3r = rdft_wk3ri_second[k1 + 0];
wk3i = rdft_wk3ri_second[k1 + 1];
for (j0 = k + m; j0 < l + (k + m); j0 += 2) {
j1 = j0 + 8;
j2 = j0 + 16;
j3 = j0 + 24;
x0r = a[j0 + 0] + a[j1 + 0];
x0i = a[j0 + 1] + a[j1 + 1];
x1r = a[j0 + 0] - a[j1 + 0];
x1i = a[j0 + 1] - a[j1 + 1];
x2r = a[j2 + 0] + a[j3 + 0];
x2i = a[j2 + 1] + a[j3 + 1];
x3r = a[j2 + 0] - a[j3 + 0];
x3i = a[j2 + 1] - a[j3 + 1];
a[j0 + 0] = x0r + x2r;
a[j0 + 1] = x0i + x2i;
x0r -= x2r;
x0i -= x2i;
a[j2 + 0] = -wk2i * x0r - wk2r * x0i;
a[j2 + 1] = -wk2i * x0i + wk2r * x0r;
x0r = x1r - x3i;
x0i = x1i + x3r;
a[j1 + 0] = wk1r * x0r - wk1i * x0i;
a[j1 + 1] = wk1r * x0i + wk1i * x0r;
x0r = x1r + x3i;
x0i = x1i - x3r;
a[j3 + 0] = wk3r * x0r - wk3i * x0i;
a[j3 + 1] = wk3r * x0i + wk3i * x0r;
}
}
}
static void cftfsub_128(float *a) {
int j, j1, j2, j3, l;
float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;
cft1st_128(a);
cftmdl_128(a);
l = 32;
for (j = 0; j < l; j += 2) {
j1 = j + l;
j2 = j1 + l;
j3 = j2 + l;
x0r = a[j] + a[j1];
x0i = a[j + 1] + a[j1 + 1];
x1r = a[j] - a[j1];
x1i = a[j + 1] - a[j1 + 1];
x2r = a[j2] + a[j3];
x2i = a[j2 + 1] + a[j3 + 1];
x3r = a[j2] - a[j3];
x3i = a[j2 + 1] - a[j3 + 1];
a[j] = x0r + x2r;
a[j + 1] = x0i + x2i;
a[j2] = x0r - x2r;
a[j2 + 1] = x0i - x2i;
a[j1] = x1r - x3i;
a[j1 + 1] = x1i + x3r;
a[j3] = x1r + x3i;
a[j3 + 1] = x1i - x3r;
}
}
static void cftbsub_128(float *a) {
int j, j1, j2, j3, l;
float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;
cft1st_128(a);
cftmdl_128(a);
l = 32;
for (j = 0; j < l; j += 2) {
j1 = j + l;
j2 = j1 + l;
j3 = j2 + l;
x0r = a[j] + a[j1];
x0i = -a[j + 1] - a[j1 + 1];
x1r = a[j] - a[j1];
x1i = -a[j + 1] + a[j1 + 1];
x2r = a[j2] + a[j3];
x2i = a[j2 + 1] + a[j3 + 1];
x3r = a[j2] - a[j3];
x3i = a[j2 + 1] - a[j3 + 1];
a[j] = x0r + x2r;
a[j + 1] = x0i - x2i;
a[j2] = x0r - x2r;
a[j2 + 1] = x0i + x2i;
a[j1] = x1r - x3i;
a[j1 + 1] = x1i - x3r;
a[j3] = x1r + x3i;
a[j3 + 1] = x1i + x3r;
}
}
static void rftfsub_128_C(float *a) {
const float *c = rdft_w + 32;
int j1, j2, k1, k2;
float wkr, wki, xr, xi, yr, yi;
for (j1 = 1, j2 = 2; j2 < 64; j1 += 1, j2 += 2) {
k2 = 128 - j2;
k1 = 32 - j1;
wkr = 0.5f - c[k1];
wki = c[j1];
xr = a[j2 + 0] - a[k2 + 0];
xi = a[j2 + 1] + a[k2 + 1];
yr = wkr * xr - wki * xi;
yi = wkr * xi + wki * xr;
a[j2 + 0] -= yr;
a[j2 + 1] -= yi;
a[k2 + 0] += yr;
a[k2 + 1] -= yi;
}
}
static void rftbsub_128_C(float *a) {
const float *c = rdft_w + 32;
int j1, j2, k1, k2;
float wkr, wki, xr, xi, yr, yi;
a[1] = -a[1];
for (j1 = 1, j2 = 2; j2 < 64; j1 += 1, j2 += 2) {
k2 = 128 - j2;
k1 = 32 - j1;
wkr = 0.5f - c[k1];
wki = c[j1];
xr = a[j2 + 0] - a[k2 + 0];
xi = a[j2 + 1] + a[k2 + 1];
yr = wkr * xr + wki * xi;
yi = wkr * xi - wki * xr;
a[j2 + 0] = a[j2 + 0] - yr;
a[j2 + 1] = yi - a[j2 + 1];
a[k2 + 0] = yr + a[k2 + 0];
a[k2 + 1] = yi - a[k2 + 1];
}
a[65] = -a[65];
}
void aec_rdft_forward_128(float *a) {
const int n = 128;
float xi;
bitrv2_32or128(n, ip + 2, a);
cftfsub_128(a);
rftfsub_128(a);
xi = a[0] - a[1];
a[0] += a[1];
a[1] = xi;
}
void aec_rdft_inverse_128(float *a) {
const int n = 128;
a[1] = 0.5f * (a[0] - a[1]);
a[0] -= a[1];
rftbsub_128(a);
bitrv2_32or128(n, ip + 2, a);
cftbsub_128(a);
}
// code path selection
rft_sub_128_t cft1st_128;
rft_sub_128_t cftmdl_128;
rft_sub_128_t rftfsub_128;
rft_sub_128_t rftbsub_128;
void aec_rdft_init(void) {
cft1st_128 = cft1st_128_C;
cftmdl_128 = cftmdl_128_C;
rftfsub_128 = rftfsub_128_C;
rftbsub_128 = rftbsub_128_C;
if (WebRtc_GetCPUInfo(kSSE2)) {
#if defined(WEBRTC_USE_SSE2)
aec_rdft_init_sse2();
#endif
}
// init library constants.
makewt_32();
makect_32();
}

View File

@ -0,0 +1,57 @@
/*
* 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.
*/
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AEC_MAIN_SOURCE_AEC_RDFT_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_AEC_MAIN_SOURCE_AEC_RDFT_H_
// These intrinsics were unavailable before VS 2008.
// TODO(andrew): move to a common file.
#if defined(_MSC_VER) && _MSC_VER < 1500
#include <emmintrin.h>
static __inline __m128 _mm_castsi128_ps(__m128i a) { return *(__m128*)&a; }
static __inline __m128i _mm_castps_si128(__m128 a) { return *(__m128i*)&a; }
#endif
#ifdef _MSC_VER /* visual c++ */
# define ALIGN16_BEG __declspec(align(16))
# define ALIGN16_END
#else /* gcc or icc */
# define ALIGN16_BEG
# define ALIGN16_END __attribute__((aligned(16)))
#endif
// constants shared by all paths (C, SSE2).
extern float rdft_w[64];
// constants used by the C path.
extern float rdft_wk3ri_first[32];
extern float rdft_wk3ri_second[32];
// constants used by SSE2 but initialized in C path.
extern float rdft_wk1r[32];
extern float rdft_wk2r[32];
extern float rdft_wk3r[32];
extern float rdft_wk1i[32];
extern float rdft_wk2i[32];
extern float rdft_wk3i[32];
extern float cftmdl_wk1r[4];
// code path selection function pointers
typedef void (*rft_sub_128_t)(float *a);
extern rft_sub_128_t rftfsub_128;
extern rft_sub_128_t rftbsub_128;
extern rft_sub_128_t cft1st_128;
extern rft_sub_128_t cftmdl_128;
// entry points
void aec_rdft_init(void);
void aec_rdft_init_sse2(void);
void aec_rdft_forward_128(float *a);
void aec_rdft_inverse_128(float *a);
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AEC_MAIN_SOURCE_AEC_RDFT_H_

View File

@ -0,0 +1,431 @@
/*
* 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.
*/
#include "typedefs.h"
#if defined(WEBRTC_USE_SSE2)
#include <emmintrin.h>
#include "aec_rdft.h"
static const ALIGN16_BEG float ALIGN16_END k_swap_sign[4] =
{-1.f, 1.f, -1.f, 1.f};
static void cft1st_128_SSE2(float *a) {
const __m128 mm_swap_sign = _mm_load_ps(k_swap_sign);
int j, k2;
for (k2 = 0, j = 0; j < 128; j += 16, k2 += 4) {
__m128 a00v = _mm_loadu_ps(&a[j + 0]);
__m128 a04v = _mm_loadu_ps(&a[j + 4]);
__m128 a08v = _mm_loadu_ps(&a[j + 8]);
__m128 a12v = _mm_loadu_ps(&a[j + 12]);
__m128 a01v = _mm_shuffle_ps(a00v, a08v, _MM_SHUFFLE(1, 0, 1 ,0));
__m128 a23v = _mm_shuffle_ps(a00v, a08v, _MM_SHUFFLE(3, 2, 3 ,2));
__m128 a45v = _mm_shuffle_ps(a04v, a12v, _MM_SHUFFLE(1, 0, 1 ,0));
__m128 a67v = _mm_shuffle_ps(a04v, a12v, _MM_SHUFFLE(3, 2, 3 ,2));
const __m128 wk1rv = _mm_load_ps(&rdft_wk1r[k2]);
const __m128 wk1iv = _mm_load_ps(&rdft_wk1i[k2]);
const __m128 wk2rv = _mm_load_ps(&rdft_wk2r[k2]);
const __m128 wk2iv = _mm_load_ps(&rdft_wk2i[k2]);
const __m128 wk3rv = _mm_load_ps(&rdft_wk3r[k2]);
const __m128 wk3iv = _mm_load_ps(&rdft_wk3i[k2]);
__m128 x0v = _mm_add_ps(a01v, a23v);
const __m128 x1v = _mm_sub_ps(a01v, a23v);
const __m128 x2v = _mm_add_ps(a45v, a67v);
const __m128 x3v = _mm_sub_ps(a45v, a67v);
__m128 x0w;
a01v = _mm_add_ps(x0v, x2v);
x0v = _mm_sub_ps(x0v, x2v);
x0w = _mm_shuffle_ps(x0v, x0v, _MM_SHUFFLE(2, 3, 0 ,1));
{
const __m128 a45_0v = _mm_mul_ps(wk2rv, x0v);
const __m128 a45_1v = _mm_mul_ps(wk2iv, x0w);
a45v = _mm_add_ps(a45_0v, a45_1v);
}
{
__m128 a23_0v, a23_1v;
const __m128 x3w = _mm_shuffle_ps(x3v, x3v, _MM_SHUFFLE(2, 3, 0 ,1));
const __m128 x3s = _mm_mul_ps(mm_swap_sign, x3w);
x0v = _mm_add_ps(x1v, x3s);
x0w = _mm_shuffle_ps(x0v, x0v, _MM_SHUFFLE(2, 3, 0 ,1));
a23_0v = _mm_mul_ps(wk1rv, x0v);
a23_1v = _mm_mul_ps(wk1iv, x0w);
a23v = _mm_add_ps(a23_0v, a23_1v);
x0v = _mm_sub_ps(x1v, x3s);
x0w = _mm_shuffle_ps(x0v, x0v, _MM_SHUFFLE(2, 3, 0 ,1));
}
{
const __m128 a67_0v = _mm_mul_ps(wk3rv, x0v);
const __m128 a67_1v = _mm_mul_ps(wk3iv, x0w);
a67v = _mm_add_ps(a67_0v, a67_1v);
}
a00v = _mm_shuffle_ps(a01v, a23v, _MM_SHUFFLE(1, 0, 1 ,0));
a04v = _mm_shuffle_ps(a45v, a67v, _MM_SHUFFLE(1, 0, 1 ,0));
a08v = _mm_shuffle_ps(a01v, a23v, _MM_SHUFFLE(3, 2, 3 ,2));
a12v = _mm_shuffle_ps(a45v, a67v, _MM_SHUFFLE(3, 2, 3 ,2));
_mm_storeu_ps(&a[j + 0], a00v);
_mm_storeu_ps(&a[j + 4], a04v);
_mm_storeu_ps(&a[j + 8], a08v);
_mm_storeu_ps(&a[j + 12], a12v);
}
}
static void cftmdl_128_SSE2(float *a) {
const int l = 8;
const __m128 mm_swap_sign = _mm_load_ps(k_swap_sign);
int j0;
__m128 wk1rv = _mm_load_ps(cftmdl_wk1r);
for (j0 = 0; j0 < l; j0 += 2) {
const __m128i a_00 = _mm_loadl_epi64((__m128i*)&a[j0 + 0]);
const __m128i a_08 = _mm_loadl_epi64((__m128i*)&a[j0 + 8]);
const __m128i a_32 = _mm_loadl_epi64((__m128i*)&a[j0 + 32]);
const __m128i a_40 = _mm_loadl_epi64((__m128i*)&a[j0 + 40]);
const __m128 a_00_32 = _mm_shuffle_ps(_mm_castsi128_ps(a_00),
_mm_castsi128_ps(a_32),
_MM_SHUFFLE(1, 0, 1 ,0));
const __m128 a_08_40 = _mm_shuffle_ps(_mm_castsi128_ps(a_08),
_mm_castsi128_ps(a_40),
_MM_SHUFFLE(1, 0, 1 ,0));
__m128 x0r0_0i0_0r1_x0i1 = _mm_add_ps(a_00_32, a_08_40);
const __m128 x1r0_1i0_1r1_x1i1 = _mm_sub_ps(a_00_32, a_08_40);
const __m128i a_16 = _mm_loadl_epi64((__m128i*)&a[j0 + 16]);
const __m128i a_24 = _mm_loadl_epi64((__m128i*)&a[j0 + 24]);
const __m128i a_48 = _mm_loadl_epi64((__m128i*)&a[j0 + 48]);
const __m128i a_56 = _mm_loadl_epi64((__m128i*)&a[j0 + 56]);
const __m128 a_16_48 = _mm_shuffle_ps(_mm_castsi128_ps(a_16),
_mm_castsi128_ps(a_48),
_MM_SHUFFLE(1, 0, 1 ,0));
const __m128 a_24_56 = _mm_shuffle_ps(_mm_castsi128_ps(a_24),
_mm_castsi128_ps(a_56),
_MM_SHUFFLE(1, 0, 1 ,0));
const __m128 x2r0_2i0_2r1_x2i1 = _mm_add_ps(a_16_48, a_24_56);
const __m128 x3r0_3i0_3r1_x3i1 = _mm_sub_ps(a_16_48, a_24_56);
const __m128 xx0 = _mm_add_ps(x0r0_0i0_0r1_x0i1, x2r0_2i0_2r1_x2i1);
const __m128 xx1 = _mm_sub_ps(x0r0_0i0_0r1_x0i1, x2r0_2i0_2r1_x2i1);
const __m128 x3i0_3r0_3i1_x3r1 = _mm_castsi128_ps(
_mm_shuffle_epi32(_mm_castps_si128(x3r0_3i0_3r1_x3i1),
_MM_SHUFFLE(2, 3, 0, 1)));
const __m128 x3_swapped = _mm_mul_ps(mm_swap_sign, x3i0_3r0_3i1_x3r1);
const __m128 x1_x3_add = _mm_add_ps(x1r0_1i0_1r1_x1i1, x3_swapped);
const __m128 x1_x3_sub = _mm_sub_ps(x1r0_1i0_1r1_x1i1, x3_swapped);
const __m128 yy0 = _mm_shuffle_ps(x1_x3_add, x1_x3_sub,
_MM_SHUFFLE(2, 2, 2 ,2));
const __m128 yy1 = _mm_shuffle_ps(x1_x3_add, x1_x3_sub,
_MM_SHUFFLE(3, 3, 3 ,3));
const __m128 yy2 = _mm_mul_ps(mm_swap_sign, yy1);
const __m128 yy3 = _mm_add_ps(yy0, yy2);
const __m128 yy4 = _mm_mul_ps(wk1rv, yy3);
_mm_storel_epi64((__m128i*)&a[j0 + 0], _mm_castps_si128(xx0));
_mm_storel_epi64((__m128i*)&a[j0 + 32],
_mm_shuffle_epi32(_mm_castps_si128(xx0),
_MM_SHUFFLE(3, 2, 3, 2)));
_mm_storel_epi64((__m128i*)&a[j0 + 16], _mm_castps_si128(xx1));
_mm_storel_epi64((__m128i*)&a[j0 + 48],
_mm_shuffle_epi32(_mm_castps_si128(xx1),
_MM_SHUFFLE(2, 3, 2, 3)));
a[j0 + 48] = -a[j0 + 48];
_mm_storel_epi64((__m128i*)&a[j0 + 8], _mm_castps_si128(x1_x3_add));
_mm_storel_epi64((__m128i*)&a[j0 + 24], _mm_castps_si128(x1_x3_sub));
_mm_storel_epi64((__m128i*)&a[j0 + 40], _mm_castps_si128(yy4));
_mm_storel_epi64((__m128i*)&a[j0 + 56],
_mm_shuffle_epi32(_mm_castps_si128(yy4),
_MM_SHUFFLE(2, 3, 2, 3)));
}
{
int k = 64;
int k1 = 2;
int k2 = 2 * k1;
const __m128 wk2rv = _mm_load_ps(&rdft_wk2r[k2+0]);
const __m128 wk2iv = _mm_load_ps(&rdft_wk2i[k2+0]);
const __m128 wk1iv = _mm_load_ps(&rdft_wk1i[k2+0]);
const __m128 wk3rv = _mm_load_ps(&rdft_wk3r[k2+0]);
const __m128 wk3iv = _mm_load_ps(&rdft_wk3i[k2+0]);
wk1rv = _mm_load_ps(&rdft_wk1r[k2+0]);
for (j0 = k; j0 < l + k; j0 += 2) {
const __m128i a_00 = _mm_loadl_epi64((__m128i*)&a[j0 + 0]);
const __m128i a_08 = _mm_loadl_epi64((__m128i*)&a[j0 + 8]);
const __m128i a_32 = _mm_loadl_epi64((__m128i*)&a[j0 + 32]);
const __m128i a_40 = _mm_loadl_epi64((__m128i*)&a[j0 + 40]);
const __m128 a_00_32 = _mm_shuffle_ps(_mm_castsi128_ps(a_00),
_mm_castsi128_ps(a_32),
_MM_SHUFFLE(1, 0, 1 ,0));
const __m128 a_08_40 = _mm_shuffle_ps(_mm_castsi128_ps(a_08),
_mm_castsi128_ps(a_40),
_MM_SHUFFLE(1, 0, 1 ,0));
__m128 x0r0_0i0_0r1_x0i1 = _mm_add_ps(a_00_32, a_08_40);
const __m128 x1r0_1i0_1r1_x1i1 = _mm_sub_ps(a_00_32, a_08_40);
const __m128i a_16 = _mm_loadl_epi64((__m128i*)&a[j0 + 16]);
const __m128i a_24 = _mm_loadl_epi64((__m128i*)&a[j0 + 24]);
const __m128i a_48 = _mm_loadl_epi64((__m128i*)&a[j0 + 48]);
const __m128i a_56 = _mm_loadl_epi64((__m128i*)&a[j0 + 56]);
const __m128 a_16_48 = _mm_shuffle_ps(_mm_castsi128_ps(a_16),
_mm_castsi128_ps(a_48),
_MM_SHUFFLE(1, 0, 1 ,0));
const __m128 a_24_56 = _mm_shuffle_ps(_mm_castsi128_ps(a_24),
_mm_castsi128_ps(a_56),
_MM_SHUFFLE(1, 0, 1 ,0));
const __m128 x2r0_2i0_2r1_x2i1 = _mm_add_ps(a_16_48, a_24_56);
const __m128 x3r0_3i0_3r1_x3i1 = _mm_sub_ps(a_16_48, a_24_56);
const __m128 xx = _mm_add_ps(x0r0_0i0_0r1_x0i1, x2r0_2i0_2r1_x2i1);
const __m128 xx1 = _mm_sub_ps(x0r0_0i0_0r1_x0i1, x2r0_2i0_2r1_x2i1);
const __m128 xx2 = _mm_mul_ps(xx1 , wk2rv);
const __m128 xx3 = _mm_mul_ps(wk2iv,
_mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(xx1),
_MM_SHUFFLE(2, 3, 0, 1))));
const __m128 xx4 = _mm_add_ps(xx2, xx3);
const __m128 x3i0_3r0_3i1_x3r1 = _mm_castsi128_ps(
_mm_shuffle_epi32(_mm_castps_si128(x3r0_3i0_3r1_x3i1),
_MM_SHUFFLE(2, 3, 0, 1)));
const __m128 x3_swapped = _mm_mul_ps(mm_swap_sign, x3i0_3r0_3i1_x3r1);
const __m128 x1_x3_add = _mm_add_ps(x1r0_1i0_1r1_x1i1, x3_swapped);
const __m128 x1_x3_sub = _mm_sub_ps(x1r0_1i0_1r1_x1i1, x3_swapped);
const __m128 xx10 = _mm_mul_ps(x1_x3_add, wk1rv);
const __m128 xx11 = _mm_mul_ps(wk1iv,
_mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(x1_x3_add),
_MM_SHUFFLE(2, 3, 0, 1))));
const __m128 xx12 = _mm_add_ps(xx10, xx11);
const __m128 xx20 = _mm_mul_ps(x1_x3_sub, wk3rv);
const __m128 xx21 = _mm_mul_ps(wk3iv,
_mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(x1_x3_sub),
_MM_SHUFFLE(2, 3, 0, 1))));
const __m128 xx22 = _mm_add_ps(xx20, xx21);
_mm_storel_epi64((__m128i*)&a[j0 + 0], _mm_castps_si128(xx));
_mm_storel_epi64((__m128i*)&a[j0 + 32],
_mm_shuffle_epi32(_mm_castps_si128(xx),
_MM_SHUFFLE(3, 2, 3, 2)));
_mm_storel_epi64((__m128i*)&a[j0 + 16], _mm_castps_si128(xx4));
_mm_storel_epi64((__m128i*)&a[j0 + 48],
_mm_shuffle_epi32(_mm_castps_si128(xx4),
_MM_SHUFFLE(3, 2, 3, 2)));
_mm_storel_epi64((__m128i*)&a[j0 + 8], _mm_castps_si128(xx12));
_mm_storel_epi64((__m128i*)&a[j0 + 40],
_mm_shuffle_epi32(_mm_castps_si128(xx12),
_MM_SHUFFLE(3, 2, 3, 2)));
_mm_storel_epi64((__m128i*)&a[j0 + 24], _mm_castps_si128(xx22));
_mm_storel_epi64((__m128i*)&a[j0 + 56],
_mm_shuffle_epi32(_mm_castps_si128(xx22),
_MM_SHUFFLE(3, 2, 3, 2)));
}
}
}
static void rftfsub_128_SSE2(float *a) {
const float *c = rdft_w + 32;
int j1, j2, k1, k2;
float wkr, wki, xr, xi, yr, yi;
static const ALIGN16_BEG float ALIGN16_END k_half[4] =
{0.5f, 0.5f, 0.5f, 0.5f};
const __m128 mm_half = _mm_load_ps(k_half);
// Vectorized code (four at once).
// Note: commented number are indexes for the first iteration of the loop.
for (j1 = 1, j2 = 2; j2 + 7 < 64; j1 += 4, j2 += 8) {
// Load 'wk'.
const __m128 c_j1 = _mm_loadu_ps(&c[ j1]); // 1, 2, 3, 4,
const __m128 c_k1 = _mm_loadu_ps(&c[29 - j1]); // 28, 29, 30, 31,
const __m128 wkrt = _mm_sub_ps(mm_half, c_k1); // 28, 29, 30, 31,
const __m128 wkr_ =
_mm_shuffle_ps(wkrt, wkrt, _MM_SHUFFLE(0, 1, 2, 3)); // 31, 30, 29, 28,
const __m128 wki_ = c_j1; // 1, 2, 3, 4,
// Load and shuffle 'a'.
const __m128 a_j2_0 = _mm_loadu_ps(&a[0 + j2]); // 2, 3, 4, 5,
const __m128 a_j2_4 = _mm_loadu_ps(&a[4 + j2]); // 6, 7, 8, 9,
const __m128 a_k2_0 = _mm_loadu_ps(&a[122 - j2]); // 120, 121, 122, 123,
const __m128 a_k2_4 = _mm_loadu_ps(&a[126 - j2]); // 124, 125, 126, 127,
const __m128 a_j2_p0 = _mm_shuffle_ps(a_j2_0, a_j2_4,
_MM_SHUFFLE(2, 0, 2 ,0)); // 2, 4, 6, 8,
const __m128 a_j2_p1 = _mm_shuffle_ps(a_j2_0, a_j2_4,
_MM_SHUFFLE(3, 1, 3 ,1)); // 3, 5, 7, 9,
const __m128 a_k2_p0 = _mm_shuffle_ps(a_k2_4, a_k2_0,
_MM_SHUFFLE(0, 2, 0 ,2)); // 126, 124, 122, 120,
const __m128 a_k2_p1 = _mm_shuffle_ps(a_k2_4, a_k2_0,
_MM_SHUFFLE(1, 3, 1 ,3)); // 127, 125, 123, 121,
// Calculate 'x'.
const __m128 xr_ = _mm_sub_ps(a_j2_p0, a_k2_p0);
// 2-126, 4-124, 6-122, 8-120,
const __m128 xi_ = _mm_add_ps(a_j2_p1, a_k2_p1);
// 3-127, 5-125, 7-123, 9-121,
// Calculate product into 'y'.
// yr = wkr * xr - wki * xi;
// yi = wkr * xi + wki * xr;
const __m128 a_ = _mm_mul_ps(wkr_, xr_);
const __m128 b_ = _mm_mul_ps(wki_, xi_);
const __m128 c_ = _mm_mul_ps(wkr_, xi_);
const __m128 d_ = _mm_mul_ps(wki_, xr_);
const __m128 yr_ = _mm_sub_ps(a_, b_); // 2-126, 4-124, 6-122, 8-120,
const __m128 yi_ = _mm_add_ps(c_, d_); // 3-127, 5-125, 7-123, 9-121,
// Update 'a'.
// a[j2 + 0] -= yr;
// a[j2 + 1] -= yi;
// a[k2 + 0] += yr;
// a[k2 + 1] -= yi;
const __m128 a_j2_p0n = _mm_sub_ps(a_j2_p0, yr_); // 2, 4, 6, 8,
const __m128 a_j2_p1n = _mm_sub_ps(a_j2_p1, yi_); // 3, 5, 7, 9,
const __m128 a_k2_p0n = _mm_add_ps(a_k2_p0, yr_); // 126, 124, 122, 120,
const __m128 a_k2_p1n = _mm_sub_ps(a_k2_p1, yi_); // 127, 125, 123, 121,
// Shuffle in right order and store.
const __m128 a_j2_0n = _mm_unpacklo_ps(a_j2_p0n, a_j2_p1n);
// 2, 3, 4, 5,
const __m128 a_j2_4n = _mm_unpackhi_ps(a_j2_p0n, a_j2_p1n);
// 6, 7, 8, 9,
const __m128 a_k2_0nt = _mm_unpackhi_ps(a_k2_p0n, a_k2_p1n);
// 122, 123, 120, 121,
const __m128 a_k2_4nt = _mm_unpacklo_ps(a_k2_p0n, a_k2_p1n);
// 126, 127, 124, 125,
const __m128 a_k2_0n = _mm_shuffle_ps(a_k2_0nt, a_k2_0nt,
_MM_SHUFFLE(1, 0, 3 ,2)); // 120, 121, 122, 123,
const __m128 a_k2_4n = _mm_shuffle_ps(a_k2_4nt, a_k2_4nt,
_MM_SHUFFLE(1, 0, 3 ,2)); // 124, 125, 126, 127,
_mm_storeu_ps(&a[0 + j2], a_j2_0n);
_mm_storeu_ps(&a[4 + j2], a_j2_4n);
_mm_storeu_ps(&a[122 - j2], a_k2_0n);
_mm_storeu_ps(&a[126 - j2], a_k2_4n);
}
// Scalar code for the remaining items.
for (; j2 < 64; j1 += 1, j2 += 2) {
k2 = 128 - j2;
k1 = 32 - j1;
wkr = 0.5f - c[k1];
wki = c[j1];
xr = a[j2 + 0] - a[k2 + 0];
xi = a[j2 + 1] + a[k2 + 1];
yr = wkr * xr - wki * xi;
yi = wkr * xi + wki * xr;
a[j2 + 0] -= yr;
a[j2 + 1] -= yi;
a[k2 + 0] += yr;
a[k2 + 1] -= yi;
}
}
static void rftbsub_128_SSE2(float *a) {
const float *c = rdft_w + 32;
int j1, j2, k1, k2;
float wkr, wki, xr, xi, yr, yi;
static const ALIGN16_BEG float ALIGN16_END k_half[4] =
{0.5f, 0.5f, 0.5f, 0.5f};
const __m128 mm_half = _mm_load_ps(k_half);
a[1] = -a[1];
// Vectorized code (four at once).
// Note: commented number are indexes for the first iteration of the loop.
for (j1 = 1, j2 = 2; j2 + 7 < 64; j1 += 4, j2 += 8) {
// Load 'wk'.
const __m128 c_j1 = _mm_loadu_ps(&c[ j1]); // 1, 2, 3, 4,
const __m128 c_k1 = _mm_loadu_ps(&c[29 - j1]); // 28, 29, 30, 31,
const __m128 wkrt = _mm_sub_ps(mm_half, c_k1); // 28, 29, 30, 31,
const __m128 wkr_ =
_mm_shuffle_ps(wkrt, wkrt, _MM_SHUFFLE(0, 1, 2, 3)); // 31, 30, 29, 28,
const __m128 wki_ = c_j1; // 1, 2, 3, 4,
// Load and shuffle 'a'.
const __m128 a_j2_0 = _mm_loadu_ps(&a[0 + j2]); // 2, 3, 4, 5,
const __m128 a_j2_4 = _mm_loadu_ps(&a[4 + j2]); // 6, 7, 8, 9,
const __m128 a_k2_0 = _mm_loadu_ps(&a[122 - j2]); // 120, 121, 122, 123,
const __m128 a_k2_4 = _mm_loadu_ps(&a[126 - j2]); // 124, 125, 126, 127,
const __m128 a_j2_p0 = _mm_shuffle_ps(a_j2_0, a_j2_4,
_MM_SHUFFLE(2, 0, 2 ,0)); // 2, 4, 6, 8,
const __m128 a_j2_p1 = _mm_shuffle_ps(a_j2_0, a_j2_4,
_MM_SHUFFLE(3, 1, 3 ,1)); // 3, 5, 7, 9,
const __m128 a_k2_p0 = _mm_shuffle_ps(a_k2_4, a_k2_0,
_MM_SHUFFLE(0, 2, 0 ,2)); // 126, 124, 122, 120,
const __m128 a_k2_p1 = _mm_shuffle_ps(a_k2_4, a_k2_0,
_MM_SHUFFLE(1, 3, 1 ,3)); // 127, 125, 123, 121,
// Calculate 'x'.
const __m128 xr_ = _mm_sub_ps(a_j2_p0, a_k2_p0);
// 2-126, 4-124, 6-122, 8-120,
const __m128 xi_ = _mm_add_ps(a_j2_p1, a_k2_p1);
// 3-127, 5-125, 7-123, 9-121,
// Calculate product into 'y'.
// yr = wkr * xr + wki * xi;
// yi = wkr * xi - wki * xr;
const __m128 a_ = _mm_mul_ps(wkr_, xr_);
const __m128 b_ = _mm_mul_ps(wki_, xi_);
const __m128 c_ = _mm_mul_ps(wkr_, xi_);
const __m128 d_ = _mm_mul_ps(wki_, xr_);
const __m128 yr_ = _mm_add_ps(a_, b_); // 2-126, 4-124, 6-122, 8-120,
const __m128 yi_ = _mm_sub_ps(c_, d_); // 3-127, 5-125, 7-123, 9-121,
// Update 'a'.
// a[j2 + 0] = a[j2 + 0] - yr;
// a[j2 + 1] = yi - a[j2 + 1];
// a[k2 + 0] = yr + a[k2 + 0];
// a[k2 + 1] = yi - a[k2 + 1];
const __m128 a_j2_p0n = _mm_sub_ps(a_j2_p0, yr_); // 2, 4, 6, 8,
const __m128 a_j2_p1n = _mm_sub_ps(yi_, a_j2_p1); // 3, 5, 7, 9,
const __m128 a_k2_p0n = _mm_add_ps(a_k2_p0, yr_); // 126, 124, 122, 120,
const __m128 a_k2_p1n = _mm_sub_ps(yi_, a_k2_p1); // 127, 125, 123, 121,
// Shuffle in right order and store.
const __m128 a_j2_0n = _mm_unpacklo_ps(a_j2_p0n, a_j2_p1n);
// 2, 3, 4, 5,
const __m128 a_j2_4n = _mm_unpackhi_ps(a_j2_p0n, a_j2_p1n);
// 6, 7, 8, 9,
const __m128 a_k2_0nt = _mm_unpackhi_ps(a_k2_p0n, a_k2_p1n);
// 122, 123, 120, 121,
const __m128 a_k2_4nt = _mm_unpacklo_ps(a_k2_p0n, a_k2_p1n);
// 126, 127, 124, 125,
const __m128 a_k2_0n = _mm_shuffle_ps(a_k2_0nt, a_k2_0nt,
_MM_SHUFFLE(1, 0, 3 ,2)); // 120, 121, 122, 123,
const __m128 a_k2_4n = _mm_shuffle_ps(a_k2_4nt, a_k2_4nt,
_MM_SHUFFLE(1, 0, 3 ,2)); // 124, 125, 126, 127,
_mm_storeu_ps(&a[0 + j2], a_j2_0n);
_mm_storeu_ps(&a[4 + j2], a_j2_4n);
_mm_storeu_ps(&a[122 - j2], a_k2_0n);
_mm_storeu_ps(&a[126 - j2], a_k2_4n);
}
// Scalar code for the remaining items.
for (; j2 < 64; j1 += 1, j2 += 2) {
k2 = 128 - j2;
k1 = 32 - j1;
wkr = 0.5f - c[k1];
wki = c[j1];
xr = a[j2 + 0] - a[k2 + 0];
xi = a[j2 + 1] + a[k2 + 1];
yr = wkr * xr + wki * xi;
yi = wkr * xi - wki * xr;
a[j2 + 0] = a[j2 + 0] - yr;
a[j2 + 1] = yi - a[j2 + 1];
a[k2 + 0] = yr + a[k2 + 0];
a[k2 + 1] = yi - a[k2 + 1];
}
a[65] = -a[65];
}
void aec_rdft_init_sse2(void) {
cft1st_128 = cft1st_128_SSE2;
cftmdl_128 = cftmdl_128_SSE2;
rftfsub_128 = rftfsub_128_SSE2;
rftbsub_128 = rftbsub_128_SSE2;
}
#endif // WEBRTC_USE_SS2

View File

@ -0,0 +1,901 @@
/*
* 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.
*/
/*
* Contains the API functions for the AEC.
*/
#include "echo_cancellation.h"
#include <math.h>
#ifdef AEC_DEBUG
#include <stdio.h>
#endif
#include <stdlib.h>
#include <string.h>
#include "aec_core.h"
#include "resampler.h"
#include "ring_buffer.h"
#define BUF_SIZE_FRAMES 50 // buffer size (frames)
// Maximum length of resampled signal. Must be an integer multiple of frames
// (ceil(1/(1 + MIN_SKEW)*2) + 1)*FRAME_LEN
// The factor of 2 handles wb, and the + 1 is as a safety margin
#define MAX_RESAMP_LEN (5 * FRAME_LEN)
static const int bufSizeSamp = BUF_SIZE_FRAMES * FRAME_LEN; // buffer size (samples)
static const int sampMsNb = 8; // samples per ms in nb
// Target suppression levels for nlp modes
// log{0.001, 0.00001, 0.00000001}
static const float targetSupp[3] = {-6.9f, -11.5f, -18.4f};
static const float minOverDrive[3] = {1.0f, 2.0f, 5.0f};
static const int initCheck = 42;
typedef struct {
int delayCtr;
int sampFreq;
int splitSampFreq;
int scSampFreq;
float sampFactor; // scSampRate / sampFreq
short nlpMode;
short autoOnOff;
short activity;
short skewMode;
short bufSizeStart;
//short bufResetCtr; // counts number of noncausal frames
int knownDelay;
// Stores the last frame added to the farend buffer
short farendOld[2][FRAME_LEN];
short initFlag; // indicates if AEC has been initialized
// Variables used for averaging far end buffer size
short counter;
short sum;
short firstVal;
short checkBufSizeCtr;
// Variables used for delay shifts
short msInSndCardBuf;
short filtDelay;
int timeForDelayChange;
int ECstartup;
int checkBuffSize;
int delayChange;
short lastDelayDiff;
#ifdef AEC_DEBUG
FILE *bufFile;
FILE *delayFile;
FILE *skewFile;
FILE *preCompFile;
FILE *postCompFile;
#endif // AEC_DEBUG
// Structures
void *farendBuf;
void *resampler;
int skewFrCtr;
int resample; // if the skew is small enough we don't resample
int highSkewCtr;
float skew;
int lastError;
aec_t *aec;
} aecpc_t;
// Estimates delay to set the position of the farend buffer read pointer
// (controlled by knownDelay)
static int EstBufDelay(aecpc_t *aecInst, short msInSndCardBuf);
// Stuffs the farend buffer if the estimated delay is too large
static int DelayComp(aecpc_t *aecInst);
WebRtc_Word32 WebRtcAec_Create(void **aecInst)
{
aecpc_t *aecpc;
if (aecInst == NULL) {
return -1;
}
aecpc = malloc(sizeof(aecpc_t));
*aecInst = aecpc;
if (aecpc == NULL) {
return -1;
}
if (WebRtcAec_CreateAec(&aecpc->aec) == -1) {
WebRtcAec_Free(aecpc);
aecpc = NULL;
return -1;
}
if (WebRtcApm_CreateBuffer(&aecpc->farendBuf, bufSizeSamp) == -1) {
WebRtcAec_Free(aecpc);
aecpc = NULL;
return -1;
}
if (WebRtcAec_CreateResampler(&aecpc->resampler) == -1) {
WebRtcAec_Free(aecpc);
aecpc = NULL;
return -1;
}
aecpc->initFlag = 0;
aecpc->lastError = 0;
#ifdef AEC_DEBUG
aecpc->aec->farFile = fopen("aecFar.pcm","wb");
aecpc->aec->nearFile = fopen("aecNear.pcm","wb");
aecpc->aec->outFile = fopen("aecOut.pcm","wb");
aecpc->aec->outLpFile = fopen("aecOutLp.pcm","wb");
aecpc->bufFile = fopen("aecBuf.dat", "wb");
aecpc->skewFile = fopen("aecSkew.dat", "wb");
aecpc->delayFile = fopen("aecDelay.dat", "wb");
aecpc->preCompFile = fopen("preComp.pcm", "wb");
aecpc->postCompFile = fopen("postComp.pcm", "wb");
#endif // AEC_DEBUG
return 0;
}
WebRtc_Word32 WebRtcAec_Free(void *aecInst)
{
aecpc_t *aecpc = aecInst;
if (aecpc == NULL) {
return -1;
}
#ifdef AEC_DEBUG
fclose(aecpc->aec->farFile);
fclose(aecpc->aec->nearFile);
fclose(aecpc->aec->outFile);
fclose(aecpc->aec->outLpFile);
fclose(aecpc->bufFile);
fclose(aecpc->skewFile);
fclose(aecpc->delayFile);
fclose(aecpc->preCompFile);
fclose(aecpc->postCompFile);
#endif // AEC_DEBUG
WebRtcAec_FreeAec(aecpc->aec);
WebRtcApm_FreeBuffer(aecpc->farendBuf);
WebRtcAec_FreeResampler(aecpc->resampler);
free(aecpc);
return 0;
}
WebRtc_Word32 WebRtcAec_Init(void *aecInst, WebRtc_Word32 sampFreq, WebRtc_Word32 scSampFreq)
{
aecpc_t *aecpc = aecInst;
AecConfig aecConfig;
if (aecpc == NULL) {
return -1;
}
if (sampFreq != 8000 && sampFreq != 16000 && sampFreq != 32000) {
aecpc->lastError = AEC_BAD_PARAMETER_ERROR;
return -1;
}
aecpc->sampFreq = sampFreq;
if (scSampFreq < 1 || scSampFreq > 96000) {
aecpc->lastError = AEC_BAD_PARAMETER_ERROR;
return -1;
}
aecpc->scSampFreq = scSampFreq;
// Initialize echo canceller core
if (WebRtcAec_InitAec(aecpc->aec, aecpc->sampFreq) == -1) {
aecpc->lastError = AEC_UNSPECIFIED_ERROR;
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) {
aecpc->lastError = AEC_UNSPECIFIED_ERROR;
return -1;
}
aecpc->initFlag = initCheck; // indicates that initialization has been done
if (aecpc->sampFreq == 32000) {
aecpc->splitSampFreq = 16000;
}
else {
aecpc->splitSampFreq = sampFreq;
}
aecpc->skewFrCtr = 0;
aecpc->activity = 0;
aecpc->delayChange = 1;
aecpc->delayCtr = 0;
aecpc->sum = 0;
aecpc->counter = 0;
aecpc->checkBuffSize = 1;
aecpc->firstVal = 0;
aecpc->ECstartup = 1;
aecpc->bufSizeStart = 0;
aecpc->checkBufSizeCtr = 0;
aecpc->filtDelay = 0;
aecpc->timeForDelayChange =0;
aecpc->knownDelay = 0;
aecpc->lastDelayDiff = 0;
aecpc->skew = 0;
aecpc->resample = kAecFalse;
aecpc->highSkewCtr = 0;
aecpc->sampFactor = (aecpc->scSampFreq * 1.0f) / aecpc->splitSampFreq;
memset(&aecpc->farendOld[0][0], 0, 160);
// Default settings.
aecConfig.nlpMode = kAecNlpModerate;
aecConfig.skewMode = kAecFalse;
aecConfig.metricsMode = kAecFalse;
aecConfig.delay_logging = kAecFalse;
if (WebRtcAec_set_config(aecpc, aecConfig) == -1) {
aecpc->lastError = AEC_UNSPECIFIED_ERROR;
return -1;
}
return 0;
}
// only buffer L band for farend
WebRtc_Word32 WebRtcAec_BufferFarend(void *aecInst, const WebRtc_Word16 *farend,
WebRtc_Word16 nrOfSamples)
{
aecpc_t *aecpc = aecInst;
WebRtc_Word32 retVal = 0;
short newNrOfSamples;
short newFarend[MAX_RESAMP_LEN];
float skew;
if (aecpc == NULL) {
return -1;
}
if (farend == NULL) {
aecpc->lastError = AEC_NULL_POINTER_ERROR;
return -1;
}
if (aecpc->initFlag != initCheck) {
aecpc->lastError = AEC_UNINITIALIZED_ERROR;
return -1;
}
// number of samples == 160 for SWB input
if (nrOfSamples != 80 && nrOfSamples != 160) {
aecpc->lastError = AEC_BAD_PARAMETER_ERROR;
return -1;
}
skew = aecpc->skew;
// TODO: Is this really a good idea?
if (!aecpc->ECstartup) {
DelayComp(aecpc);
}
if (aecpc->skewMode == kAecTrue && aecpc->resample == kAecTrue) {
// Resample and get a new number of samples
newNrOfSamples = WebRtcAec_ResampleLinear(aecpc->resampler,
farend,
nrOfSamples,
skew,
newFarend);
WebRtcApm_WriteBuffer(aecpc->farendBuf, newFarend, newNrOfSamples);
#ifdef AEC_DEBUG
fwrite(farend, 2, nrOfSamples, aecpc->preCompFile);
fwrite(newFarend, 2, newNrOfSamples, aecpc->postCompFile);
#endif
}
else {
WebRtcApm_WriteBuffer(aecpc->farendBuf, farend, nrOfSamples);
}
return retVal;
}
WebRtc_Word32 WebRtcAec_Process(void *aecInst, const WebRtc_Word16 *nearend,
const WebRtc_Word16 *nearendH, WebRtc_Word16 *out, WebRtc_Word16 *outH,
WebRtc_Word16 nrOfSamples, WebRtc_Word16 msInSndCardBuf, WebRtc_Word32 skew)
{
aecpc_t *aecpc = aecInst;
WebRtc_Word32 retVal = 0;
short i;
short farend[FRAME_LEN];
short nmbrOfFilledBuffers;
short nBlocks10ms;
short nFrames;
#ifdef AEC_DEBUG
short msInAECBuf;
#endif
// Limit resampling to doubling/halving of signal
const float minSkewEst = -0.5f;
const float maxSkewEst = 1.0f;
if (aecpc == NULL) {
return -1;
}
if (nearend == NULL) {
aecpc->lastError = AEC_NULL_POINTER_ERROR;
return -1;
}
if (out == NULL) {
aecpc->lastError = AEC_NULL_POINTER_ERROR;
return -1;
}
if (aecpc->initFlag != initCheck) {
aecpc->lastError = AEC_UNINITIALIZED_ERROR;
return -1;
}
// number of samples == 160 for SWB input
if (nrOfSamples != 80 && nrOfSamples != 160) {
aecpc->lastError = AEC_BAD_PARAMETER_ERROR;
return -1;
}
// Check for valid pointers based on sampling rate
if (aecpc->sampFreq == 32000 && nearendH == NULL) {
aecpc->lastError = AEC_NULL_POINTER_ERROR;
return -1;
}
if (msInSndCardBuf < 0) {
msInSndCardBuf = 0;
aecpc->lastError = AEC_BAD_PARAMETER_WARNING;
retVal = -1;
}
else if (msInSndCardBuf > 500) {
msInSndCardBuf = 500;
aecpc->lastError = AEC_BAD_PARAMETER_WARNING;
retVal = -1;
}
msInSndCardBuf += 10;
aecpc->msInSndCardBuf = msInSndCardBuf;
if (aecpc->skewMode == kAecTrue) {
if (aecpc->skewFrCtr < 25) {
aecpc->skewFrCtr++;
}
else {
retVal = WebRtcAec_GetSkew(aecpc->resampler, skew, &aecpc->skew);
if (retVal == -1) {
aecpc->skew = 0;
aecpc->lastError = AEC_BAD_PARAMETER_WARNING;
}
aecpc->skew /= aecpc->sampFactor*nrOfSamples;
if (aecpc->skew < 1.0e-3 && aecpc->skew > -1.0e-3) {
aecpc->resample = kAecFalse;
}
else {
aecpc->resample = kAecTrue;
}
if (aecpc->skew < minSkewEst) {
aecpc->skew = minSkewEst;
}
else if (aecpc->skew > maxSkewEst) {
aecpc->skew = maxSkewEst;
}
#ifdef AEC_DEBUG
fwrite(&aecpc->skew, sizeof(aecpc->skew), 1, aecpc->skewFile);
#endif
}
}
nFrames = nrOfSamples / FRAME_LEN;
nBlocks10ms = nFrames / aecpc->aec->mult;
if (aecpc->ECstartup) {
if (nearend != out) {
// Only needed if they don't already point to the same place.
memcpy(out, nearend, sizeof(short) * nrOfSamples);
}
nmbrOfFilledBuffers = WebRtcApm_get_buffer_size(aecpc->farendBuf) / FRAME_LEN;
// The AEC is in the start up mode
// AEC is disabled until the soundcard buffer and farend buffers are OK
// Mechanism to ensure that the soundcard buffer is reasonably stable.
if (aecpc->checkBuffSize) {
aecpc->checkBufSizeCtr++;
// Before we fill up the far end buffer we require the amount of data on the
// sound card to be stable (+/-8 ms) compared to the first value. This
// comparison is made during the following 4 consecutive frames. If it seems
// to be stable then we start to fill up the far end buffer.
if (aecpc->counter == 0) {
aecpc->firstVal = aecpc->msInSndCardBuf;
aecpc->sum = 0;
}
if (abs(aecpc->firstVal - aecpc->msInSndCardBuf) <
WEBRTC_SPL_MAX(0.2 * aecpc->msInSndCardBuf, sampMsNb)) {
aecpc->sum += aecpc->msInSndCardBuf;
aecpc->counter++;
}
else {
aecpc->counter = 0;
}
if (aecpc->counter*nBlocks10ms >= 6) {
// The farend buffer size is determined in blocks of 80 samples
// Use 75% of the average value of the soundcard buffer
aecpc->bufSizeStart = WEBRTC_SPL_MIN((int) (0.75 * (aecpc->sum *
aecpc->aec->mult) / (aecpc->counter * 10)), BUF_SIZE_FRAMES);
// buffersize has now been determined
aecpc->checkBuffSize = 0;
}
if (aecpc->checkBufSizeCtr * nBlocks10ms > 50) {
// for really bad sound cards, don't disable echocanceller for more than 0.5 sec
aecpc->bufSizeStart = WEBRTC_SPL_MIN((int) (0.75 * (aecpc->msInSndCardBuf *
aecpc->aec->mult) / 10), BUF_SIZE_FRAMES);
aecpc->checkBuffSize = 0;
}
}
// if checkBuffSize changed in the if-statement above
if (!aecpc->checkBuffSize) {
// soundcard buffer is now reasonably stable
// When the far end buffer is filled with approximately the same amount of
// data as the amount on the sound card we end the start up phase and start
// to cancel echoes.
if (nmbrOfFilledBuffers == aecpc->bufSizeStart) {
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;
}
}
}
else {
// AEC is enabled
// Note only 1 block supported for nb and 2 blocks for wb
for (i = 0; i < nFrames; i++) {
nmbrOfFilledBuffers = WebRtcApm_get_buffer_size(aecpc->farendBuf) / FRAME_LEN;
// Check that there is data in the far end buffer
if (nmbrOfFilledBuffers > 0) {
// Get the next 80 samples from the farend buffer
WebRtcApm_ReadBuffer(aecpc->farendBuf, farend, FRAME_LEN);
// Always store the last frame for use when we run out of data
memcpy(&(aecpc->farendOld[i][0]), farend, FRAME_LEN * sizeof(short));
}
else {
// We have no data so we use the last played frame
memcpy(farend, &(aecpc->farendOld[i][0]), FRAME_LEN * sizeof(short));
}
// Call buffer delay estimator when all data is extracted,
// i.e. i = 0 for NB and i = 1 for WB or SWB
if ((i == 0 && aecpc->splitSampFreq == 8000) ||
(i == 1 && (aecpc->splitSampFreq == 16000))) {
EstBufDelay(aecpc, aecpc->msInSndCardBuf);
}
// 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 AEC_DEBUG
msInAECBuf = WebRtcApm_get_buffer_size(aecpc->farendBuf) / (sampMsNb*aecpc->aec->mult);
fwrite(&msInAECBuf, 2, 1, aecpc->bufFile);
fwrite(&(aecpc->knownDelay), sizeof(aecpc->knownDelay), 1, aecpc->delayFile);
#endif
return retVal;
}
WebRtc_Word32 WebRtcAec_set_config(void *aecInst, AecConfig config)
{
aecpc_t *aecpc = aecInst;
if (aecpc == NULL) {
return -1;
}
if (aecpc->initFlag != initCheck) {
aecpc->lastError = AEC_UNINITIALIZED_ERROR;
return -1;
}
if (config.skewMode != kAecFalse && config.skewMode != kAecTrue) {
aecpc->lastError = AEC_BAD_PARAMETER_ERROR;
return -1;
}
aecpc->skewMode = config.skewMode;
if (config.nlpMode != kAecNlpConservative && config.nlpMode !=
kAecNlpModerate && config.nlpMode != kAecNlpAggressive) {
aecpc->lastError = AEC_BAD_PARAMETER_ERROR;
return -1;
}
aecpc->nlpMode = config.nlpMode;
aecpc->aec->targetSupp = targetSupp[aecpc->nlpMode];
aecpc->aec->minOverDrive = minOverDrive[aecpc->nlpMode];
if (config.metricsMode != kAecFalse && config.metricsMode != kAecTrue) {
aecpc->lastError = AEC_BAD_PARAMETER_ERROR;
return -1;
}
aecpc->aec->metricsMode = config.metricsMode;
if (aecpc->aec->metricsMode == kAecTrue) {
WebRtcAec_InitMetrics(aecpc->aec);
}
if (config.delay_logging != kAecFalse && config.delay_logging != kAecTrue) {
aecpc->lastError = AEC_BAD_PARAMETER_ERROR;
return -1;
}
aecpc->aec->delay_logging_enabled = config.delay_logging;
if (aecpc->aec->delay_logging_enabled == kAecTrue) {
memset(aecpc->aec->delay_histogram, 0, sizeof(aecpc->aec->delay_histogram));
}
return 0;
}
WebRtc_Word32 WebRtcAec_get_config(void *aecInst, AecConfig *config)
{
aecpc_t *aecpc = aecInst;
if (aecpc == NULL) {
return -1;
}
if (config == NULL) {
aecpc->lastError = AEC_NULL_POINTER_ERROR;
return -1;
}
if (aecpc->initFlag != initCheck) {
aecpc->lastError = AEC_UNINITIALIZED_ERROR;
return -1;
}
config->nlpMode = aecpc->nlpMode;
config->skewMode = aecpc->skewMode;
config->metricsMode = aecpc->aec->metricsMode;
config->delay_logging = aecpc->aec->delay_logging_enabled;
return 0;
}
WebRtc_Word32 WebRtcAec_get_echo_status(void *aecInst, WebRtc_Word16 *status)
{
aecpc_t *aecpc = aecInst;
if (aecpc == NULL) {
return -1;
}
if (status == NULL) {
aecpc->lastError = AEC_NULL_POINTER_ERROR;
return -1;
}
if (aecpc->initFlag != initCheck) {
aecpc->lastError = AEC_UNINITIALIZED_ERROR;
return -1;
}
*status = aecpc->aec->echoState;
return 0;
}
WebRtc_Word32 WebRtcAec_GetMetrics(void *aecInst, AecMetrics *metrics)
{
const float upweight = 0.7f;
float dtmp;
short stmp;
aecpc_t *aecpc = aecInst;
if (aecpc == NULL) {
return -1;
}
if (metrics == NULL) {
aecpc->lastError = AEC_NULL_POINTER_ERROR;
return -1;
}
if (aecpc->initFlag != initCheck) {
aecpc->lastError = AEC_UNINITIALIZED_ERROR;
return -1;
}
// ERL
metrics->erl.instant = (short) aecpc->aec->erl.instant;
if ((aecpc->aec->erl.himean > offsetLevel) && (aecpc->aec->erl.average > offsetLevel)) {
// Use a mix between regular average and upper part average
dtmp = upweight * aecpc->aec->erl.himean + (1 - upweight) * aecpc->aec->erl.average;
metrics->erl.average = (short) dtmp;
}
else {
metrics->erl.average = offsetLevel;
}
metrics->erl.max = (short) aecpc->aec->erl.max;
if (aecpc->aec->erl.min < (offsetLevel * (-1))) {
metrics->erl.min = (short) aecpc->aec->erl.min;
}
else {
metrics->erl.min = offsetLevel;
}
// ERLE
metrics->erle.instant = (short) aecpc->aec->erle.instant;
if ((aecpc->aec->erle.himean > offsetLevel) && (aecpc->aec->erle.average > offsetLevel)) {
// Use a mix between regular average and upper part average
dtmp = upweight * aecpc->aec->erle.himean + (1 - upweight) * aecpc->aec->erle.average;
metrics->erle.average = (short) dtmp;
}
else {
metrics->erle.average = offsetLevel;
}
metrics->erle.max = (short) aecpc->aec->erle.max;
if (aecpc->aec->erle.min < (offsetLevel * (-1))) {
metrics->erle.min = (short) aecpc->aec->erle.min;
} else {
metrics->erle.min = offsetLevel;
}
// RERL
if ((metrics->erl.average > offsetLevel) && (metrics->erle.average > offsetLevel)) {
stmp = metrics->erl.average + metrics->erle.average;
}
else {
stmp = offsetLevel;
}
metrics->rerl.average = stmp;
// No other statistics needed, but returned for completeness
metrics->rerl.instant = stmp;
metrics->rerl.max = stmp;
metrics->rerl.min = stmp;
// A_NLP
metrics->aNlp.instant = (short) aecpc->aec->aNlp.instant;
if ((aecpc->aec->aNlp.himean > offsetLevel) && (aecpc->aec->aNlp.average > offsetLevel)) {
// Use a mix between regular average and upper part average
dtmp = upweight * aecpc->aec->aNlp.himean + (1 - upweight) * aecpc->aec->aNlp.average;
metrics->aNlp.average = (short) dtmp;
}
else {
metrics->aNlp.average = offsetLevel;
}
metrics->aNlp.max = (short) aecpc->aec->aNlp.max;
if (aecpc->aec->aNlp.min < (offsetLevel * (-1))) {
metrics->aNlp.min = (short) aecpc->aec->aNlp.min;
}
else {
metrics->aNlp.min = offsetLevel;
}
return 0;
}
int WebRtcAec_GetDelayMetrics(void* handle, int* median, int* std) {
aecpc_t* self = handle;
int i = 0;
int delay_values = 0;
int num_delay_values = 0;
int my_median = 0;
const int kMsPerBlock = (PART_LEN * 1000) / self->splitSampFreq;
float l1_norm = 0;
if (self == NULL) {
return -1;
}
if (median == NULL) {
self->lastError = AEC_NULL_POINTER_ERROR;
return -1;
}
if (std == NULL) {
self->lastError = AEC_NULL_POINTER_ERROR;
return -1;
}
if (self->initFlag != initCheck) {
self->lastError = AEC_UNINITIALIZED_ERROR;
return -1;
}
if (self->aec->delay_logging_enabled == 0) {
// Logging disabled
self->lastError = AEC_UNSUPPORTED_FUNCTION_ERROR;
return -1;
}
// Get number of delay values since last update
for (i = 0; i < kMaxDelay; i++) {
num_delay_values += self->aec->delay_histogram[i];
}
if (num_delay_values == 0) {
// We have no new delay value data
*median = -1;
*std = -1;
return 0;
}
delay_values = num_delay_values >> 1; // Start value for median count down
// Get median of delay values since last update
for (i = 0; i < kMaxDelay; i++) {
delay_values -= self->aec->delay_histogram[i];
if (delay_values < 0) {
my_median = i;
break;
}
}
*median = my_median * kMsPerBlock;
// Calculate the L1 norm, with median value as central moment
for (i = 0; i < kMaxDelay; i++) {
l1_norm += (float) (fabs(i - my_median) * self->aec->delay_histogram[i]);
}
*std = (int) (l1_norm / (float) num_delay_values + 0.5f) * kMsPerBlock;
// Reset histogram
memset(self->aec->delay_histogram, 0, sizeof(self->aec->delay_histogram));
return 0;
}
WebRtc_Word32 WebRtcAec_get_version(WebRtc_Word8 *versionStr, WebRtc_Word16 len)
{
const char version[] = "AEC 2.5.0";
const short versionLen = (short)strlen(version) + 1; // +1 for null-termination
if (versionStr == NULL) {
return -1;
}
if (versionLen > len) {
return -1;
}
strncpy(versionStr, version, versionLen);
return 0;
}
WebRtc_Word32 WebRtcAec_get_error_code(void *aecInst)
{
aecpc_t *aecpc = aecInst;
if (aecpc == NULL) {
return -1;
}
return aecpc->lastError;
}
static int EstBufDelay(aecpc_t *aecpc, short msInSndCardBuf)
{
short delayNew, nSampFar, nSampSndCard;
short diff;
nSampFar = WebRtcApm_get_buffer_size(aecpc->farendBuf);
nSampSndCard = msInSndCardBuf * sampMsNb * aecpc->aec->mult;
delayNew = nSampSndCard - nSampFar;
// Account for resampling frame delay
if (aecpc->skewMode == kAecTrue && aecpc->resample == kAecTrue) {
delayNew -= kResamplingDelay;
}
if (delayNew < FRAME_LEN) {
WebRtcApm_FlushBuffer(aecpc->farendBuf, FRAME_LEN);
delayNew += FRAME_LEN;
}
aecpc->filtDelay = WEBRTC_SPL_MAX(0, (short)(0.8*aecpc->filtDelay + 0.2*delayNew));
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;
}

View File

@ -0,0 +1,278 @@
/*
* 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.
*/
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AEC_MAIN_INTERFACE_ECHO_CANCELLATION_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_AEC_MAIN_INTERFACE_ECHO_CANCELLATION_H_
#include "typedefs.h"
// Errors
#define AEC_UNSPECIFIED_ERROR 12000
#define AEC_UNSUPPORTED_FUNCTION_ERROR 12001
#define AEC_UNINITIALIZED_ERROR 12002
#define AEC_NULL_POINTER_ERROR 12003
#define AEC_BAD_PARAMETER_ERROR 12004
// Warnings
#define AEC_BAD_PARAMETER_WARNING 12050
enum {
kAecNlpConservative = 0,
kAecNlpModerate,
kAecNlpAggressive
};
enum {
kAecFalse = 0,
kAecTrue
};
typedef struct {
WebRtc_Word16 nlpMode; // default kAecNlpModerate
WebRtc_Word16 skewMode; // default kAecFalse
WebRtc_Word16 metricsMode; // default kAecFalse
int delay_logging; // default kAecFalse
//float realSkew;
} AecConfig;
typedef struct {
WebRtc_Word16 instant;
WebRtc_Word16 average;
WebRtc_Word16 max;
WebRtc_Word16 min;
} AecLevel;
typedef struct {
AecLevel rerl;
AecLevel erl;
AecLevel erle;
AecLevel aNlp;
} AecMetrics;
#ifdef __cplusplus
extern "C" {
#endif
/*
* Allocates the memory needed by the AEC. The memory needs to be initialized
* separately using the WebRtcAec_Init() function.
*
* Inputs Description
* -------------------------------------------------------------------
* void **aecInst Pointer to the AEC instance to be created
* and initialized
*
* Outputs Description
* -------------------------------------------------------------------
* WebRtc_Word32 return 0: OK
* -1: error
*/
WebRtc_Word32 WebRtcAec_Create(void **aecInst);
/*
* This function releases the memory allocated by WebRtcAec_Create().
*
* Inputs Description
* -------------------------------------------------------------------
* void *aecInst Pointer to the AEC instance
*
* Outputs Description
* -------------------------------------------------------------------
* WebRtc_Word32 return 0: OK
* -1: error
*/
WebRtc_Word32 WebRtcAec_Free(void *aecInst);
/*
* Initializes an AEC instance.
*
* Inputs Description
* -------------------------------------------------------------------
* void *aecInst Pointer to the AEC instance
* WebRtc_Word32 sampFreq Sampling frequency of data
* WebRtc_Word32 scSampFreq Soundcard sampling frequency
*
* Outputs Description
* -------------------------------------------------------------------
* WebRtc_Word32 return 0: OK
* -1: error
*/
WebRtc_Word32 WebRtcAec_Init(void *aecInst,
WebRtc_Word32 sampFreq,
WebRtc_Word32 scSampFreq);
/*
* Inserts an 80 or 160 sample block of data into the farend buffer.
*
* Inputs Description
* -------------------------------------------------------------------
* void *aecInst Pointer to the AEC instance
* WebRtc_Word16 *farend In buffer containing one frame of
* farend signal for L band
* WebRtc_Word16 nrOfSamples Number of samples in farend buffer
*
* Outputs Description
* -------------------------------------------------------------------
* WebRtc_Word32 return 0: OK
* -1: error
*/
WebRtc_Word32 WebRtcAec_BufferFarend(void *aecInst,
const WebRtc_Word16 *farend,
WebRtc_Word16 nrOfSamples);
/*
* Runs the echo canceller on an 80 or 160 sample blocks of data.
*
* Inputs Description
* -------------------------------------------------------------------
* void *aecInst Pointer to the AEC instance
* WebRtc_Word16 *nearend In buffer containing one frame of
* nearend+echo signal for L band
* WebRtc_Word16 *nearendH In buffer containing one frame of
* nearend+echo signal for H band
* WebRtc_Word16 nrOfSamples Number of samples in nearend buffer
* WebRtc_Word16 msInSndCardBuf Delay estimate for sound card and
* system buffers
* WebRtc_Word16 skew Difference between number of samples played
* and recorded at the soundcard (for clock skew
* compensation)
*
* Outputs Description
* -------------------------------------------------------------------
* WebRtc_Word16 *out Out buffer, one frame of processed nearend
* for L band
* WebRtc_Word16 *outH Out buffer, one frame of processed nearend
* for H band
* WebRtc_Word32 return 0: OK
* -1: error
*/
WebRtc_Word32 WebRtcAec_Process(void *aecInst,
const WebRtc_Word16 *nearend,
const WebRtc_Word16 *nearendH,
WebRtc_Word16 *out,
WebRtc_Word16 *outH,
WebRtc_Word16 nrOfSamples,
WebRtc_Word16 msInSndCardBuf,
WebRtc_Word32 skew);
/*
* This function enables the user to set certain parameters on-the-fly.
*
* Inputs Description
* -------------------------------------------------------------------
* void *aecInst Pointer to the AEC instance
* AecConfig config Config instance that contains all
* properties to be set
*
* Outputs Description
* -------------------------------------------------------------------
* WebRtc_Word32 return 0: OK
* -1: error
*/
WebRtc_Word32 WebRtcAec_set_config(void *aecInst, AecConfig config);
/*
* Gets the on-the-fly paramters.
*
* Inputs Description
* -------------------------------------------------------------------
* void *aecInst Pointer to the AEC instance
*
* Outputs Description
* -------------------------------------------------------------------
* AecConfig *config Pointer to the config instance that
* all properties will be written to
* WebRtc_Word32 return 0: OK
* -1: error
*/
WebRtc_Word32 WebRtcAec_get_config(void *aecInst, AecConfig *config);
/*
* Gets the current echo status of the nearend signal.
*
* Inputs Description
* -------------------------------------------------------------------
* void *aecInst Pointer to the AEC instance
*
* Outputs Description
* -------------------------------------------------------------------
* WebRtc_Word16 *status 0: Almost certainly nearend single-talk
* 1: Might not be neared single-talk
* WebRtc_Word32 return 0: OK
* -1: error
*/
WebRtc_Word32 WebRtcAec_get_echo_status(void *aecInst, WebRtc_Word16 *status);
/*
* Gets the current echo metrics for the session.
*
* Inputs Description
* -------------------------------------------------------------------
* void *aecInst Pointer to the AEC instance
*
* Outputs Description
* -------------------------------------------------------------------
* AecMetrics *metrics Struct which will be filled out with the
* current echo metrics.
* WebRtc_Word32 return 0: OK
* -1: error
*/
WebRtc_Word32 WebRtcAec_GetMetrics(void *aecInst, AecMetrics *metrics);
/*
* Gets the current delay metrics for the session.
*
* Inputs Description
* -------------------------------------------------------------------
* void* handle Pointer to the AEC instance
*
* Outputs Description
* -------------------------------------------------------------------
* int* median Delay median value.
* int* std Delay standard deviation.
*
* int return 0: OK
* -1: error
*/
int WebRtcAec_GetDelayMetrics(void* handle, int* median, int* std);
/*
* Gets the last error code.
*
* Inputs Description
* -------------------------------------------------------------------
* void *aecInst Pointer to the AEC instance
*
* Outputs Description
* -------------------------------------------------------------------
* WebRtc_Word32 return 11000-11100: error code
*/
WebRtc_Word32 WebRtcAec_get_error_code(void *aecInst);
/*
* Gets a version string.
*
* Inputs Description
* -------------------------------------------------------------------
* char *versionStr Pointer to a string array
* WebRtc_Word16 len The maximum length of the string
*
* Outputs Description
* -------------------------------------------------------------------
* WebRtc_Word8 *versionStr Pointer to a string array
* WebRtc_Word32 return 0: OK
* -1: error
*/
WebRtc_Word32 WebRtcAec_get_version(WebRtc_Word8 *versionStr, WebRtc_Word16 len);
#ifdef __cplusplus
}
#endif
#endif /* WEBRTC_MODULES_AUDIO_PROCESSING_AEC_MAIN_INTERFACE_ECHO_CANCELLATION_H_ */

View File

@ -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 <assert.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "resampler.h"
#include "aec_core.h"
enum { kFrameBufferSize = FRAME_LEN * 4 };
enum { kEstimateLengthFrames = 400 };
typedef struct {
short buffer[kFrameBufferSize];
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],
(kFrameBufferSize - 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;
}

View File

@ -0,0 +1,32 @@
/*
* 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.
*/
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AEC_MAIN_SOURCE_RESAMPLER_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_AEC_MAIN_SOURCE_RESAMPLER_H_
enum { kResamplingDelay = 1 };
// Unless otherwise specified, functions return 0 on success and -1 on error
int WebRtcAec_CreateResampler(void **resampInst);
int WebRtcAec_InitResampler(void *resampInst, int deviceSampleRateHz);
int WebRtcAec_FreeResampler(void *resampInst);
// Estimates skew from raw measurement.
int WebRtcAec_GetSkew(void *resampInst, int rawSkew, float *skewEst);
// Resamples input using linear interpolation.
// Returns size of resampled array.
int WebRtcAec_ResampleLinear(void *resampInst,
const short *inspeech,
int size,
float skew,
short *outspeech);
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AEC_MAIN_SOURCE_RESAMPLER_H_

View File

@ -0,0 +1,9 @@
noinst_LTLIBRARIES = libaecm.la
libaecm_la_SOURCES = interface/echo_control_mobile.h \
echo_control_mobile.c \
aecm_core.c \
aecm_core.h
libaecm_la_CFLAGS = $(AM_CFLAGS) $(COMMON_CFLAGS) \
-I$(top_srcdir)/src/common_audio/signal_processing_library/main/interface \
-I$(top_srcdir)/src/modules/audio_processing/utility

View File

@ -0,0 +1,34 @@
# 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.
{
'targets': [
{
'target_name': 'aecm',
'type': '<(library)',
'dependencies': [
'<(webrtc_root)/common_audio/common_audio.gyp:spl',
'apm_util'
],
'include_dirs': [
'interface',
],
'direct_dependent_settings': {
'include_dirs': [
'interface',
],
},
'sources': [
'interface/echo_control_mobile.h',
'echo_control_mobile.c',
'aecm_core.c',
'aecm_core.h',
],
},
],
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,358 @@
/*
* 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.
*/
// Performs echo control (suppression) with fft routines in fixed-point
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AECM_MAIN_SOURCE_AECM_CORE_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_AECM_MAIN_SOURCE_AECM_CORE_H_
#define AECM_DYNAMIC_Q // turn on/off dynamic Q-domain
//#define AECM_WITH_ABS_APPROX
//#define AECM_SHORT // for 32 sample partition length (otherwise 64)
#include "typedefs.h"
#include "signal_processing_library.h"
// Algorithm parameters
#define FRAME_LEN 80 // Total frame length, 10 ms
#ifdef AECM_SHORT
#define PART_LEN 32 // Length of partition
#define PART_LEN_SHIFT 6 // Length of (PART_LEN * 2) in base 2
#else
#define PART_LEN 64 // Length of partition
#define PART_LEN_SHIFT 7 // Length of (PART_LEN * 2) in base 2
#endif
#define PART_LEN1 (PART_LEN + 1) // Unique fft coefficients
#define PART_LEN2 (PART_LEN << 1) // Length of partition * 2
#define PART_LEN4 (PART_LEN << 2) // Length of partition * 4
#define FAR_BUF_LEN PART_LEN4 // Length of buffers
#define MAX_DELAY 100
// Counter parameters
#ifdef AECM_SHORT
#define CONV_LEN 1024 // Convergence length used at startup
#else
#define CONV_LEN 512 // Convergence length used at startup
#endif
#define CONV_LEN2 (CONV_LEN << 1) // Convergence length * 2 used at startup
// Energy parameters
#define MAX_BUF_LEN 64 // History length of energy signals
#define FAR_ENERGY_MIN 1025 // Lowest Far energy level: At least 2 in energy
#define FAR_ENERGY_DIFF 929 // Allowed difference between max and min
#define ENERGY_DEV_OFFSET 0 // The energy error offset in Q8
#define ENERGY_DEV_TOL 400 // The energy estimation tolerance in Q8
#define FAR_ENERGY_VAD_REGION 230 // Far VAD tolerance region
// Stepsize parameters
#define MU_MIN 10 // Min stepsize 2^-MU_MIN (far end energy dependent)
#define MU_MAX 1 // Max stepsize 2^-MU_MAX (far end energy dependent)
#define MU_DIFF 9 // MU_MIN - MU_MAX
// Channel parameters
#define MIN_MSE_COUNT 20 // Min number of consecutive blocks with enough far end
// energy to compare channel estimates
#define MIN_MSE_DIFF 29 // The ratio between adapted and stored channel to
// accept a new storage (0.8 in Q-MSE_RESOLUTION)
#define MSE_RESOLUTION 5 // MSE parameter resolution
#define RESOLUTION_CHANNEL16 12 // W16 Channel in Q-RESOLUTION_CHANNEL16
#define RESOLUTION_CHANNEL32 28 // W32 Channel in Q-RESOLUTION_CHANNEL
#define CHANNEL_VAD 16 // Minimum energy in frequency band to update channel
// Suppression gain parameters: SUPGAIN_ parameters in Q-(RESOLUTION_SUPGAIN)
#define RESOLUTION_SUPGAIN 8 // Channel in Q-(RESOLUTION_SUPGAIN)
#define SUPGAIN_DEFAULT (1 << RESOLUTION_SUPGAIN) // Default suppression gain
#define SUPGAIN_ERROR_PARAM_A 3072 // Estimation error parameter (Maximum gain) (8 in Q8)
#define SUPGAIN_ERROR_PARAM_B 1536 // Estimation error parameter (Gain before going down)
#define SUPGAIN_ERROR_PARAM_D SUPGAIN_DEFAULT // Estimation error parameter
// (Should be the same as Default) (1 in Q8)
#define SUPGAIN_EPC_DT 200 // = SUPGAIN_ERROR_PARAM_C * ENERGY_DEV_TOL
// Defines for "check delay estimation"
#define CORR_WIDTH 31 // Number of samples to correlate over.
#define CORR_MAX 16 // Maximum correlation offset
#define CORR_MAX_BUF 63
#define CORR_DEV 4
#define CORR_MAX_LEVEL 20
#define CORR_MAX_LOW 4
#define CORR_BUF_LEN (CORR_MAX << 1) + 1
// Note that CORR_WIDTH + 2*CORR_MAX <= MAX_BUF_LEN
#define ONE_Q14 (1 << 14)
// NLP defines
#define NLP_COMP_LOW 3277 // 0.2 in Q14
#define NLP_COMP_HIGH ONE_Q14 // 1 in Q14
extern const WebRtc_Word16 WebRtcAecm_kSqrtHanning[];
typedef struct {
WebRtc_Word16 real;
WebRtc_Word16 imag;
} complex16_t;
typedef struct
{
int farBufWritePos;
int farBufReadPos;
int knownDelay;
int lastKnownDelay;
int firstVAD; // Parameter to control poorly initialized channels
void *farFrameBuf;
void *nearNoisyFrameBuf;
void *nearCleanFrameBuf;
void *outFrameBuf;
WebRtc_Word16 farBuf[FAR_BUF_LEN];
WebRtc_Word16 mult;
WebRtc_UWord32 seed;
// Delay estimation variables
void* delay_estimator;
WebRtc_UWord16 currentDelay;
WebRtc_Word16 nlpFlag;
WebRtc_Word16 fixedDelay;
WebRtc_UWord32 totCount;
WebRtc_Word16 dfaCleanQDomain;
WebRtc_Word16 dfaCleanQDomainOld;
WebRtc_Word16 dfaNoisyQDomain;
WebRtc_Word16 dfaNoisyQDomainOld;
WebRtc_Word16 nearLogEnergy[MAX_BUF_LEN];
WebRtc_Word16 farLogEnergy;
WebRtc_Word16 echoAdaptLogEnergy[MAX_BUF_LEN];
WebRtc_Word16 echoStoredLogEnergy[MAX_BUF_LEN];
// The extra 16 or 32 bytes in the following buffers are for alignment based Neon code.
// It's designed this way since the current GCC compiler can't align a buffer in 16 or 32
// byte boundaries properly.
WebRtc_Word16 channelStored_buf[PART_LEN1 + 8];
WebRtc_Word16 channelAdapt16_buf[PART_LEN1 + 8];
WebRtc_Word32 channelAdapt32_buf[PART_LEN1 + 8];
WebRtc_Word16 xBuf_buf[PART_LEN2 + 16]; // farend
WebRtc_Word16 dBufClean_buf[PART_LEN2 + 16]; // nearend
WebRtc_Word16 dBufNoisy_buf[PART_LEN2 + 16]; // nearend
WebRtc_Word16 outBuf_buf[PART_LEN + 8];
// Pointers to the above buffers
WebRtc_Word16 *channelStored;
WebRtc_Word16 *channelAdapt16;
WebRtc_Word32 *channelAdapt32;
WebRtc_Word16 *xBuf;
WebRtc_Word16 *dBufClean;
WebRtc_Word16 *dBufNoisy;
WebRtc_Word16 *outBuf;
WebRtc_Word32 echoFilt[PART_LEN1];
WebRtc_Word16 nearFilt[PART_LEN1];
WebRtc_Word32 noiseEst[PART_LEN1];
int noiseEstTooLowCtr[PART_LEN1];
int noiseEstTooHighCtr[PART_LEN1];
WebRtc_Word16 noiseEstCtr;
WebRtc_Word16 cngMode;
WebRtc_Word32 mseAdaptOld;
WebRtc_Word32 mseStoredOld;
WebRtc_Word32 mseThreshold;
WebRtc_Word16 farEnergyMin;
WebRtc_Word16 farEnergyMax;
WebRtc_Word16 farEnergyMaxMin;
WebRtc_Word16 farEnergyVAD;
WebRtc_Word16 farEnergyMSE;
int currentVADValue;
WebRtc_Word16 vadUpdateCount;
WebRtc_Word16 startupState;
WebRtc_Word16 mseChannelCount;
WebRtc_Word16 supGain;
WebRtc_Word16 supGainOld;
WebRtc_Word16 supGainErrParamA;
WebRtc_Word16 supGainErrParamD;
WebRtc_Word16 supGainErrParamDiffAB;
WebRtc_Word16 supGainErrParamDiffBD;
#ifdef AEC_DEBUG
FILE *farFile;
FILE *nearFile;
FILE *outFile;
#endif
} AecmCore_t;
///////////////////////////////////////////////////////////////////////////////////////////////
// WebRtcAecm_CreateCore(...)
//
// Allocates the memory needed by the AECM. The memory needs to be
// initialized separately using the WebRtcAecm_InitCore() function.
//
// Input:
// - aecm : Instance that should be created
//
// Output:
// - aecm : Created instance
//
// Return value : 0 - Ok
// -1 - Error
//
int WebRtcAecm_CreateCore(AecmCore_t **aecm);
///////////////////////////////////////////////////////////////////////////////////////////////
// WebRtcAecm_InitCore(...)
//
// This function initializes the AECM instant created with WebRtcAecm_CreateCore(...)
// Input:
// - aecm : Pointer to the AECM instance
// - samplingFreq : Sampling Frequency
//
// Output:
// - aecm : Initialized instance
//
// Return value : 0 - Ok
// -1 - Error
//
int WebRtcAecm_InitCore(AecmCore_t * const aecm, int samplingFreq);
///////////////////////////////////////////////////////////////////////////////////////////////
// WebRtcAecm_FreeCore(...)
//
// This function releases the memory allocated by WebRtcAecm_CreateCore()
// Input:
// - aecm : Pointer to the AECM instance
//
// Return value : 0 - Ok
// -1 - Error
// 11001-11016: Error
//
int WebRtcAecm_FreeCore(AecmCore_t *aecm);
int WebRtcAecm_Control(AecmCore_t *aecm, int delay, int nlpFlag);
///////////////////////////////////////////////////////////////////////////////////////////////
// WebRtcAecm_InitEchoPathCore(...)
//
// This function resets the echo channel adaptation with the specified channel.
// Input:
// - aecm : Pointer to the AECM instance
// - echo_path : Pointer to the data that should initialize the echo path
//
// Output:
// - aecm : Initialized instance
//
void WebRtcAecm_InitEchoPathCore(AecmCore_t* aecm, const WebRtc_Word16* echo_path);
///////////////////////////////////////////////////////////////////////////////////////////////
// WebRtcAecm_ProcessFrame(...)
//
// This function processes frames and sends blocks to WebRtcAecm_ProcessBlock(...)
//
// Inputs:
// - aecm : Pointer to the AECM instance
// - farend : In buffer containing one frame of echo signal
// - nearendNoisy : In buffer containing one frame of nearend+echo signal without NS
// - nearendClean : In buffer containing one frame of nearend+echo signal with NS
//
// Output:
// - out : Out buffer, one frame of nearend signal :
//
//
int WebRtcAecm_ProcessFrame(AecmCore_t * aecm, const WebRtc_Word16 * farend,
const WebRtc_Word16 * nearendNoisy,
const WebRtc_Word16 * nearendClean,
WebRtc_Word16 * out);
///////////////////////////////////////////////////////////////////////////////////////////////
// WebRtcAecm_ProcessBlock(...)
//
// This function is called for every block within one frame
// This function is called by WebRtcAecm_ProcessFrame(...)
//
// Inputs:
// - aecm : Pointer to the AECM instance
// - farend : In buffer containing one block of echo signal
// - nearendNoisy : In buffer containing one frame of nearend+echo signal without NS
// - nearendClean : In buffer containing one frame of nearend+echo signal with NS
//
// Output:
// - out : Out buffer, one block of nearend signal :
//
//
int WebRtcAecm_ProcessBlock(AecmCore_t * aecm, const WebRtc_Word16 * farend,
const WebRtc_Word16 * nearendNoisy,
const WebRtc_Word16 * noisyClean,
WebRtc_Word16 * out);
///////////////////////////////////////////////////////////////////////////////////////////////
// WebRtcAecm_BufferFarFrame()
//
// Inserts a frame of data into farend buffer.
//
// Inputs:
// - aecm : Pointer to the AECM instance
// - farend : In buffer containing one frame of farend signal
// - farLen : Length of frame
//
void WebRtcAecm_BufferFarFrame(AecmCore_t * const aecm, const WebRtc_Word16 * const farend,
const int farLen);
///////////////////////////////////////////////////////////////////////////////////////////////
// WebRtcAecm_FetchFarFrame()
//
// Read the farend buffer to account for known delay
//
// Inputs:
// - aecm : Pointer to the AECM instance
// - farend : In buffer containing one frame of farend signal
// - farLen : Length of frame
// - knownDelay : known delay
//
void WebRtcAecm_FetchFarFrame(AecmCore_t * const aecm, WebRtc_Word16 * const farend,
const int farLen, const int knownDelay);
///////////////////////////////////////////////////////////////////////////////////////////////
// Some internal functions shared by ARM NEON and generic C code:
//
void WebRtcAecm_CalcLinearEnergies(AecmCore_t* aecm,
const WebRtc_UWord16* far_spectrum,
WebRtc_Word32* echoEst,
WebRtc_UWord32* far_energy,
WebRtc_UWord32* echo_energy_adapt,
WebRtc_UWord32* echo_energy_stored);
void WebRtcAecm_StoreAdaptiveChannel(AecmCore_t* aecm,
const WebRtc_UWord16* far_spectrum,
WebRtc_Word32* echo_est);
void WebRtcAecm_ResetAdaptiveChannel(AecmCore_t *aecm);
void WebRtcAecm_WindowAndFFT(WebRtc_Word16* fft,
const WebRtc_Word16* time_signal,
complex16_t* freq_signal,
int time_signal_scaling);
void WebRtcAecm_InverseFFTAndWindow(AecmCore_t* aecm,
WebRtc_Word16* fft,
complex16_t* efw,
WebRtc_Word16* output,
const WebRtc_Word16* nearendClean);
#endif

View File

@ -0,0 +1,314 @@
/*
* 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.
*/
#if defined(WEBRTC_ANDROID) && defined(WEBRTC_ARCH_ARM_NEON)
#include "aecm_core.h"
#include <arm_neon.h>
#include <assert.h>
// Square root of Hanning window in Q14.
static const WebRtc_Word16 kSqrtHanningReversed[] __attribute__ ((aligned (8))) = {
16384, 16373, 16354, 16325,
16286, 16237, 16179, 16111,
16034, 15947, 15851, 15746,
15631, 15506, 15373, 15231,
15079, 14918, 14749, 14571,
14384, 14189, 13985, 13773,
13553, 13325, 13089, 12845,
12594, 12335, 12068, 11795,
11514, 11227, 10933, 10633,
10326, 10013, 9695, 9370,
9040, 8705, 8364, 8019,
7668, 7313, 6954, 6591,
6224, 5853, 5478, 5101,
4720, 4337, 3951, 3562,
3172, 2780, 2386, 1990,
1594, 1196, 798, 399
};
void WebRtcAecm_WindowAndFFT(WebRtc_Word16* fft,
const WebRtc_Word16* time_signal,
complex16_t* freq_signal,
int time_signal_scaling)
{
int i, j;
int16x4_t tmp16x4_scaling = vdup_n_s16(time_signal_scaling);
__asm__("vmov.i16 d21, #0" ::: "d21");
for(i = 0, j = 0; i < PART_LEN; i += 4, j += 8)
{
int16x4_t tmp16x4_0;
int16x4_t tmp16x4_1;
int32x4_t tmp32x4_0;
/* Window near end */
// fft[j] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT((time_signal[i]
// << time_signal_scaling), WebRtcAecm_kSqrtHanning[i], 14);
__asm__("vld1.16 %P0, [%1, :64]" : "=w"(tmp16x4_0) : "r"(&time_signal[i]));
tmp16x4_0 = vshl_s16(tmp16x4_0, tmp16x4_scaling);
__asm__("vld1.16 %P0, [%1, :64]" : "=w"(tmp16x4_1) : "r"(&WebRtcAecm_kSqrtHanning[i]));
tmp32x4_0 = vmull_s16(tmp16x4_0, tmp16x4_1);
__asm__("vshrn.i32 d20, %q0, #14" : : "w"(tmp32x4_0) : "d20");
__asm__("vst2.16 {d20, d21}, [%0, :128]" : : "r"(&fft[j]) : "q10");
// fft[PART_LEN2 + j] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(
// (time_signal[PART_LEN + i] << time_signal_scaling),
// WebRtcAecm_kSqrtHanning[PART_LEN - i], 14);
__asm__("vld1.16 %P0, [%1, :64]" : "=w"(tmp16x4_0) : "r"(&time_signal[i + PART_LEN]));
tmp16x4_0 = vshl_s16(tmp16x4_0, tmp16x4_scaling);
__asm__("vld1.16 %P0, [%1, :64]" : "=w"(tmp16x4_1) : "r"(&kSqrtHanningReversed[i]));
tmp32x4_0 = vmull_s16(tmp16x4_0, tmp16x4_1);
__asm__("vshrn.i32 d20, %q0, #14" : : "w"(tmp32x4_0) : "d20");
__asm__("vst2.16 {d20, d21}, [%0, :128]" : : "r"(&fft[PART_LEN2 + j]) : "q10");
}
WebRtcSpl_ComplexBitReverse(fft, PART_LEN_SHIFT);
WebRtcSpl_ComplexFFT(fft, PART_LEN_SHIFT, 1);
// Take only the first PART_LEN2 samples, and switch the sign of the imaginary part.
for(i = 0, j = 0; j < PART_LEN2; i += 8, j += 16)
{
__asm__("vld2.16 {d20, d21, d22, d23}, [%0, :256]" : : "r"(&fft[j]) : "q10", "q11");
__asm__("vneg.s16 d22, d22" : : : "q10");
__asm__("vneg.s16 d23, d23" : : : "q11");
__asm__("vst2.16 {d20, d21, d22, d23}, [%0, :256]" : :
"r"(&freq_signal[i].real): "q10", "q11");
}
}
void WebRtcAecm_InverseFFTAndWindow(AecmCore_t* aecm,
WebRtc_Word16* fft,
complex16_t* efw,
WebRtc_Word16* output,
const WebRtc_Word16* nearendClean)
{
int i, j, outCFFT;
WebRtc_Word32 tmp32no1;
// Synthesis
for(i = 0, j = 0; i < PART_LEN; i += 4, j += 8)
{
// We overwrite two more elements in fft[], but it's ok.
__asm__("vld2.16 {d20, d21}, [%0, :128]" : : "r"(&(efw[i].real)) : "q10");
__asm__("vmov q11, q10" : : : "q10", "q11");
__asm__("vneg.s16 d23, d23" : : : "q11");
__asm__("vst2.16 {d22, d23}, [%0, :128]" : : "r"(&fft[j]): "q11");
__asm__("vrev64.16 q10, q10" : : : "q10");
__asm__("vst2.16 {d20, d21}, [%0]" : : "r"(&fft[PART_LEN4 - j - 6]): "q10");
}
fft[PART_LEN2] = efw[PART_LEN].real;
fft[PART_LEN2 + 1] = -efw[PART_LEN].imag;
// Inverse FFT, result should be scaled with outCFFT.
WebRtcSpl_ComplexBitReverse(fft, PART_LEN_SHIFT);
outCFFT = WebRtcSpl_ComplexIFFT(fft, PART_LEN_SHIFT, 1);
// Take only the real values and scale with outCFFT.
for (i = 0, j = 0; i < PART_LEN2; i += 8, j+= 16)
{
__asm__("vld2.16 {d20, d21, d22, d23}, [%0, :256]" : : "r"(&fft[j]) : "q10", "q11");
__asm__("vst1.16 {d20, d21}, [%0, :128]" : : "r"(&fft[i]): "q10");
}
int32x4_t tmp32x4_2;
__asm__("vdup.32 %q0, %1" : "=w"(tmp32x4_2) : "r"((WebRtc_Word32)
(outCFFT - aecm->dfaCleanQDomain)));
for (i = 0; i < PART_LEN; i += 4)
{
int16x4_t tmp16x4_0;
int16x4_t tmp16x4_1;
int32x4_t tmp32x4_0;
int32x4_t tmp32x4_1;
// fft[i] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(
// fft[i], WebRtcAecm_kSqrtHanning[i], 14);
__asm__("vld1.16 %P0, [%1, :64]" : "=w"(tmp16x4_0) : "r"(&fft[i]));
__asm__("vld1.16 %P0, [%1, :64]" : "=w"(tmp16x4_1) : "r"(&WebRtcAecm_kSqrtHanning[i]));
__asm__("vmull.s16 %q0, %P1, %P2" : "=w"(tmp32x4_0) : "w"(tmp16x4_0), "w"(tmp16x4_1));
__asm__("vrshr.s32 %q0, %q1, #14" : "=w"(tmp32x4_0) : "0"(tmp32x4_0));
// tmp32no1 = WEBRTC_SPL_SHIFT_W32((WebRtc_Word32)fft[i],
// outCFFT - aecm->dfaCleanQDomain);
__asm__("vshl.s32 %q0, %q1, %q2" : "=w"(tmp32x4_0) : "0"(tmp32x4_0), "w"(tmp32x4_2));
// fft[i] = (WebRtc_Word16)WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX,
// tmp32no1 + outBuf[i], WEBRTC_SPL_WORD16_MIN);
// output[i] = fft[i];
__asm__("vld1.16 %P0, [%1, :64]" : "=w"(tmp16x4_0) : "r"(&aecm->outBuf[i]));
__asm__("vmovl.s16 %q0, %P1" : "=w"(tmp32x4_1) : "w"(tmp16x4_0));
__asm__("vadd.i32 %q0, %q1" : : "w"(tmp32x4_0), "w"(tmp32x4_1));
__asm__("vqshrn.s32 %P0, %q1, #0" : "=w"(tmp16x4_0) : "w"(tmp32x4_0));
__asm__("vst1.16 %P0, [%1, :64]" : : "w"(tmp16x4_0), "r"(&fft[i]));
__asm__("vst1.16 %P0, [%1, :64]" : : "w"(tmp16x4_0), "r"(&output[i]));
// tmp32no1 = WEBRTC_SPL_MUL_16_16_RSFT(
// fft[PART_LEN + i], WebRtcAecm_kSqrtHanning[PART_LEN - i], 14);
__asm__("vld1.16 %P0, [%1, :64]" : "=w"(tmp16x4_0) : "r"(&fft[PART_LEN + i]));
__asm__("vld1.16 %P0, [%1, :64]" : "=w"(tmp16x4_1) : "r"(&kSqrtHanningReversed[i]));
__asm__("vmull.s16 %q0, %P1, %P2" : "=w"(tmp32x4_0) : "w"(tmp16x4_0), "w"(tmp16x4_1));
__asm__("vshr.s32 %q0, %q1, #14" : "=w"(tmp32x4_0) : "0"(tmp32x4_0));
// tmp32no1 = WEBRTC_SPL_SHIFT_W32(tmp32no1, outCFFT - aecm->dfaCleanQDomain);
__asm__("vshl.s32 %q0, %q1, %q2" : "=w"(tmp32x4_0) : "0"(tmp32x4_0), "w"(tmp32x4_2));
// outBuf[i] = (WebRtc_Word16)WEBRTC_SPL_SAT(
// WEBRTC_SPL_WORD16_MAX, tmp32no1, WEBRTC_SPL_WORD16_MIN);
__asm__("vqshrn.s32 %P0, %q1, #0" : "=w"(tmp16x4_0) : "w"(tmp32x4_0));
__asm__("vst1.16 %P0, [%1, :64]" : : "w"(tmp16x4_0), "r"(&aecm->outBuf[i]));
}
// Copy the current block to the old position (outBuf is shifted elsewhere).
for (i = 0; i < PART_LEN; i += 16)
{
__asm__("vld1.16 {d20, d21, d22, d23}, [%0, :256]" : :
"r"(&aecm->xBuf[i + PART_LEN]) : "q10");
__asm__("vst1.16 {d20, d21, d22, d23}, [%0, :256]" : : "r"(&aecm->xBuf[i]): "q10");
}
for (i = 0; i < PART_LEN; i += 16)
{
__asm__("vld1.16 {d20, d21, d22, d23}, [%0, :256]" : :
"r"(&aecm->dBufNoisy[i + PART_LEN]) : "q10");
__asm__("vst1.16 {d20, d21, d22, d23}, [%0, :256]" : :
"r"(&aecm->dBufNoisy[i]): "q10");
}
if (nearendClean != NULL) {
for (i = 0; i < PART_LEN; i += 16)
{
__asm__("vld1.16 {d20, d21, d22, d23}, [%0, :256]" : :
"r"(&aecm->dBufClean[i + PART_LEN]) : "q10");
__asm__("vst1.16 {d20, d21, d22, d23}, [%0, :256]" : :
"r"(&aecm->dBufClean[i]): "q10");
}
}
}
void WebRtcAecm_CalcLinearEnergies(AecmCore_t* aecm,
const WebRtc_UWord16* far_spectrum,
WebRtc_Word32* echo_est,
WebRtc_UWord32* far_energy,
WebRtc_UWord32* echo_energy_adapt,
WebRtc_UWord32* echo_energy_stored)
{
int i;
register WebRtc_UWord32 far_energy_r;
register WebRtc_UWord32 echo_energy_stored_r;
register WebRtc_UWord32 echo_energy_adapt_r;
uint32x4_t tmp32x4_0;
__asm__("vmov.i32 q14, #0" : : : "q14"); // far_energy
__asm__("vmov.i32 q8, #0" : : : "q8"); // echo_energy_stored
__asm__("vmov.i32 q9, #0" : : : "q9"); // echo_energy_adapt
for(i = 0; i < PART_LEN -7; i += 8)
{
// far_energy += (WebRtc_UWord32)(far_spectrum[i]);
__asm__("vld1.16 {d26, d27}, [%0]" : : "r"(&far_spectrum[i]) : "q13");
__asm__("vaddw.u16 q14, q14, d26" : : : "q14", "q13");
__asm__("vaddw.u16 q14, q14, d27" : : : "q14", "q13");
// Get estimated echo energies for adaptive channel and stored channel.
// echoEst[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], far_spectrum[i]);
__asm__("vld1.16 {d24, d25}, [%0, :128]" : : "r"(&aecm->channelStored[i]) : "q12");
__asm__("vmull.u16 q10, d26, d24" : : : "q12", "q13", "q10");
__asm__("vmull.u16 q11, d27, d25" : : : "q12", "q13", "q11");
__asm__("vst1.32 {d20, d21, d22, d23}, [%0, :256]" : : "r"(&echo_est[i]):
"q10", "q11");
// echo_energy_stored += (WebRtc_UWord32)echoEst[i];
__asm__("vadd.u32 q8, q10" : : : "q10", "q8");
__asm__("vadd.u32 q8, q11" : : : "q11", "q8");
// echo_energy_adapt += WEBRTC_SPL_UMUL_16_16(
// aecm->channelAdapt16[i], far_spectrum[i]);
__asm__("vld1.16 {d24, d25}, [%0, :128]" : : "r"(&aecm->channelAdapt16[i]) : "q12");
__asm__("vmull.u16 q10, d26, d24" : : : "q12", "q13", "q10");
__asm__("vmull.u16 q11, d27, d25" : : : "q12", "q13", "q11");
__asm__("vadd.u32 q9, q10" : : : "q9", "q15");
__asm__("vadd.u32 q9, q11" : : : "q9", "q11");
}
__asm__("vadd.u32 d28, d29" : : : "q14");
__asm__("vpadd.u32 d28, d28" : : : "q14");
__asm__("vmov.32 %0, d28[0]" : "=r"(far_energy_r): : "q14");
__asm__("vadd.u32 d18, d19" : : : "q9");
__asm__("vpadd.u32 d18, d18" : : : "q9");
__asm__("vmov.32 %0, d18[0]" : "=r"(echo_energy_adapt_r): : "q9");
__asm__("vadd.u32 d16, d17" : : : "q8");
__asm__("vpadd.u32 d16, d16" : : : "q8");
__asm__("vmov.32 %0, d16[0]" : "=r"(echo_energy_stored_r): : "q8");
// Get estimated echo energies for adaptive channel and stored channel.
echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], far_spectrum[i]);
*echo_energy_stored = echo_energy_stored_r + (WebRtc_UWord32)echo_est[i];
*far_energy = far_energy_r + (WebRtc_UWord32)(far_spectrum[i]);
*echo_energy_adapt = echo_energy_adapt_r + WEBRTC_SPL_UMUL_16_16(
aecm->channelAdapt16[i], far_spectrum[i]);
}
void WebRtcAecm_StoreAdaptiveChannel(AecmCore_t* aecm,
const WebRtc_UWord16* far_spectrum,
WebRtc_Word32* echo_est)
{
int i;
// During startup we store the channel every block.
// Recalculate echo estimate.
for(i = 0; i < PART_LEN -7; i += 8)
{
// aecm->channelStored[i] = acem->channelAdapt16[i];
// echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], far_spectrum[i]);
__asm__("vld1.16 {d26, d27}, [%0]" : : "r"(&far_spectrum[i]) : "q13");
__asm__("vld1.16 {d24, d25}, [%0, :128]" : : "r"(&aecm->channelAdapt16[i]) : "q12");
__asm__("vst1.16 {d24, d25}, [%0, :128]" : : "r"(&aecm->channelStored[i]) : "q12");
__asm__("vmull.u16 q10, d26, d24" : : : "q12", "q13", "q10");
__asm__("vmull.u16 q11, d27, d25" : : : "q12", "q13", "q11");
__asm__("vst1.16 {d20, d21, d22, d23}, [%0, :256]" : :
"r"(&echo_est[i]) : "q10", "q11");
}
aecm->channelStored[i] = aecm->channelAdapt16[i];
echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], far_spectrum[i]);
}
void WebRtcAecm_ResetAdaptiveChannel(AecmCore_t* aecm)
{
int i;
for(i = 0; i < PART_LEN -7; i += 8)
{
// aecm->channelAdapt16[i] = aecm->channelStored[i];
// aecm->channelAdapt32[i] = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)
// aecm->channelStored[i], 16);
__asm__("vld1.16 {d24, d25}, [%0, :128]" : :
"r"(&aecm->channelStored[i]) : "q12");
__asm__("vst1.16 {d24, d25}, [%0, :128]" : :
"r"(&aecm->channelAdapt16[i]) : "q12");
__asm__("vshll.s16 q10, d24, #16" : : : "q12", "q13", "q10");
__asm__("vshll.s16 q11, d25, #16" : : : "q12", "q13", "q11");
__asm__("vst1.16 {d20, d21, d22, d23}, [%0, :256]" : :
"r"(&aecm->channelAdapt32[i]): "q10", "q11");
}
aecm->channelAdapt16[i] = aecm->channelStored[i];
aecm->channelAdapt32[i] = WEBRTC_SPL_LSHIFT_W32(
(WebRtc_Word32)aecm->channelStored[i], 16);
}
#endif // #if defined(WEBRTC_ANDROID) && defined(WEBRTC_ARCH_ARM_NEON)

View File

@ -0,0 +1,800 @@
/*
* 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.
*/
#include <stdlib.h>
//#include <string.h>
#include "echo_control_mobile.h"
#include "aecm_core.h"
#include "ring_buffer.h"
#ifdef AEC_DEBUG
#include <stdio.h>
#endif
#ifdef MAC_IPHONE_PRINT
#include <time.h>
#include <stdio.h>
#elif defined ARM_WINM_LOG
#include "windows.h"
extern HANDLE logFile;
#endif
#define BUF_SIZE_FRAMES 50 // buffer size (frames)
// Maximum length of resampled signal. Must be an integer multiple of frames
// (ceil(1/(1 + MIN_SKEW)*2) + 1)*FRAME_LEN
// The factor of 2 handles wb, and the + 1 is as a safety margin
#define MAX_RESAMP_LEN (5 * FRAME_LEN)
static const int kBufSizeSamp = BUF_SIZE_FRAMES * FRAME_LEN; // buffer size (samples)
static const int kSampMsNb = 8; // samples per ms in nb
// Target suppression levels for nlp modes
// log{0.001, 0.00001, 0.00000001}
static const int kInitCheck = 42;
typedef struct
{
int sampFreq;
int scSampFreq;
short bufSizeStart;
int knownDelay;
// Stores the last frame added to the farend buffer
short farendOld[2][FRAME_LEN];
short initFlag; // indicates if AEC has been initialized
// Variables used for averaging far end buffer size
short counter;
short sum;
short firstVal;
short checkBufSizeCtr;
// Variables used for delay shifts
short msInSndCardBuf;
short filtDelay;
int timeForDelayChange;
int ECstartup;
int checkBuffSize;
int delayChange;
short lastDelayDiff;
WebRtc_Word16 echoMode;
#ifdef AEC_DEBUG
FILE *bufFile;
FILE *delayFile;
FILE *preCompFile;
FILE *postCompFile;
#endif // AEC_DEBUG
// Structures
void *farendBuf;
int lastError;
AecmCore_t *aecmCore;
} aecmob_t;
// Estimates delay to set the position of the farend buffer read pointer
// (controlled by knownDelay)
static int WebRtcAecm_EstBufDelay(aecmob_t *aecmInst, short msInSndCardBuf);
// Stuffs the farend buffer if the estimated delay is too large
static int WebRtcAecm_DelayComp(aecmob_t *aecmInst);
WebRtc_Word32 WebRtcAecm_Create(void **aecmInst)
{
aecmob_t *aecm;
if (aecmInst == NULL)
{
return -1;
}
aecm = malloc(sizeof(aecmob_t));
*aecmInst = aecm;
if (aecm == NULL)
{
return -1;
}
if (WebRtcAecm_CreateCore(&aecm->aecmCore) == -1)
{
WebRtcAecm_Free(aecm);
aecm = NULL;
return -1;
}
if (WebRtcApm_CreateBuffer(&aecm->farendBuf, kBufSizeSamp) == -1)
{
WebRtcAecm_Free(aecm);
aecm = NULL;
return -1;
}
aecm->initFlag = 0;
aecm->lastError = 0;
#ifdef AEC_DEBUG
aecm->aecmCore->farFile = fopen("aecFar.pcm","wb");
aecm->aecmCore->nearFile = fopen("aecNear.pcm","wb");
aecm->aecmCore->outFile = fopen("aecOut.pcm","wb");
//aecm->aecmCore->outLpFile = fopen("aecOutLp.pcm","wb");
aecm->bufFile = fopen("aecBuf.dat", "wb");
aecm->delayFile = fopen("aecDelay.dat", "wb");
aecm->preCompFile = fopen("preComp.pcm", "wb");
aecm->postCompFile = fopen("postComp.pcm", "wb");
#endif // AEC_DEBUG
return 0;
}
WebRtc_Word32 WebRtcAecm_Free(void *aecmInst)
{
aecmob_t *aecm = aecmInst;
if (aecm == NULL)
{
return -1;
}
#ifdef AEC_DEBUG
fclose(aecm->aecmCore->farFile);
fclose(aecm->aecmCore->nearFile);
fclose(aecm->aecmCore->outFile);
//fclose(aecm->aecmCore->outLpFile);
fclose(aecm->bufFile);
fclose(aecm->delayFile);
fclose(aecm->preCompFile);
fclose(aecm->postCompFile);
#endif // AEC_DEBUG
WebRtcAecm_FreeCore(aecm->aecmCore);
WebRtcApm_FreeBuffer(aecm->farendBuf);
free(aecm);
return 0;
}
WebRtc_Word32 WebRtcAecm_Init(void *aecmInst, WebRtc_Word32 sampFreq)
{
aecmob_t *aecm = aecmInst;
AecmConfig aecConfig;
if (aecm == NULL)
{
return -1;
}
if (sampFreq != 8000 && sampFreq != 16000)
{
aecm->lastError = AECM_BAD_PARAMETER_ERROR;
return -1;
}
aecm->sampFreq = sampFreq;
// Initialize AECM core
if (WebRtcAecm_InitCore(aecm->aecmCore, aecm->sampFreq) == -1)
{
aecm->lastError = AECM_UNSPECIFIED_ERROR;
return -1;
}
// Initialize farend buffer
if (WebRtcApm_InitBuffer(aecm->farendBuf) == -1)
{
aecm->lastError = AECM_UNSPECIFIED_ERROR;
return -1;
}
aecm->initFlag = kInitCheck; // indicates that initialization has been done
aecm->delayChange = 1;
aecm->sum = 0;
aecm->counter = 0;
aecm->checkBuffSize = 1;
aecm->firstVal = 0;
aecm->ECstartup = 1;
aecm->bufSizeStart = 0;
aecm->checkBufSizeCtr = 0;
aecm->filtDelay = 0;
aecm->timeForDelayChange = 0;
aecm->knownDelay = 0;
aecm->lastDelayDiff = 0;
memset(&aecm->farendOld[0][0], 0, 160);
// Default settings.
aecConfig.cngMode = AecmTrue;
aecConfig.echoMode = 3;
if (WebRtcAecm_set_config(aecm, aecConfig) == -1)
{
aecm->lastError = AECM_UNSPECIFIED_ERROR;
return -1;
}
return 0;
}
WebRtc_Word32 WebRtcAecm_BufferFarend(void *aecmInst, const WebRtc_Word16 *farend,
WebRtc_Word16 nrOfSamples)
{
aecmob_t *aecm = aecmInst;
WebRtc_Word32 retVal = 0;
if (aecm == NULL)
{
return -1;
}
if (farend == NULL)
{
aecm->lastError = AECM_NULL_POINTER_ERROR;
return -1;
}
if (aecm->initFlag != kInitCheck)
{
aecm->lastError = AECM_UNINITIALIZED_ERROR;
return -1;
}
if (nrOfSamples != 80 && nrOfSamples != 160)
{
aecm->lastError = AECM_BAD_PARAMETER_ERROR;
return -1;
}
// TODO: Is this really a good idea?
if (!aecm->ECstartup)
{
WebRtcAecm_DelayComp(aecm);
}
WebRtcApm_WriteBuffer(aecm->farendBuf, farend, nrOfSamples);
return retVal;
}
WebRtc_Word32 WebRtcAecm_Process(void *aecmInst, const WebRtc_Word16 *nearendNoisy,
const WebRtc_Word16 *nearendClean, WebRtc_Word16 *out,
WebRtc_Word16 nrOfSamples, WebRtc_Word16 msInSndCardBuf)
{
aecmob_t *aecm = aecmInst;
WebRtc_Word32 retVal = 0;
short i;
short farend[FRAME_LEN];
short nmbrOfFilledBuffers;
short nBlocks10ms;
short nFrames;
#ifdef AEC_DEBUG
short msInAECBuf;
#endif
#ifdef ARM_WINM_LOG
__int64 freq, start, end, diff;
unsigned int milliseconds;
DWORD temp;
#elif defined MAC_IPHONE_PRINT
// double endtime = 0, starttime = 0;
struct timeval starttime;
struct timeval endtime;
static long int timeused = 0;
static int timecount = 0;
#endif
if (aecm == NULL)
{
return -1;
}
if (nearendNoisy == NULL)
{
aecm->lastError = AECM_NULL_POINTER_ERROR;
return -1;
}
if (out == NULL)
{
aecm->lastError = AECM_NULL_POINTER_ERROR;
return -1;
}
if (aecm->initFlag != kInitCheck)
{
aecm->lastError = AECM_UNINITIALIZED_ERROR;
return -1;
}
if (nrOfSamples != 80 && nrOfSamples != 160)
{
aecm->lastError = AECM_BAD_PARAMETER_ERROR;
return -1;
}
if (msInSndCardBuf < 0)
{
msInSndCardBuf = 0;
aecm->lastError = AECM_BAD_PARAMETER_WARNING;
retVal = -1;
} else if (msInSndCardBuf > 500)
{
msInSndCardBuf = 500;
aecm->lastError = AECM_BAD_PARAMETER_WARNING;
retVal = -1;
}
msInSndCardBuf += 10;
aecm->msInSndCardBuf = msInSndCardBuf;
nFrames = nrOfSamples / FRAME_LEN;
nBlocks10ms = nFrames / aecm->aecmCore->mult;
if (aecm->ECstartup)
{
if (nearendClean == NULL)
{
memcpy(out, nearendNoisy, sizeof(short) * nrOfSamples);
} else
{
memcpy(out, nearendClean, sizeof(short) * nrOfSamples);
}
nmbrOfFilledBuffers = WebRtcApm_get_buffer_size(aecm->farendBuf) / FRAME_LEN;
// The AECM is in the start up mode
// AECM is disabled until the soundcard buffer and farend buffers are OK
// Mechanism to ensure that the soundcard buffer is reasonably stable.
if (aecm->checkBuffSize)
{
aecm->checkBufSizeCtr++;
// Before we fill up the far end buffer we require the amount of data on the
// sound card to be stable (+/-8 ms) compared to the first value. This
// comparison is made during the following 4 consecutive frames. If it seems
// to be stable then we start to fill up the far end buffer.
if (aecm->counter == 0)
{
aecm->firstVal = aecm->msInSndCardBuf;
aecm->sum = 0;
}
if (abs(aecm->firstVal - aecm->msInSndCardBuf)
< WEBRTC_SPL_MAX(0.2 * aecm->msInSndCardBuf, kSampMsNb))
{
aecm->sum += aecm->msInSndCardBuf;
aecm->counter++;
} else
{
aecm->counter = 0;
}
if (aecm->counter * nBlocks10ms >= 6)
{
// The farend buffer size is determined in blocks of 80 samples
// Use 75% of the average value of the soundcard buffer
aecm->bufSizeStart
= WEBRTC_SPL_MIN((3 * aecm->sum
* aecm->aecmCore->mult) / (aecm->counter * 40), BUF_SIZE_FRAMES);
// buffersize has now been determined
aecm->checkBuffSize = 0;
}
if (aecm->checkBufSizeCtr * nBlocks10ms > 50)
{
// for really bad sound cards, don't disable echocanceller for more than 0.5 sec
aecm->bufSizeStart = WEBRTC_SPL_MIN((3 * aecm->msInSndCardBuf
* aecm->aecmCore->mult) / 40, BUF_SIZE_FRAMES);
aecm->checkBuffSize = 0;
}
}
// if checkBuffSize changed in the if-statement above
if (!aecm->checkBuffSize)
{
// soundcard buffer is now reasonably stable
// When the far end buffer is filled with approximately the same amount of
// data as the amount on the sound card we end the start up phase and start
// to cancel echoes.
if (nmbrOfFilledBuffers == aecm->bufSizeStart)
{
aecm->ECstartup = 0; // Enable the AECM
} else if (nmbrOfFilledBuffers > aecm->bufSizeStart)
{
WebRtcApm_FlushBuffer(
aecm->farendBuf,
WebRtcApm_get_buffer_size(aecm->farendBuf)
- aecm->bufSizeStart * FRAME_LEN);
aecm->ECstartup = 0;
}
}
} else
{
// AECM is enabled
// Note only 1 block supported for nb and 2 blocks for wb
for (i = 0; i < nFrames; i++)
{
nmbrOfFilledBuffers = WebRtcApm_get_buffer_size(aecm->farendBuf) / FRAME_LEN;
// Check that there is data in the far end buffer
if (nmbrOfFilledBuffers > 0)
{
// Get the next 80 samples from the farend buffer
WebRtcApm_ReadBuffer(aecm->farendBuf, farend, FRAME_LEN);
// Always store the last frame for use when we run out of data
memcpy(&(aecm->farendOld[i][0]), farend, FRAME_LEN * sizeof(short));
} else
{
// We have no data so we use the last played frame
memcpy(farend, &(aecm->farendOld[i][0]), FRAME_LEN * sizeof(short));
}
// Call buffer delay estimator when all data is extracted,
// i,e. i = 0 for NB and i = 1 for WB
if ((i == 0 && aecm->sampFreq == 8000) || (i == 1 && aecm->sampFreq == 16000))
{
WebRtcAecm_EstBufDelay(aecm, aecm->msInSndCardBuf);
}
#ifdef ARM_WINM_LOG
// measure tick start
QueryPerformanceFrequency((LARGE_INTEGER*)&freq);
QueryPerformanceCounter((LARGE_INTEGER*)&start);
#elif defined MAC_IPHONE_PRINT
// starttime = clock()/(double)CLOCKS_PER_SEC;
gettimeofday(&starttime, NULL);
#endif
// Call the AECM
/*WebRtcAecm_ProcessFrame(aecm->aecmCore, farend, &nearend[FRAME_LEN * i],
&out[FRAME_LEN * i], aecm->knownDelay);*/
if (nearendClean == NULL)
{
if (WebRtcAecm_ProcessFrame(aecm->aecmCore,
farend,
&nearendNoisy[FRAME_LEN * i],
NULL,
&out[FRAME_LEN * i]) == -1)
{
return -1;
}
} else
{
if (WebRtcAecm_ProcessFrame(aecm->aecmCore,
farend,
&nearendNoisy[FRAME_LEN * i],
&nearendClean[FRAME_LEN * i],
&out[FRAME_LEN * i]) == -1)
{
return -1;
}
}
#ifdef ARM_WINM_LOG
// measure tick end
QueryPerformanceCounter((LARGE_INTEGER*)&end);
if(end > start)
{
diff = ((end - start) * 1000) / (freq/1000);
milliseconds = (unsigned int)(diff & 0xffffffff);
WriteFile (logFile, &milliseconds, sizeof(unsigned int), &temp, NULL);
}
#elif defined MAC_IPHONE_PRINT
// endtime = clock()/(double)CLOCKS_PER_SEC;
// printf("%f\n", endtime - starttime);
gettimeofday(&endtime, NULL);
if( endtime.tv_usec > starttime.tv_usec)
{
timeused += endtime.tv_usec - starttime.tv_usec;
} else
{
timeused += endtime.tv_usec + 1000000 - starttime.tv_usec;
}
if(++timecount == 1000)
{
timecount = 0;
printf("AEC: %ld\n", timeused);
timeused = 0;
}
#endif
}
}
#ifdef AEC_DEBUG
msInAECBuf = WebRtcApm_get_buffer_size(aecm->farendBuf) / (kSampMsNb*aecm->aecmCore->mult);
fwrite(&msInAECBuf, 2, 1, aecm->bufFile);
fwrite(&(aecm->knownDelay), sizeof(aecm->knownDelay), 1, aecm->delayFile);
#endif
return retVal;
}
WebRtc_Word32 WebRtcAecm_set_config(void *aecmInst, AecmConfig config)
{
aecmob_t *aecm = aecmInst;
if (aecm == NULL)
{
return -1;
}
if (aecm->initFlag != kInitCheck)
{
aecm->lastError = AECM_UNINITIALIZED_ERROR;
return -1;
}
if (config.cngMode != AecmFalse && config.cngMode != AecmTrue)
{
aecm->lastError = AECM_BAD_PARAMETER_ERROR;
return -1;
}
aecm->aecmCore->cngMode = config.cngMode;
if (config.echoMode < 0 || config.echoMode > 4)
{
aecm->lastError = AECM_BAD_PARAMETER_ERROR;
return -1;
}
aecm->echoMode = config.echoMode;
if (aecm->echoMode == 0)
{
aecm->aecmCore->supGain = SUPGAIN_DEFAULT >> 3;
aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT >> 3;
aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A >> 3;
aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D >> 3;
aecm->aecmCore->supGainErrParamDiffAB = (SUPGAIN_ERROR_PARAM_A >> 3)
- (SUPGAIN_ERROR_PARAM_B >> 3);
aecm->aecmCore->supGainErrParamDiffBD = (SUPGAIN_ERROR_PARAM_B >> 3)
- (SUPGAIN_ERROR_PARAM_D >> 3);
} else if (aecm->echoMode == 1)
{
aecm->aecmCore->supGain = SUPGAIN_DEFAULT >> 2;
aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT >> 2;
aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A >> 2;
aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D >> 2;
aecm->aecmCore->supGainErrParamDiffAB = (SUPGAIN_ERROR_PARAM_A >> 2)
- (SUPGAIN_ERROR_PARAM_B >> 2);
aecm->aecmCore->supGainErrParamDiffBD = (SUPGAIN_ERROR_PARAM_B >> 2)
- (SUPGAIN_ERROR_PARAM_D >> 2);
} else if (aecm->echoMode == 2)
{
aecm->aecmCore->supGain = SUPGAIN_DEFAULT >> 1;
aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT >> 1;
aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A >> 1;
aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D >> 1;
aecm->aecmCore->supGainErrParamDiffAB = (SUPGAIN_ERROR_PARAM_A >> 1)
- (SUPGAIN_ERROR_PARAM_B >> 1);
aecm->aecmCore->supGainErrParamDiffBD = (SUPGAIN_ERROR_PARAM_B >> 1)
- (SUPGAIN_ERROR_PARAM_D >> 1);
} else if (aecm->echoMode == 3)
{
aecm->aecmCore->supGain = SUPGAIN_DEFAULT;
aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT;
aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A;
aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D;
aecm->aecmCore->supGainErrParamDiffAB = SUPGAIN_ERROR_PARAM_A - SUPGAIN_ERROR_PARAM_B;
aecm->aecmCore->supGainErrParamDiffBD = SUPGAIN_ERROR_PARAM_B - SUPGAIN_ERROR_PARAM_D;
} else if (aecm->echoMode == 4)
{
aecm->aecmCore->supGain = SUPGAIN_DEFAULT << 1;
aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT << 1;
aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A << 1;
aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D << 1;
aecm->aecmCore->supGainErrParamDiffAB = (SUPGAIN_ERROR_PARAM_A << 1)
- (SUPGAIN_ERROR_PARAM_B << 1);
aecm->aecmCore->supGainErrParamDiffBD = (SUPGAIN_ERROR_PARAM_B << 1)
- (SUPGAIN_ERROR_PARAM_D << 1);
}
return 0;
}
WebRtc_Word32 WebRtcAecm_get_config(void *aecmInst, AecmConfig *config)
{
aecmob_t *aecm = aecmInst;
if (aecm == NULL)
{
return -1;
}
if (config == NULL)
{
aecm->lastError = AECM_NULL_POINTER_ERROR;
return -1;
}
if (aecm->initFlag != kInitCheck)
{
aecm->lastError = AECM_UNINITIALIZED_ERROR;
return -1;
}
config->cngMode = aecm->aecmCore->cngMode;
config->echoMode = aecm->echoMode;
return 0;
}
WebRtc_Word32 WebRtcAecm_InitEchoPath(void* aecmInst,
const void* echo_path,
size_t size_bytes)
{
aecmob_t *aecm = aecmInst;
const WebRtc_Word16* echo_path_ptr = echo_path;
if ((aecm == NULL) || (echo_path == NULL))
{
aecm->lastError = AECM_NULL_POINTER_ERROR;
return -1;
}
if (size_bytes != WebRtcAecm_echo_path_size_bytes())
{
// Input channel size does not match the size of AECM
aecm->lastError = AECM_BAD_PARAMETER_ERROR;
return -1;
}
if (aecm->initFlag != kInitCheck)
{
aecm->lastError = AECM_UNINITIALIZED_ERROR;
return -1;
}
WebRtcAecm_InitEchoPathCore(aecm->aecmCore, echo_path_ptr);
return 0;
}
WebRtc_Word32 WebRtcAecm_GetEchoPath(void* aecmInst,
void* echo_path,
size_t size_bytes)
{
aecmob_t *aecm = aecmInst;
WebRtc_Word16* echo_path_ptr = echo_path;
if ((aecm == NULL) || (echo_path == NULL))
{
aecm->lastError = AECM_NULL_POINTER_ERROR;
return -1;
}
if (size_bytes != WebRtcAecm_echo_path_size_bytes())
{
// Input channel size does not match the size of AECM
aecm->lastError = AECM_BAD_PARAMETER_ERROR;
return -1;
}
if (aecm->initFlag != kInitCheck)
{
aecm->lastError = AECM_UNINITIALIZED_ERROR;
return -1;
}
memcpy(echo_path_ptr, aecm->aecmCore->channelStored, size_bytes);
return 0;
}
size_t WebRtcAecm_echo_path_size_bytes()
{
return (PART_LEN1 * sizeof(WebRtc_Word16));
}
WebRtc_Word32 WebRtcAecm_get_version(WebRtc_Word8 *versionStr, WebRtc_Word16 len)
{
const char version[] = "AECM 1.2.0";
const short versionLen = (short)strlen(version) + 1; // +1 for null-termination
if (versionStr == NULL)
{
return -1;
}
if (versionLen > len)
{
return -1;
}
strncpy(versionStr, version, versionLen);
return 0;
}
WebRtc_Word32 WebRtcAecm_get_error_code(void *aecmInst)
{
aecmob_t *aecm = aecmInst;
if (aecm == NULL)
{
return -1;
}
return aecm->lastError;
}
static int WebRtcAecm_EstBufDelay(aecmob_t *aecm, short msInSndCardBuf)
{
short delayNew, nSampFar, nSampSndCard;
short diff;
nSampFar = WebRtcApm_get_buffer_size(aecm->farendBuf);
nSampSndCard = msInSndCardBuf * kSampMsNb * aecm->aecmCore->mult;
delayNew = nSampSndCard - nSampFar;
if (delayNew < FRAME_LEN)
{
WebRtcApm_FlushBuffer(aecm->farendBuf, FRAME_LEN);
delayNew += FRAME_LEN;
}
aecm->filtDelay = WEBRTC_SPL_MAX(0, (8 * aecm->filtDelay + 2 * delayNew) / 10);
diff = aecm->filtDelay - aecm->knownDelay;
if (diff > 224)
{
if (aecm->lastDelayDiff < 96)
{
aecm->timeForDelayChange = 0;
} else
{
aecm->timeForDelayChange++;
}
} else if (diff < 96 && aecm->knownDelay > 0)
{
if (aecm->lastDelayDiff > 224)
{
aecm->timeForDelayChange = 0;
} else
{
aecm->timeForDelayChange++;
}
} else
{
aecm->timeForDelayChange = 0;
}
aecm->lastDelayDiff = diff;
if (aecm->timeForDelayChange > 25)
{
aecm->knownDelay = WEBRTC_SPL_MAX((int)aecm->filtDelay - 160, 0);
}
return 0;
}
static int WebRtcAecm_DelayComp(aecmob_t *aecm)
{
int nSampFar, nSampSndCard, delayNew, nSampAdd;
const int maxStuffSamp = 10 * FRAME_LEN;
nSampFar = WebRtcApm_get_buffer_size(aecm->farendBuf);
nSampSndCard = aecm->msInSndCardBuf * kSampMsNb * aecm->aecmCore->mult;
delayNew = nSampSndCard - nSampFar;
if (delayNew > FAR_BUF_LEN - FRAME_LEN * aecm->aecmCore->mult)
{
// The difference of the buffer sizes is larger than the maximum
// allowed known delay. Compensate by stuffing the buffer.
nSampAdd = (int)(WEBRTC_SPL_MAX(((nSampSndCard >> 1) - nSampFar),
FRAME_LEN));
nSampAdd = WEBRTC_SPL_MIN(nSampAdd, maxStuffSamp);
WebRtcApm_StuffBuffer(aecm->farendBuf, nSampAdd);
aecm->delayChange = 1; // the delay needs to be updated
}
return 0;
}

View File

@ -0,0 +1,250 @@
/*
* 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.
*/
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AECM_MAIN_INTERFACE_ECHO_CONTROL_MOBILE_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_AECM_MAIN_INTERFACE_ECHO_CONTROL_MOBILE_H_
#include "typedefs.h"
enum {
AecmFalse = 0,
AecmTrue
};
// Errors
#define AECM_UNSPECIFIED_ERROR 12000
#define AECM_UNSUPPORTED_FUNCTION_ERROR 12001
#define AECM_UNINITIALIZED_ERROR 12002
#define AECM_NULL_POINTER_ERROR 12003
#define AECM_BAD_PARAMETER_ERROR 12004
// Warnings
#define AECM_BAD_PARAMETER_WARNING 12100
typedef struct {
WebRtc_Word16 cngMode; // AECM_FALSE, AECM_TRUE (default)
WebRtc_Word16 echoMode; // 0, 1, 2, 3 (default), 4
} AecmConfig;
#ifdef __cplusplus
extern "C" {
#endif
/*
* Allocates the memory needed by the AECM. The memory needs to be
* initialized separately using the WebRtcAecm_Init() function.
*
* Inputs Description
* -------------------------------------------------------------------
* void **aecmInst Pointer to the AECM instance to be
* created and initialized
*
* Outputs Description
* -------------------------------------------------------------------
* WebRtc_Word32 return 0: OK
* -1: error
*/
WebRtc_Word32 WebRtcAecm_Create(void **aecmInst);
/*
* This function releases the memory allocated by WebRtcAecm_Create()
*
* Inputs Description
* -------------------------------------------------------------------
* void *aecmInst Pointer to the AECM instance
*
* Outputs Description
* -------------------------------------------------------------------
* WebRtc_Word32 return 0: OK
* -1: error
*/
WebRtc_Word32 WebRtcAecm_Free(void *aecmInst);
/*
* Initializes an AECM instance.
*
* Inputs Description
* -------------------------------------------------------------------
* void *aecmInst Pointer to the AECM instance
* WebRtc_Word32 sampFreq Sampling frequency of data
*
* Outputs Description
* -------------------------------------------------------------------
* WebRtc_Word32 return 0: OK
* -1: error
*/
WebRtc_Word32 WebRtcAecm_Init(void* aecmInst,
WebRtc_Word32 sampFreq);
/*
* Inserts an 80 or 160 sample block of data into the farend buffer.
*
* Inputs Description
* -------------------------------------------------------------------
* void *aecmInst Pointer to the AECM instance
* WebRtc_Word16 *farend In buffer containing one frame of
* farend signal
* WebRtc_Word16 nrOfSamples Number of samples in farend buffer
*
* Outputs Description
* -------------------------------------------------------------------
* WebRtc_Word32 return 0: OK
* -1: error
*/
WebRtc_Word32 WebRtcAecm_BufferFarend(void* aecmInst,
const WebRtc_Word16* farend,
WebRtc_Word16 nrOfSamples);
/*
* Runs the AECM on an 80 or 160 sample blocks of data.
*
* Inputs Description
* -------------------------------------------------------------------
* void *aecmInst Pointer to the AECM instance
* WebRtc_Word16 *nearendNoisy In buffer containing one frame of
* reference nearend+echo signal. If
* noise reduction is active, provide
* the noisy signal here.
* WebRtc_Word16 *nearendClean In buffer containing one frame of
* nearend+echo signal. If noise
* reduction is active, provide the
* clean signal here. Otherwise pass a
* NULL pointer.
* WebRtc_Word16 nrOfSamples Number of samples in nearend buffer
* WebRtc_Word16 msInSndCardBuf Delay estimate for sound card and
* system buffers
*
* Outputs Description
* -------------------------------------------------------------------
* WebRtc_Word16 *out Out buffer, one frame of processed nearend
* WebRtc_Word32 return 0: OK
* -1: error
*/
WebRtc_Word32 WebRtcAecm_Process(void* aecmInst,
const WebRtc_Word16* nearendNoisy,
const WebRtc_Word16* nearendClean,
WebRtc_Word16* out,
WebRtc_Word16 nrOfSamples,
WebRtc_Word16 msInSndCardBuf);
/*
* This function enables the user to set certain parameters on-the-fly
*
* Inputs Description
* -------------------------------------------------------------------
* void *aecmInst Pointer to the AECM instance
* AecmConfig config Config instance that contains all
* properties to be set
*
* Outputs Description
* -------------------------------------------------------------------
* WebRtc_Word32 return 0: OK
* -1: error
*/
WebRtc_Word32 WebRtcAecm_set_config(void* aecmInst,
AecmConfig config);
/*
* This function enables the user to set certain parameters on-the-fly
*
* Inputs Description
* -------------------------------------------------------------------
* void *aecmInst Pointer to the AECM instance
*
* Outputs Description
* -------------------------------------------------------------------
* AecmConfig *config Pointer to the config instance that
* all properties will be written to
* WebRtc_Word32 return 0: OK
* -1: error
*/
WebRtc_Word32 WebRtcAecm_get_config(void *aecmInst,
AecmConfig *config);
/*
* This function enables the user to set the echo path on-the-fly.
*
* Inputs Description
* -------------------------------------------------------------------
* void* aecmInst Pointer to the AECM instance
* void* echo_path Pointer to the echo path to be set
* size_t size_bytes Size in bytes of the echo path
*
* Outputs Description
* -------------------------------------------------------------------
* WebRtc_Word32 return 0: OK
* -1: error
*/
WebRtc_Word32 WebRtcAecm_InitEchoPath(void* aecmInst,
const void* echo_path,
size_t size_bytes);
/*
* This function enables the user to get the currently used echo path
* on-the-fly
*
* Inputs Description
* -------------------------------------------------------------------
* void* aecmInst Pointer to the AECM instance
* void* echo_path Pointer to echo path
* size_t size_bytes Size in bytes of the echo path
*
* Outputs Description
* -------------------------------------------------------------------
* WebRtc_Word32 return 0: OK
* -1: error
*/
WebRtc_Word32 WebRtcAecm_GetEchoPath(void* aecmInst,
void* echo_path,
size_t size_bytes);
/*
* This function enables the user to get the echo path size in bytes
*
* Outputs Description
* -------------------------------------------------------------------
* size_t return : size in bytes
*/
size_t WebRtcAecm_echo_path_size_bytes();
/*
* Gets the last error code.
*
* Inputs Description
* -------------------------------------------------------------------
* void *aecmInst Pointer to the AECM instance
*
* Outputs Description
* -------------------------------------------------------------------
* WebRtc_Word32 return 11000-11100: error code
*/
WebRtc_Word32 WebRtcAecm_get_error_code(void *aecmInst);
/*
* Gets a version string
*
* Inputs Description
* -------------------------------------------------------------------
* char *versionStr Pointer to a string array
* WebRtc_Word16 len The maximum length of the string
*
* Outputs Description
* -------------------------------------------------------------------
* WebRtc_Word8 *versionStr Pointer to a string array
* WebRtc_Word32 return 0: OK
* -1: error
*/
WebRtc_Word32 WebRtcAecm_get_version(WebRtc_Word8 *versionStr,
WebRtc_Word16 len);
#ifdef __cplusplus
}
#endif
#endif /* WEBRTC_MODULES_AUDIO_PROCESSING_AECM_MAIN_INTERFACE_ECHO_CONTROL_MOBILE_H_ */

View File

@ -0,0 +1,10 @@
noinst_LTLIBRARIES = libagc.la
libagc_la_SOURCES = interface/gain_control.h \
analog_agc.c \
analog_agc.h \
digital_agc.c \
digital_agc.h
libagc_la_CFLAGS = $(AM_CFLAGS) $(COMMON_CFLAGS) \
-I$(top_srcdir)/src/common_audio/signal_processing_library/main/interface \
-I$(top_srcdir)/src/modules/audio_processing/utility

View File

@ -0,0 +1,34 @@
# 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.
{
'targets': [
{
'target_name': 'agc',
'type': '<(library)',
'dependencies': [
'<(webrtc_root)/common_audio/common_audio.gyp:spl',
],
'include_dirs': [
'interface',
],
'direct_dependent_settings': {
'include_dirs': [
'interface',
],
},
'sources': [
'interface/gain_control.h',
'analog_agc.c',
'analog_agc.h',
'digital_agc.c',
'digital_agc.h',
],
},
],
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,133 @@
/*
* 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.
*/
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AGC_MAIN_SOURCE_ANALOG_AGC_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_AGC_MAIN_SOURCE_ANALOG_AGC_H_
#include "typedefs.h"
#include "gain_control.h"
#include "digital_agc.h"
//#define AGC_DEBUG
//#define MIC_LEVEL_FEEDBACK
#ifdef AGC_DEBUG
#include <stdio.h>
#endif
/* Analog Automatic Gain Control variables:
* Constant declarations (inner limits inside which no changes are done)
* In the beginning the range is narrower to widen as soon as the measure
* 'Rxx160_LP' is inside it. Currently the starting limits are -22.2+/-1dBm0
* and the final limits -22.2+/-2.5dBm0. These levels makes the speech signal
* go towards -25.4dBm0 (-31.4dBov). Tuned with wbfile-31.4dBov.pcm
* The limits are created by running the AGC with a file having the desired
* signal level and thereafter plotting Rxx160_LP in the dBm0-domain defined
* by out=10*log10(in/260537279.7); Set the target level to the average level
* of our measure Rxx160_LP. Remember that the levels are in blocks of 16 in
* Q(-7). (Example matlab code: round(db2pow(-21.2)*16/2^7) )
*/
#define RXX_BUFFER_LEN 10
static const WebRtc_Word16 kMsecSpeechInner = 520;
static const WebRtc_Word16 kMsecSpeechOuter = 340;
static const WebRtc_Word16 kNormalVadThreshold = 400;
static const WebRtc_Word16 kAlphaShortTerm = 6; // 1 >> 6 = 0.0156
static const WebRtc_Word16 kAlphaLongTerm = 10; // 1 >> 10 = 0.000977
typedef struct
{
// Configurable parameters/variables
WebRtc_UWord32 fs; // Sampling frequency
WebRtc_Word16 compressionGaindB; // Fixed gain level in dB
WebRtc_Word16 targetLevelDbfs; // Target level in -dBfs of envelope (default -3)
WebRtc_Word16 agcMode; // Hard coded mode (adaptAna/adaptDig/fixedDig)
WebRtc_UWord8 limiterEnable; // Enabling limiter (on/off (default off))
WebRtcAgc_config_t defaultConfig;
WebRtcAgc_config_t usedConfig;
// General variables
WebRtc_Word16 initFlag;
WebRtc_Word16 lastError;
// Target level parameters
// Based on the above: analogTargetLevel = round((32767*10^(-22/20))^2*16/2^7)
WebRtc_Word32 analogTargetLevel; // = RXX_BUFFER_LEN * 846805; -22 dBfs
WebRtc_Word32 startUpperLimit; // = RXX_BUFFER_LEN * 1066064; -21 dBfs
WebRtc_Word32 startLowerLimit; // = RXX_BUFFER_LEN * 672641; -23 dBfs
WebRtc_Word32 upperPrimaryLimit; // = RXX_BUFFER_LEN * 1342095; -20 dBfs
WebRtc_Word32 lowerPrimaryLimit; // = RXX_BUFFER_LEN * 534298; -24 dBfs
WebRtc_Word32 upperSecondaryLimit;// = RXX_BUFFER_LEN * 2677832; -17 dBfs
WebRtc_Word32 lowerSecondaryLimit;// = RXX_BUFFER_LEN * 267783; -27 dBfs
WebRtc_UWord16 targetIdx; // Table index for corresponding target level
#ifdef MIC_LEVEL_FEEDBACK
WebRtc_UWord16 targetIdxOffset; // Table index offset for level compensation
#endif
WebRtc_Word16 analogTarget; // Digital reference level in ENV scale
// Analog AGC specific variables
WebRtc_Word32 filterState[8]; // For downsampling wb to nb
WebRtc_Word32 upperLimit; // Upper limit for mic energy
WebRtc_Word32 lowerLimit; // Lower limit for mic energy
WebRtc_Word32 Rxx160w32; // Average energy for one frame
WebRtc_Word32 Rxx16_LPw32; // Low pass filtered subframe energies
WebRtc_Word32 Rxx160_LPw32; // Low pass filtered frame energies
WebRtc_Word32 Rxx16_LPw32Max; // Keeps track of largest energy subframe
WebRtc_Word32 Rxx16_vectorw32[RXX_BUFFER_LEN];// Array with subframe energies
WebRtc_Word32 Rxx16w32_array[2][5];// Energy values of microphone signal
WebRtc_Word32 env[2][10]; // Envelope values of subframes
WebRtc_Word16 Rxx16pos; // Current position in the Rxx16_vectorw32
WebRtc_Word16 envSum; // Filtered scaled envelope in subframes
WebRtc_Word16 vadThreshold; // Threshold for VAD decision
WebRtc_Word16 inActive; // Inactive time in milliseconds
WebRtc_Word16 msTooLow; // Milliseconds of speech at a too low level
WebRtc_Word16 msTooHigh; // Milliseconds of speech at a too high level
WebRtc_Word16 changeToSlowMode; // Change to slow mode after some time at target
WebRtc_Word16 firstCall; // First call to the process-function
WebRtc_Word16 msZero; // Milliseconds of zero input
WebRtc_Word16 msecSpeechOuterChange;// Min ms of speech between volume changes
WebRtc_Word16 msecSpeechInnerChange;// Min ms of speech between volume changes
WebRtc_Word16 activeSpeech; // Milliseconds of active speech
WebRtc_Word16 muteGuardMs; // Counter to prevent mute action
WebRtc_Word16 inQueue; // 10 ms batch indicator
// Microphone level variables
WebRtc_Word32 micRef; // Remember ref. mic level for virtual mic
WebRtc_UWord16 gainTableIdx; // Current position in virtual gain table
WebRtc_Word32 micGainIdx; // Gain index of mic level to increase slowly
WebRtc_Word32 micVol; // Remember volume between frames
WebRtc_Word32 maxLevel; // Max possible vol level, incl dig gain
WebRtc_Word32 maxAnalog; // Maximum possible analog volume level
WebRtc_Word32 maxInit; // Initial value of "max"
WebRtc_Word32 minLevel; // Minimum possible volume level
WebRtc_Word32 minOutput; // Minimum output volume level
WebRtc_Word32 zeroCtrlMax; // Remember max gain => don't amp low input
WebRtc_Word16 scale; // Scale factor for internal volume levels
#ifdef MIC_LEVEL_FEEDBACK
WebRtc_Word16 numBlocksMicLvlSat;
WebRtc_UWord8 micLvlSat;
#endif
// Structs for VAD and digital_agc
AgcVad_t vadMic;
DigitalAgc_t digitalAgc;
#ifdef AGC_DEBUG
FILE* fpt;
FILE* agcLog;
WebRtc_Word32 fcount;
#endif
WebRtc_Word16 lowLevelSignal;
} Agc_t;
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AGC_MAIN_SOURCE_ANALOG_AGC_H_

View File

@ -0,0 +1,786 @@
/*
* 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.
*/
/* digital_agc.c
*
*/
#include <string.h>
#ifdef AGC_DEBUG
#include <stdio.h>
#endif
#include "digital_agc.h"
#include "gain_control.h"
// To generate the gaintable, copy&paste the following lines to a Matlab window:
// MaxGain = 6; MinGain = 0; CompRatio = 3; Knee = 1;
// zeros = 0:31; lvl = 2.^(1-zeros);
// A = -10*log10(lvl) * (CompRatio - 1) / CompRatio;
// B = MaxGain - MinGain;
// gains = round(2^16*10.^(0.05 * (MinGain + B * ( log(exp(-Knee*A)+exp(-Knee*B)) - log(1+exp(-Knee*B)) ) / log(1/(1+exp(Knee*B))))));
// fprintf(1, '\t%i, %i, %i, %i,\n', gains);
// % Matlab code for plotting the gain and input/output level characteristic (copy/paste the following 3 lines):
// in = 10*log10(lvl); out = 20*log10(gains/65536);
// subplot(121); plot(in, out); axis([-30, 0, -5, 20]); grid on; xlabel('Input (dB)'); ylabel('Gain (dB)');
// subplot(122); plot(in, in+out); axis([-30, 0, -30, 5]); grid on; xlabel('Input (dB)'); ylabel('Output (dB)');
// zoom on;
// Generator table for y=log2(1+e^x) in Q8.
static const WebRtc_UWord16 kGenFuncTable[128] = {
256, 485, 786, 1126, 1484, 1849, 2217, 2586,
2955, 3324, 3693, 4063, 4432, 4801, 5171, 5540,
5909, 6279, 6648, 7017, 7387, 7756, 8125, 8495,
8864, 9233, 9603, 9972, 10341, 10711, 11080, 11449,
11819, 12188, 12557, 12927, 13296, 13665, 14035, 14404,
14773, 15143, 15512, 15881, 16251, 16620, 16989, 17359,
17728, 18097, 18466, 18836, 19205, 19574, 19944, 20313,
20682, 21052, 21421, 21790, 22160, 22529, 22898, 23268,
23637, 24006, 24376, 24745, 25114, 25484, 25853, 26222,
26592, 26961, 27330, 27700, 28069, 28438, 28808, 29177,
29546, 29916, 30285, 30654, 31024, 31393, 31762, 32132,
32501, 32870, 33240, 33609, 33978, 34348, 34717, 35086,
35456, 35825, 36194, 36564, 36933, 37302, 37672, 38041,
38410, 38780, 39149, 39518, 39888, 40257, 40626, 40996,
41365, 41734, 42104, 42473, 42842, 43212, 43581, 43950,
44320, 44689, 45058, 45428, 45797, 46166, 46536, 46905
};
static const WebRtc_Word16 kAvgDecayTime = 250; // frames; < 3000
WebRtc_Word32 WebRtcAgc_CalculateGainTable(WebRtc_Word32 *gainTable, // Q16
WebRtc_Word16 digCompGaindB, // Q0
WebRtc_Word16 targetLevelDbfs,// Q0
WebRtc_UWord8 limiterEnable,
WebRtc_Word16 analogTarget) // Q0
{
// This function generates the compressor gain table used in the fixed digital part.
WebRtc_UWord32 tmpU32no1, tmpU32no2, absInLevel, logApprox;
WebRtc_Word32 inLevel, limiterLvl;
WebRtc_Word32 tmp32, tmp32no1, tmp32no2, numFIX, den, y32;
const WebRtc_UWord16 kLog10 = 54426; // log2(10) in Q14
const WebRtc_UWord16 kLog10_2 = 49321; // 10*log10(2) in Q14
const WebRtc_UWord16 kLogE_1 = 23637; // log2(e) in Q14
WebRtc_UWord16 constMaxGain;
WebRtc_UWord16 tmpU16, intPart, fracPart;
const WebRtc_Word16 kCompRatio = 3;
const WebRtc_Word16 kSoftLimiterLeft = 1;
WebRtc_Word16 limiterOffset = 0; // Limiter offset
WebRtc_Word16 limiterIdx, limiterLvlX;
WebRtc_Word16 constLinApprox, zeroGainLvl, maxGain, diffGain;
WebRtc_Word16 i, tmp16, tmp16no1;
int zeros, zerosScale;
// Constants
// kLogE_1 = 23637; // log2(e) in Q14
// kLog10 = 54426; // log2(10) in Q14
// kLog10_2 = 49321; // 10*log10(2) in Q14
// Calculate maximum digital gain and zero gain level
tmp32no1 = WEBRTC_SPL_MUL_16_16(digCompGaindB - analogTarget, kCompRatio - 1);
tmp16no1 = analogTarget - targetLevelDbfs;
tmp16no1 += WebRtcSpl_DivW32W16ResW16(tmp32no1 + (kCompRatio >> 1), kCompRatio);
maxGain = WEBRTC_SPL_MAX(tmp16no1, (analogTarget - targetLevelDbfs));
tmp32no1 = WEBRTC_SPL_MUL_16_16(maxGain, kCompRatio);
zeroGainLvl = digCompGaindB;
zeroGainLvl -= WebRtcSpl_DivW32W16ResW16(tmp32no1 + ((kCompRatio - 1) >> 1),
kCompRatio - 1);
if ((digCompGaindB <= analogTarget) && (limiterEnable))
{
zeroGainLvl += (analogTarget - digCompGaindB + kSoftLimiterLeft);
limiterOffset = 0;
}
// Calculate the difference between maximum gain and gain at 0dB0v:
// diffGain = maxGain + (compRatio-1)*zeroGainLvl/compRatio
// = (compRatio-1)*digCompGaindB/compRatio
tmp32no1 = WEBRTC_SPL_MUL_16_16(digCompGaindB, kCompRatio - 1);
diffGain = WebRtcSpl_DivW32W16ResW16(tmp32no1 + (kCompRatio >> 1), kCompRatio);
if (diffGain < 0)
{
return -1;
}
// Calculate the limiter level and index:
// limiterLvlX = analogTarget - limiterOffset
// limiterLvl = targetLevelDbfs + limiterOffset/compRatio
limiterLvlX = analogTarget - limiterOffset;
limiterIdx = 2
+ WebRtcSpl_DivW32W16ResW16(WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)limiterLvlX, 13),
WEBRTC_SPL_RSHIFT_U16(kLog10_2, 1));
tmp16no1 = WebRtcSpl_DivW32W16ResW16(limiterOffset + (kCompRatio >> 1), kCompRatio);
limiterLvl = targetLevelDbfs + tmp16no1;
// Calculate (through table lookup):
// constMaxGain = log2(1+2^(log2(e)*diffGain)); (in Q8)
constMaxGain = kGenFuncTable[diffGain]; // in Q8
// Calculate a parameter used to approximate the fractional part of 2^x with a
// piecewise linear function in Q14:
// constLinApprox = round(3/2*(4*(3-2*sqrt(2))/(log(2)^2)-0.5)*2^14);
constLinApprox = 22817; // in Q14
// Calculate a denominator used in the exponential part to convert from dB to linear scale:
// den = 20*constMaxGain (in Q8)
den = WEBRTC_SPL_MUL_16_U16(20, constMaxGain); // in Q8
for (i = 0; i < 32; i++)
{
// Calculate scaled input level (compressor):
// inLevel = fix((-constLog10_2*(compRatio-1)*(1-i)+fix(compRatio/2))/compRatio)
tmp16 = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16(kCompRatio - 1, i - 1); // Q0
tmp32 = WEBRTC_SPL_MUL_16_U16(tmp16, kLog10_2) + 1; // Q14
inLevel = WebRtcSpl_DivW32W16(tmp32, kCompRatio); // Q14
// Calculate diffGain-inLevel, to map using the genFuncTable
inLevel = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)diffGain, 14) - inLevel; // Q14
// Make calculations on abs(inLevel) and compensate for the sign afterwards.
absInLevel = (WebRtc_UWord32)WEBRTC_SPL_ABS_W32(inLevel); // Q14
// LUT with interpolation
intPart = (WebRtc_UWord16)WEBRTC_SPL_RSHIFT_U32(absInLevel, 14);
fracPart = (WebRtc_UWord16)(absInLevel & 0x00003FFF); // extract the fractional part
tmpU16 = kGenFuncTable[intPart + 1] - kGenFuncTable[intPart]; // Q8
tmpU32no1 = WEBRTC_SPL_UMUL_16_16(tmpU16, fracPart); // Q22
tmpU32no1 += WEBRTC_SPL_LSHIFT_U32((WebRtc_UWord32)kGenFuncTable[intPart], 14); // Q22
logApprox = WEBRTC_SPL_RSHIFT_U32(tmpU32no1, 8); // Q14
// Compensate for negative exponent using the relation:
// log2(1 + 2^-x) = log2(1 + 2^x) - x
if (inLevel < 0)
{
zeros = WebRtcSpl_NormU32(absInLevel);
zerosScale = 0;
if (zeros < 15)
{
// Not enough space for multiplication
tmpU32no2 = WEBRTC_SPL_RSHIFT_U32(absInLevel, 15 - zeros); // Q(zeros-1)
tmpU32no2 = WEBRTC_SPL_UMUL_32_16(tmpU32no2, kLogE_1); // Q(zeros+13)
if (zeros < 9)
{
tmpU32no1 = WEBRTC_SPL_RSHIFT_U32(tmpU32no1, 9 - zeros); // Q(zeros+13)
zerosScale = 9 - zeros;
} else
{
tmpU32no2 = WEBRTC_SPL_RSHIFT_U32(tmpU32no2, zeros - 9); // Q22
}
} else
{
tmpU32no2 = WEBRTC_SPL_UMUL_32_16(absInLevel, kLogE_1); // Q28
tmpU32no2 = WEBRTC_SPL_RSHIFT_U32(tmpU32no2, 6); // Q22
}
logApprox = 0;
if (tmpU32no2 < tmpU32no1)
{
logApprox = WEBRTC_SPL_RSHIFT_U32(tmpU32no1 - tmpU32no2, 8 - zerosScale); //Q14
}
}
numFIX = WEBRTC_SPL_LSHIFT_W32(WEBRTC_SPL_MUL_16_U16(maxGain, constMaxGain), 6); // Q14
numFIX -= WEBRTC_SPL_MUL_32_16((WebRtc_Word32)logApprox, diffGain); // Q14
// Calculate ratio
// Shift numFIX as much as possible
zeros = WebRtcSpl_NormW32(numFIX);
numFIX = WEBRTC_SPL_LSHIFT_W32(numFIX, zeros); // Q(14+zeros)
// Shift den so we end up in Qy1
tmp32no1 = WEBRTC_SPL_SHIFT_W32(den, zeros - 8); // Q(zeros)
if (numFIX < 0)
{
numFIX -= WEBRTC_SPL_RSHIFT_W32(tmp32no1, 1);
} else
{
numFIX += WEBRTC_SPL_RSHIFT_W32(tmp32no1, 1);
}
y32 = WEBRTC_SPL_DIV(numFIX, tmp32no1); // in Q14
if (limiterEnable && (i < limiterIdx))
{
tmp32 = WEBRTC_SPL_MUL_16_U16(i - 1, kLog10_2); // Q14
tmp32 -= WEBRTC_SPL_LSHIFT_W32(limiterLvl, 14); // Q14
y32 = WebRtcSpl_DivW32W16(tmp32 + 10, 20);
}
if (y32 > 39000)
{
tmp32 = WEBRTC_SPL_MUL(y32 >> 1, kLog10) + 4096; // in Q27
tmp32 = WEBRTC_SPL_RSHIFT_W32(tmp32, 13); // in Q14
} else
{
tmp32 = WEBRTC_SPL_MUL(y32, kLog10) + 8192; // in Q28
tmp32 = WEBRTC_SPL_RSHIFT_W32(tmp32, 14); // in Q14
}
tmp32 += WEBRTC_SPL_LSHIFT_W32(16, 14); // in Q14 (Make sure final output is in Q16)
// Calculate power
if (tmp32 > 0)
{
intPart = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp32, 14);
fracPart = (WebRtc_UWord16)(tmp32 & 0x00003FFF); // in Q14
if (WEBRTC_SPL_RSHIFT_W32(fracPart, 13))
{
tmp16 = WEBRTC_SPL_LSHIFT_W16(2, 14) - constLinApprox;
tmp32no2 = WEBRTC_SPL_LSHIFT_W32(1, 14) - fracPart;
tmp32no2 = WEBRTC_SPL_MUL_32_16(tmp32no2, tmp16);
tmp32no2 = WEBRTC_SPL_RSHIFT_W32(tmp32no2, 13);
tmp32no2 = WEBRTC_SPL_LSHIFT_W32(1, 14) - tmp32no2;
} else
{
tmp16 = constLinApprox - WEBRTC_SPL_LSHIFT_W16(1, 14);
tmp32no2 = WEBRTC_SPL_MUL_32_16(fracPart, tmp16);
tmp32no2 = WEBRTC_SPL_RSHIFT_W32(tmp32no2, 13);
}
fracPart = (WebRtc_UWord16)tmp32no2;
gainTable[i] = WEBRTC_SPL_LSHIFT_W32(1, intPart)
+ WEBRTC_SPL_SHIFT_W32(fracPart, intPart - 14);
} else
{
gainTable[i] = 0;
}
}
return 0;
}
WebRtc_Word32 WebRtcAgc_InitDigital(DigitalAgc_t *stt, WebRtc_Word16 agcMode)
{
if (agcMode == kAgcModeFixedDigital)
{
// start at minimum to find correct gain faster
stt->capacitorSlow = 0;
} else
{
// start out with 0 dB gain
stt->capacitorSlow = 134217728; // (WebRtc_Word32)(0.125f * 32768.0f * 32768.0f);
}
stt->capacitorFast = 0;
stt->gain = 65536;
stt->gatePrevious = 0;
stt->agcMode = agcMode;
#ifdef AGC_DEBUG
stt->frameCounter = 0;
#endif
// initialize VADs
WebRtcAgc_InitVad(&stt->vadNearend);
WebRtcAgc_InitVad(&stt->vadFarend);
return 0;
}
WebRtc_Word32 WebRtcAgc_AddFarendToDigital(DigitalAgc_t *stt, const WebRtc_Word16 *in_far,
WebRtc_Word16 nrSamples)
{
// Check for valid pointer
if (&stt->vadFarend == NULL)
{
return -1;
}
// VAD for far end
WebRtcAgc_ProcessVad(&stt->vadFarend, in_far, nrSamples);
return 0;
}
WebRtc_Word32 WebRtcAgc_ProcessDigital(DigitalAgc_t *stt, const WebRtc_Word16 *in_near,
const WebRtc_Word16 *in_near_H, WebRtc_Word16 *out,
WebRtc_Word16 *out_H, WebRtc_UWord32 FS,
WebRtc_Word16 lowlevelSignal)
{
// array for gains (one value per ms, incl start & end)
WebRtc_Word32 gains[11];
WebRtc_Word32 out_tmp, tmp32;
WebRtc_Word32 env[10];
WebRtc_Word32 nrg, max_nrg;
WebRtc_Word32 cur_level;
WebRtc_Word32 gain32, delta;
WebRtc_Word16 logratio;
WebRtc_Word16 lower_thr, upper_thr;
WebRtc_Word16 zeros, zeros_fast, frac;
WebRtc_Word16 decay;
WebRtc_Word16 gate, gain_adj;
WebRtc_Word16 k, n;
WebRtc_Word16 L, L2; // samples/subframe
// determine number of samples per ms
if (FS == 8000)
{
L = 8;
L2 = 3;
} else if (FS == 16000)
{
L = 16;
L2 = 4;
} else if (FS == 32000)
{
L = 16;
L2 = 4;
} else
{
return -1;
}
// TODO(andrew): again, we don't need input and output pointers...
if (in_near != out)
{
// Only needed if they don't already point to the same place.
memcpy(out, in_near, 10 * L * sizeof(WebRtc_Word16));
}
if (FS == 32000)
{
if (in_near_H != out_H)
{
memcpy(out_H, in_near_H, 10 * L * sizeof(WebRtc_Word16));
}
}
// VAD for near end
logratio = WebRtcAgc_ProcessVad(&stt->vadNearend, out, L * 10);
// Account for far end VAD
if (stt->vadFarend.counter > 10)
{
tmp32 = WEBRTC_SPL_MUL_16_16(3, logratio);
logratio = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp32 - stt->vadFarend.logRatio, 2);
}
// Determine decay factor depending on VAD
// upper_thr = 1.0f;
// lower_thr = 0.25f;
upper_thr = 1024; // Q10
lower_thr = 0; // Q10
if (logratio > upper_thr)
{
// decay = -2^17 / DecayTime; -> -65
decay = -65;
} else if (logratio < lower_thr)
{
decay = 0;
} else
{
// decay = (WebRtc_Word16)(((lower_thr - logratio)
// * (2^27/(DecayTime*(upper_thr-lower_thr)))) >> 10);
// SUBSTITUTED: 2^27/(DecayTime*(upper_thr-lower_thr)) -> 65
tmp32 = WEBRTC_SPL_MUL_16_16((lower_thr - logratio), 65);
decay = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp32, 10);
}
// adjust decay factor for long silence (detected as low standard deviation)
// This is only done in the adaptive modes
if (stt->agcMode != kAgcModeFixedDigital)
{
if (stt->vadNearend.stdLongTerm < 4000)
{
decay = 0;
} else if (stt->vadNearend.stdLongTerm < 8096)
{
// decay = (WebRtc_Word16)(((stt->vadNearend.stdLongTerm - 4000) * decay) >> 12);
tmp32 = WEBRTC_SPL_MUL_16_16((stt->vadNearend.stdLongTerm - 4000), decay);
decay = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp32, 12);
}
if (lowlevelSignal != 0)
{
decay = 0;
}
}
#ifdef AGC_DEBUG
stt->frameCounter++;
fprintf(stt->logFile, "%5.2f\t%d\t%d\t%d\t", (float)(stt->frameCounter) / 100, logratio, decay, stt->vadNearend.stdLongTerm);
#endif
// Find max amplitude per sub frame
// iterate over sub frames
for (k = 0; k < 10; k++)
{
// iterate over samples
max_nrg = 0;
for (n = 0; n < L; n++)
{
nrg = WEBRTC_SPL_MUL_16_16(out[k * L + n], out[k * L + n]);
if (nrg > max_nrg)
{
max_nrg = nrg;
}
}
env[k] = max_nrg;
}
// Calculate gain per sub frame
gains[0] = stt->gain;
for (k = 0; k < 10; k++)
{
// Fast envelope follower
// decay time = -131000 / -1000 = 131 (ms)
stt->capacitorFast = AGC_SCALEDIFF32(-1000, stt->capacitorFast, stt->capacitorFast);
if (env[k] > stt->capacitorFast)
{
stt->capacitorFast = env[k];
}
// Slow envelope follower
if (env[k] > stt->capacitorSlow)
{
// increase capacitorSlow
stt->capacitorSlow
= AGC_SCALEDIFF32(500, (env[k] - stt->capacitorSlow), stt->capacitorSlow);
} else
{
// decrease capacitorSlow
stt->capacitorSlow
= AGC_SCALEDIFF32(decay, stt->capacitorSlow, stt->capacitorSlow);
}
// use maximum of both capacitors as current level
if (stt->capacitorFast > stt->capacitorSlow)
{
cur_level = stt->capacitorFast;
} else
{
cur_level = stt->capacitorSlow;
}
// Translate signal level into gain, using a piecewise linear approximation
// find number of leading zeros
zeros = WebRtcSpl_NormU32((WebRtc_UWord32)cur_level);
if (cur_level == 0)
{
zeros = 31;
}
tmp32 = (WEBRTC_SPL_LSHIFT_W32(cur_level, zeros) & 0x7FFFFFFF);
frac = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp32, 19); // Q12
tmp32 = WEBRTC_SPL_MUL((stt->gainTable[zeros-1] - stt->gainTable[zeros]), frac);
gains[k + 1] = stt->gainTable[zeros] + WEBRTC_SPL_RSHIFT_W32(tmp32, 12);
#ifdef AGC_DEBUG
if (k == 0)
{
fprintf(stt->logFile, "%d\t%d\t%d\t%d\t%d\n", env[0], cur_level, stt->capacitorFast, stt->capacitorSlow, zeros);
}
#endif
}
// Gate processing (lower gain during absence of speech)
zeros = WEBRTC_SPL_LSHIFT_W16(zeros, 9) - WEBRTC_SPL_RSHIFT_W16(frac, 3);
// find number of leading zeros
zeros_fast = WebRtcSpl_NormU32((WebRtc_UWord32)stt->capacitorFast);
if (stt->capacitorFast == 0)
{
zeros_fast = 31;
}
tmp32 = (WEBRTC_SPL_LSHIFT_W32(stt->capacitorFast, zeros_fast) & 0x7FFFFFFF);
zeros_fast = WEBRTC_SPL_LSHIFT_W16(zeros_fast, 9);
zeros_fast -= (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp32, 22);
gate = 1000 + zeros_fast - zeros - stt->vadNearend.stdShortTerm;
if (gate < 0)
{
stt->gatePrevious = 0;
} else
{
tmp32 = WEBRTC_SPL_MUL_16_16(stt->gatePrevious, 7);
gate = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32((WebRtc_Word32)gate + tmp32, 3);
stt->gatePrevious = gate;
}
// gate < 0 -> no gate
// gate > 2500 -> max gate
if (gate > 0)
{
if (gate < 2500)
{
gain_adj = WEBRTC_SPL_RSHIFT_W16(2500 - gate, 5);
} else
{
gain_adj = 0;
}
for (k = 0; k < 10; k++)
{
if ((gains[k + 1] - stt->gainTable[0]) > 8388608)
{
// To prevent wraparound
tmp32 = WEBRTC_SPL_RSHIFT_W32((gains[k+1] - stt->gainTable[0]), 8);
tmp32 = WEBRTC_SPL_MUL(tmp32, (178 + gain_adj));
} else
{
tmp32 = WEBRTC_SPL_MUL((gains[k+1] - stt->gainTable[0]), (178 + gain_adj));
tmp32 = WEBRTC_SPL_RSHIFT_W32(tmp32, 8);
}
gains[k + 1] = stt->gainTable[0] + tmp32;
}
}
// Limit gain to avoid overload distortion
for (k = 0; k < 10; k++)
{
// To prevent wrap around
zeros = 10;
if (gains[k + 1] > 47453132)
{
zeros = 16 - WebRtcSpl_NormW32(gains[k + 1]);
}
gain32 = WEBRTC_SPL_RSHIFT_W32(gains[k+1], zeros) + 1;
gain32 = WEBRTC_SPL_MUL(gain32, gain32);
// check for overflow
while (AGC_MUL32(WEBRTC_SPL_RSHIFT_W32(env[k], 12) + 1, gain32)
> WEBRTC_SPL_SHIFT_W32((WebRtc_Word32)32767, 2 * (1 - zeros + 10)))
{
// multiply by 253/256 ==> -0.1 dB
if (gains[k + 1] > 8388607)
{
// Prevent wrap around
gains[k + 1] = WEBRTC_SPL_MUL(WEBRTC_SPL_RSHIFT_W32(gains[k+1], 8), 253);
} else
{
gains[k + 1] = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(gains[k+1], 253), 8);
}
gain32 = WEBRTC_SPL_RSHIFT_W32(gains[k+1], zeros) + 1;
gain32 = WEBRTC_SPL_MUL(gain32, gain32);
}
}
// gain reductions should be done 1 ms earlier than gain increases
for (k = 1; k < 10; k++)
{
if (gains[k] > gains[k + 1])
{
gains[k] = gains[k + 1];
}
}
// save start gain for next frame
stt->gain = gains[10];
// Apply gain
// handle first sub frame separately
delta = WEBRTC_SPL_LSHIFT_W32(gains[1] - gains[0], (4 - L2));
gain32 = WEBRTC_SPL_LSHIFT_W32(gains[0], 4);
// iterate over samples
for (n = 0; n < L; n++)
{
// For lower band
tmp32 = WEBRTC_SPL_MUL((WebRtc_Word32)out[n], WEBRTC_SPL_RSHIFT_W32(gain32 + 127, 7));
out_tmp = WEBRTC_SPL_RSHIFT_W32(tmp32 , 16);
if (out_tmp > 4095)
{
out[n] = (WebRtc_Word16)32767;
} else if (out_tmp < -4096)
{
out[n] = (WebRtc_Word16)-32768;
} else
{
tmp32 = WEBRTC_SPL_MUL((WebRtc_Word32)out[n], WEBRTC_SPL_RSHIFT_W32(gain32, 4));
out[n] = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp32 , 16);
}
// For higher band
if (FS == 32000)
{
tmp32 = WEBRTC_SPL_MUL((WebRtc_Word32)out_H[n],
WEBRTC_SPL_RSHIFT_W32(gain32 + 127, 7));
out_tmp = WEBRTC_SPL_RSHIFT_W32(tmp32 , 16);
if (out_tmp > 4095)
{
out_H[n] = (WebRtc_Word16)32767;
} else if (out_tmp < -4096)
{
out_H[n] = (WebRtc_Word16)-32768;
} else
{
tmp32 = WEBRTC_SPL_MUL((WebRtc_Word32)out_H[n],
WEBRTC_SPL_RSHIFT_W32(gain32, 4));
out_H[n] = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp32 , 16);
}
}
//
gain32 += delta;
}
// iterate over subframes
for (k = 1; k < 10; k++)
{
delta = WEBRTC_SPL_LSHIFT_W32(gains[k+1] - gains[k], (4 - L2));
gain32 = WEBRTC_SPL_LSHIFT_W32(gains[k], 4);
// iterate over samples
for (n = 0; n < L; n++)
{
// For lower band
tmp32 = WEBRTC_SPL_MUL((WebRtc_Word32)out[k * L + n],
WEBRTC_SPL_RSHIFT_W32(gain32, 4));
out[k * L + n] = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp32 , 16);
// For higher band
if (FS == 32000)
{
tmp32 = WEBRTC_SPL_MUL((WebRtc_Word32)out_H[k * L + n],
WEBRTC_SPL_RSHIFT_W32(gain32, 4));
out_H[k * L + n] = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp32 , 16);
}
gain32 += delta;
}
}
return 0;
}
void WebRtcAgc_InitVad(AgcVad_t *state)
{
WebRtc_Word16 k;
state->HPstate = 0; // state of high pass filter
state->logRatio = 0; // log( P(active) / P(inactive) )
// average input level (Q10)
state->meanLongTerm = WEBRTC_SPL_LSHIFT_W16(15, 10);
// variance of input level (Q8)
state->varianceLongTerm = WEBRTC_SPL_LSHIFT_W32(500, 8);
state->stdLongTerm = 0; // standard deviation of input level in dB
// short-term average input level (Q10)
state->meanShortTerm = WEBRTC_SPL_LSHIFT_W16(15, 10);
// short-term variance of input level (Q8)
state->varianceShortTerm = WEBRTC_SPL_LSHIFT_W32(500, 8);
state->stdShortTerm = 0; // short-term standard deviation of input level in dB
state->counter = 3; // counts updates
for (k = 0; k < 8; k++)
{
// downsampling filter
state->downState[k] = 0;
}
}
WebRtc_Word16 WebRtcAgc_ProcessVad(AgcVad_t *state, // (i) VAD state
const WebRtc_Word16 *in, // (i) Speech signal
WebRtc_Word16 nrSamples) // (i) number of samples
{
WebRtc_Word32 out, nrg, tmp32, tmp32b;
WebRtc_UWord16 tmpU16;
WebRtc_Word16 k, subfr, tmp16;
WebRtc_Word16 buf1[8];
WebRtc_Word16 buf2[4];
WebRtc_Word16 HPstate;
WebRtc_Word16 zeros, dB;
// process in 10 sub frames of 1 ms (to save on memory)
nrg = 0;
HPstate = state->HPstate;
for (subfr = 0; subfr < 10; subfr++)
{
// downsample to 4 kHz
if (nrSamples == 160)
{
for (k = 0; k < 8; k++)
{
tmp32 = (WebRtc_Word32)in[2 * k] + (WebRtc_Word32)in[2 * k + 1];
tmp32 = WEBRTC_SPL_RSHIFT_W32(tmp32, 1);
buf1[k] = (WebRtc_Word16)tmp32;
}
in += 16;
WebRtcSpl_DownsampleBy2(buf1, 8, buf2, state->downState);
} else
{
WebRtcSpl_DownsampleBy2(in, 8, buf2, state->downState);
in += 8;
}
// high pass filter and compute energy
for (k = 0; k < 4; k++)
{
out = buf2[k] + HPstate;
tmp32 = WEBRTC_SPL_MUL(600, out);
HPstate = (WebRtc_Word16)(WEBRTC_SPL_RSHIFT_W32(tmp32, 10) - buf2[k]);
tmp32 = WEBRTC_SPL_MUL(out, out);
nrg += WEBRTC_SPL_RSHIFT_W32(tmp32, 6);
}
}
state->HPstate = HPstate;
// find number of leading zeros
if (!(0xFFFF0000 & nrg))
{
zeros = 16;
} else
{
zeros = 0;
}
if (!(0xFF000000 & (nrg << zeros)))
{
zeros += 8;
}
if (!(0xF0000000 & (nrg << zeros)))
{
zeros += 4;
}
if (!(0xC0000000 & (nrg << zeros)))
{
zeros += 2;
}
if (!(0x80000000 & (nrg << zeros)))
{
zeros += 1;
}
// energy level (range {-32..30}) (Q10)
dB = WEBRTC_SPL_LSHIFT_W16(15 - zeros, 11);
// Update statistics
if (state->counter < kAvgDecayTime)
{
// decay time = AvgDecTime * 10 ms
state->counter++;
}
// update short-term estimate of mean energy level (Q10)
tmp32 = (WEBRTC_SPL_MUL_16_16(state->meanShortTerm, 15) + (WebRtc_Word32)dB);
state->meanShortTerm = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp32, 4);
// update short-term estimate of variance in energy level (Q8)
tmp32 = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_16(dB, dB), 12);
tmp32 += WEBRTC_SPL_MUL(state->varianceShortTerm, 15);
state->varianceShortTerm = WEBRTC_SPL_RSHIFT_W32(tmp32, 4);
// update short-term estimate of standard deviation in energy level (Q10)
tmp32 = WEBRTC_SPL_MUL_16_16(state->meanShortTerm, state->meanShortTerm);
tmp32 = WEBRTC_SPL_LSHIFT_W32(state->varianceShortTerm, 12) - tmp32;
state->stdShortTerm = (WebRtc_Word16)WebRtcSpl_Sqrt(tmp32);
// update long-term estimate of mean energy level (Q10)
tmp32 = WEBRTC_SPL_MUL_16_16(state->meanLongTerm, state->counter) + (WebRtc_Word32)dB;
state->meanLongTerm = WebRtcSpl_DivW32W16ResW16(tmp32,
WEBRTC_SPL_ADD_SAT_W16(state->counter, 1));
// update long-term estimate of variance in energy level (Q8)
tmp32 = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_16(dB, dB), 12);
tmp32 += WEBRTC_SPL_MUL(state->varianceLongTerm, state->counter);
state->varianceLongTerm = WebRtcSpl_DivW32W16(tmp32,
WEBRTC_SPL_ADD_SAT_W16(state->counter, 1));
// update long-term estimate of standard deviation in energy level (Q10)
tmp32 = WEBRTC_SPL_MUL_16_16(state->meanLongTerm, state->meanLongTerm);
tmp32 = WEBRTC_SPL_LSHIFT_W32(state->varianceLongTerm, 12) - tmp32;
state->stdLongTerm = (WebRtc_Word16)WebRtcSpl_Sqrt(tmp32);
// update voice activity measure (Q10)
tmp16 = WEBRTC_SPL_LSHIFT_W16(3, 12);
tmp32 = WEBRTC_SPL_MUL_16_16(tmp16, (dB - state->meanLongTerm));
tmp32 = WebRtcSpl_DivW32W16(tmp32, state->stdLongTerm);
tmpU16 = WEBRTC_SPL_LSHIFT_U16((WebRtc_UWord16)13, 12);
tmp32b = WEBRTC_SPL_MUL_16_U16(state->logRatio, tmpU16);
tmp32 += WEBRTC_SPL_RSHIFT_W32(tmp32b, 10);
state->logRatio = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp32, 6);
// limit
if (state->logRatio > 2048)
{
state->logRatio = 2048;
}
if (state->logRatio < -2048)
{
state->logRatio = -2048;
}
return state->logRatio; // Q10
}

View File

@ -0,0 +1,76 @@
/*
* 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.
*/
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AGC_MAIN_SOURCE_DIGITAL_AGC_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_AGC_MAIN_SOURCE_DIGITAL_AGC_H_
#ifdef AGC_DEBUG
#include <stdio.h>
#endif
#include "typedefs.h"
#include "signal_processing_library.h"
// the 32 most significant bits of A(19) * B(26) >> 13
#define AGC_MUL32(A, B) (((B)>>13)*(A) + ( ((0x00001FFF & (B))*(A)) >> 13 ))
// C + the 32 most significant bits of A * B
#define AGC_SCALEDIFF32(A, B, C) ((C) + ((B)>>16)*(A) + ( ((0x0000FFFF & (B))*(A)) >> 16 ))
typedef struct
{
WebRtc_Word32 downState[8];
WebRtc_Word16 HPstate;
WebRtc_Word16 counter;
WebRtc_Word16 logRatio; // log( P(active) / P(inactive) ) (Q10)
WebRtc_Word16 meanLongTerm; // Q10
WebRtc_Word32 varianceLongTerm; // Q8
WebRtc_Word16 stdLongTerm; // Q10
WebRtc_Word16 meanShortTerm; // Q10
WebRtc_Word32 varianceShortTerm; // Q8
WebRtc_Word16 stdShortTerm; // Q10
} AgcVad_t; // total = 54 bytes
typedef struct
{
WebRtc_Word32 capacitorSlow;
WebRtc_Word32 capacitorFast;
WebRtc_Word32 gain;
WebRtc_Word32 gainTable[32];
WebRtc_Word16 gatePrevious;
WebRtc_Word16 agcMode;
AgcVad_t vadNearend;
AgcVad_t vadFarend;
#ifdef AGC_DEBUG
FILE* logFile;
int frameCounter;
#endif
} DigitalAgc_t;
WebRtc_Word32 WebRtcAgc_InitDigital(DigitalAgc_t *digitalAgcInst, WebRtc_Word16 agcMode);
WebRtc_Word32 WebRtcAgc_ProcessDigital(DigitalAgc_t *digitalAgcInst, const WebRtc_Word16 *inNear,
const WebRtc_Word16 *inNear_H, WebRtc_Word16 *out,
WebRtc_Word16 *out_H, WebRtc_UWord32 FS,
WebRtc_Word16 lowLevelSignal);
WebRtc_Word32 WebRtcAgc_AddFarendToDigital(DigitalAgc_t *digitalAgcInst, const WebRtc_Word16 *inFar,
WebRtc_Word16 nrSamples);
void WebRtcAgc_InitVad(AgcVad_t *vadInst);
WebRtc_Word16 WebRtcAgc_ProcessVad(AgcVad_t *vadInst, // (i) VAD state
const WebRtc_Word16 *in, // (i) Speech signal
WebRtc_Word16 nrSamples); // (i) number of samples
WebRtc_Word32 WebRtcAgc_CalculateGainTable(WebRtc_Word32 *gainTable, // Q16
WebRtc_Word16 compressionGaindB, // Q0 (in dB)
WebRtc_Word16 targetLevelDbfs,// Q0 (in dB)
WebRtc_UWord8 limiterEnable, WebRtc_Word16 analogTarget);
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AGC_MAIN_SOURCE_ANALOG_AGC_H_

View File

@ -0,0 +1,273 @@
/*
* 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.
*/
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AGC_MAIN_INTERFACE_GAIN_CONTROL_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_AGC_MAIN_INTERFACE_GAIN_CONTROL_H_
#include "typedefs.h"
// Errors
#define AGC_UNSPECIFIED_ERROR 18000
#define AGC_UNSUPPORTED_FUNCTION_ERROR 18001
#define AGC_UNINITIALIZED_ERROR 18002
#define AGC_NULL_POINTER_ERROR 18003
#define AGC_BAD_PARAMETER_ERROR 18004
// Warnings
#define AGC_BAD_PARAMETER_WARNING 18050
enum
{
kAgcModeUnchanged,
kAgcModeAdaptiveAnalog,
kAgcModeAdaptiveDigital,
kAgcModeFixedDigital
};
enum
{
kAgcFalse = 0,
kAgcTrue
};
typedef struct
{
WebRtc_Word16 targetLevelDbfs; // default 3 (-3 dBOv)
WebRtc_Word16 compressionGaindB; // default 9 dB
WebRtc_UWord8 limiterEnable; // default kAgcTrue (on)
} WebRtcAgc_config_t;
#if defined(__cplusplus)
extern "C"
{
#endif
/*
* This function processes a 10/20ms frame of far-end speech to determine
* if there is active speech. Far-end speech length can be either 10ms or
* 20ms. The length of the input speech vector must be given in samples
* (80/160 when FS=8000, and 160/320 when FS=16000 or FS=32000).
*
* Input:
* - agcInst : AGC instance.
* - inFar : Far-end input speech vector (10 or 20ms)
* - samples : Number of samples in input vector
*
* Return value:
* : 0 - Normal operation.
* : -1 - Error
*/
int WebRtcAgc_AddFarend(void* agcInst,
const WebRtc_Word16* inFar,
WebRtc_Word16 samples);
/*
* This function processes a 10/20ms frame of microphone speech to determine
* if there is active speech. Microphone speech length can be either 10ms or
* 20ms. The length of the input speech vector must be given in samples
* (80/160 when FS=8000, and 160/320 when FS=16000 or FS=32000). For very low
* input levels, the input signal is increased in level by multiplying and
* overwriting the samples in inMic[].
*
* This function should be called before any further processing of the
* near-end microphone signal.
*
* Input:
* - agcInst : AGC instance.
* - inMic : Microphone input speech vector (10 or 20 ms) for
* L band
* - inMic_H : Microphone input speech vector (10 or 20 ms) for
* H band
* - samples : Number of samples in input vector
*
* Return value:
* : 0 - Normal operation.
* : -1 - Error
*/
int WebRtcAgc_AddMic(void* agcInst,
WebRtc_Word16* inMic,
WebRtc_Word16* inMic_H,
WebRtc_Word16 samples);
/*
* This function replaces the analog microphone with a virtual one.
* It is a digital gain applied to the input signal and is used in the
* agcAdaptiveDigital mode where no microphone level is adjustable.
* Microphone speech length can be either 10ms or 20ms. The length of the
* input speech vector must be given in samples (80/160 when FS=8000, and
* 160/320 when FS=16000 or FS=32000).
*
* Input:
* - agcInst : AGC instance.
* - inMic : Microphone input speech vector for (10 or 20 ms)
* L band
* - inMic_H : Microphone input speech vector for (10 or 20 ms)
* H band
* - samples : Number of samples in input vector
* - micLevelIn : Input level of microphone (static)
*
* Output:
* - inMic : Microphone output after processing (L band)
* - inMic_H : Microphone output after processing (H band)
* - micLevelOut : Adjusted microphone level after processing
*
* Return value:
* : 0 - Normal operation.
* : -1 - Error
*/
int WebRtcAgc_VirtualMic(void* agcInst,
WebRtc_Word16* inMic,
WebRtc_Word16* inMic_H,
WebRtc_Word16 samples,
WebRtc_Word32 micLevelIn,
WebRtc_Word32* micLevelOut);
/*
* This function processes a 10/20ms frame and adjusts (normalizes) the gain
* both analog and digitally. The gain adjustments are done only during
* active periods of speech. The input speech length can be either 10ms or
* 20ms and the output is of the same length. The length of the speech
* vectors must be given in samples (80/160 when FS=8000, and 160/320 when
* FS=16000 or FS=32000). The echo parameter can be used to ensure the AGC will
* not adjust upward in the presence of echo.
*
* This function should be called after processing the near-end microphone
* signal, in any case after any echo cancellation.
*
* Input:
* - agcInst : AGC instance
* - inNear : Near-end input speech vector (10 or 20 ms) for
* L band
* - inNear_H : Near-end input speech vector (10 or 20 ms) for
* H band
* - samples : Number of samples in input/output vector
* - inMicLevel : Current microphone volume level
* - echo : Set to 0 if the signal passed to add_mic is
* almost certainly free of echo; otherwise set
* to 1. If you have no information regarding echo
* set to 0.
*
* Output:
* - outMicLevel : Adjusted microphone volume level
* - out : Gain-adjusted near-end speech vector (L band)
* : May be the same vector as the input.
* - out_H : Gain-adjusted near-end speech vector (H band)
* - saturationWarning : A returned value of 1 indicates a saturation event
* has occurred and the volume cannot be further
* reduced. Otherwise will be set to 0.
*
* Return value:
* : 0 - Normal operation.
* : -1 - Error
*/
int WebRtcAgc_Process(void* agcInst,
const WebRtc_Word16* inNear,
const WebRtc_Word16* inNear_H,
WebRtc_Word16 samples,
WebRtc_Word16* out,
WebRtc_Word16* out_H,
WebRtc_Word32 inMicLevel,
WebRtc_Word32* outMicLevel,
WebRtc_Word16 echo,
WebRtc_UWord8* saturationWarning);
/*
* This function sets the config parameters (targetLevelDbfs,
* compressionGaindB and limiterEnable).
*
* Input:
* - agcInst : AGC instance
* - config : config struct
*
* Output:
*
* Return value:
* : 0 - Normal operation.
* : -1 - Error
*/
int WebRtcAgc_set_config(void* agcInst, WebRtcAgc_config_t config);
/*
* This function returns the config parameters (targetLevelDbfs,
* compressionGaindB and limiterEnable).
*
* Input:
* - agcInst : AGC instance
*
* Output:
* - config : config struct
*
* Return value:
* : 0 - Normal operation.
* : -1 - Error
*/
int WebRtcAgc_get_config(void* agcInst, WebRtcAgc_config_t* config);
/*
* This function creates an AGC instance, which will contain the state
* information for one (duplex) channel.
*
* Return value : AGC instance if successful
* : 0 (i.e., a NULL pointer) if unsuccessful
*/
int WebRtcAgc_Create(void **agcInst);
/*
* This function frees the AGC instance created at the beginning.
*
* Input:
* - agcInst : AGC instance.
*
* Return value : 0 - Ok
* -1 - Error
*/
int WebRtcAgc_Free(void *agcInst);
/*
* This function initializes an AGC instance.
*
* Input:
* - agcInst : AGC instance.
* - minLevel : Minimum possible mic level
* - maxLevel : Maximum possible mic level
* - agcMode : 0 - Unchanged
* : 1 - Adaptive Analog Automatic Gain Control -3dBOv
* : 2 - Adaptive Digital Automatic Gain Control -3dBOv
* : 3 - Fixed Digital Gain 0dB
* - fs : Sampling frequency
*
* Return value : 0 - Ok
* -1 - Error
*/
int WebRtcAgc_Init(void *agcInst,
WebRtc_Word32 minLevel,
WebRtc_Word32 maxLevel,
WebRtc_Word16 agcMode,
WebRtc_UWord32 fs);
/*
* This function returns a text string containing the version.
*
* Input:
* - length : Length of the char array pointed to by version
* Output:
* - version : Pointer to a char array of to which the version
* : string will be copied.
*
* Return value : 0 - OK
* -1 - Error
*/
int WebRtcAgc_Version(WebRtc_Word8 *versionStr, WebRtc_Word16 length);
#if defined(__cplusplus)
}
#endif
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AGC_MAIN_INTERFACE_GAIN_CONTROL_H_

View File

@ -0,0 +1,100 @@
# 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.
{
'variables': {
'protoc_out_dir': '<(SHARED_INTERMEDIATE_DIR)/protoc_out',
'protoc_out_relpath': 'webrtc/audio_processing',
},
'targets': [
{
'target_name': 'audioproc_unittest',
'type': 'executable',
'conditions': [
['prefer_fixed_point==1', {
'defines': ['WEBRTC_APM_UNIT_TEST_FIXED_PROFILE'],
}, {
'defines': ['WEBRTC_APM_UNIT_TEST_FLOAT_PROFILE'],
}],
],
'dependencies': [
'audioproc_unittest_proto',
'audio_processing',
'<(webrtc_root)/common_audio/common_audio.gyp:spl',
'<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers',
'<(webrtc_root)/../test/test.gyp:test_support',
'<(webrtc_root)/../testing/gtest.gyp:gtest',
'<(webrtc_root)/../third_party/protobuf/protobuf.gyp:protobuf_lite',
],
'include_dirs': [
'<(webrtc_root)/../testing/gtest/include',
'<(protoc_out_dir)',
],
'sources': [
'test/unit_test.cc',
'<(protoc_out_dir)/<(protoc_out_relpath)/unittest.pb.cc',
'<(protoc_out_dir)/<(protoc_out_relpath)/unittest.pb.h',
],
},
{
# Protobuf compiler / generate rule for audioproc_unittest
'target_name': 'audioproc_unittest_proto',
'type': 'none',
'variables': {
'proto_relpath':
'<(webrtc_root)/modules/audio_processing/test',
},
'sources': [
'<(proto_relpath)/unittest.proto',
],
'rules': [
{
'rule_name': 'genproto',
'extension': 'proto',
'inputs': [
'<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)protoc<(EXECUTABLE_SUFFIX)',
],
'outputs': [
'<(protoc_out_dir)/<(protoc_out_relpath)/<(RULE_INPUT_ROOT).pb.cc',
'<(protoc_out_dir)/<(RULE_INPUT_ROOT).pb.h',
],
'action': [
'<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)protoc<(EXECUTABLE_SUFFIX)',
'--proto_path=<(proto_relpath)',
'<(proto_relpath)/<(RULE_INPUT_NAME)',
'--cpp_out=<(protoc_out_dir)/<(protoc_out_relpath)',
],
'message': 'Generating C++ code from <(RULE_INPUT_PATH)',
},
],
'dependencies': [
'<(webrtc_root)/../third_party/protobuf/protobuf.gyp:protoc#host',
],
# This target exports a hard dependency because it generates header
# files.
'hard_dependency': 1,
},
{
'target_name': 'audioproc_process_test',
'type': 'executable',
'dependencies': [
'audio_processing',
'<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers',
'<(webrtc_root)/../testing/gtest.gyp:gtest',
'<(webrtc_root)/../third_party/protobuf/protobuf.gyp:protobuf_lite',
],
'include_dirs': [
'<(webrtc_root)/../testing/gtest/include',
'<(protoc_out_dir)',
],
'sources': [
'test/process_test.cc',
],
},
],
}

View File

@ -0,0 +1,288 @@
/*
* 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.
*/
#include "audio_buffer.h"
namespace webrtc {
namespace {
enum {
kSamplesPer8kHzChannel = 80,
kSamplesPer16kHzChannel = 160,
kSamplesPer32kHzChannel = 320
};
void StereoToMono(const WebRtc_Word16* left, const WebRtc_Word16* right,
WebRtc_Word16* out, int samples_per_channel) {
WebRtc_Word32 data_int32 = 0;
for (int i = 0; i < samples_per_channel; i++) {
data_int32 = (left[i] + right[i]) >> 1;
if (data_int32 > 32767) {
data_int32 = 32767;
} else if (data_int32 < -32768) {
data_int32 = -32768;
}
out[i] = static_cast<WebRtc_Word16>(data_int32);
}
}
} // namespace
struct AudioChannel {
AudioChannel() {
memset(data, 0, sizeof(data));
}
WebRtc_Word16 data[kSamplesPer32kHzChannel];
};
struct SplitAudioChannel {
SplitAudioChannel() {
memset(low_pass_data, 0, sizeof(low_pass_data));
memset(high_pass_data, 0, sizeof(high_pass_data));
memset(analysis_filter_state1, 0, sizeof(analysis_filter_state1));
memset(analysis_filter_state2, 0, sizeof(analysis_filter_state2));
memset(synthesis_filter_state1, 0, sizeof(synthesis_filter_state1));
memset(synthesis_filter_state2, 0, sizeof(synthesis_filter_state2));
}
WebRtc_Word16 low_pass_data[kSamplesPer16kHzChannel];
WebRtc_Word16 high_pass_data[kSamplesPer16kHzChannel];
WebRtc_Word32 analysis_filter_state1[6];
WebRtc_Word32 analysis_filter_state2[6];
WebRtc_Word32 synthesis_filter_state1[6];
WebRtc_Word32 synthesis_filter_state2[6];
};
// TODO(andrew): check range of input parameters?
AudioBuffer::AudioBuffer(int max_num_channels,
int samples_per_channel)
: max_num_channels_(max_num_channels),
num_channels_(0),
num_mixed_channels_(0),
num_mixed_low_pass_channels_(0),
samples_per_channel_(samples_per_channel),
samples_per_split_channel_(samples_per_channel),
reference_copied_(false),
activity_(AudioFrame::kVadUnknown),
data_(NULL),
channels_(NULL),
split_channels_(NULL),
mixed_low_pass_channels_(NULL),
low_pass_reference_channels_(NULL) {
if (max_num_channels_ > 1) {
channels_ = new AudioChannel[max_num_channels_];
mixed_low_pass_channels_ = new AudioChannel[max_num_channels_];
}
low_pass_reference_channels_ = new AudioChannel[max_num_channels_];
if (samples_per_channel_ == kSamplesPer32kHzChannel) {
split_channels_ = new SplitAudioChannel[max_num_channels_];
samples_per_split_channel_ = kSamplesPer16kHzChannel;
}
}
AudioBuffer::~AudioBuffer() {
if (channels_ != NULL) {
delete [] channels_;
}
if (mixed_low_pass_channels_ != NULL) {
delete [] mixed_low_pass_channels_;
}
if (low_pass_reference_channels_ != NULL) {
delete [] low_pass_reference_channels_;
}
if (split_channels_ != NULL) {
delete [] split_channels_;
}
}
WebRtc_Word16* AudioBuffer::data(int channel) const {
assert(channel >= 0 && channel < num_channels_);
if (data_ != NULL) {
return data_;
}
return channels_[channel].data;
}
WebRtc_Word16* AudioBuffer::low_pass_split_data(int channel) const {
assert(channel >= 0 && channel < num_channels_);
if (split_channels_ == NULL) {
return data(channel);
}
return split_channels_[channel].low_pass_data;
}
WebRtc_Word16* AudioBuffer::high_pass_split_data(int channel) const {
assert(channel >= 0 && channel < num_channels_);
if (split_channels_ == NULL) {
return NULL;
}
return split_channels_[channel].high_pass_data;
}
WebRtc_Word16* AudioBuffer::mixed_low_pass_data(int channel) const {
assert(channel >= 0 && channel < num_mixed_low_pass_channels_);
return mixed_low_pass_channels_[channel].data;
}
WebRtc_Word16* AudioBuffer::low_pass_reference(int channel) const {
assert(channel >= 0 && channel < num_channels_);
if (!reference_copied_) {
return NULL;
}
return low_pass_reference_channels_[channel].data;
}
WebRtc_Word32* AudioBuffer::analysis_filter_state1(int channel) const {
assert(channel >= 0 && channel < num_channels_);
return split_channels_[channel].analysis_filter_state1;
}
WebRtc_Word32* AudioBuffer::analysis_filter_state2(int channel) const {
assert(channel >= 0 && channel < num_channels_);
return split_channels_[channel].analysis_filter_state2;
}
WebRtc_Word32* AudioBuffer::synthesis_filter_state1(int channel) const {
assert(channel >= 0 && channel < num_channels_);
return split_channels_[channel].synthesis_filter_state1;
}
WebRtc_Word32* AudioBuffer::synthesis_filter_state2(int channel) const {
assert(channel >= 0 && channel < num_channels_);
return split_channels_[channel].synthesis_filter_state2;
}
void AudioBuffer::set_activity(AudioFrame::VADActivity activity) {
activity_ = activity;
}
AudioFrame::VADActivity AudioBuffer::activity() {
return activity_;
}
int AudioBuffer::num_channels() const {
return num_channels_;
}
int AudioBuffer::samples_per_channel() const {
return samples_per_channel_;
}
int AudioBuffer::samples_per_split_channel() const {
return samples_per_split_channel_;
}
// TODO(andrew): Do deinterleaving and mixing in one step?
void AudioBuffer::DeinterleaveFrom(AudioFrame* frame) {
assert(frame->_audioChannel <= max_num_channels_);
assert(frame->_payloadDataLengthInSamples == samples_per_channel_);
num_channels_ = frame->_audioChannel;
num_mixed_channels_ = 0;
num_mixed_low_pass_channels_ = 0;
reference_copied_ = false;
activity_ = frame->_vadActivity;
if (num_channels_ == 1) {
// We can get away with a pointer assignment in this case.
data_ = frame->_payloadData;
return;
}
WebRtc_Word16* interleaved = frame->_payloadData;
for (int i = 0; i < num_channels_; i++) {
WebRtc_Word16* deinterleaved = channels_[i].data;
int interleaved_idx = i;
for (int j = 0; j < samples_per_channel_; j++) {
deinterleaved[j] = interleaved[interleaved_idx];
interleaved_idx += num_channels_;
}
}
}
void AudioBuffer::InterleaveTo(AudioFrame* frame) const {
assert(frame->_audioChannel == num_channels_);
assert(frame->_payloadDataLengthInSamples == samples_per_channel_);
frame->_vadActivity = activity_;
if (num_channels_ == 1) {
if (num_mixed_channels_ == 1) {
memcpy(frame->_payloadData,
channels_[0].data,
sizeof(WebRtc_Word16) * samples_per_channel_);
} else {
// These should point to the same buffer in this case.
assert(data_ == frame->_payloadData);
}
return;
}
WebRtc_Word16* interleaved = frame->_payloadData;
for (int i = 0; i < num_channels_; i++) {
WebRtc_Word16* deinterleaved = channels_[i].data;
int interleaved_idx = i;
for (int j = 0; j < samples_per_channel_; j++) {
interleaved[interleaved_idx] = deinterleaved[j];
interleaved_idx += num_channels_;
}
}
}
// TODO(andrew): would be good to support the no-mix case with pointer
// assignment.
// TODO(andrew): handle mixing to multiple channels?
void AudioBuffer::Mix(int num_mixed_channels) {
// We currently only support the stereo to mono case.
assert(num_channels_ == 2);
assert(num_mixed_channels == 1);
StereoToMono(channels_[0].data,
channels_[1].data,
channels_[0].data,
samples_per_channel_);
num_channels_ = num_mixed_channels;
num_mixed_channels_ = num_mixed_channels;
}
void AudioBuffer::CopyAndMixLowPass(int num_mixed_channels) {
// We currently only support the stereo to mono case.
assert(num_channels_ == 2);
assert(num_mixed_channels == 1);
StereoToMono(low_pass_split_data(0),
low_pass_split_data(1),
mixed_low_pass_channels_[0].data,
samples_per_split_channel_);
num_mixed_low_pass_channels_ = num_mixed_channels;
}
void AudioBuffer::CopyLowPassToReference() {
reference_copied_ = true;
for (int i = 0; i < num_channels_; i++) {
memcpy(low_pass_reference_channels_[i].data,
low_pass_split_data(i),
sizeof(WebRtc_Word16) * samples_per_split_channel_);
}
}
} // namespace webrtc

View File

@ -0,0 +1,71 @@
/*
* 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.
*/
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_AUDIO_BUFFER_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_AUDIO_BUFFER_H_
#include "module_common_types.h"
#include "typedefs.h"
namespace webrtc {
struct AudioChannel;
struct SplitAudioChannel;
class AudioBuffer {
public:
AudioBuffer(int max_num_channels, int samples_per_channel);
virtual ~AudioBuffer();
int num_channels() const;
int samples_per_channel() const;
int samples_per_split_channel() const;
WebRtc_Word16* data(int channel) const;
WebRtc_Word16* low_pass_split_data(int channel) const;
WebRtc_Word16* high_pass_split_data(int channel) const;
WebRtc_Word16* mixed_low_pass_data(int channel) const;
WebRtc_Word16* low_pass_reference(int channel) const;
WebRtc_Word32* analysis_filter_state1(int channel) const;
WebRtc_Word32* analysis_filter_state2(int channel) const;
WebRtc_Word32* synthesis_filter_state1(int channel) const;
WebRtc_Word32* synthesis_filter_state2(int channel) const;
void set_activity(AudioFrame::VADActivity activity);
AudioFrame::VADActivity activity();
void DeinterleaveFrom(AudioFrame* audioFrame);
void InterleaveTo(AudioFrame* audioFrame) const;
void Mix(int num_mixed_channels);
void CopyAndMixLowPass(int num_mixed_channels);
void CopyLowPassToReference();
private:
const int max_num_channels_;
int num_channels_;
int num_mixed_channels_;
int num_mixed_low_pass_channels_;
const int samples_per_channel_;
int samples_per_split_channel_;
bool reference_copied_;
AudioFrame::VADActivity activity_;
WebRtc_Word16* data_;
// TODO(andrew): use vectors here.
AudioChannel* channels_;
SplitAudioChannel* split_channels_;
// TODO(andrew): improve this, we don't need the full 32 kHz space here.
AudioChannel* mixed_low_pass_channels_;
AudioChannel* low_pass_reference_channels_;
};
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_AUDIO_BUFFER_H_

View File

@ -0,0 +1,130 @@
# 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.
{
'variables': {
'protoc_out_dir': '<(SHARED_INTERMEDIATE_DIR)/protoc_out',
'protoc_out_relpath': 'webrtc/audio_processing',
},
'targets': [
{
'target_name': 'audio_processing',
'type': '<(library)',
'conditions': [
['prefer_fixed_point==1', {
'dependencies': ['ns_fix'],
'defines': ['WEBRTC_NS_FIXED'],
}, {
'dependencies': ['ns'],
'defines': ['WEBRTC_NS_FLOAT'],
}],
['build_with_chromium==1', {
'dependencies': [
'<(webrtc_root)/../protobuf/protobuf.gyp:protobuf_lite',
],
}, {
'dependencies': [
'<(webrtc_root)/../third_party/protobuf/protobuf.gyp:protobuf_lite',
],
}],
],
'dependencies': [
'debug_proto',
'aec',
'aecm',
'agc',
'<(webrtc_root)/common_audio/common_audio.gyp:spl',
'<(webrtc_root)/common_audio/common_audio.gyp:vad',
'<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers',
],
'include_dirs': [
'interface',
'../interface',
'<(protoc_out_dir)',
],
'direct_dependent_settings': {
'include_dirs': [
'interface',
'../interface',
],
},
'sources': [
'interface/audio_processing.h',
'audio_buffer.cc',
'audio_buffer.h',
'audio_processing_impl.cc',
'audio_processing_impl.h',
'echo_cancellation_impl.cc',
'echo_cancellation_impl.h',
'echo_control_mobile_impl.cc',
'echo_control_mobile_impl.h',
'gain_control_impl.cc',
'gain_control_impl.h',
'high_pass_filter_impl.cc',
'high_pass_filter_impl.h',
'level_estimator_impl.cc',
'level_estimator_impl.h',
'noise_suppression_impl.cc',
'noise_suppression_impl.h',
'splitting_filter.cc',
'splitting_filter.h',
'processing_component.cc',
'processing_component.h',
'voice_detection_impl.cc',
'voice_detection_impl.h',
'<(protoc_out_dir)/<(protoc_out_relpath)/debug.pb.cc',
'<(protoc_out_dir)/<(protoc_out_relpath)/debug.pb.h',
],
},
{
# Protobuf compiler / generate rule for audio_processing
'target_name': 'debug_proto',
'type': 'none',
'variables': {
'proto_relpath': '<(webrtc_root)/modules/audio_processing',
},
'sources': [
'<(proto_relpath)/debug.proto',
],
'rules': [
{
'rule_name': 'genproto',
'extension': 'proto',
'inputs': [
'<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)protoc<(EXECUTABLE_SUFFIX)',
],
'outputs': [
'<(protoc_out_dir)/<(protoc_out_relpath)/<(RULE_INPUT_ROOT).pb.cc',
'<(protoc_out_dir)/<(protoc_out_relpath)/<(RULE_INPUT_ROOT).pb.h',
],
'action': [
'<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)protoc<(EXECUTABLE_SUFFIX)',
'--proto_path=<(proto_relpath)',
'<(proto_relpath)/<(RULE_INPUT_NAME)',
'--cpp_out=<(protoc_out_dir)/<(protoc_out_relpath)',
],
'message': 'Generating C++ code from <(RULE_INPUT_PATH)',
},
],
'conditions': [
['build_with_chromium==1', {
'dependencies': [
'<(webrtc_root)/../protobuf/protobuf.gyp:protoc#host',
],
}, {
'dependencies': [
'<(webrtc_root)/../third_party/protobuf/protobuf.gyp:protoc#host',
],
}],
],
# This target exports a hard dependency because it generates header
# files.
'hard_dependency': 1,
},
],
}

View File

@ -0,0 +1,673 @@
/*
* 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.
*/
#include "audio_processing_impl.h"
#include <assert.h>
#include "audio_buffer.h"
#include "critical_section_wrapper.h"
#include "echo_cancellation_impl.h"
#include "echo_control_mobile_impl.h"
#ifndef NDEBUG
#include "file_wrapper.h"
#endif
#include "high_pass_filter_impl.h"
#include "gain_control_impl.h"
#include "level_estimator_impl.h"
#include "module_common_types.h"
#include "noise_suppression_impl.h"
#include "processing_component.h"
#include "splitting_filter.h"
#include "voice_detection_impl.h"
#ifndef NDEBUG
#ifdef WEBRTC_ANDROID
#include "external/webrtc/src/modules/audio_processing/main/source/debug.pb.h"
#else
#include "webrtc/audio_processing/debug.pb.h"
#endif
#endif /* NDEBUG */
namespace webrtc {
AudioProcessing* AudioProcessing::Create(int id) {
/*WEBRTC_TRACE(webrtc::kTraceModuleCall,
webrtc::kTraceAudioProcessing,
id,
"AudioProcessing::Create()");*/
AudioProcessingImpl* apm = new AudioProcessingImpl(id);
if (apm->Initialize() != kNoError) {
delete apm;
apm = NULL;
}
return apm;
}
void AudioProcessing::Destroy(AudioProcessing* apm) {
delete static_cast<AudioProcessingImpl*>(apm);
}
AudioProcessingImpl::AudioProcessingImpl(int id)
: id_(id),
echo_cancellation_(NULL),
echo_control_mobile_(NULL),
gain_control_(NULL),
high_pass_filter_(NULL),
level_estimator_(NULL),
noise_suppression_(NULL),
voice_detection_(NULL),
#ifndef NDEBUG
debug_file_(FileWrapper::Create()),
event_msg_(new audioproc::Event()),
#endif
crit_(CriticalSectionWrapper::CreateCriticalSection()),
render_audio_(NULL),
capture_audio_(NULL),
sample_rate_hz_(kSampleRate16kHz),
split_sample_rate_hz_(kSampleRate16kHz),
samples_per_channel_(sample_rate_hz_ / 100),
stream_delay_ms_(0),
was_stream_delay_set_(false),
num_reverse_channels_(1),
num_input_channels_(1),
num_output_channels_(1) {
echo_cancellation_ = new EchoCancellationImpl(this);
component_list_.push_back(echo_cancellation_);
echo_control_mobile_ = new EchoControlMobileImpl(this);
component_list_.push_back(echo_control_mobile_);
gain_control_ = new GainControlImpl(this);
component_list_.push_back(gain_control_);
high_pass_filter_ = new HighPassFilterImpl(this);
component_list_.push_back(high_pass_filter_);
level_estimator_ = new LevelEstimatorImpl(this);
component_list_.push_back(level_estimator_);
noise_suppression_ = new NoiseSuppressionImpl(this);
component_list_.push_back(noise_suppression_);
voice_detection_ = new VoiceDetectionImpl(this);
component_list_.push_back(voice_detection_);
}
AudioProcessingImpl::~AudioProcessingImpl() {
while (!component_list_.empty()) {
ProcessingComponent* component = component_list_.front();
component->Destroy();
delete component;
component_list_.pop_front();
}
#ifndef NDEBUG
if (debug_file_->Open()) {
debug_file_->CloseFile();
}
delete debug_file_;
debug_file_ = NULL;
delete event_msg_;
event_msg_ = NULL;
#endif
delete crit_;
crit_ = NULL;
if (render_audio_) {
delete render_audio_;
render_audio_ = NULL;
}
if (capture_audio_) {
delete capture_audio_;
capture_audio_ = NULL;
}
}
CriticalSectionWrapper* AudioProcessingImpl::crit() const {
return crit_;
}
int AudioProcessingImpl::split_sample_rate_hz() const {
return split_sample_rate_hz_;
}
int AudioProcessingImpl::Initialize() {
CriticalSectionScoped crit_scoped(*crit_);
return InitializeLocked();
}
int AudioProcessingImpl::InitializeLocked() {
if (render_audio_ != NULL) {
delete render_audio_;
render_audio_ = NULL;
}
if (capture_audio_ != NULL) {
delete capture_audio_;
capture_audio_ = NULL;
}
render_audio_ = new AudioBuffer(num_reverse_channels_,
samples_per_channel_);
capture_audio_ = new AudioBuffer(num_input_channels_,
samples_per_channel_);
was_stream_delay_set_ = false;
// Initialize all components.
std::list<ProcessingComponent*>::iterator it;
for (it = component_list_.begin(); it != component_list_.end(); it++) {
int err = (*it)->Initialize();
if (err != kNoError) {
return err;
}
}
#ifndef NDEBUG
if (debug_file_->Open()) {
int err = WriteInitMessage();
if (err != kNoError) {
return err;
}
}
#endif
return kNoError;
}
int AudioProcessingImpl::set_sample_rate_hz(int rate) {
CriticalSectionScoped crit_scoped(*crit_);
if (rate != kSampleRate8kHz &&
rate != kSampleRate16kHz &&
rate != kSampleRate32kHz) {
return kBadParameterError;
}
sample_rate_hz_ = rate;
samples_per_channel_ = rate / 100;
if (sample_rate_hz_ == kSampleRate32kHz) {
split_sample_rate_hz_ = kSampleRate16kHz;
} else {
split_sample_rate_hz_ = sample_rate_hz_;
}
return InitializeLocked();
}
int AudioProcessingImpl::sample_rate_hz() const {
return sample_rate_hz_;
}
int AudioProcessingImpl::set_num_reverse_channels(int channels) {
CriticalSectionScoped crit_scoped(*crit_);
// Only stereo supported currently.
if (channels > 2 || channels < 1) {
return kBadParameterError;
}
num_reverse_channels_ = channels;
return InitializeLocked();
}
int AudioProcessingImpl::num_reverse_channels() const {
return num_reverse_channels_;
}
int AudioProcessingImpl::set_num_channels(
int input_channels,
int output_channels) {
CriticalSectionScoped crit_scoped(*crit_);
if (output_channels > input_channels) {
return kBadParameterError;
}
// Only stereo supported currently.
if (input_channels > 2 || input_channels < 1) {
return kBadParameterError;
}
if (output_channels > 2 || output_channels < 1) {
return kBadParameterError;
}
num_input_channels_ = input_channels;
num_output_channels_ = output_channels;
return InitializeLocked();
}
int AudioProcessingImpl::num_input_channels() const {
return num_input_channels_;
}
int AudioProcessingImpl::num_output_channels() const {
return num_output_channels_;
}
int AudioProcessingImpl::ProcessStream(AudioFrame* frame) {
CriticalSectionScoped crit_scoped(*crit_);
int err = kNoError;
if (frame == NULL) {
return kNullPointerError;
}
if (frame->_frequencyInHz != sample_rate_hz_) {
return kBadSampleRateError;
}
if (frame->_audioChannel != num_input_channels_) {
return kBadNumberChannelsError;
}
if (frame->_payloadDataLengthInSamples != samples_per_channel_) {
return kBadDataLengthError;
}
#ifndef NDEBUG
if (debug_file_->Open()) {
event_msg_->set_type(audioproc::Event::STREAM);
audioproc::Stream* msg = event_msg_->mutable_stream();
const size_t data_size = sizeof(WebRtc_Word16) *
frame->_payloadDataLengthInSamples *
frame->_audioChannel;
msg->set_input_data(frame->_payloadData, data_size);
msg->set_delay(stream_delay_ms_);
msg->set_drift(echo_cancellation_->stream_drift_samples());
msg->set_level(gain_control_->stream_analog_level());
}
#endif
capture_audio_->DeinterleaveFrom(frame);
// TODO(ajm): experiment with mixing and AEC placement.
if (num_output_channels_ < num_input_channels_) {
capture_audio_->Mix(num_output_channels_);
frame->_audioChannel = num_output_channels_;
}
if (sample_rate_hz_ == kSampleRate32kHz) {
for (int i = 0; i < num_input_channels_; i++) {
// Split into a low and high band.
SplittingFilterAnalysis(capture_audio_->data(i),
capture_audio_->low_pass_split_data(i),
capture_audio_->high_pass_split_data(i),
capture_audio_->analysis_filter_state1(i),
capture_audio_->analysis_filter_state2(i));
}
}
err = high_pass_filter_->ProcessCaptureAudio(capture_audio_);
if (err != kNoError) {
return err;
}
err = gain_control_->AnalyzeCaptureAudio(capture_audio_);
if (err != kNoError) {
return err;
}
err = echo_cancellation_->ProcessCaptureAudio(capture_audio_);
if (err != kNoError) {
return err;
}
if (echo_control_mobile_->is_enabled() &&
noise_suppression_->is_enabled()) {
capture_audio_->CopyLowPassToReference();
}
err = noise_suppression_->ProcessCaptureAudio(capture_audio_);
if (err != kNoError) {
return err;
}
err = echo_control_mobile_->ProcessCaptureAudio(capture_audio_);
if (err != kNoError) {
return err;
}
err = voice_detection_->ProcessCaptureAudio(capture_audio_);
if (err != kNoError) {
return err;
}
err = gain_control_->ProcessCaptureAudio(capture_audio_);
if (err != kNoError) {
return err;
}
//err = level_estimator_->ProcessCaptureAudio(capture_audio_);
//if (err != kNoError) {
// return err;
//}
if (sample_rate_hz_ == kSampleRate32kHz) {
for (int i = 0; i < num_output_channels_; i++) {
// Recombine low and high bands.
SplittingFilterSynthesis(capture_audio_->low_pass_split_data(i),
capture_audio_->high_pass_split_data(i),
capture_audio_->data(i),
capture_audio_->synthesis_filter_state1(i),
capture_audio_->synthesis_filter_state2(i));
}
}
capture_audio_->InterleaveTo(frame);
#ifndef NDEBUG
if (debug_file_->Open()) {
audioproc::Stream* msg = event_msg_->mutable_stream();
const size_t data_size = sizeof(WebRtc_Word16) *
frame->_payloadDataLengthInSamples *
frame->_audioChannel;
msg->set_output_data(frame->_payloadData, data_size);
err = WriteMessageToDebugFile();
if (err != kNoError) {
return err;
}
}
#endif
return kNoError;
}
int AudioProcessingImpl::AnalyzeReverseStream(AudioFrame* frame) {
CriticalSectionScoped crit_scoped(*crit_);
int err = kNoError;
if (frame == NULL) {
return kNullPointerError;
}
if (frame->_frequencyInHz != sample_rate_hz_) {
return kBadSampleRateError;
}
if (frame->_audioChannel != num_reverse_channels_) {
return kBadNumberChannelsError;
}
if (frame->_payloadDataLengthInSamples != samples_per_channel_) {
return kBadDataLengthError;
}
#ifndef NDEBUG
if (debug_file_->Open()) {
event_msg_->set_type(audioproc::Event::REVERSE_STREAM);
audioproc::ReverseStream* msg = event_msg_->mutable_reverse_stream();
const size_t data_size = sizeof(WebRtc_Word16) *
frame->_payloadDataLengthInSamples *
frame->_audioChannel;
msg->set_data(frame->_payloadData, data_size);
err = WriteMessageToDebugFile();
if (err != kNoError) {
return err;
}
}
#endif
render_audio_->DeinterleaveFrom(frame);
// TODO(ajm): turn the splitting filter into a component?
if (sample_rate_hz_ == kSampleRate32kHz) {
for (int i = 0; i < num_reverse_channels_; i++) {
// Split into low and high band.
SplittingFilterAnalysis(render_audio_->data(i),
render_audio_->low_pass_split_data(i),
render_audio_->high_pass_split_data(i),
render_audio_->analysis_filter_state1(i),
render_audio_->analysis_filter_state2(i));
}
}
// TODO(ajm): warnings possible from components?
err = echo_cancellation_->ProcessRenderAudio(render_audio_);
if (err != kNoError) {
return err;
}
err = echo_control_mobile_->ProcessRenderAudio(render_audio_);
if (err != kNoError) {
return err;
}
err = gain_control_->ProcessRenderAudio(render_audio_);
if (err != kNoError) {
return err;
}
//err = level_estimator_->AnalyzeReverseStream(render_audio_);
//if (err != kNoError) {
// return err;
//}
was_stream_delay_set_ = false;
return err; // TODO(ajm): this is for returning warnings; necessary?
}
int AudioProcessingImpl::set_stream_delay_ms(int delay) {
was_stream_delay_set_ = true;
if (delay < 0) {
return kBadParameterError;
}
// TODO(ajm): the max is rather arbitrarily chosen; investigate.
if (delay > 500) {
stream_delay_ms_ = 500;
return kBadStreamParameterWarning;
}
stream_delay_ms_ = delay;
return kNoError;
}
int AudioProcessingImpl::stream_delay_ms() const {
return stream_delay_ms_;
}
bool AudioProcessingImpl::was_stream_delay_set() const {
return was_stream_delay_set_;
}
int AudioProcessingImpl::StartDebugRecording(
const char filename[AudioProcessing::kMaxFilenameSize]) {
#ifndef NDEBUG
CriticalSectionScoped crit_scoped(*crit_);
assert(kMaxFilenameSize == FileWrapper::kMaxFileNameSize);
if (filename == NULL) {
return kNullPointerError;
}
// Stop any ongoing recording.
if (debug_file_->Open()) {
if (debug_file_->CloseFile() == -1) {
return kFileError;
}
}
if (debug_file_->OpenFile(filename, false) == -1) {
debug_file_->CloseFile();
return kFileError;
}
int err = WriteInitMessage();
if (err != kNoError) {
return err;
}
#endif
return kNoError;
}
int AudioProcessingImpl::StopDebugRecording() {
#ifndef NDEBUG
CriticalSectionScoped crit_scoped(*crit_);
// We just return if recording hasn't started.
if (debug_file_->Open()) {
if (debug_file_->CloseFile() == -1) {
return kFileError;
}
}
#endif
return kNoError;
}
EchoCancellation* AudioProcessingImpl::echo_cancellation() const {
return echo_cancellation_;
}
EchoControlMobile* AudioProcessingImpl::echo_control_mobile() const {
return echo_control_mobile_;
}
GainControl* AudioProcessingImpl::gain_control() const {
return gain_control_;
}
HighPassFilter* AudioProcessingImpl::high_pass_filter() const {
return high_pass_filter_;
}
LevelEstimator* AudioProcessingImpl::level_estimator() const {
return level_estimator_;
}
NoiseSuppression* AudioProcessingImpl::noise_suppression() const {
return noise_suppression_;
}
VoiceDetection* AudioProcessingImpl::voice_detection() const {
return voice_detection_;
}
WebRtc_Word32 AudioProcessingImpl::Version(WebRtc_Word8* version,
WebRtc_UWord32& bytes_remaining, WebRtc_UWord32& position) const {
if (version == NULL) {
/*WEBRTC_TRACE(webrtc::kTraceError,
webrtc::kTraceAudioProcessing,
-1,
"Null version pointer");*/
return kNullPointerError;
}
memset(&version[position], 0, bytes_remaining);
char my_version[] = "AudioProcessing 1.0.0";
// Includes null termination.
WebRtc_UWord32 length = static_cast<WebRtc_UWord32>(strlen(my_version));
if (bytes_remaining < length) {
/*WEBRTC_TRACE(webrtc::kTraceError,
webrtc::kTraceAudioProcessing,
-1,
"Buffer of insufficient length");*/
return kBadParameterError;
}
memcpy(&version[position], my_version, length);
bytes_remaining -= length;
position += length;
std::list<ProcessingComponent*>::const_iterator it;
for (it = component_list_.begin(); it != component_list_.end(); it++) {
char component_version[256];
strcpy(component_version, "\n");
int err = (*it)->get_version(&component_version[1],
sizeof(component_version) - 1);
if (err != kNoError) {
return err;
}
if (strncmp(&component_version[1], "\0", 1) == 0) {
// Assume empty if first byte is NULL.
continue;
}
length = static_cast<WebRtc_UWord32>(strlen(component_version));
if (bytes_remaining < length) {
/*WEBRTC_TRACE(webrtc::kTraceError,
webrtc::kTraceAudioProcessing,
-1,
"Buffer of insufficient length");*/
return kBadParameterError;
}
memcpy(&version[position], component_version, length);
bytes_remaining -= length;
position += length;
}
return kNoError;
}
WebRtc_Word32 AudioProcessingImpl::ChangeUniqueId(const WebRtc_Word32 id) {
CriticalSectionScoped crit_scoped(*crit_);
/*WEBRTC_TRACE(webrtc::kTraceModuleCall,
webrtc::kTraceAudioProcessing,
id_,
"ChangeUniqueId(new id = %d)",
id);*/
id_ = id;
return kNoError;
}
#ifndef NDEBUG
int AudioProcessingImpl::WriteMessageToDebugFile() {
int32_t size = event_msg_->ByteSize();
if (size <= 0) {
return kUnspecifiedError;
}
#if defined(WEBRTC_BIG_ENDIAN)
// TODO(ajm): Use little-endian "on the wire". For the moment, we can be
// pretty safe in assuming little-endian.
#endif
if (!event_msg_->SerializeToString(&event_str_)) {
return kUnspecifiedError;
}
// Write message preceded by its size.
if (!debug_file_->Write(&size, sizeof(int32_t))) {
return kFileError;
}
if (!debug_file_->Write(event_str_.data(), event_str_.length())) {
return kFileError;
}
event_msg_->Clear();
return 0;
}
int AudioProcessingImpl::WriteInitMessage() {
event_msg_->set_type(audioproc::Event::INIT);
audioproc::Init* msg = event_msg_->mutable_init();
msg->set_sample_rate(sample_rate_hz_);
msg->set_device_sample_rate(echo_cancellation_->device_sample_rate_hz());
msg->set_num_input_channels(num_input_channels_);
msg->set_num_output_channels(num_output_channels_);
msg->set_num_reverse_channels(num_reverse_channels_);
int err = WriteMessageToDebugFile();
if (err != kNoError) {
return err;
}
return kNoError;
}
#endif
} // namespace webrtc

View File

@ -0,0 +1,117 @@
/*
* 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.
*/
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_AUDIO_PROCESSING_IMPL_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_AUDIO_PROCESSING_IMPL_H_
#include <list>
#include <string>
#include "audio_processing.h"
namespace webrtc {
namespace audioproc {
class Event;
} // audioproc
class AudioBuffer;
class CriticalSectionWrapper;
class EchoCancellationImpl;
class EchoControlMobileImpl;
class FileWrapper;
class GainControlImpl;
class HighPassFilterImpl;
class LevelEstimatorImpl;
class NoiseSuppressionImpl;
class ProcessingComponent;
class VoiceDetectionImpl;
class AudioProcessingImpl : public AudioProcessing {
public:
enum {
kSampleRate8kHz = 8000,
kSampleRate16kHz = 16000,
kSampleRate32kHz = 32000
};
explicit AudioProcessingImpl(int id);
virtual ~AudioProcessingImpl();
CriticalSectionWrapper* crit() const;
int split_sample_rate_hz() const;
bool was_stream_delay_set() const;
// AudioProcessing methods.
virtual int Initialize();
virtual int InitializeLocked();
virtual int set_sample_rate_hz(int rate);
virtual int sample_rate_hz() const;
virtual int set_num_channels(int input_channels, int output_channels);
virtual int num_input_channels() const;
virtual int num_output_channels() const;
virtual int set_num_reverse_channels(int channels);
virtual int num_reverse_channels() const;
virtual int ProcessStream(AudioFrame* frame);
virtual int AnalyzeReverseStream(AudioFrame* frame);
virtual int set_stream_delay_ms(int delay);
virtual int stream_delay_ms() const;
virtual int StartDebugRecording(const char filename[kMaxFilenameSize]);
virtual int StopDebugRecording();
virtual EchoCancellation* echo_cancellation() const;
virtual EchoControlMobile* echo_control_mobile() const;
virtual GainControl* gain_control() const;
virtual HighPassFilter* high_pass_filter() const;
virtual LevelEstimator* level_estimator() const;
virtual NoiseSuppression* noise_suppression() const;
virtual VoiceDetection* voice_detection() const;
// Module methods.
virtual WebRtc_Word32 Version(WebRtc_Word8* version,
WebRtc_UWord32& remainingBufferInBytes,
WebRtc_UWord32& position) const;
virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id);
private:
int WriteMessageToDebugFile();
int WriteInitMessage();
int id_;
EchoCancellationImpl* echo_cancellation_;
EchoControlMobileImpl* echo_control_mobile_;
GainControlImpl* gain_control_;
HighPassFilterImpl* high_pass_filter_;
LevelEstimatorImpl* level_estimator_;
NoiseSuppressionImpl* noise_suppression_;
VoiceDetectionImpl* voice_detection_;
std::list<ProcessingComponent*> component_list_;
FileWrapper* debug_file_;
audioproc::Event* event_msg_; // Protobuf message.
std::string event_str_; // Memory for protobuf serialization.
CriticalSectionWrapper* crit_;
AudioBuffer* render_audio_;
AudioBuffer* capture_audio_;
int sample_rate_hz_;
int split_sample_rate_hz_;
int samples_per_channel_;
int stream_delay_ms_;
bool was_stream_delay_set_;
int num_reverse_channels_;
int num_input_channels_;
int num_output_channels_;
};
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_AUDIO_PROCESSING_IMPL_H_

View File

@ -0,0 +1,37 @@
syntax = "proto2";
option optimize_for = LITE_RUNTIME;
package webrtc.audioproc;
message Init {
optional int32 sample_rate = 1;
optional int32 device_sample_rate = 2;
optional int32 num_input_channels = 3;
optional int32 num_output_channels = 4;
optional int32 num_reverse_channels = 5;
}
message ReverseStream {
optional bytes data = 1;
}
message Stream {
optional bytes input_data = 1;
optional bytes output_data = 2;
optional int32 delay = 3;
optional sint32 drift = 4;
optional int32 level = 5;
}
message Event {
enum Type {
INIT = 0;
REVERSE_STREAM = 1;
STREAM = 2;
}
required Type type = 1;
optional Init init = 2;
optional ReverseStream reverse_stream = 3;
optional Stream stream = 4;
}

View File

@ -0,0 +1,383 @@
/*
* 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.
*/
#include "echo_cancellation_impl.h"
#include <cassert>
#include <string.h>
#include "critical_section_wrapper.h"
#include "echo_cancellation.h"
#include "audio_processing_impl.h"
#include "audio_buffer.h"
namespace webrtc {
typedef void Handle;
namespace {
WebRtc_Word16 MapSetting(EchoCancellation::SuppressionLevel level) {
switch (level) {
case EchoCancellation::kLowSuppression:
return kAecNlpConservative;
case EchoCancellation::kModerateSuppression:
return kAecNlpModerate;
case EchoCancellation::kHighSuppression:
return kAecNlpAggressive;
default:
return -1;
}
}
int MapError(int err) {
switch (err) {
case AEC_UNSUPPORTED_FUNCTION_ERROR:
return AudioProcessing::kUnsupportedFunctionError;
break;
case AEC_BAD_PARAMETER_ERROR:
return AudioProcessing::kBadParameterError;
break;
case AEC_BAD_PARAMETER_WARNING:
return AudioProcessing::kBadStreamParameterWarning;
break;
default:
// AEC_UNSPECIFIED_ERROR
// AEC_UNINITIALIZED_ERROR
// AEC_NULL_POINTER_ERROR
return AudioProcessing::kUnspecifiedError;
}
}
} // namespace
EchoCancellationImpl::EchoCancellationImpl(const AudioProcessingImpl* apm)
: ProcessingComponent(apm),
apm_(apm),
drift_compensation_enabled_(false),
metrics_enabled_(false),
suppression_level_(kModerateSuppression),
device_sample_rate_hz_(48000),
stream_drift_samples_(0),
was_stream_drift_set_(false),
stream_has_echo_(false),
delay_logging_enabled_(false) {}
EchoCancellationImpl::~EchoCancellationImpl() {}
int EchoCancellationImpl::ProcessRenderAudio(const AudioBuffer* audio) {
if (!is_component_enabled()) {
return apm_->kNoError;
}
assert(audio->samples_per_split_channel() <= 160);
assert(audio->num_channels() == apm_->num_reverse_channels());
int err = apm_->kNoError;
// The ordering convention must be followed to pass to the correct AEC.
size_t handle_index = 0;
for (int i = 0; i < apm_->num_output_channels(); i++) {
for (int j = 0; j < audio->num_channels(); j++) {
Handle* my_handle = static_cast<Handle*>(handle(handle_index));
err = WebRtcAec_BufferFarend(
my_handle,
audio->low_pass_split_data(j),
static_cast<WebRtc_Word16>(audio->samples_per_split_channel()));
if (err != apm_->kNoError) {
return GetHandleError(my_handle); // TODO(ajm): warning possible?
}
handle_index++;
}
}
return apm_->kNoError;
}
int EchoCancellationImpl::ProcessCaptureAudio(AudioBuffer* audio) {
if (!is_component_enabled()) {
return apm_->kNoError;
}
if (!apm_->was_stream_delay_set()) {
return apm_->kStreamParameterNotSetError;
}
if (drift_compensation_enabled_ && !was_stream_drift_set_) {
return apm_->kStreamParameterNotSetError;
}
assert(audio->samples_per_split_channel() <= 160);
assert(audio->num_channels() == apm_->num_output_channels());
int err = apm_->kNoError;
// The ordering convention must be followed to pass to the correct AEC.
size_t handle_index = 0;
stream_has_echo_ = false;
for (int i = 0; i < audio->num_channels(); i++) {
for (int j = 0; j < apm_->num_reverse_channels(); j++) {
Handle* my_handle = handle(handle_index);
err = WebRtcAec_Process(
my_handle,
audio->low_pass_split_data(i),
audio->high_pass_split_data(i),
audio->low_pass_split_data(i),
audio->high_pass_split_data(i),
static_cast<WebRtc_Word16>(audio->samples_per_split_channel()),
apm_->stream_delay_ms(),
stream_drift_samples_);
if (err != apm_->kNoError) {
err = GetHandleError(my_handle);
// TODO(ajm): Figure out how to return warnings properly.
if (err != apm_->kBadStreamParameterWarning) {
return err;
}
}
WebRtc_Word16 status = 0;
err = WebRtcAec_get_echo_status(my_handle, &status);
if (err != apm_->kNoError) {
return GetHandleError(my_handle);
}
if (status == 1) {
stream_has_echo_ = true;
}
handle_index++;
}
}
was_stream_drift_set_ = false;
return apm_->kNoError;
}
int EchoCancellationImpl::Enable(bool enable) {
CriticalSectionScoped crit_scoped(*apm_->crit());
// Ensure AEC and AECM are not both enabled.
if (enable && apm_->echo_control_mobile()->is_enabled()) {
return apm_->kBadParameterError;
}
return EnableComponent(enable);
}
bool EchoCancellationImpl::is_enabled() const {
return is_component_enabled();
}
int EchoCancellationImpl::set_suppression_level(SuppressionLevel level) {
CriticalSectionScoped crit_scoped(*apm_->crit());
if (MapSetting(level) == -1) {
return apm_->kBadParameterError;
}
suppression_level_ = level;
return Configure();
}
EchoCancellation::SuppressionLevel EchoCancellationImpl::suppression_level()
const {
return suppression_level_;
}
int EchoCancellationImpl::enable_drift_compensation(bool enable) {
CriticalSectionScoped crit_scoped(*apm_->crit());
drift_compensation_enabled_ = enable;
return Configure();
}
bool EchoCancellationImpl::is_drift_compensation_enabled() const {
return drift_compensation_enabled_;
}
int EchoCancellationImpl::set_device_sample_rate_hz(int rate) {
CriticalSectionScoped crit_scoped(*apm_->crit());
if (rate < 8000 || rate > 96000) {
return apm_->kBadParameterError;
}
device_sample_rate_hz_ = rate;
return Initialize();
}
int EchoCancellationImpl::device_sample_rate_hz() const {
return device_sample_rate_hz_;
}
int EchoCancellationImpl::set_stream_drift_samples(int drift) {
was_stream_drift_set_ = true;
stream_drift_samples_ = drift;
return apm_->kNoError;
}
int EchoCancellationImpl::stream_drift_samples() const {
return stream_drift_samples_;
}
int EchoCancellationImpl::enable_metrics(bool enable) {
CriticalSectionScoped crit_scoped(*apm_->crit());
metrics_enabled_ = enable;
return Configure();
}
bool EchoCancellationImpl::are_metrics_enabled() const {
return metrics_enabled_;
}
// TODO(ajm): we currently just use the metrics from the first AEC. Think more
// aboue the best way to extend this to multi-channel.
int EchoCancellationImpl::GetMetrics(Metrics* metrics) {
CriticalSectionScoped crit_scoped(*apm_->crit());
if (metrics == NULL) {
return apm_->kNullPointerError;
}
if (!is_component_enabled() || !metrics_enabled_) {
return apm_->kNotEnabledError;
}
AecMetrics my_metrics;
memset(&my_metrics, 0, sizeof(my_metrics));
memset(metrics, 0, sizeof(Metrics));
Handle* my_handle = static_cast<Handle*>(handle(0));
int err = WebRtcAec_GetMetrics(my_handle, &my_metrics);
if (err != apm_->kNoError) {
return GetHandleError(my_handle);
}
metrics->residual_echo_return_loss.instant = my_metrics.rerl.instant;
metrics->residual_echo_return_loss.average = my_metrics.rerl.average;
metrics->residual_echo_return_loss.maximum = my_metrics.rerl.max;
metrics->residual_echo_return_loss.minimum = my_metrics.rerl.min;
metrics->echo_return_loss.instant = my_metrics.erl.instant;
metrics->echo_return_loss.average = my_metrics.erl.average;
metrics->echo_return_loss.maximum = my_metrics.erl.max;
metrics->echo_return_loss.minimum = my_metrics.erl.min;
metrics->echo_return_loss_enhancement.instant = my_metrics.erle.instant;
metrics->echo_return_loss_enhancement.average = my_metrics.erle.average;
metrics->echo_return_loss_enhancement.maximum = my_metrics.erle.max;
metrics->echo_return_loss_enhancement.minimum = my_metrics.erle.min;
metrics->a_nlp.instant = my_metrics.aNlp.instant;
metrics->a_nlp.average = my_metrics.aNlp.average;
metrics->a_nlp.maximum = my_metrics.aNlp.max;
metrics->a_nlp.minimum = my_metrics.aNlp.min;
return apm_->kNoError;
}
bool EchoCancellationImpl::stream_has_echo() const {
return stream_has_echo_;
}
int EchoCancellationImpl::enable_delay_logging(bool enable) {
CriticalSectionScoped crit_scoped(*apm_->crit());
delay_logging_enabled_ = enable;
return Configure();
}
bool EchoCancellationImpl::is_delay_logging_enabled() const {
return delay_logging_enabled_;
}
// TODO(bjornv): How should we handle the multi-channel case?
int EchoCancellationImpl::GetDelayMetrics(int* median, int* std) {
CriticalSectionScoped crit_scoped(*apm_->crit());
if (median == NULL) {
return apm_->kNullPointerError;
}
if (std == NULL) {
return apm_->kNullPointerError;
}
if (!is_component_enabled() || !delay_logging_enabled_) {
return apm_->kNotEnabledError;
}
Handle* my_handle = static_cast<Handle*>(handle(0));
if (WebRtcAec_GetDelayMetrics(my_handle, median, std) !=
apm_->kNoError) {
return GetHandleError(my_handle);
}
return apm_->kNoError;
}
int EchoCancellationImpl::Initialize() {
int err = ProcessingComponent::Initialize();
if (err != apm_->kNoError || !is_component_enabled()) {
return err;
}
was_stream_drift_set_ = false;
return apm_->kNoError;
}
int EchoCancellationImpl::get_version(char* version,
int version_len_bytes) const {
if (WebRtcAec_get_version(version, version_len_bytes) != 0) {
return apm_->kBadParameterError;
}
return apm_->kNoError;
}
void* EchoCancellationImpl::CreateHandle() const {
Handle* handle = NULL;
if (WebRtcAec_Create(&handle) != apm_->kNoError) {
handle = NULL;
} else {
assert(handle != NULL);
}
return handle;
}
int EchoCancellationImpl::DestroyHandle(void* handle) const {
assert(handle != NULL);
return WebRtcAec_Free(static_cast<Handle*>(handle));
}
int EchoCancellationImpl::InitializeHandle(void* handle) const {
assert(handle != NULL);
return WebRtcAec_Init(static_cast<Handle*>(handle),
apm_->sample_rate_hz(),
device_sample_rate_hz_);
}
int EchoCancellationImpl::ConfigureHandle(void* handle) const {
assert(handle != NULL);
AecConfig config;
config.metricsMode = metrics_enabled_;
config.nlpMode = MapSetting(suppression_level_);
config.skewMode = drift_compensation_enabled_;
config.delay_logging = delay_logging_enabled_;
return WebRtcAec_set_config(static_cast<Handle*>(handle), config);
}
int EchoCancellationImpl::num_handles_required() const {
return apm_->num_output_channels() *
apm_->num_reverse_channels();
}
int EchoCancellationImpl::GetHandleError(void* handle) const {
assert(handle != NULL);
return MapError(WebRtcAec_get_error_code(static_cast<Handle*>(handle)));
}
} // namespace webrtc

View File

@ -0,0 +1,76 @@
/*
* 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.
*/
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_ECHO_CANCELLATION_IMPL_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_ECHO_CANCELLATION_IMPL_H_
#include "audio_processing.h"
#include "processing_component.h"
namespace webrtc {
class AudioProcessingImpl;
class AudioBuffer;
class EchoCancellationImpl : public EchoCancellation,
public ProcessingComponent {
public:
explicit EchoCancellationImpl(const AudioProcessingImpl* apm);
virtual ~EchoCancellationImpl();
int ProcessRenderAudio(const AudioBuffer* audio);
int ProcessCaptureAudio(AudioBuffer* audio);
// EchoCancellation implementation.
virtual bool is_enabled() const;
virtual int device_sample_rate_hz() const;
virtual int stream_drift_samples() const;
// ProcessingComponent implementation.
virtual int Initialize();
virtual int get_version(char* version, int version_len_bytes) const;
private:
// EchoCancellation implementation.
virtual int Enable(bool enable);
virtual int enable_drift_compensation(bool enable);
virtual bool is_drift_compensation_enabled() const;
virtual int set_device_sample_rate_hz(int rate);
virtual int set_stream_drift_samples(int drift);
virtual int set_suppression_level(SuppressionLevel level);
virtual SuppressionLevel suppression_level() const;
virtual int enable_metrics(bool enable);
virtual bool are_metrics_enabled() const;
virtual bool stream_has_echo() const;
virtual int GetMetrics(Metrics* metrics);
virtual int enable_delay_logging(bool enable);
virtual bool is_delay_logging_enabled() const;
virtual int GetDelayMetrics(int* median, int* std);
// ProcessingComponent implementation.
virtual void* CreateHandle() const;
virtual int InitializeHandle(void* handle) const;
virtual int ConfigureHandle(void* handle) const;
virtual int DestroyHandle(void* handle) const;
virtual int num_handles_required() const;
virtual int GetHandleError(void* handle) const;
const AudioProcessingImpl* apm_;
bool drift_compensation_enabled_;
bool metrics_enabled_;
SuppressionLevel suppression_level_;
int device_sample_rate_hz_;
int stream_drift_samples_;
bool was_stream_drift_set_;
bool stream_has_echo_;
bool delay_logging_enabled_;
};
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_ECHO_CANCELLATION_IMPL_H_

View File

@ -0,0 +1,309 @@
/*
* 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.
*/
#include "echo_control_mobile_impl.h"
#include <cassert>
#include <cstring>
#include "critical_section_wrapper.h"
#include "echo_control_mobile.h"
#include "audio_processing_impl.h"
#include "audio_buffer.h"
namespace webrtc {
typedef void Handle;
namespace {
WebRtc_Word16 MapSetting(EchoControlMobile::RoutingMode mode) {
switch (mode) {
case EchoControlMobile::kQuietEarpieceOrHeadset:
return 0;
case EchoControlMobile::kEarpiece:
return 1;
case EchoControlMobile::kLoudEarpiece:
return 2;
case EchoControlMobile::kSpeakerphone:
return 3;
case EchoControlMobile::kLoudSpeakerphone:
return 4;
default:
return -1;
}
}
int MapError(int err) {
switch (err) {
case AECM_UNSUPPORTED_FUNCTION_ERROR:
return AudioProcessing::kUnsupportedFunctionError;
case AECM_NULL_POINTER_ERROR:
return AudioProcessing::kNullPointerError;
case AECM_BAD_PARAMETER_ERROR:
return AudioProcessing::kBadParameterError;
case AECM_BAD_PARAMETER_WARNING:
return AudioProcessing::kBadStreamParameterWarning;
default:
// AECM_UNSPECIFIED_ERROR
// AECM_UNINITIALIZED_ERROR
return AudioProcessing::kUnspecifiedError;
}
}
} // namespace
size_t EchoControlMobile::echo_path_size_bytes() {
return WebRtcAecm_echo_path_size_bytes();
}
EchoControlMobileImpl::EchoControlMobileImpl(const AudioProcessingImpl* apm)
: ProcessingComponent(apm),
apm_(apm),
routing_mode_(kSpeakerphone),
comfort_noise_enabled_(true),
external_echo_path_(NULL) {}
EchoControlMobileImpl::~EchoControlMobileImpl() {
if (external_echo_path_ != NULL) {
delete [] external_echo_path_;
external_echo_path_ = NULL;
}
}
int EchoControlMobileImpl::ProcessRenderAudio(const AudioBuffer* audio) {
if (!is_component_enabled()) {
return apm_->kNoError;
}
assert(audio->samples_per_split_channel() <= 160);
assert(audio->num_channels() == apm_->num_reverse_channels());
int err = apm_->kNoError;
// The ordering convention must be followed to pass to the correct AECM.
size_t handle_index = 0;
for (int i = 0; i < apm_->num_output_channels(); i++) {
for (int j = 0; j < audio->num_channels(); j++) {
Handle* my_handle = static_cast<Handle*>(handle(handle_index));
err = WebRtcAecm_BufferFarend(
my_handle,
audio->low_pass_split_data(j),
static_cast<WebRtc_Word16>(audio->samples_per_split_channel()));
if (err != apm_->kNoError) {
return GetHandleError(my_handle); // TODO(ajm): warning possible?
}
handle_index++;
}
}
return apm_->kNoError;
}
int EchoControlMobileImpl::ProcessCaptureAudio(AudioBuffer* audio) {
if (!is_component_enabled()) {
return apm_->kNoError;
}
if (!apm_->was_stream_delay_set()) {
return apm_->kStreamParameterNotSetError;
}
assert(audio->samples_per_split_channel() <= 160);
assert(audio->num_channels() == apm_->num_output_channels());
int err = apm_->kNoError;
// The ordering convention must be followed to pass to the correct AECM.
size_t handle_index = 0;
for (int i = 0; i < audio->num_channels(); i++) {
// TODO(ajm): improve how this works, possibly inside AECM.
// This is kind of hacked up.
WebRtc_Word16* noisy = audio->low_pass_reference(i);
WebRtc_Word16* clean = audio->low_pass_split_data(i);
if (noisy == NULL) {
noisy = clean;
clean = NULL;
}
for (int j = 0; j < apm_->num_reverse_channels(); j++) {
Handle* my_handle = static_cast<Handle*>(handle(handle_index));
err = WebRtcAecm_Process(
my_handle,
noisy,
clean,
audio->low_pass_split_data(i),
static_cast<WebRtc_Word16>(audio->samples_per_split_channel()),
apm_->stream_delay_ms());
if (err != apm_->kNoError) {
return GetHandleError(my_handle); // TODO(ajm): warning possible?
}
handle_index++;
}
}
return apm_->kNoError;
}
int EchoControlMobileImpl::Enable(bool enable) {
CriticalSectionScoped crit_scoped(*apm_->crit());
// Ensure AEC and AECM are not both enabled.
if (enable && apm_->echo_cancellation()->is_enabled()) {
return apm_->kBadParameterError;
}
return EnableComponent(enable);
}
bool EchoControlMobileImpl::is_enabled() const {
return is_component_enabled();
}
int EchoControlMobileImpl::set_routing_mode(RoutingMode mode) {
CriticalSectionScoped crit_scoped(*apm_->crit());
if (MapSetting(mode) == -1) {
return apm_->kBadParameterError;
}
routing_mode_ = mode;
return Configure();
}
EchoControlMobile::RoutingMode EchoControlMobileImpl::routing_mode()
const {
return routing_mode_;
}
int EchoControlMobileImpl::enable_comfort_noise(bool enable) {
CriticalSectionScoped crit_scoped(*apm_->crit());
comfort_noise_enabled_ = enable;
return Configure();
}
bool EchoControlMobileImpl::is_comfort_noise_enabled() const {
return comfort_noise_enabled_;
}
int EchoControlMobileImpl::SetEchoPath(const void* echo_path,
size_t size_bytes) {
CriticalSectionScoped crit_scoped(*apm_->crit());
if (echo_path == NULL) {
return apm_->kNullPointerError;
}
if (size_bytes != echo_path_size_bytes()) {
// Size mismatch
return apm_->kBadParameterError;
}
if (external_echo_path_ == NULL) {
external_echo_path_ = new unsigned char[size_bytes];
}
memcpy(external_echo_path_, echo_path, size_bytes);
return Initialize();
}
int EchoControlMobileImpl::GetEchoPath(void* echo_path,
size_t size_bytes) const {
CriticalSectionScoped crit_scoped(*apm_->crit());
if (echo_path == NULL) {
return apm_->kNullPointerError;
}
if (size_bytes != echo_path_size_bytes()) {
// Size mismatch
return apm_->kBadParameterError;
}
if (!is_component_enabled()) {
return apm_->kNotEnabledError;
}
// Get the echo path from the first channel
Handle* my_handle = static_cast<Handle*>(handle(0));
if (WebRtcAecm_GetEchoPath(my_handle, echo_path, size_bytes) != 0) {
return GetHandleError(my_handle);
}
return apm_->kNoError;
}
int EchoControlMobileImpl::Initialize() {
if (!is_component_enabled()) {
return apm_->kNoError;
}
if (apm_->sample_rate_hz() == apm_->kSampleRate32kHz) {
// AECM doesn't support super-wideband.
return apm_->kBadSampleRateError;
}
return ProcessingComponent::Initialize();
}
int EchoControlMobileImpl::get_version(char* version,
int version_len_bytes) const {
if (WebRtcAecm_get_version(version, version_len_bytes) != 0) {
return apm_->kBadParameterError;
}
return apm_->kNoError;
}
void* EchoControlMobileImpl::CreateHandle() const {
Handle* handle = NULL;
if (WebRtcAecm_Create(&handle) != apm_->kNoError) {
handle = NULL;
} else {
assert(handle != NULL);
}
return handle;
}
int EchoControlMobileImpl::DestroyHandle(void* handle) const {
return WebRtcAecm_Free(static_cast<Handle*>(handle));
}
int EchoControlMobileImpl::InitializeHandle(void* handle) const {
assert(handle != NULL);
Handle* my_handle = static_cast<Handle*>(handle);
if (WebRtcAecm_Init(my_handle, apm_->sample_rate_hz()) != 0) {
return GetHandleError(my_handle);
}
if (external_echo_path_ != NULL) {
if (WebRtcAecm_InitEchoPath(my_handle,
external_echo_path_,
echo_path_size_bytes()) != 0) {
return GetHandleError(my_handle);
}
}
return apm_->kNoError;
}
int EchoControlMobileImpl::ConfigureHandle(void* handle) const {
AecmConfig config;
config.cngMode = comfort_noise_enabled_;
config.echoMode = MapSetting(routing_mode_);
return WebRtcAecm_set_config(static_cast<Handle*>(handle), config);
}
int EchoControlMobileImpl::num_handles_required() const {
return apm_->num_output_channels() *
apm_->num_reverse_channels();
}
int EchoControlMobileImpl::GetHandleError(void* handle) const {
assert(handle != NULL);
return MapError(WebRtcAecm_get_error_code(static_cast<Handle*>(handle)));
}
} // namespace webrtc

View File

@ -0,0 +1,62 @@
/*
* 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.
*/
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_ECHO_CONTROL_MOBILE_IMPL_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_ECHO_CONTROL_MOBILE_IMPL_H_
#include "audio_processing.h"
#include "processing_component.h"
namespace webrtc {
class AudioProcessingImpl;
class AudioBuffer;
class EchoControlMobileImpl : public EchoControlMobile,
public ProcessingComponent {
public:
explicit EchoControlMobileImpl(const AudioProcessingImpl* apm);
virtual ~EchoControlMobileImpl();
int ProcessRenderAudio(const AudioBuffer* audio);
int ProcessCaptureAudio(AudioBuffer* audio);
// EchoControlMobile implementation.
virtual bool is_enabled() const;
// ProcessingComponent implementation.
virtual int Initialize();
virtual int get_version(char* version, int version_len_bytes) const;
private:
// EchoControlMobile implementation.
virtual int Enable(bool enable);
virtual int set_routing_mode(RoutingMode mode);
virtual RoutingMode routing_mode() const;
virtual int enable_comfort_noise(bool enable);
virtual bool is_comfort_noise_enabled() const;
virtual int SetEchoPath(const void* echo_path, size_t size_bytes);
virtual int GetEchoPath(void* echo_path, size_t size_bytes) const;
// ProcessingComponent implementation.
virtual void* CreateHandle() const;
virtual int InitializeHandle(void* handle) const;
virtual int ConfigureHandle(void* handle) const;
virtual int DestroyHandle(void* handle) const;
virtual int num_handles_required() const;
virtual int GetHandleError(void* handle) const;
const AudioProcessingImpl* apm_;
RoutingMode routing_mode_;
bool comfort_noise_enabled_;
unsigned char* external_echo_path_;
};
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_ECHO_CONTROL_MOBILE_IMPL_H_

View File

@ -0,0 +1,391 @@
/*
* 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.
*/
#include "gain_control_impl.h"
#include <cassert>
#include "critical_section_wrapper.h"
#include "gain_control.h"
#include "audio_processing_impl.h"
#include "audio_buffer.h"
namespace webrtc {
typedef void Handle;
/*template <class T>
class GainControlHandle : public ComponentHandle<T> {
public:
GainControlHandle();
virtual ~GainControlHandle();
virtual int Create();
virtual T* ptr() const;
private:
T* handle;
};*/
namespace {
WebRtc_Word16 MapSetting(GainControl::Mode mode) {
switch (mode) {
case GainControl::kAdaptiveAnalog:
return kAgcModeAdaptiveAnalog;
break;
case GainControl::kAdaptiveDigital:
return kAgcModeAdaptiveDigital;
break;
case GainControl::kFixedDigital:
return kAgcModeFixedDigital;
break;
default:
return -1;
}
}
} // namespace
GainControlImpl::GainControlImpl(const AudioProcessingImpl* apm)
: ProcessingComponent(apm),
apm_(apm),
mode_(kAdaptiveAnalog),
minimum_capture_level_(0),
maximum_capture_level_(255),
limiter_enabled_(true),
target_level_dbfs_(3),
compression_gain_db_(9),
analog_capture_level_(0),
was_analog_level_set_(false),
stream_is_saturated_(false) {}
GainControlImpl::~GainControlImpl() {}
int GainControlImpl::ProcessRenderAudio(AudioBuffer* audio) {
if (!is_component_enabled()) {
return apm_->kNoError;
}
assert(audio->samples_per_split_channel() <= 160);
WebRtc_Word16* mixed_data = audio->low_pass_split_data(0);
if (audio->num_channels() > 1) {
audio->CopyAndMixLowPass(1);
mixed_data = audio->mixed_low_pass_data(0);
}
for (int i = 0; i < num_handles(); i++) {
Handle* my_handle = static_cast<Handle*>(handle(i));
int err = WebRtcAgc_AddFarend(
my_handle,
mixed_data,
static_cast<WebRtc_Word16>(audio->samples_per_split_channel()));
if (err != apm_->kNoError) {
return GetHandleError(my_handle);
}
}
return apm_->kNoError;
}
int GainControlImpl::AnalyzeCaptureAudio(AudioBuffer* audio) {
if (!is_component_enabled()) {
return apm_->kNoError;
}
assert(audio->samples_per_split_channel() <= 160);
assert(audio->num_channels() == num_handles());
int err = apm_->kNoError;
if (mode_ == kAdaptiveAnalog) {
for (int i = 0; i < num_handles(); i++) {
Handle* my_handle = static_cast<Handle*>(handle(i));
err = WebRtcAgc_AddMic(
my_handle,
audio->low_pass_split_data(i),
audio->high_pass_split_data(i),
static_cast<WebRtc_Word16>(audio->samples_per_split_channel()));
if (err != apm_->kNoError) {
return GetHandleError(my_handle);
}
}
} else if (mode_ == kAdaptiveDigital) {
for (int i = 0; i < num_handles(); i++) {
Handle* my_handle = static_cast<Handle*>(handle(i));
WebRtc_Word32 capture_level_out = 0;
err = WebRtcAgc_VirtualMic(
my_handle,
audio->low_pass_split_data(i),
audio->high_pass_split_data(i),
static_cast<WebRtc_Word16>(audio->samples_per_split_channel()),
//capture_levels_[i],
analog_capture_level_,
&capture_level_out);
capture_levels_[i] = capture_level_out;
if (err != apm_->kNoError) {
return GetHandleError(my_handle);
}
}
}
return apm_->kNoError;
}
int GainControlImpl::ProcessCaptureAudio(AudioBuffer* audio) {
if (!is_component_enabled()) {
return apm_->kNoError;
}
if (mode_ == kAdaptiveAnalog && !was_analog_level_set_) {
return apm_->kStreamParameterNotSetError;
}
assert(audio->samples_per_split_channel() <= 160);
assert(audio->num_channels() == num_handles());
stream_is_saturated_ = false;
for (int i = 0; i < num_handles(); i++) {
Handle* my_handle = static_cast<Handle*>(handle(i));
WebRtc_Word32 capture_level_out = 0;
WebRtc_UWord8 saturation_warning = 0;
int err = WebRtcAgc_Process(
my_handle,
audio->low_pass_split_data(i),
audio->high_pass_split_data(i),
static_cast<WebRtc_Word16>(audio->samples_per_split_channel()),
audio->low_pass_split_data(i),
audio->high_pass_split_data(i),
capture_levels_[i],
&capture_level_out,
apm_->echo_cancellation()->stream_has_echo(),
&saturation_warning);
if (err != apm_->kNoError) {
return GetHandleError(my_handle);
}
capture_levels_[i] = capture_level_out;
if (saturation_warning == 1) {
stream_is_saturated_ = true;
}
}
if (mode_ == kAdaptiveAnalog) {
// Take the analog level to be the average across the handles.
analog_capture_level_ = 0;
for (int i = 0; i < num_handles(); i++) {
analog_capture_level_ += capture_levels_[i];
}
analog_capture_level_ /= num_handles();
}
was_analog_level_set_ = false;
return apm_->kNoError;
}
// TODO(ajm): ensure this is called under kAdaptiveAnalog.
int GainControlImpl::set_stream_analog_level(int level) {
was_analog_level_set_ = true;
if (level < minimum_capture_level_ || level > maximum_capture_level_) {
return apm_->kBadParameterError;
}
if (mode_ == kAdaptiveAnalog) {
if (level != analog_capture_level_) {
// The analog level has been changed; update our internal levels.
capture_levels_.assign(num_handles(), level);
}
}
analog_capture_level_ = level;
return apm_->kNoError;
}
int GainControlImpl::stream_analog_level() {
// TODO(ajm): enable this assertion?
//assert(mode_ == kAdaptiveAnalog);
return analog_capture_level_;
}
int GainControlImpl::Enable(bool enable) {
CriticalSectionScoped crit_scoped(*apm_->crit());
return EnableComponent(enable);
}
bool GainControlImpl::is_enabled() const {
return is_component_enabled();
}
int GainControlImpl::set_mode(Mode mode) {
CriticalSectionScoped crit_scoped(*apm_->crit());
if (MapSetting(mode) == -1) {
return apm_->kBadParameterError;
}
mode_ = mode;
return Initialize();
}
GainControl::Mode GainControlImpl::mode() const {
return mode_;
}
int GainControlImpl::set_analog_level_limits(int minimum,
int maximum) {
CriticalSectionScoped crit_scoped(*apm_->crit());
if (minimum < 0) {
return apm_->kBadParameterError;
}
if (maximum > 65535) {
return apm_->kBadParameterError;
}
if (maximum < minimum) {
return apm_->kBadParameterError;
}
minimum_capture_level_ = minimum;
maximum_capture_level_ = maximum;
return Initialize();
}
int GainControlImpl::analog_level_minimum() const {
return minimum_capture_level_;
}
int GainControlImpl::analog_level_maximum() const {
return maximum_capture_level_;
}
bool GainControlImpl::stream_is_saturated() const {
return stream_is_saturated_;
}
int GainControlImpl::set_target_level_dbfs(int level) {
CriticalSectionScoped crit_scoped(*apm_->crit());
if (level > 31 || level < 0) {
return apm_->kBadParameterError;
}
target_level_dbfs_ = level;
return Configure();
}
int GainControlImpl::target_level_dbfs() const {
return target_level_dbfs_;
}
int GainControlImpl::set_compression_gain_db(int gain) {
CriticalSectionScoped crit_scoped(*apm_->crit());
if (gain < 0 || gain > 90) {
return apm_->kBadParameterError;
}
compression_gain_db_ = gain;
return Configure();
}
int GainControlImpl::compression_gain_db() const {
return compression_gain_db_;
}
int GainControlImpl::enable_limiter(bool enable) {
CriticalSectionScoped crit_scoped(*apm_->crit());
limiter_enabled_ = enable;
return Configure();
}
bool GainControlImpl::is_limiter_enabled() const {
return limiter_enabled_;
}
int GainControlImpl::Initialize() {
int err = ProcessingComponent::Initialize();
if (err != apm_->kNoError || !is_component_enabled()) {
return err;
}
analog_capture_level_ =
(maximum_capture_level_ - minimum_capture_level_) >> 1;
capture_levels_.assign(num_handles(), analog_capture_level_);
was_analog_level_set_ = false;
return apm_->kNoError;
}
int GainControlImpl::get_version(char* version, int version_len_bytes) const {
if (WebRtcAgc_Version(version, version_len_bytes) != 0) {
return apm_->kBadParameterError;
}
return apm_->kNoError;
}
void* GainControlImpl::CreateHandle() const {
Handle* handle = NULL;
if (WebRtcAgc_Create(&handle) != apm_->kNoError) {
handle = NULL;
} else {
assert(handle != NULL);
}
return handle;
}
int GainControlImpl::DestroyHandle(void* handle) const {
return WebRtcAgc_Free(static_cast<Handle*>(handle));
}
int GainControlImpl::InitializeHandle(void* handle) const {
return WebRtcAgc_Init(static_cast<Handle*>(handle),
minimum_capture_level_,
maximum_capture_level_,
MapSetting(mode_),
apm_->sample_rate_hz());
}
int GainControlImpl::ConfigureHandle(void* handle) const {
WebRtcAgc_config_t config;
// TODO(ajm): Flip the sign here (since AGC expects a positive value) if we
// change the interface.
//assert(target_level_dbfs_ <= 0);
//config.targetLevelDbfs = static_cast<WebRtc_Word16>(-target_level_dbfs_);
config.targetLevelDbfs = static_cast<WebRtc_Word16>(target_level_dbfs_);
config.compressionGaindB =
static_cast<WebRtc_Word16>(compression_gain_db_);
config.limiterEnable = limiter_enabled_;
return WebRtcAgc_set_config(static_cast<Handle*>(handle), config);
}
int GainControlImpl::num_handles_required() const {
return apm_->num_output_channels();
}
int GainControlImpl::GetHandleError(void* handle) const {
// The AGC has no get_error() function.
// (Despite listing errors in its interface...)
assert(handle != NULL);
return apm_->kUnspecifiedError;
}
} // namespace webrtc

View File

@ -0,0 +1,80 @@
/*
* 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.
*/
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_GAIN_CONTROL_IMPL_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_GAIN_CONTROL_IMPL_H_
#include <vector>
#include "audio_processing.h"
#include "processing_component.h"
namespace webrtc {
class AudioProcessingImpl;
class AudioBuffer;
class GainControlImpl : public GainControl,
public ProcessingComponent {
public:
explicit GainControlImpl(const AudioProcessingImpl* apm);
virtual ~GainControlImpl();
int ProcessRenderAudio(AudioBuffer* audio);
int AnalyzeCaptureAudio(AudioBuffer* audio);
int ProcessCaptureAudio(AudioBuffer* audio);
// ProcessingComponent implementation.
virtual int Initialize();
virtual int get_version(char* version, int version_len_bytes) const;
// GainControl implementation.
virtual bool is_enabled() const;
virtual int stream_analog_level();
private:
// GainControl implementation.
virtual int Enable(bool enable);
virtual int set_stream_analog_level(int level);
virtual int set_mode(Mode mode);
virtual Mode mode() const;
virtual int set_target_level_dbfs(int level);
virtual int target_level_dbfs() const;
virtual int set_compression_gain_db(int gain);
virtual int compression_gain_db() const;
virtual int enable_limiter(bool enable);
virtual bool is_limiter_enabled() const;
virtual int set_analog_level_limits(int minimum, int maximum);
virtual int analog_level_minimum() const;
virtual int analog_level_maximum() const;
virtual bool stream_is_saturated() const;
// ProcessingComponent implementation.
virtual void* CreateHandle() const;
virtual int InitializeHandle(void* handle) const;
virtual int ConfigureHandle(void* handle) const;
virtual int DestroyHandle(void* handle) const;
virtual int num_handles_required() const;
virtual int GetHandleError(void* handle) const;
const AudioProcessingImpl* apm_;
Mode mode_;
int minimum_capture_level_;
int maximum_capture_level_;
bool limiter_enabled_;
int target_level_dbfs_;
int compression_gain_db_;
std::vector<int> capture_levels_;
int analog_capture_level_;
bool was_analog_level_set_;
bool stream_is_saturated_;
};
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_GAIN_CONTROL_IMPL_H_

View File

@ -0,0 +1,180 @@
/*
* 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.
*/
#include "high_pass_filter_impl.h"
#include <cassert>
#include "critical_section_wrapper.h"
#include "typedefs.h"
#include "signal_processing_library.h"
#include "audio_processing_impl.h"
#include "audio_buffer.h"
namespace webrtc {
namespace {
const WebRtc_Word16 kFilterCoefficients8kHz[5] =
{3798, -7596, 3798, 7807, -3733};
const WebRtc_Word16 kFilterCoefficients[5] =
{4012, -8024, 4012, 8002, -3913};
struct FilterState {
WebRtc_Word16 y[4];
WebRtc_Word16 x[2];
const WebRtc_Word16* ba;
};
int InitializeFilter(FilterState* hpf, int sample_rate_hz) {
assert(hpf != NULL);
if (sample_rate_hz == AudioProcessingImpl::kSampleRate8kHz) {
hpf->ba = kFilterCoefficients8kHz;
} else {
hpf->ba = kFilterCoefficients;
}
WebRtcSpl_MemSetW16(hpf->x, 0, 2);
WebRtcSpl_MemSetW16(hpf->y, 0, 4);
return AudioProcessing::kNoError;
}
int Filter(FilterState* hpf, WebRtc_Word16* data, int length) {
assert(hpf != NULL);
WebRtc_Word32 tmp_int32 = 0;
WebRtc_Word16* y = hpf->y;
WebRtc_Word16* x = hpf->x;
const WebRtc_Word16* ba = hpf->ba;
for (int i = 0; i < length; i++) {
// y[i] = b[0] * x[i] + b[1] * x[i-1] + b[2] * x[i-2]
// + -a[1] * y[i-1] + -a[2] * y[i-2];
tmp_int32 =
WEBRTC_SPL_MUL_16_16(y[1], ba[3]); // -a[1] * y[i-1] (low part)
tmp_int32 +=
WEBRTC_SPL_MUL_16_16(y[3], ba[4]); // -a[2] * y[i-2] (low part)
tmp_int32 = (tmp_int32 >> 15);
tmp_int32 +=
WEBRTC_SPL_MUL_16_16(y[0], ba[3]); // -a[1] * y[i-1] (high part)
tmp_int32 +=
WEBRTC_SPL_MUL_16_16(y[2], ba[4]); // -a[2] * y[i-2] (high part)
tmp_int32 = (tmp_int32 << 1);
tmp_int32 += WEBRTC_SPL_MUL_16_16(data[i], ba[0]); // b[0]*x[0]
tmp_int32 += WEBRTC_SPL_MUL_16_16(x[0], ba[1]); // b[1]*x[i-1]
tmp_int32 += WEBRTC_SPL_MUL_16_16(x[1], ba[2]); // b[2]*x[i-2]
// Update state (input part)
x[1] = x[0];
x[0] = data[i];
// Update state (filtered part)
y[2] = y[0];
y[3] = y[1];
y[0] = static_cast<WebRtc_Word16>(tmp_int32 >> 13);
y[1] = static_cast<WebRtc_Word16>((tmp_int32 -
WEBRTC_SPL_LSHIFT_W32(static_cast<WebRtc_Word32>(y[0]), 13)) << 2);
// Rounding in Q12, i.e. add 2^11
tmp_int32 += 2048;
// Saturate (to 2^27) so that the HP filtered signal does not overflow
tmp_int32 = WEBRTC_SPL_SAT(static_cast<WebRtc_Word32>(134217727),
tmp_int32,
static_cast<WebRtc_Word32>(-134217728));
// Convert back to Q0 and use rounding
data[i] = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp_int32, 12);
}
return AudioProcessing::kNoError;
}
} // namespace
typedef FilterState Handle;
HighPassFilterImpl::HighPassFilterImpl(const AudioProcessingImpl* apm)
: ProcessingComponent(apm),
apm_(apm) {}
HighPassFilterImpl::~HighPassFilterImpl() {}
int HighPassFilterImpl::ProcessCaptureAudio(AudioBuffer* audio) {
int err = apm_->kNoError;
if (!is_component_enabled()) {
return apm_->kNoError;
}
assert(audio->samples_per_split_channel() <= 160);
for (int i = 0; i < num_handles(); i++) {
Handle* my_handle = static_cast<Handle*>(handle(i));
err = Filter(my_handle,
audio->low_pass_split_data(i),
audio->samples_per_split_channel());
if (err != apm_->kNoError) {
return GetHandleError(my_handle);
}
}
return apm_->kNoError;
}
int HighPassFilterImpl::Enable(bool enable) {
CriticalSectionScoped crit_scoped(*apm_->crit());
return EnableComponent(enable);
}
bool HighPassFilterImpl::is_enabled() const {
return is_component_enabled();
}
int HighPassFilterImpl::get_version(char* version,
int version_len_bytes) const {
// An empty string is used to indicate no version information.
memset(version, 0, version_len_bytes);
return apm_->kNoError;
}
void* HighPassFilterImpl::CreateHandle() const {
return new FilterState;
}
int HighPassFilterImpl::DestroyHandle(void* handle) const {
delete static_cast<Handle*>(handle);
return apm_->kNoError;
}
int HighPassFilterImpl::InitializeHandle(void* handle) const {
return InitializeFilter(static_cast<Handle*>(handle),
apm_->sample_rate_hz());
}
int HighPassFilterImpl::ConfigureHandle(void* /*handle*/) const {
return apm_->kNoError; // Not configurable.
}
int HighPassFilterImpl::num_handles_required() const {
return apm_->num_output_channels();
}
int HighPassFilterImpl::GetHandleError(void* handle) const {
// The component has no detailed errors.
assert(handle != NULL);
return apm_->kUnspecifiedError;
}
} // namespace webrtc

View File

@ -0,0 +1,51 @@
/*
* 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.
*/
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_HIGH_PASS_FILTER_IMPL_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_HIGH_PASS_FILTER_IMPL_H_
#include "audio_processing.h"
#include "processing_component.h"
namespace webrtc {
class AudioProcessingImpl;
class AudioBuffer;
class HighPassFilterImpl : public HighPassFilter,
public ProcessingComponent {
public:
explicit HighPassFilterImpl(const AudioProcessingImpl* apm);
virtual ~HighPassFilterImpl();
int ProcessCaptureAudio(AudioBuffer* audio);
// HighPassFilter implementation.
virtual bool is_enabled() const;
// ProcessingComponent implementation.
virtual int get_version(char* version, int version_len_bytes) const;
private:
// HighPassFilter implementation.
virtual int Enable(bool enable);
// ProcessingComponent implementation.
virtual void* CreateHandle() const;
virtual int InitializeHandle(void* handle) const;
virtual int ConfigureHandle(void* handle) const;
virtual int DestroyHandle(void* handle) const;
virtual int num_handles_required() const;
virtual int GetHandleError(void* handle) const;
const AudioProcessingImpl* apm_;
};
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_HIGH_PASS_FILTER_IMPL_H_

View File

@ -0,0 +1,601 @@
/*
* 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.
*/
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_INTERFACE_AUDIO_PROCESSING_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_INTERFACE_AUDIO_PROCESSING_H_
#include <stddef.h> // size_t
#include "typedefs.h"
#include "module.h"
namespace webrtc {
class AudioFrame;
class EchoCancellation;
class EchoControlMobile;
class GainControl;
class HighPassFilter;
class LevelEstimator;
class NoiseSuppression;
class VoiceDetection;
// The Audio Processing Module (APM) provides a collection of voice processing
// components designed for real-time communications software.
//
// APM operates on two audio streams on a frame-by-frame basis. Frames of the
// primary stream, on which all processing is applied, are passed to
// |ProcessStream()|. Frames of the reverse direction stream, which are used for
// analysis by some components, are passed to |AnalyzeReverseStream()|. On the
// client-side, this will typically be the near-end (capture) and far-end
// (render) streams, respectively. APM should be placed in the signal chain as
// close to the audio hardware abstraction layer (HAL) as possible.
//
// On the server-side, the reverse stream will normally not be used, with
// processing occurring on each incoming stream.
//
// Component interfaces follow a similar pattern and are accessed through
// corresponding getters in APM. All components are disabled at create-time,
// with default settings that are recommended for most situations. New settings
// can be applied without enabling a component. Enabling a component triggers
// memory allocation and initialization to allow it to start processing the
// streams.
//
// Thread safety is provided with the following assumptions to reduce locking
// overhead:
// 1. The stream getters and setters are called from the same thread as
// ProcessStream(). More precisely, stream functions are never called
// concurrently with ProcessStream().
// 2. Parameter getters are never called concurrently with the corresponding
// setter.
//
// APM accepts only 16-bit linear PCM audio data in frames of 10 ms. Multiple
// channels should be interleaved.
//
// Usage example, omitting error checking:
// AudioProcessing* apm = AudioProcessing::Create(0);
// apm->set_sample_rate_hz(32000); // Super-wideband processing.
//
// // Mono capture and stereo render.
// apm->set_num_channels(1, 1);
// apm->set_num_reverse_channels(2);
//
// apm->high_pass_filter()->Enable(true);
//
// apm->echo_cancellation()->enable_drift_compensation(false);
// apm->echo_cancellation()->Enable(true);
//
// apm->noise_reduction()->set_level(kHighSuppression);
// apm->noise_reduction()->Enable(true);
//
// apm->gain_control()->set_analog_level_limits(0, 255);
// apm->gain_control()->set_mode(kAdaptiveAnalog);
// apm->gain_control()->Enable(true);
//
// apm->voice_detection()->Enable(true);
//
// // Start a voice call...
//
// // ... Render frame arrives bound for the audio HAL ...
// apm->AnalyzeReverseStream(render_frame);
//
// // ... Capture frame arrives from the audio HAL ...
// // Call required set_stream_ functions.
// apm->set_stream_delay_ms(delay_ms);
// apm->gain_control()->set_stream_analog_level(analog_level);
//
// apm->ProcessStream(capture_frame);
//
// // Call required stream_ functions.
// analog_level = apm->gain_control()->stream_analog_level();
// has_voice = apm->stream_has_voice();
//
// // Repeate render and capture processing for the duration of the call...
// // Start a new call...
// apm->Initialize();
//
// // Close the application...
// AudioProcessing::Destroy(apm);
// apm = NULL;
//
class AudioProcessing : public Module {
public:
// Creates a APM instance, with identifier |id|. Use one instance for every
// primary audio stream requiring processing. On the client-side, this would
// typically be one instance for the near-end stream, and additional instances
// for each far-end stream which requires processing. On the server-side,
// this would typically be one instance for every incoming stream.
static AudioProcessing* Create(int id);
// Destroys a |apm| instance.
static void Destroy(AudioProcessing* apm);
// Initializes internal states, while retaining all user settings. This
// should be called before beginning to process a new audio stream. However,
// it is not necessary to call before processing the first stream after
// creation.
virtual int Initialize() = 0;
// Sets the sample |rate| in Hz for both the primary and reverse audio
// streams. 8000, 16000 or 32000 Hz are permitted.
virtual int set_sample_rate_hz(int rate) = 0;
virtual int sample_rate_hz() const = 0;
// Sets the number of channels for the primary audio stream. Input frames must
// contain a number of channels given by |input_channels|, while output frames
// will be returned with number of channels given by |output_channels|.
virtual int set_num_channels(int input_channels, int output_channels) = 0;
virtual int num_input_channels() const = 0;
virtual int num_output_channels() const = 0;
// Sets the number of channels for the reverse audio stream. Input frames must
// contain a number of channels given by |channels|.
virtual int set_num_reverse_channels(int channels) = 0;
virtual int num_reverse_channels() const = 0;
// Processes a 10 ms |frame| of the primary audio stream. On the client-side,
// this is the near-end (or captured) audio.
//
// If needed for enabled functionality, any function with the set_stream_ tag
// must be called prior to processing the current frame. Any getter function
// with the stream_ tag which is needed should be called after processing.
//
// The |_frequencyInHz|, |_audioChannel|, and |_payloadDataLengthInSamples|
// members of |frame| must be valid, and correspond to settings supplied
// to APM.
virtual int ProcessStream(AudioFrame* frame) = 0;
// Analyzes a 10 ms |frame| of the reverse direction audio stream. The frame
// will not be modified. On the client-side, this is the far-end (or to be
// rendered) audio.
//
// It is only necessary to provide this if echo processing is enabled, as the
// reverse stream forms the echo reference signal. It is recommended, but not
// necessary, to provide if gain control is enabled. On the server-side this
// typically will not be used. If you're not sure what to pass in here,
// chances are you don't need to use it.
//
// The |_frequencyInHz|, |_audioChannel|, and |_payloadDataLengthInSamples|
// members of |frame| must be valid.
//
// TODO(ajm): add const to input; requires an implementation fix.
virtual int AnalyzeReverseStream(AudioFrame* frame) = 0;
// This must be called if and only if echo processing is enabled.
//
// Sets the |delay| in ms between AnalyzeReverseStream() receiving a far-end
// frame and ProcessStream() receiving a near-end frame containing the
// corresponding echo. On the client-side this can be expressed as
// delay = (t_render - t_analyze) + (t_process - t_capture)
// where,
// - t_analyze is the time a frame is passed to AnalyzeReverseStream() and
// t_render is the time the first sample of the same frame is rendered by
// the audio hardware.
// - t_capture is the time the first sample of a frame is captured by the
// audio hardware and t_pull is the time the same frame is passed to
// ProcessStream().
virtual int set_stream_delay_ms(int delay) = 0;
virtual int stream_delay_ms() const = 0;
// Starts recording debugging information to a file specified by |filename|,
// a NULL-terminated string. If there is an ongoing recording, the old file
// will be closed, and recording will continue in the newly specified file.
// An already existing file will be overwritten without warning.
static const int kMaxFilenameSize = 1024;
virtual int StartDebugRecording(const char filename[kMaxFilenameSize]) = 0;
// Stops recording debugging information, and closes the file. Recording
// cannot be resumed in the same file (without overwriting it).
virtual int StopDebugRecording() = 0;
// These provide access to the component interfaces and should never return
// NULL. The pointers will be valid for the lifetime of the APM instance.
// The memory for these objects is entirely managed internally.
virtual EchoCancellation* echo_cancellation() const = 0;
virtual EchoControlMobile* echo_control_mobile() const = 0;
virtual GainControl* gain_control() const = 0;
virtual HighPassFilter* high_pass_filter() const = 0;
virtual LevelEstimator* level_estimator() const = 0;
virtual NoiseSuppression* noise_suppression() const = 0;
virtual VoiceDetection* voice_detection() const = 0;
struct Statistic {
int instant; // Instantaneous value.
int average; // Long-term average.
int maximum; // Long-term maximum.
int minimum; // Long-term minimum.
};
// Fatal errors.
enum Errors {
kNoError = 0,
kUnspecifiedError = -1,
kCreationFailedError = -2,
kUnsupportedComponentError = -3,
kUnsupportedFunctionError = -4,
kNullPointerError = -5,
kBadParameterError = -6,
kBadSampleRateError = -7,
kBadDataLengthError = -8,
kBadNumberChannelsError = -9,
kFileError = -10,
kStreamParameterNotSetError = -11,
kNotEnabledError = -12
};
// Warnings are non-fatal.
enum Warnings {
// This results when a set_stream_ parameter is out of range. Processing
// will continue, but the parameter may have been truncated.
kBadStreamParameterWarning = -13,
};
// Inherited from Module.
virtual WebRtc_Word32 TimeUntilNextProcess() { return -1; };
virtual WebRtc_Word32 Process() { return -1; };
protected:
virtual ~AudioProcessing() {};
};
// The acoustic echo cancellation (AEC) component provides better performance
// than AECM but also requires more processing power and is dependent on delay
// stability and reporting accuracy. As such it is well-suited and recommended
// for PC and IP phone applications.
//
// Not recommended to be enabled on the server-side.
class EchoCancellation {
public:
// EchoCancellation and EchoControlMobile may not be enabled simultaneously.
// Enabling one will disable the other.
virtual int Enable(bool enable) = 0;
virtual bool is_enabled() const = 0;
// Differences in clock speed on the primary and reverse streams can impact
// the AEC performance. On the client-side, this could be seen when different
// render and capture devices are used, particularly with webcams.
//
// This enables a compensation mechanism, and requires that
// |set_device_sample_rate_hz()| and |set_stream_drift_samples()| be called.
virtual int enable_drift_compensation(bool enable) = 0;
virtual bool is_drift_compensation_enabled() const = 0;
// Provides the sampling rate of the audio devices. It is assumed the render
// and capture devices use the same nominal sample rate. Required if and only
// if drift compensation is enabled.
virtual int set_device_sample_rate_hz(int rate) = 0;
virtual int device_sample_rate_hz() const = 0;
// Sets the difference between the number of samples rendered and captured by
// the audio devices since the last call to |ProcessStream()|. Must be called
// if and only if drift compensation is enabled, prior to |ProcessStream()|.
virtual int set_stream_drift_samples(int drift) = 0;
virtual int stream_drift_samples() const = 0;
enum SuppressionLevel {
kLowSuppression,
kModerateSuppression,
kHighSuppression
};
// Sets the aggressiveness of the suppressor. A higher level trades off
// double-talk performance for increased echo suppression.
virtual int set_suppression_level(SuppressionLevel level) = 0;
virtual SuppressionLevel suppression_level() const = 0;
// Returns false if the current frame almost certainly contains no echo
// and true if it _might_ contain echo.
virtual bool stream_has_echo() const = 0;
// Enables the computation of various echo metrics. These are obtained
// through |GetMetrics()|.
virtual int enable_metrics(bool enable) = 0;
virtual bool are_metrics_enabled() const = 0;
// Each statistic is reported in dB.
// P_far: Far-end (render) signal power.
// P_echo: Near-end (capture) echo signal power.
// P_out: Signal power at the output of the AEC.
// P_a: Internal signal power at the point before the AEC's non-linear
// processor.
struct Metrics {
// RERL = ERL + ERLE
AudioProcessing::Statistic residual_echo_return_loss;
// ERL = 10log_10(P_far / P_echo)
AudioProcessing::Statistic echo_return_loss;
// ERLE = 10log_10(P_echo / P_out)
AudioProcessing::Statistic echo_return_loss_enhancement;
// (Pre non-linear processing suppression) A_NLP = 10log_10(P_echo / P_a)
AudioProcessing::Statistic a_nlp;
};
// TODO(ajm): discuss the metrics update period.
virtual int GetMetrics(Metrics* metrics) = 0;
// Enables computation and logging of delay values. Statistics are obtained
// through |GetDelayMetrics()|.
virtual int enable_delay_logging(bool enable) = 0;
virtual bool is_delay_logging_enabled() const = 0;
// The delay metrics consists of the delay |median| and the delay standard
// deviation |std|. The values are averaged over the time period since the
// last call to |GetDelayMetrics()|.
virtual int GetDelayMetrics(int* median, int* std) = 0;
protected:
virtual ~EchoCancellation() {};
};
// The acoustic echo control for mobile (AECM) component is a low complexity
// robust option intended for use on mobile devices.
//
// Not recommended to be enabled on the server-side.
class EchoControlMobile {
public:
// EchoCancellation and EchoControlMobile may not be enabled simultaneously.
// Enabling one will disable the other.
virtual int Enable(bool enable) = 0;
virtual bool is_enabled() const = 0;
// Recommended settings for particular audio routes. In general, the louder
// the echo is expected to be, the higher this value should be set. The
// preferred setting may vary from device to device.
enum RoutingMode {
kQuietEarpieceOrHeadset,
kEarpiece,
kLoudEarpiece,
kSpeakerphone,
kLoudSpeakerphone
};
// Sets echo control appropriate for the audio routing |mode| on the device.
// It can and should be updated during a call if the audio routing changes.
virtual int set_routing_mode(RoutingMode mode) = 0;
virtual RoutingMode routing_mode() const = 0;
// Comfort noise replaces suppressed background noise to maintain a
// consistent signal level.
virtual int enable_comfort_noise(bool enable) = 0;
virtual bool is_comfort_noise_enabled() const = 0;
// A typical use case is to initialize the component with an echo path from a
// previous call. The echo path is retrieved using |GetEchoPath()|, typically
// at the end of a call. The data can then be stored for later use as an
// initializer before the next call, using |SetEchoPath()|.
//
// Controlling the echo path this way requires the data |size_bytes| to match
// the internal echo path size. This size can be acquired using
// |echo_path_size_bytes()|. |SetEchoPath()| causes an entire reset, worth
// noting if it is to be called during an ongoing call.
//
// It is possible that version incompatibilities may result in a stored echo
// path of the incorrect size. In this case, the stored path should be
// discarded.
virtual int SetEchoPath(const void* echo_path, size_t size_bytes) = 0;
virtual int GetEchoPath(void* echo_path, size_t size_bytes) const = 0;
// The returned path size is guaranteed not to change for the lifetime of
// the application.
static size_t echo_path_size_bytes();
protected:
virtual ~EchoControlMobile() {};
};
// The automatic gain control (AGC) component brings the signal to an
// appropriate range. This is done by applying a digital gain directly and, in
// the analog mode, prescribing an analog gain to be applied at the audio HAL.
//
// Recommended to be enabled on the client-side.
class GainControl {
public:
virtual int Enable(bool enable) = 0;
virtual bool is_enabled() const = 0;
// When an analog mode is set, this must be called prior to |ProcessStream()|
// to pass the current analog level from the audio HAL. Must be within the
// range provided to |set_analog_level_limits()|.
virtual int set_stream_analog_level(int level) = 0;
// When an analog mode is set, this should be called after |ProcessStream()|
// to obtain the recommended new analog level for the audio HAL. It is the
// users responsibility to apply this level.
virtual int stream_analog_level() = 0;
enum Mode {
// Adaptive mode intended for use if an analog volume control is available
// on the capture device. It will require the user to provide coupling
// between the OS mixer controls and AGC through the |stream_analog_level()|
// functions.
//
// It consists of an analog gain prescription for the audio device and a
// digital compression stage.
kAdaptiveAnalog,
// Adaptive mode intended for situations in which an analog volume control
// is unavailable. It operates in a similar fashion to the adaptive analog
// mode, but with scaling instead applied in the digital domain. As with
// the analog mode, it additionally uses a digital compression stage.
kAdaptiveDigital,
// Fixed mode which enables only the digital compression stage also used by
// the two adaptive modes.
//
// It is distinguished from the adaptive modes by considering only a
// short time-window of the input signal. It applies a fixed gain through
// most of the input level range, and compresses (gradually reduces gain
// with increasing level) the input signal at higher levels. This mode is
// preferred on embedded devices where the capture signal level is
// predictable, so that a known gain can be applied.
kFixedDigital
};
virtual int set_mode(Mode mode) = 0;
virtual Mode mode() const = 0;
// Sets the target peak |level| (or envelope) of the AGC in dBFs (decibels
// from digital full-scale). The convention is to use positive values. For
// instance, passing in a value of 3 corresponds to -3 dBFs, or a target
// level 3 dB below full-scale. Limited to [0, 31].
//
// TODO(ajm): use a negative value here instead, if/when VoE will similarly
// update its interface.
virtual int set_target_level_dbfs(int level) = 0;
virtual int target_level_dbfs() const = 0;
// Sets the maximum |gain| the digital compression stage may apply, in dB. A
// higher number corresponds to greater compression, while a value of 0 will
// leave the signal uncompressed. Limited to [0, 90].
virtual int set_compression_gain_db(int gain) = 0;
virtual int compression_gain_db() const = 0;
// When enabled, the compression stage will hard limit the signal to the
// target level. Otherwise, the signal will be compressed but not limited
// above the target level.
virtual int enable_limiter(bool enable) = 0;
virtual bool is_limiter_enabled() const = 0;
// Sets the |minimum| and |maximum| analog levels of the audio capture device.
// Must be set if and only if an analog mode is used. Limited to [0, 65535].
virtual int set_analog_level_limits(int minimum,
int maximum) = 0;
virtual int analog_level_minimum() const = 0;
virtual int analog_level_maximum() const = 0;
// Returns true if the AGC has detected a saturation event (period where the
// signal reaches digital full-scale) in the current frame and the analog
// level cannot be reduced.
//
// This could be used as an indicator to reduce or disable analog mic gain at
// the audio HAL.
virtual bool stream_is_saturated() const = 0;
protected:
virtual ~GainControl() {};
};
// A filtering component which removes DC offset and low-frequency noise.
// Recommended to be enabled on the client-side.
class HighPassFilter {
public:
virtual int Enable(bool enable) = 0;
virtual bool is_enabled() const = 0;
protected:
virtual ~HighPassFilter() {};
};
// An estimation component used to retrieve level metrics.
// NOTE: currently unavailable. All methods return errors.
class LevelEstimator {
public:
virtual int Enable(bool enable) = 0;
virtual bool is_enabled() const = 0;
// The metrics are reported in dBFs calculated as:
// Level = 10log_10(P_s / P_max) [dBFs], where
// P_s is the signal power and P_max is the maximum possible (or peak)
// power. With 16-bit signals, P_max = (2^15)^2.
struct Metrics {
AudioProcessing::Statistic signal; // Overall signal level.
AudioProcessing::Statistic speech; // Speech level.
AudioProcessing::Statistic noise; // Noise level.
};
virtual int GetMetrics(Metrics* metrics, Metrics* reverse_metrics) = 0;
//virtual int enable_noise_warning(bool enable) = 0;
//bool is_noise_warning_enabled() const = 0;
//virtual bool stream_has_high_noise() const = 0;
protected:
virtual ~LevelEstimator() {};
};
// The noise suppression (NS) component attempts to remove noise while
// retaining speech. Recommended to be enabled on the client-side.
//
// Recommended to be enabled on the client-side.
class NoiseSuppression {
public:
virtual int Enable(bool enable) = 0;
virtual bool is_enabled() const = 0;
// Determines the aggressiveness of the suppression. Increasing the level
// will reduce the noise level at the expense of a higher speech distortion.
enum Level {
kLow,
kModerate,
kHigh,
kVeryHigh
};
virtual int set_level(Level level) = 0;
virtual Level level() const = 0;
protected:
virtual ~NoiseSuppression() {};
};
// The voice activity detection (VAD) component analyzes the stream to
// determine if voice is present. A facility is also provided to pass in an
// external VAD decision.
//
// In addition to |stream_has_voice()| the VAD decision is provided through the
// |AudioFrame| passed to |ProcessStream()|. The |_vadActivity| member will be
// modified to reflect the current decision.
class VoiceDetection {
public:
virtual int Enable(bool enable) = 0;
virtual bool is_enabled() const = 0;
// Returns true if voice is detected in the current frame. Should be called
// after |ProcessStream()|.
virtual bool stream_has_voice() const = 0;
// Some of the APM functionality requires a VAD decision. In the case that
// a decision is externally available for the current frame, it can be passed
// in here, before |ProcessStream()| is called.
//
// VoiceDetection does _not_ need to be enabled to use this. If it happens to
// be enabled, detection will be skipped for any frame in which an external
// VAD decision is provided.
virtual int set_stream_has_voice(bool has_voice) = 0;
// Specifies the likelihood that a frame will be declared to contain voice.
// A higher value makes it more likely that speech will not be clipped, at
// the expense of more noise being detected as voice.
enum Likelihood {
kVeryLowLikelihood,
kLowLikelihood,
kModerateLikelihood,
kHighLikelihood
};
virtual int set_likelihood(Likelihood likelihood) = 0;
virtual Likelihood likelihood() const = 0;
// Sets the |size| of the frames in ms on which the VAD will operate. Larger
// frames will improve detection accuracy, but reduce the frequency of
// updates.
//
// This does not impact the size of frames passed to |ProcessStream()|.
virtual int set_frame_size_ms(int size) = 0;
virtual int frame_size_ms() const = 0;
protected:
virtual ~VoiceDetection() {};
};
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_INTERFACE_AUDIO_PROCESSING_H_

View File

@ -0,0 +1,182 @@
/*
* 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.
*/
#include "level_estimator_impl.h"
#include <cassert>
#include <cstring>
#include "critical_section_wrapper.h"
#include "audio_processing_impl.h"
#include "audio_buffer.h"
// TODO(ajm): implement the underlying level estimator component.
namespace webrtc {
typedef void Handle;
namespace {
/*int EstimateLevel(AudioBuffer* audio, Handle* my_handle) {
assert(audio->samples_per_split_channel() <= 160);
WebRtc_Word16* mixed_data = audio->low_pass_split_data(0);
if (audio->num_channels() > 1) {
audio->CopyAndMixLowPass(1);
mixed_data = audio->mixed_low_pass_data(0);
}
int err = UpdateLvlEst(my_handle,
mixed_data,
audio->samples_per_split_channel());
if (err != AudioProcessing::kNoError) {
return GetHandleError(my_handle);
}
return AudioProcessing::kNoError;
}
int GetMetricsLocal(Handle* my_handle, LevelEstimator::Metrics* metrics) {
level_t levels;
memset(&levels, 0, sizeof(levels));
int err = ExportLevels(my_handle, &levels, 2);
if (err != AudioProcessing::kNoError) {
return err;
}
metrics->signal.instant = levels.instant;
metrics->signal.average = levels.average;
metrics->signal.maximum = levels.max;
metrics->signal.minimum = levels.min;
err = ExportLevels(my_handle, &levels, 1);
if (err != AudioProcessing::kNoError) {
return err;
}
metrics->speech.instant = levels.instant;
metrics->speech.average = levels.average;
metrics->speech.maximum = levels.max;
metrics->speech.minimum = levels.min;
err = ExportLevels(my_handle, &levels, 0);
if (err != AudioProcessing::kNoError) {
return err;
}
metrics->noise.instant = levels.instant;
metrics->noise.average = levels.average;
metrics->noise.maximum = levels.max;
metrics->noise.minimum = levels.min;
return AudioProcessing::kNoError;
}*/
} // namespace
LevelEstimatorImpl::LevelEstimatorImpl(const AudioProcessingImpl* apm)
: ProcessingComponent(apm),
apm_(apm) {}
LevelEstimatorImpl::~LevelEstimatorImpl() {}
int LevelEstimatorImpl::AnalyzeReverseStream(AudioBuffer* /*audio*/) {
return apm_->kUnsupportedComponentError;
/*if (!is_component_enabled()) {
return apm_->kNoError;
}
return EstimateLevel(audio, static_cast<Handle*>(handle(1)));*/
}
int LevelEstimatorImpl::ProcessCaptureAudio(AudioBuffer* /*audio*/) {
return apm_->kUnsupportedComponentError;
/*if (!is_component_enabled()) {
return apm_->kNoError;
}
return EstimateLevel(audio, static_cast<Handle*>(handle(0)));*/
}
int LevelEstimatorImpl::Enable(bool /*enable*/) {
CriticalSectionScoped crit_scoped(*apm_->crit());
return apm_->kUnsupportedComponentError;
//return EnableComponent(enable);
}
bool LevelEstimatorImpl::is_enabled() const {
return is_component_enabled();
}
int LevelEstimatorImpl::GetMetrics(LevelEstimator::Metrics* /*metrics*/,
LevelEstimator::Metrics* /*reverse_metrics*/) {
return apm_->kUnsupportedComponentError;
/*if (!is_component_enabled()) {
return apm_->kNotEnabledError;
}
int err = GetMetricsLocal(static_cast<Handle*>(handle(0)), metrics);
if (err != apm_->kNoError) {
return err;
}
err = GetMetricsLocal(static_cast<Handle*>(handle(1)), reverse_metrics);
if (err != apm_->kNoError) {
return err;
}
return apm_->kNoError;*/
}
int LevelEstimatorImpl::get_version(char* version,
int version_len_bytes) const {
// An empty string is used to indicate no version information.
memset(version, 0, version_len_bytes);
return apm_->kNoError;
}
void* LevelEstimatorImpl::CreateHandle() const {
Handle* handle = NULL;
/*if (CreateLvlEst(&handle) != apm_->kNoError) {
handle = NULL;
} else {
assert(handle != NULL);
}*/
return handle;
}
int LevelEstimatorImpl::DestroyHandle(void* /*handle*/) const {
return apm_->kUnsupportedComponentError;
//return FreeLvlEst(static_cast<Handle*>(handle));
}
int LevelEstimatorImpl::InitializeHandle(void* /*handle*/) const {
return apm_->kUnsupportedComponentError;
/*const double kIntervalSeconds = 1.5;
return InitLvlEst(static_cast<Handle*>(handle),
apm_->sample_rate_hz(),
kIntervalSeconds);*/
}
int LevelEstimatorImpl::ConfigureHandle(void* /*handle*/) const {
return apm_->kUnsupportedComponentError;
//return apm_->kNoError;
}
int LevelEstimatorImpl::num_handles_required() const {
return apm_->kUnsupportedComponentError;
//return 2;
}
int LevelEstimatorImpl::GetHandleError(void* handle) const {
// The component has no detailed errors.
assert(handle != NULL);
return apm_->kUnspecifiedError;
}
} // namespace webrtc

View File

@ -0,0 +1,53 @@
/*
* 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.
*/
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_LEVEL_ESTIMATOR_IMPL_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_LEVEL_ESTIMATOR_IMPL_H_
#include "audio_processing.h"
#include "processing_component.h"
namespace webrtc {
class AudioProcessingImpl;
class AudioBuffer;
class LevelEstimatorImpl : public LevelEstimator,
public ProcessingComponent {
public:
explicit LevelEstimatorImpl(const AudioProcessingImpl* apm);
virtual ~LevelEstimatorImpl();
int AnalyzeReverseStream(AudioBuffer* audio);
int ProcessCaptureAudio(AudioBuffer* audio);
// LevelEstimator implementation.
virtual bool is_enabled() const;
// ProcessingComponent implementation.
virtual int get_version(char* version, int version_len_bytes) const;
private:
// LevelEstimator implementation.
virtual int Enable(bool enable);
virtual int GetMetrics(Metrics* metrics, Metrics* reverse_metrics);
// ProcessingComponent implementation.
virtual void* CreateHandle() const;
virtual int InitializeHandle(void* handle) const;
virtual int ConfigureHandle(void* handle) const;
virtual int DestroyHandle(void* handle) const;
virtual int num_handles_required() const;
virtual int GetHandleError(void* handle) const;
const AudioProcessingImpl* apm_;
};
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_LEVEL_ESTIMATOR_IMPL_H_

View File

@ -0,0 +1,179 @@
/*
* 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.
*/
#include "noise_suppression_impl.h"
#include <cassert>
#include "critical_section_wrapper.h"
#if defined(WEBRTC_NS_FLOAT)
#include "noise_suppression.h"
#elif defined(WEBRTC_NS_FIXED)
#include "noise_suppression_x.h"
#endif
#include "audio_processing_impl.h"
#include "audio_buffer.h"
namespace webrtc {
#if defined(WEBRTC_NS_FLOAT)
typedef NsHandle Handle;
#elif defined(WEBRTC_NS_FIXED)
typedef NsxHandle Handle;
#endif
namespace {
int MapSetting(NoiseSuppression::Level level) {
switch (level) {
case NoiseSuppression::kLow:
return 0;
case NoiseSuppression::kModerate:
return 1;
case NoiseSuppression::kHigh:
return 2;
case NoiseSuppression::kVeryHigh:
return 3;
default:
return -1;
}
}
} // namespace
NoiseSuppressionImpl::NoiseSuppressionImpl(const AudioProcessingImpl* apm)
: ProcessingComponent(apm),
apm_(apm),
level_(kModerate) {}
NoiseSuppressionImpl::~NoiseSuppressionImpl() {}
int NoiseSuppressionImpl::ProcessCaptureAudio(AudioBuffer* audio) {
int err = apm_->kNoError;
if (!is_component_enabled()) {
return apm_->kNoError;
}
assert(audio->samples_per_split_channel() <= 160);
assert(audio->num_channels() == num_handles());
for (int i = 0; i < num_handles(); i++) {
Handle* my_handle = static_cast<Handle*>(handle(i));
#if defined(WEBRTC_NS_FLOAT)
err = WebRtcNs_Process(static_cast<Handle*>(handle(i)),
audio->low_pass_split_data(i),
audio->high_pass_split_data(i),
audio->low_pass_split_data(i),
audio->high_pass_split_data(i));
#elif defined(WEBRTC_NS_FIXED)
err = WebRtcNsx_Process(static_cast<Handle*>(handle(i)),
audio->low_pass_split_data(i),
audio->high_pass_split_data(i),
audio->low_pass_split_data(i),
audio->high_pass_split_data(i));
#endif
if (err != apm_->kNoError) {
return GetHandleError(my_handle);
}
}
return apm_->kNoError;
}
int NoiseSuppressionImpl::Enable(bool enable) {
CriticalSectionScoped crit_scoped(*apm_->crit());
return EnableComponent(enable);
}
bool NoiseSuppressionImpl::is_enabled() const {
return is_component_enabled();
}
int NoiseSuppressionImpl::set_level(Level level) {
CriticalSectionScoped crit_scoped(*apm_->crit());
if (MapSetting(level) == -1) {
return apm_->kBadParameterError;
}
level_ = level;
return Configure();
}
NoiseSuppression::Level NoiseSuppressionImpl::level() const {
return level_;
}
int NoiseSuppressionImpl::get_version(char* version,
int version_len_bytes) const {
#if defined(WEBRTC_NS_FLOAT)
if (WebRtcNs_get_version(version, version_len_bytes) != 0)
#elif defined(WEBRTC_NS_FIXED)
if (WebRtcNsx_get_version(version, version_len_bytes) != 0)
#endif
{
return apm_->kBadParameterError;
}
return apm_->kNoError;
}
void* NoiseSuppressionImpl::CreateHandle() const {
Handle* handle = NULL;
#if defined(WEBRTC_NS_FLOAT)
if (WebRtcNs_Create(&handle) != apm_->kNoError)
#elif defined(WEBRTC_NS_FIXED)
if (WebRtcNsx_Create(&handle) != apm_->kNoError)
#endif
{
handle = NULL;
} else {
assert(handle != NULL);
}
return handle;
}
int NoiseSuppressionImpl::DestroyHandle(void* handle) const {
#if defined(WEBRTC_NS_FLOAT)
return WebRtcNs_Free(static_cast<Handle*>(handle));
#elif defined(WEBRTC_NS_FIXED)
return WebRtcNsx_Free(static_cast<Handle*>(handle));
#endif
}
int NoiseSuppressionImpl::InitializeHandle(void* handle) const {
#if defined(WEBRTC_NS_FLOAT)
return WebRtcNs_Init(static_cast<Handle*>(handle), apm_->sample_rate_hz());
#elif defined(WEBRTC_NS_FIXED)
return WebRtcNsx_Init(static_cast<Handle*>(handle), apm_->sample_rate_hz());
#endif
}
int NoiseSuppressionImpl::ConfigureHandle(void* handle) const {
#if defined(WEBRTC_NS_FLOAT)
return WebRtcNs_set_policy(static_cast<Handle*>(handle),
MapSetting(level_));
#elif defined(WEBRTC_NS_FIXED)
return WebRtcNsx_set_policy(static_cast<Handle*>(handle),
MapSetting(level_));
#endif
}
int NoiseSuppressionImpl::num_handles_required() const {
return apm_->num_output_channels();
}
int NoiseSuppressionImpl::GetHandleError(void* handle) const {
// The NS has no get_error() function.
assert(handle != NULL);
return apm_->kUnspecifiedError;
}
} // namespace webrtc

View File

@ -0,0 +1,54 @@
/*
* 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.
*/
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_NOISE_SUPPRESSION_IMPL_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_NOISE_SUPPRESSION_IMPL_H_
#include "audio_processing.h"
#include "processing_component.h"
namespace webrtc {
class AudioProcessingImpl;
class AudioBuffer;
class NoiseSuppressionImpl : public NoiseSuppression,
public ProcessingComponent {
public:
explicit NoiseSuppressionImpl(const AudioProcessingImpl* apm);
virtual ~NoiseSuppressionImpl();
int ProcessCaptureAudio(AudioBuffer* audio);
// NoiseSuppression implementation.
virtual bool is_enabled() const;
// ProcessingComponent implementation.
virtual int get_version(char* version, int version_len_bytes) const;
private:
// NoiseSuppression implementation.
virtual int Enable(bool enable);
virtual int set_level(Level level);
virtual Level level() const;
// ProcessingComponent implementation.
virtual void* CreateHandle() const;
virtual int InitializeHandle(void* handle) const;
virtual int ConfigureHandle(void* handle) const;
virtual int DestroyHandle(void* handle) const;
virtual int num_handles_required() const;
virtual int GetHandleError(void* handle) const;
const AudioProcessingImpl* apm_;
Level level_;
};
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_NOISE_SUPPRESSION_IMPL_H_

View File

@ -0,0 +1,20 @@
noinst_LTLIBRARIES = libns.la libns_fix.la
libns_la_SOURCES = interface/noise_suppression.h \
noise_suppression.c \
windows_private.h \
defines.h \
ns_core.c \
ns_core.h
libns_la_CFLAGS = $(AM_CFLAGS) $(COMMON_CFLAGS) \
-I$(top_srcdir)/src/common_audio/signal_processing_library/main/interface \
-I$(top_srcdir)/src/modules/audio_processing/utility
libns_fix_la_SOURCES = interface/noise_suppression_x.h \
noise_suppression_x.c \
nsx_defines.h \
nsx_core.c \
nsx_core.h
libns_fix_la_CFLAGS = $(AM_CFLAGS) $(COMMON_CFLAGS) \
-I$(top_srcdir)/src/common_audio/signal_processing_library/main/interface \
-I$(top_srcdir)/src/modules/audio_processing/utility

View File

@ -0,0 +1,53 @@
/*
* 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.
*/
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_DEFINES_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_DEFINES_H_
//#define PROCESS_FLOW_0 // Use the traditional method.
//#define PROCESS_FLOW_1 // Use traditional with DD estimate of prior SNR.
#define PROCESS_FLOW_2 // Use the new method of speech/noise classification.
#define BLOCKL_MAX 160 // max processing block length: 160
#define ANAL_BLOCKL_MAX 256 // max analysis block length: 256
#define HALF_ANAL_BLOCKL 129 // half max analysis block length + 1
#define QUANTILE (float)0.25
#define SIMULT 3
#define END_STARTUP_LONG 200
#define END_STARTUP_SHORT 50
#define FACTOR (float)40.0
#define WIDTH (float)0.01
#define SMOOTH (float)0.75 // filter smoothing
// Length of fft work arrays.
#define IP_LENGTH (ANAL_BLOCKL_MAX >> 1) // must be at least ceil(2 + sqrt(ANAL_BLOCKL_MAX/2))
#define W_LENGTH (ANAL_BLOCKL_MAX >> 1)
//PARAMETERS FOR NEW METHOD
#define DD_PR_SNR (float)0.98 // DD update of prior SNR
#define LRT_TAVG (float)0.50 // tavg parameter for LRT (previously 0.90)
#define SPECT_FL_TAVG (float)0.30 // tavg parameter for spectral flatness measure
#define SPECT_DIFF_TAVG (float)0.30 // tavg parameter for spectral difference measure
#define PRIOR_UPDATE (float)0.10 // update parameter of prior model
#define NOISE_UPDATE (float)0.90 // update parameter for noise
#define SPEECH_UPDATE (float)0.99 // update parameter when likely speech
#define WIDTH_PR_MAP (float)4.0 // width parameter in sigmoid map for prior model
#define LRT_FEATURE_THR (float)0.5 // default threshold for LRT feature
#define SF_FEATURE_THR (float)0.5 // default threshold for Spectral Flatness feature
#define SD_FEATURE_THR (float)0.5 // default threshold for Spectral Difference feature
#define PROB_RANGE (float)0.20 // probability threshold for noise state in
// speech/noise likelihood
#define HIST_PAR_EST 1000 // histogram size for estimation of parameters
#define GAMMA_PAUSE (float)0.05 // update for conservative noise estimate
//
#define B_LIM (float)0.5 // threshold in final energy gain factor calculation
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_DEFINES_H_

View File

@ -0,0 +1,124 @@
/*
* 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.
*/
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_INTERFACE_NOISE_SUPPRESSION_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_INTERFACE_NOISE_SUPPRESSION_H_
#include "typedefs.h"
typedef struct NsHandleT NsHandle;
#ifdef __cplusplus
extern "C" {
#endif
/*
* This function returns the version number of the code.
*
* Input:
* - version : Pointer to a character array where the version
* info is stored.
* - length : Length of version.
*
* Return value : 0 - Ok
* -1 - Error (probably length is not sufficient)
*/
int WebRtcNs_get_version(char* version, short length);
/*
* This function creates an instance to the noise reduction structure
*
* Input:
* - NS_inst : Pointer to noise reduction instance that should be
* created
*
* Output:
* - NS_inst : Pointer to created noise reduction instance
*
* Return value : 0 - Ok
* -1 - Error
*/
int WebRtcNs_Create(NsHandle** NS_inst);
/*
* This function frees the dynamic memory of a specified Noise Reduction
* instance.
*
* Input:
* - NS_inst : Pointer to NS instance that should be freed
*
* Return value : 0 - Ok
* -1 - Error
*/
int WebRtcNs_Free(NsHandle* NS_inst);
/*
* This function initializes a NS instance
*
* Input:
* - NS_inst : Instance that should be initialized
* - fs : sampling frequency
*
* Output:
* - NS_inst : Initialized instance
*
* Return value : 0 - Ok
* -1 - Error
*/
int WebRtcNs_Init(NsHandle* NS_inst, WebRtc_UWord32 fs);
/*
* This changes the aggressiveness of the noise suppression method.
*
* Input:
* - NS_inst : Instance that should be initialized
* - mode : 0: Mild, 1: Medium , 2: Aggressive
*
* Output:
* - NS_inst : Initialized instance
*
* Return value : 0 - Ok
* -1 - Error
*/
int WebRtcNs_set_policy(NsHandle* NS_inst, int mode);
/*
* This functions does Noise Suppression for the inserted speech frame. The
* input and output signals should always be 10ms (80 or 160 samples).
*
* Input
* - NS_inst : NS Instance. Needs to be initiated before call.
* - spframe : Pointer to speech frame buffer for L band
* - spframe_H : Pointer to speech frame buffer for H band
* - fs : sampling frequency
*
* Output:
* - NS_inst : Updated NS instance
* - outframe : Pointer to output frame for L band
* - outframe_H : Pointer to output frame for H band
*
* Return value : 0 - OK
* -1 - Error
*/
int WebRtcNs_Process(NsHandle* NS_inst,
short* spframe,
short* spframe_H,
short* outframe,
short* outframe_H);
#ifdef __cplusplus
}
#endif
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_INTERFACE_NOISE_SUPPRESSION_H_

View File

@ -0,0 +1,123 @@
/*
* 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.
*/
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_INTERFACE_NOISE_SUPPRESSION_X_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_INTERFACE_NOISE_SUPPRESSION_X_H_
#include "typedefs.h"
typedef struct NsxHandleT NsxHandle;
#ifdef __cplusplus
extern "C" {
#endif
/*
* This function returns the version number of the code.
*
* Input:
* - version : Pointer to a character array where the version
* info is stored.
* - length : Length of version.
*
* Return value : 0 - Ok
* -1 - Error (probably length is not sufficient)
*/
int WebRtcNsx_get_version(char* version, short length);
/*
* This function creates an instance to the noise reduction structure
*
* Input:
* - nsxInst : Pointer to noise reduction instance that should be
* created
*
* Output:
* - nsxInst : Pointer to created noise reduction instance
*
* Return value : 0 - Ok
* -1 - Error
*/
int WebRtcNsx_Create(NsxHandle** nsxInst);
/*
* This function frees the dynamic memory of a specified Noise Suppression
* instance.
*
* Input:
* - nsxInst : Pointer to NS instance that should be freed
*
* Return value : 0 - Ok
* -1 - Error
*/
int WebRtcNsx_Free(NsxHandle* nsxInst);
/*
* This function initializes a NS instance
*
* Input:
* - nsxInst : Instance that should be initialized
* - fs : sampling frequency
*
* Output:
* - nsxInst : Initialized instance
*
* Return value : 0 - Ok
* -1 - Error
*/
int WebRtcNsx_Init(NsxHandle* nsxInst, WebRtc_UWord32 fs);
/*
* This changes the aggressiveness of the noise suppression method.
*
* Input:
* - nsxInst : Instance that should be initialized
* - mode : 0: Mild, 1: Medium , 2: Aggressive
*
* Output:
* - nsxInst : Initialized instance
*
* Return value : 0 - Ok
* -1 - Error
*/
int WebRtcNsx_set_policy(NsxHandle* nsxInst, int mode);
/*
* This functions does noise suppression for the inserted speech frame. The
* input and output signals should always be 10ms (80 or 160 samples).
*
* Input
* - nsxInst : NSx instance. Needs to be initiated before call.
* - speechFrame : Pointer to speech frame buffer for L band
* - speechFrameHB : Pointer to speech frame buffer for H band
* - fs : sampling frequency
*
* Output:
* - nsxInst : Updated NSx instance
* - outFrame : Pointer to output frame for L band
* - outFrameHB : Pointer to output frame for H band
*
* Return value : 0 - OK
* -1 - Error
*/
int WebRtcNsx_Process(NsxHandle* nsxInst,
short* speechFrame,
short* speechFrameHB,
short* outFrame,
short* outFrameHB);
#ifdef __cplusplus
}
#endif
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_INTERFACE_NOISE_SUPPRESSION_X_H_

View File

@ -0,0 +1,65 @@
/*
* 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.
*/
#include <stdlib.h>
#include <string.h>
#include "noise_suppression.h"
#include "ns_core.h"
#include "defines.h"
int WebRtcNs_get_version(char* versionStr, short length) {
const char version[] = "NS 2.2.0";
const short versionLen = (short)strlen(version) + 1; // +1: null-termination
if (versionStr == NULL) {
return -1;
}
if (versionLen > length) {
return -1;
}
strncpy(versionStr, version, versionLen);
return 0;
}
int WebRtcNs_Create(NsHandle** NS_inst) {
*NS_inst = (NsHandle*) malloc(sizeof(NSinst_t));
if (*NS_inst != NULL) {
(*(NSinst_t**)NS_inst)->initFlag = 0;
return 0;
} else {
return -1;
}
}
int WebRtcNs_Free(NsHandle* NS_inst) {
free(NS_inst);
return 0;
}
int WebRtcNs_Init(NsHandle* NS_inst, WebRtc_UWord32 fs) {
return WebRtcNs_InitCore((NSinst_t*) NS_inst, fs);
}
int WebRtcNs_set_policy(NsHandle* NS_inst, int mode) {
return WebRtcNs_set_policy_core((NSinst_t*) NS_inst, mode);
}
int WebRtcNs_Process(NsHandle* NS_inst, short* spframe, short* spframe_H,
short* outframe, short* outframe_H) {
return WebRtcNs_ProcessCore(
(NSinst_t*) NS_inst, spframe, spframe_H, outframe, outframe_H);
}

View File

@ -0,0 +1,65 @@
/*
* 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.
*/
#include <stdlib.h>
#include <string.h>
#include "noise_suppression_x.h"
#include "nsx_core.h"
#include "nsx_defines.h"
int WebRtcNsx_get_version(char* versionStr, short length) {
const char version[] = "NS\t3.1.0";
const short versionLen = (short)strlen(version) + 1; // +1: null-termination
if (versionStr == NULL) {
return -1;
}
if (versionLen > length) {
return -1;
}
strncpy(versionStr, version, versionLen);
return 0;
}
int WebRtcNsx_Create(NsxHandle** nsxInst) {
*nsxInst = (NsxHandle*)malloc(sizeof(NsxInst_t));
if (*nsxInst != NULL) {
(*(NsxInst_t**)nsxInst)->initFlag = 0;
return 0;
} else {
return -1;
}
}
int WebRtcNsx_Free(NsxHandle* nsxInst) {
free(nsxInst);
return 0;
}
int WebRtcNsx_Init(NsxHandle* nsxInst, WebRtc_UWord32 fs) {
return WebRtcNsx_InitCore((NsxInst_t*)nsxInst, fs);
}
int WebRtcNsx_set_policy(NsxHandle* nsxInst, int mode) {
return WebRtcNsx_set_policy_core((NsxInst_t*)nsxInst, mode);
}
int WebRtcNsx_Process(NsxHandle* nsxInst, short* speechFrame,
short* speechFrameHB, short* outFrame,
short* outFrameHB) {
return WebRtcNsx_ProcessCore(
(NsxInst_t*)nsxInst, speechFrame, speechFrameHB, outFrame, outFrameHB);
}

View File

@ -0,0 +1,58 @@
# 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.
{
'targets': [
{
'target_name': 'ns',
'type': '<(library)',
'dependencies': [
'<(webrtc_root)/common_audio/common_audio.gyp:spl',
'apm_util'
],
'include_dirs': [
'interface',
],
'direct_dependent_settings': {
'include_dirs': [
'interface',
],
},
'sources': [
'interface/noise_suppression.h',
'noise_suppression.c',
'windows_private.h',
'defines.h',
'ns_core.c',
'ns_core.h',
],
},
{
'target_name': 'ns_fix',
'type': '<(library)',
'dependencies': [
'<(webrtc_root)/common_audio/common_audio.gyp:spl',
],
'include_dirs': [
'interface',
],
'direct_dependent_settings': {
'include_dirs': [
'interface',
],
},
'sources': [
'interface/noise_suppression_x.h',
'noise_suppression_x.c',
'nsx_defines.h',
'nsx_core.c',
'nsx_core.h',
],
},
],
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,179 @@
/*
* 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.
*/
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_NS_CORE_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_NS_CORE_H_
#include "defines.h"
typedef struct NSParaExtract_t_ {
//bin size of histogram
float binSizeLrt;
float binSizeSpecFlat;
float binSizeSpecDiff;
//range of histogram over which lrt threshold is computed
float rangeAvgHistLrt;
//scale parameters: multiply dominant peaks of the histograms by scale factor to obtain
//thresholds for prior model
float factor1ModelPars; //for lrt and spectral difference
float factor2ModelPars; //for spectral_flatness: used when noise is flatter than speech
//peak limit for spectral flatness (varies between 0 and 1)
float thresPosSpecFlat;
//limit on spacing of two highest peaks in histogram: spacing determined by bin size
float limitPeakSpacingSpecFlat;
float limitPeakSpacingSpecDiff;
//limit on relevance of second peak:
float limitPeakWeightsSpecFlat;
float limitPeakWeightsSpecDiff;
//limit on fluctuation of lrt feature
float thresFluctLrt;
//limit on the max and min values for the feature thresholds
float maxLrt;
float minLrt;
float maxSpecFlat;
float minSpecFlat;
float maxSpecDiff;
float minSpecDiff;
//criteria of weight of histogram peak to accept/reject feature
int thresWeightSpecFlat;
int thresWeightSpecDiff;
} NSParaExtract_t;
typedef struct NSinst_t_ {
WebRtc_UWord32 fs;
int blockLen;
int blockLen10ms;
int windShift;
int outLen;
int anaLen;
int magnLen;
int aggrMode;
const float* window;
float dataBuf[ANAL_BLOCKL_MAX];
float syntBuf[ANAL_BLOCKL_MAX];
float outBuf[3 * BLOCKL_MAX];
int initFlag;
// parameters for quantile noise estimation
float density[SIMULT* HALF_ANAL_BLOCKL];
float lquantile[SIMULT* HALF_ANAL_BLOCKL];
float quantile[HALF_ANAL_BLOCKL];
int counter[SIMULT];
int updates;
// parameters for Wiener filter
float smooth[HALF_ANAL_BLOCKL];
float overdrive;
float denoiseBound;
int gainmap;
// fft work arrays.
int ip[IP_LENGTH];
float wfft[W_LENGTH];
// parameters for new method: some not needed, will reduce/cleanup later
WebRtc_Word32 blockInd; //frame index counter
int modelUpdatePars[4]; //parameters for updating or estimating
// thresholds/weights for prior model
float priorModelPars[7]; //parameters for prior model
float noisePrev[HALF_ANAL_BLOCKL]; //noise spectrum from previous frame
float magnPrev[HALF_ANAL_BLOCKL]; //magnitude spectrum of previous frame
float logLrtTimeAvg[HALF_ANAL_BLOCKL]; //log lrt factor with time-smoothing
float priorSpeechProb; //prior speech/noise probability
float featureData[7]; //data for features
float magnAvgPause[HALF_ANAL_BLOCKL]; //conservative noise spectrum estimate
float signalEnergy; //energy of magn
float sumMagn; //sum of magn
float whiteNoiseLevel; //initial noise estimate
float initMagnEst[HALF_ANAL_BLOCKL]; //initial magnitude spectrum estimate
float pinkNoiseNumerator; //pink noise parameter: numerator
float pinkNoiseExp; //pink noise parameter: power of freq
NSParaExtract_t featureExtractionParams; //parameters for feature extraction
//histograms for parameter estimation
int histLrt[HIST_PAR_EST];
int histSpecFlat[HIST_PAR_EST];
int histSpecDiff[HIST_PAR_EST];
//quantities for high band estimate
float speechProbHB[HALF_ANAL_BLOCKL]; //final speech/noise prob: prior + LRT
float dataBufHB[ANAL_BLOCKL_MAX]; //buffering data for HB
} NSinst_t;
#ifdef __cplusplus
extern "C" {
#endif
/****************************************************************************
* WebRtcNs_InitCore(...)
*
* This function initializes a noise suppression instance
*
* Input:
* - inst : Instance that should be initialized
* - fs : Sampling frequency
*
* Output:
* - inst : Initialized instance
*
* Return value : 0 - Ok
* -1 - Error
*/
int WebRtcNs_InitCore(NSinst_t* inst, WebRtc_UWord32 fs);
/****************************************************************************
* WebRtcNs_set_policy_core(...)
*
* This changes the aggressiveness of the noise suppression method.
*
* Input:
* - inst : Instance that should be initialized
* - mode : 0: Mild (6 dB), 1: Medium (10 dB), 2: Aggressive (15 dB)
*
* Output:
* - NS_inst : Initialized instance
*
* Return value : 0 - Ok
* -1 - Error
*/
int WebRtcNs_set_policy_core(NSinst_t* inst, int mode);
/****************************************************************************
* WebRtcNs_ProcessCore
*
* Do noise suppression.
*
* Input:
* - inst : Instance that should be initialized
* - inFrameLow : Input speech frame for lower band
* - inFrameHigh : Input speech frame for higher band
*
* Output:
* - inst : Updated instance
* - outFrameLow : Output speech frame for lower band
* - outFrameHigh : Output speech frame for higher band
*
* Return value : 0 - OK
* -1 - Error
*/
int WebRtcNs_ProcessCore(NSinst_t* inst,
short* inFrameLow,
short* inFrameHigh,
short* outFrameLow,
short* outFrameHigh);
#ifdef __cplusplus
}
#endif
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_NS_CORE_H_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,180 @@
/*
* 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.
*/
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_NSX_CORE_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_NSX_CORE_H_
#include "typedefs.h"
#include "signal_processing_library.h"
#include "nsx_defines.h"
#ifdef NS_FILEDEBUG
#include <stdio.h>
#endif
typedef struct NsxInst_t_ {
WebRtc_UWord32 fs;
const WebRtc_Word16* window;
WebRtc_Word16 analysisBuffer[ANAL_BLOCKL_MAX];
WebRtc_Word16 synthesisBuffer[ANAL_BLOCKL_MAX];
WebRtc_UWord16 noiseSupFilter[HALF_ANAL_BLOCKL];
WebRtc_UWord16 overdrive; /* Q8 */
WebRtc_UWord16 denoiseBound; /* Q14 */
const WebRtc_Word16* factor2Table;
WebRtc_Word16 noiseEstLogQuantile[SIMULT* HALF_ANAL_BLOCKL];
WebRtc_Word16 noiseEstDensity[SIMULT* HALF_ANAL_BLOCKL];
WebRtc_Word16 noiseEstCounter[SIMULT];
WebRtc_Word16 noiseEstQuantile[HALF_ANAL_BLOCKL];
WebRtc_Word16 anaLen;
int anaLen2;
int magnLen;
int aggrMode;
int stages;
int initFlag;
int gainMap;
WebRtc_Word32 maxLrt;
WebRtc_Word32 minLrt;
WebRtc_Word32 logLrtTimeAvgW32[HALF_ANAL_BLOCKL]; //log lrt factor with time-smoothing in Q8
WebRtc_Word32 featureLogLrt;
WebRtc_Word32 thresholdLogLrt;
WebRtc_Word16 weightLogLrt;
WebRtc_UWord32 featureSpecDiff;
WebRtc_UWord32 thresholdSpecDiff;
WebRtc_Word16 weightSpecDiff;
WebRtc_UWord32 featureSpecFlat;
WebRtc_UWord32 thresholdSpecFlat;
WebRtc_Word16 weightSpecFlat;
WebRtc_Word32 avgMagnPause[HALF_ANAL_BLOCKL]; //conservative estimate of noise spectrum
WebRtc_UWord32 magnEnergy;
WebRtc_UWord32 sumMagn;
WebRtc_UWord32 curAvgMagnEnergy;
WebRtc_UWord32 timeAvgMagnEnergy;
WebRtc_UWord32 timeAvgMagnEnergyTmp;
WebRtc_UWord32 whiteNoiseLevel; //initial noise estimate
WebRtc_UWord32 initMagnEst[HALF_ANAL_BLOCKL];//initial magnitude spectrum estimate
WebRtc_Word32 pinkNoiseNumerator; //pink noise parameter: numerator
WebRtc_Word32 pinkNoiseExp; //pink noise parameter: power of freq
int minNorm; //smallest normalization factor
int zeroInputSignal; //zero input signal flag
WebRtc_UWord32 prevNoiseU32[HALF_ANAL_BLOCKL]; //noise spectrum from previous frame
WebRtc_UWord16 prevMagnU16[HALF_ANAL_BLOCKL]; //magnitude spectrum from previous frame
WebRtc_Word16 priorNonSpeechProb; //prior speech/noise probability // Q14
int blockIndex; //frame index counter
int modelUpdate; //parameter for updating or estimating thresholds/weights for prior model
int cntThresUpdate;
//histograms for parameter estimation
WebRtc_Word16 histLrt[HIST_PAR_EST];
WebRtc_Word16 histSpecFlat[HIST_PAR_EST];
WebRtc_Word16 histSpecDiff[HIST_PAR_EST];
//quantities for high band estimate
WebRtc_Word16 dataBufHBFX[ANAL_BLOCKL_MAX]; /* Q0 */
int qNoise;
int prevQNoise;
int prevQMagn;
int blockLen10ms;
WebRtc_Word16 real[ANAL_BLOCKL_MAX];
WebRtc_Word16 imag[ANAL_BLOCKL_MAX];
WebRtc_Word32 energyIn;
int scaleEnergyIn;
int normData;
} NsxInst_t;
#ifdef __cplusplus
extern "C"
{
#endif
/****************************************************************************
* WebRtcNsx_InitCore(...)
*
* This function initializes a noise suppression instance
*
* Input:
* - inst : Instance that should be initialized
* - fs : Sampling frequency
*
* Output:
* - inst : Initialized instance
*
* Return value : 0 - Ok
* -1 - Error
*/
WebRtc_Word32 WebRtcNsx_InitCore(NsxInst_t* inst, WebRtc_UWord32 fs);
/****************************************************************************
* WebRtcNsx_set_policy_core(...)
*
* This changes the aggressiveness of the noise suppression method.
*
* Input:
* - inst : Instance that should be initialized
* - mode : 0: Mild (6 dB), 1: Medium (10 dB), 2: Aggressive (15 dB)
*
* Output:
* - NS_inst : Initialized instance
*
* Return value : 0 - Ok
* -1 - Error
*/
int WebRtcNsx_set_policy_core(NsxInst_t* inst, int mode);
/****************************************************************************
* WebRtcNsx_ProcessCore
*
* Do noise suppression.
*
* Input:
* - inst : Instance that should be initialized
* - inFrameLow : Input speech frame for lower band
* - inFrameHigh : Input speech frame for higher band
*
* Output:
* - inst : Updated instance
* - outFrameLow : Output speech frame for lower band
* - outFrameHigh : Output speech frame for higher band
*
* Return value : 0 - OK
* -1 - Error
*/
int WebRtcNsx_ProcessCore(NsxInst_t* inst, short* inFrameLow, short* inFrameHigh,
short* outFrameLow, short* outFrameHigh);
/****************************************************************************
* Internal functions and variable declarations shared with optimized code.
*/
void WebRtcNsx_UpdateNoiseEstimate(NsxInst_t* inst, int offset);
void WebRtcNsx_NoiseEstimation(NsxInst_t* inst, WebRtc_UWord16* magn, WebRtc_UWord32* noise,
WebRtc_Word16* qNoise);
extern const WebRtc_Word16 WebRtcNsx_kLogTable[9];
extern const WebRtc_Word16 WebRtcNsx_kLogTableFrac[256];
extern const WebRtc_Word16 WebRtcNsx_kCounterDiv[201];
#ifdef __cplusplus
}
#endif
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_NSX_CORE_H_

View File

@ -0,0 +1,240 @@
/*
* 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.
*/
#if defined(WEBRTC_ARCH_ARM_NEON) && defined(WEBRTC_ANDROID)
#include "nsx_core.h"
#include <arm_neon.h>
#include <assert.h>
void WebRtcNsx_NoiseEstimation(NsxInst_t* inst, WebRtc_UWord16* magn, WebRtc_UWord32* noise,
WebRtc_Word16* qNoise) {
WebRtc_Word32 numerator;
WebRtc_Word16 lmagn[HALF_ANAL_BLOCKL], counter, countDiv, countProd, delta, zeros, frac;
WebRtc_Word16 log2, tabind, logval, tmp16, tmp16no1, tmp16no2;
WebRtc_Word16 log2Const = 22713;
WebRtc_Word16 widthFactor = 21845;
int i, s, offset;
numerator = FACTOR_Q16;
tabind = inst->stages - inst->normData;
assert(tabind < 9);
assert(tabind > -9);
if (tabind < 0) {
logval = -WebRtcNsx_kLogTable[-tabind];
} else {
logval = WebRtcNsx_kLogTable[tabind];
}
int16x8_t logval_16x8 = vdupq_n_s16(logval);
// lmagn(i)=log(magn(i))=log(2)*log2(magn(i))
// magn is in Q(-stages), and the real lmagn values are:
// real_lmagn(i)=log(magn(i)*2^stages)=log(magn(i))+log(2^stages)
// lmagn in Q8
for (i = 0; i < inst->magnLen; i++) {
if (magn[i]) {
zeros = WebRtcSpl_NormU32((WebRtc_UWord32)magn[i]);
frac = (WebRtc_Word16)((((WebRtc_UWord32)magn[i] << zeros) & 0x7FFFFFFF) >> 23);
assert(frac < 256);
// log2(magn(i))
log2 = (WebRtc_Word16)(((31 - zeros) << 8) + WebRtcNsx_kLogTableFrac[frac]);
// log2(magn(i))*log(2)
lmagn[i] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(log2, log2Const, 15);
// + log(2^stages)
lmagn[i] += logval;
} else {
lmagn[i] = logval;
}
}
int16x4_t Q3_16x4 = vdup_n_s16(3);
int16x8_t WIDTHQ8_16x8 = vdupq_n_s16(WIDTH_Q8);
int16x8_t WIDTHFACTOR_16x8 = vdupq_n_s16(widthFactor);
WebRtc_Word16 factor = FACTOR_Q7;
if (inst->blockIndex < END_STARTUP_LONG)
factor = FACTOR_Q7_STARTUP;
// Loop over simultaneous estimates
for (s = 0; s < SIMULT; s++) {
offset = s * inst->magnLen;
// Get counter values from state
counter = inst->noiseEstCounter[s];
assert(counter < 201);
countDiv = WebRtcNsx_kCounterDiv[counter];
countProd = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16(counter, countDiv);
// quant_est(...)
WebRtc_Word16 deltaBuff[8];
int16x4_t tmp16x4_0;
int16x4_t tmp16x4_1;
int16x4_t countDiv_16x4 = vdup_n_s16(countDiv);
int16x8_t countProd_16x8 = vdupq_n_s16(countProd);
int16x8_t tmp16x8_0 = vdupq_n_s16(countDiv);
int16x8_t prod16x8 = vqrdmulhq_s16(WIDTHFACTOR_16x8, tmp16x8_0);
int16x8_t tmp16x8_1;
int16x8_t tmp16x8_2;
int16x8_t tmp16x8_3;
int16x8_t tmp16x8_4;
int16x8_t tmp16x8_5;
int32x4_t tmp32x4;
for (i = 0; i < inst->magnLen - 7; i += 8) {
// Compute delta.
// Smaller step size during startup. This prevents from using
// unrealistic values causing overflow.
tmp16x8_0 = vdupq_n_s16(factor);
vst1q_s16(deltaBuff, tmp16x8_0);
int j;
for (j = 0; j < 8; j++) {
if (inst->noiseEstDensity[offset + i + j] > 512) {
deltaBuff[j] = WebRtcSpl_DivW32W16ResW16(
numerator, inst->noiseEstDensity[offset + i + j]);
}
}
// Update log quantile estimate
// tmp16 = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(delta, countDiv, 14);
tmp32x4 = vmull_s16(vld1_s16(&deltaBuff[0]), countDiv_16x4);
tmp16x4_1 = vshrn_n_s32(tmp32x4, 14);
tmp32x4 = vmull_s16(vld1_s16(&deltaBuff[4]), countDiv_16x4);
tmp16x4_0 = vshrn_n_s32(tmp32x4, 14);
tmp16x8_0 = vcombine_s16(tmp16x4_1, tmp16x4_0); // Keep for several lines.
// prepare for the "if" branch
// tmp16 += 2;
// tmp16_1 = (Word16)(tmp16>>2);
tmp16x8_1 = vrshrq_n_s16(tmp16x8_0, 2);
// inst->noiseEstLogQuantile[offset+i] + tmp16_1;
tmp16x8_2 = vld1q_s16(&inst->noiseEstLogQuantile[offset + i]); // Keep
tmp16x8_1 = vaddq_s16(tmp16x8_2, tmp16x8_1); // Keep for several lines
// Prepare for the "else" branch
// tmp16 += 1;
// tmp16_1 = (Word16)(tmp16>>1);
tmp16x8_0 = vrshrq_n_s16(tmp16x8_0, 1);
// tmp16_2 = (Word16)WEBRTC_SPL_MUL_16_16_RSFT(tmp16_1,3,1);
tmp32x4 = vmull_s16(vget_low_s16(tmp16x8_0), Q3_16x4);
tmp16x4_1 = vshrn_n_s32(tmp32x4, 1);
// tmp16_2 = (Word16)WEBRTC_SPL_MUL_16_16_RSFT(tmp16_1,3,1);
tmp32x4 = vmull_s16(vget_high_s16(tmp16x8_0), Q3_16x4);
tmp16x4_0 = vshrn_n_s32(tmp32x4, 1);
// inst->noiseEstLogQuantile[offset + i] - tmp16_2;
tmp16x8_0 = vcombine_s16(tmp16x4_1, tmp16x4_0); // keep
tmp16x8_0 = vsubq_s16(tmp16x8_2, tmp16x8_0);
// logval is the smallest fixed point representation we can have. Values below
// that will correspond to values in the interval [0, 1], which can't possibly
// occur.
tmp16x8_0 = vmaxq_s16(tmp16x8_0, logval_16x8);
// Do the if-else branches:
tmp16x8_3 = vld1q_s16(&lmagn[i]); // keep for several lines
tmp16x8_5 = vsubq_s16(tmp16x8_3, tmp16x8_2);
__asm__("vcgt.s16 %q0, %q1, #0"::"w"(tmp16x8_4), "w"(tmp16x8_5));
__asm__("vbit %q0, %q1, %q2"::"w"(tmp16x8_2), "w"(tmp16x8_1), "w"(tmp16x8_4));
__asm__("vbif %q0, %q1, %q2"::"w"(tmp16x8_2), "w"(tmp16x8_0), "w"(tmp16x8_4));
vst1q_s16(&inst->noiseEstLogQuantile[offset + i], tmp16x8_2);
// Update density estimate
// tmp16_1 + tmp16_2
tmp16x8_1 = vld1q_s16(&inst->noiseEstDensity[offset + i]);
tmp16x8_0 = vqrdmulhq_s16(tmp16x8_1, countProd_16x8);
tmp16x8_0 = vaddq_s16(tmp16x8_0, prod16x8);
// lmagn[i] - inst->noiseEstLogQuantile[offset + i]
tmp16x8_3 = vsubq_s16(tmp16x8_3, tmp16x8_2);
tmp16x8_3 = vabsq_s16(tmp16x8_3);
tmp16x8_4 = vcgtq_s16(WIDTHQ8_16x8, tmp16x8_3);
__asm__("vbit %q0, %q1, %q2"::"w"(tmp16x8_1), "w"(tmp16x8_0), "w"(tmp16x8_4));
vst1q_s16(&inst->noiseEstDensity[offset + i], tmp16x8_1);
} // End loop over magnitude spectrum
for (; i < inst->magnLen; i++) {
// compute delta
if (inst->noiseEstDensity[offset + i] > 512) {
delta = WebRtcSpl_DivW32W16ResW16(numerator,
inst->noiseEstDensity[offset + i]);
} else {
delta = FACTOR_Q7;
if (inst->blockIndex < END_STARTUP_LONG) {
// Smaller step size during startup. This prevents from using
// unrealistic values causing overflow.
delta = FACTOR_Q7_STARTUP;
}
}
// update log quantile estimate
tmp16 = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(delta, countDiv, 14);
if (lmagn[i] > inst->noiseEstLogQuantile[offset + i]) {
// +=QUANTILE*delta/(inst->counter[s]+1) QUANTILE=0.25, =1 in Q2
// CounterDiv=1/(inst->counter[s]+1) in Q15
tmp16 += 2;
tmp16no1 = WEBRTC_SPL_RSHIFT_W16(tmp16, 2);
inst->noiseEstLogQuantile[offset + i] += tmp16no1;
} else {
tmp16 += 1;
tmp16no1 = WEBRTC_SPL_RSHIFT_W16(tmp16, 1);
// *(1-QUANTILE), in Q2 QUANTILE=0.25, 1-0.25=0.75=3 in Q2
tmp16no2 = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(tmp16no1, 3, 1);
inst->noiseEstLogQuantile[offset + i] -= tmp16no2;
if (inst->noiseEstLogQuantile[offset + i] < logval) {
// logval is the smallest fixed point representation we can have.
// Values below that will correspond to values in the interval
// [0, 1], which can't possibly occur.
inst->noiseEstLogQuantile[offset + i] = logval;
}
}
// update density estimate
if (WEBRTC_SPL_ABS_W16(lmagn[i] - inst->noiseEstLogQuantile[offset + i])
< WIDTH_Q8) {
tmp16no1 = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(
inst->noiseEstDensity[offset + i], countProd, 15);
tmp16no2 = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(
widthFactor, countDiv, 15);
inst->noiseEstDensity[offset + i] = tmp16no1 + tmp16no2;
}
} // end loop over magnitude spectrum
if (counter >= END_STARTUP_LONG) {
inst->noiseEstCounter[s] = 0;
if (inst->blockIndex >= END_STARTUP_LONG) {
WebRtcNsx_UpdateNoiseEstimate(inst, offset);
}
}
inst->noiseEstCounter[s]++;
} // end loop over simultaneous estimates
// Sequentially update the noise during startup
if (inst->blockIndex < END_STARTUP_LONG) {
WebRtcNsx_UpdateNoiseEstimate(inst, offset);
}
for (i = 0; i < inst->magnLen; i++) {
noise[i] = (WebRtc_UWord32)(inst->noiseEstQuantile[i]); // Q(qNoise)
}
(*qNoise) = (WebRtc_Word16)inst->qNoise;
}
#endif // defined(WEBRTC_ARCH_ARM_NEON) && defined(WEBRTC_ANDROID)

View File

@ -0,0 +1,59 @@
/*
* 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.
*/
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_NSX_DEFINES_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_NSX_DEFINES_H_
#define ANAL_BLOCKL_MAX 256 // max analysis block length
#define HALF_ANAL_BLOCKL 129 // half max analysis block length + 1
#define SIMULT 3
#define END_STARTUP_LONG 200
#define END_STARTUP_SHORT 50
#define FACTOR_Q16 (WebRtc_Word32)2621440 // 40 in Q16
#define FACTOR_Q7 (WebRtc_Word16)5120 // 40 in Q7
#define FACTOR_Q7_STARTUP (WebRtc_Word16)1024 // 8 in Q7
#define WIDTH_Q8 3 // 0.01 in Q8 (or 25 )
//PARAMETERS FOR NEW METHOD
#define DD_PR_SNR_Q11 2007 // ~= Q11(0.98) DD update of prior SNR
#define ONE_MINUS_DD_PR_SNR_Q11 41 // DD update of prior SNR
#define SPECT_FLAT_TAVG_Q14 4915 // (0.30) tavg parameter for spectral flatness measure
#define SPECT_DIFF_TAVG_Q8 77 // (0.30) tavg parameter for spectral flatness measure
#define PRIOR_UPDATE_Q14 1638 // Q14(0.1) update parameter of prior model
#define NOISE_UPDATE_Q8 26 // 26 ~= Q8(0.1) update parameter for noise
// probability threshold for noise state in speech/noise likelihood
#define ONE_MINUS_PROB_RANGE_Q8 205 // 205 ~= Q8(0.8)
#define HIST_PAR_EST 1000 // histogram size for estimation of parameters
//FEATURE EXTRACTION CONFIG
//bin size of histogram
#define BIN_SIZE_LRT 10
//scale parameters: multiply dominant peaks of the histograms by scale factor to obtain
// thresholds for prior model
#define FACTOR_1_LRT_DIFF 6 //for LRT and spectral difference (5 times bigger)
//for spectral_flatness: used when noise is flatter than speech (10 times bigger)
#define FACTOR_2_FLAT_Q10 922
//peak limit for spectral flatness (varies between 0 and 1)
#define THRES_PEAK_FLAT 24 // * 2 * BIN_SIZE_FLAT_FX
//limit on spacing of two highest peaks in histogram: spacing determined by bin size
#define LIM_PEAK_SPACE_FLAT_DIFF 4 // * 2 * BIN_SIZE_DIFF_FX
//limit on relevance of second peak:
#define LIM_PEAK_WEIGHT_FLAT_DIFF 2
#define THRES_FLUCT_LRT 10240 //=20 * inst->modelUpdate; fluctuation limit of LRT feat.
//limit on the max and min values for the feature thresholds
#define MAX_FLAT_Q10 38912 // * 2 * BIN_SIZE_FLAT_FX
#define MIN_FLAT_Q10 4096 // * 2 * BIN_SIZE_FLAT_FX
#define MAX_DIFF 100 // * 2 * BIN_SIZE_DIFF_FX
#define MIN_DIFF 16 // * 2 * BIN_SIZE_DIFF_FX
//criteria of weight of histogram peak to accept/reject feature
#define THRES_WEIGHT_FLAT_DIFF 154//(int)(0.3*(inst->modelUpdate)) for flatness and difference
//
#define STAT_UPDATES 9 // Update every 512 = 1 << 9 block
#define ONE_MINUS_GAMMA_PAUSE_Q8 13 // ~= Q8(0.05) update for conservative noise estimate
#define GAMMA_NOISE_TRANS_AND_SPEECH_Q8 3 // ~= Q8(0.01) update for transition and noise region
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_NSX_DEFINES_H_

View File

@ -0,0 +1,574 @@
/*
* 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.
*/
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_WINDOWS_PRIVATE_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_WINDOWS_PRIVATE_H_
// Hanning window for 4ms 16kHz
static const float kHanning64w128[128] = {
0.00000000000000f, 0.02454122852291f, 0.04906767432742f,
0.07356456359967f, 0.09801714032956f, 0.12241067519922f,
0.14673047445536f, 0.17096188876030f, 0.19509032201613f,
0.21910124015687f, 0.24298017990326f, 0.26671275747490f,
0.29028467725446f, 0.31368174039889f, 0.33688985339222f,
0.35989503653499f, 0.38268343236509f, 0.40524131400499f,
0.42755509343028f, 0.44961132965461f, 0.47139673682600f,
0.49289819222978f, 0.51410274419322f, 0.53499761988710f,
0.55557023301960f, 0.57580819141785f, 0.59569930449243f,
0.61523159058063f, 0.63439328416365f, 0.65317284295378f,
0.67155895484702f, 0.68954054473707f, 0.70710678118655f,
0.72424708295147f, 0.74095112535496f, 0.75720884650648f,
0.77301045336274f, 0.78834642762661f, 0.80320753148064f,
0.81758481315158f, 0.83146961230255f, 0.84485356524971f,
0.85772861000027f, 0.87008699110871f, 0.88192126434835f,
0.89322430119552f, 0.90398929312344f, 0.91420975570353f,
0.92387953251129f, 0.93299279883474f, 0.94154406518302f,
0.94952818059304f, 0.95694033573221f, 0.96377606579544f,
0.97003125319454f, 0.97570213003853f, 0.98078528040323f,
0.98527764238894f, 0.98917650996478f, 0.99247953459871f,
0.99518472667220f, 0.99729045667869f, 0.99879545620517f,
0.99969881869620f, 1.00000000000000f,
0.99969881869620f, 0.99879545620517f, 0.99729045667869f,
0.99518472667220f, 0.99247953459871f, 0.98917650996478f,
0.98527764238894f, 0.98078528040323f, 0.97570213003853f,
0.97003125319454f, 0.96377606579544f, 0.95694033573221f,
0.94952818059304f, 0.94154406518302f, 0.93299279883474f,
0.92387953251129f, 0.91420975570353f, 0.90398929312344f,
0.89322430119552f, 0.88192126434835f, 0.87008699110871f,
0.85772861000027f, 0.84485356524971f, 0.83146961230255f,
0.81758481315158f, 0.80320753148064f, 0.78834642762661f,
0.77301045336274f, 0.75720884650648f, 0.74095112535496f,
0.72424708295147f, 0.70710678118655f, 0.68954054473707f,
0.67155895484702f, 0.65317284295378f, 0.63439328416365f,
0.61523159058063f, 0.59569930449243f, 0.57580819141785f,
0.55557023301960f, 0.53499761988710f, 0.51410274419322f,
0.49289819222978f, 0.47139673682600f, 0.44961132965461f,
0.42755509343028f, 0.40524131400499f, 0.38268343236509f,
0.35989503653499f, 0.33688985339222f, 0.31368174039889f,
0.29028467725446f, 0.26671275747490f, 0.24298017990326f,
0.21910124015687f, 0.19509032201613f, 0.17096188876030f,
0.14673047445536f, 0.12241067519922f, 0.09801714032956f,
0.07356456359967f, 0.04906767432742f, 0.02454122852291f
};
// hybrib Hanning & flat window
static const float kBlocks80w128[128] = {
(float)0.00000000, (float)0.03271908, (float)0.06540313, (float)0.09801714, (float)0.13052619,
(float)0.16289547, (float)0.19509032, (float)0.22707626, (float)0.25881905, (float)0.29028468,
(float)0.32143947, (float)0.35225005, (float)0.38268343, (float)0.41270703, (float)0.44228869,
(float)0.47139674, (float)0.50000000, (float)0.52806785, (float)0.55557023, (float)0.58247770,
(float)0.60876143, (float)0.63439328, (float)0.65934582, (float)0.68359230, (float)0.70710678,
(float)0.72986407, (float)0.75183981, (float)0.77301045, (float)0.79335334, (float)0.81284668,
(float)0.83146961, (float)0.84920218, (float)0.86602540, (float)0.88192126, (float)0.89687274,
(float)0.91086382, (float)0.92387953, (float)0.93590593, (float)0.94693013, (float)0.95694034,
(float)0.96592583, (float)0.97387698, (float)0.98078528, (float)0.98664333, (float)0.99144486,
(float)0.99518473, (float)0.99785892, (float)0.99946459, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)0.99946459, (float)0.99785892, (float)0.99518473, (float)0.99144486,
(float)0.98664333, (float)0.98078528, (float)0.97387698, (float)0.96592583, (float)0.95694034,
(float)0.94693013, (float)0.93590593, (float)0.92387953, (float)0.91086382, (float)0.89687274,
(float)0.88192126, (float)0.86602540, (float)0.84920218, (float)0.83146961, (float)0.81284668,
(float)0.79335334, (float)0.77301045, (float)0.75183981, (float)0.72986407, (float)0.70710678,
(float)0.68359230, (float)0.65934582, (float)0.63439328, (float)0.60876143, (float)0.58247770,
(float)0.55557023, (float)0.52806785, (float)0.50000000, (float)0.47139674, (float)0.44228869,
(float)0.41270703, (float)0.38268343, (float)0.35225005, (float)0.32143947, (float)0.29028468,
(float)0.25881905, (float)0.22707626, (float)0.19509032, (float)0.16289547, (float)0.13052619,
(float)0.09801714, (float)0.06540313, (float)0.03271908
};
// hybrib Hanning & flat window
static const float kBlocks160w256[256] = {
(float)0.00000000, (float)0.01636173, (float)0.03271908, (float)0.04906767, (float)0.06540313,
(float)0.08172107, (float)0.09801714, (float)0.11428696, (float)0.13052619, (float)0.14673047,
(float)0.16289547, (float)0.17901686, (float)0.19509032, (float)0.21111155, (float)0.22707626,
(float)0.24298018, (float)0.25881905, (float)0.27458862, (float)0.29028468, (float)0.30590302,
(float)0.32143947, (float)0.33688985, (float)0.35225005, (float)0.36751594, (float)0.38268343,
(float)0.39774847, (float)0.41270703, (float)0.42755509, (float)0.44228869, (float)0.45690388,
(float)0.47139674, (float)0.48576339, (float)0.50000000, (float)0.51410274, (float)0.52806785,
(float)0.54189158, (float)0.55557023, (float)0.56910015, (float)0.58247770, (float)0.59569930,
(float)0.60876143, (float)0.62166057, (float)0.63439328, (float)0.64695615, (float)0.65934582,
(float)0.67155895, (float)0.68359230, (float)0.69544264, (float)0.70710678, (float)0.71858162,
(float)0.72986407, (float)0.74095113, (float)0.75183981, (float)0.76252720, (float)0.77301045,
(float)0.78328675, (float)0.79335334, (float)0.80320753, (float)0.81284668, (float)0.82226822,
(float)0.83146961, (float)0.84044840, (float)0.84920218, (float)0.85772861, (float)0.86602540,
(float)0.87409034, (float)0.88192126, (float)0.88951608, (float)0.89687274, (float)0.90398929,
(float)0.91086382, (float)0.91749450, (float)0.92387953, (float)0.93001722, (float)0.93590593,
(float)0.94154407, (float)0.94693013, (float)0.95206268, (float)0.95694034, (float)0.96156180,
(float)0.96592583, (float)0.97003125, (float)0.97387698, (float)0.97746197, (float)0.98078528,
(float)0.98384601, (float)0.98664333, (float)0.98917651, (float)0.99144486, (float)0.99344778,
(float)0.99518473, (float)0.99665524, (float)0.99785892, (float)0.99879546, (float)0.99946459,
(float)0.99986614, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)0.99986614, (float)0.99946459, (float)0.99879546, (float)0.99785892,
(float)0.99665524, (float)0.99518473, (float)0.99344778, (float)0.99144486, (float)0.98917651,
(float)0.98664333, (float)0.98384601, (float)0.98078528, (float)0.97746197, (float)0.97387698,
(float)0.97003125, (float)0.96592583, (float)0.96156180, (float)0.95694034, (float)0.95206268,
(float)0.94693013, (float)0.94154407, (float)0.93590593, (float)0.93001722, (float)0.92387953,
(float)0.91749450, (float)0.91086382, (float)0.90398929, (float)0.89687274, (float)0.88951608,
(float)0.88192126, (float)0.87409034, (float)0.86602540, (float)0.85772861, (float)0.84920218,
(float)0.84044840, (float)0.83146961, (float)0.82226822, (float)0.81284668, (float)0.80320753,
(float)0.79335334, (float)0.78328675, (float)0.77301045, (float)0.76252720, (float)0.75183981,
(float)0.74095113, (float)0.72986407, (float)0.71858162, (float)0.70710678, (float)0.69544264,
(float)0.68359230, (float)0.67155895, (float)0.65934582, (float)0.64695615, (float)0.63439328,
(float)0.62166057, (float)0.60876143, (float)0.59569930, (float)0.58247770, (float)0.56910015,
(float)0.55557023, (float)0.54189158, (float)0.52806785, (float)0.51410274, (float)0.50000000,
(float)0.48576339, (float)0.47139674, (float)0.45690388, (float)0.44228869, (float)0.42755509,
(float)0.41270703, (float)0.39774847, (float)0.38268343, (float)0.36751594, (float)0.35225005,
(float)0.33688985, (float)0.32143947, (float)0.30590302, (float)0.29028468, (float)0.27458862,
(float)0.25881905, (float)0.24298018, (float)0.22707626, (float)0.21111155, (float)0.19509032,
(float)0.17901686, (float)0.16289547, (float)0.14673047, (float)0.13052619, (float)0.11428696,
(float)0.09801714, (float)0.08172107, (float)0.06540313, (float)0.04906767, (float)0.03271908,
(float)0.01636173
};
// hybrib Hanning & flat window: for 20ms
static const float kBlocks320w512[512] = {
(float)0.00000000, (float)0.00818114, (float)0.01636173, (float)0.02454123, (float)0.03271908,
(float)0.04089475, (float)0.04906767, (float)0.05723732, (float)0.06540313, (float)0.07356456,
(float)0.08172107, (float)0.08987211, (float)0.09801714, (float)0.10615561, (float)0.11428696,
(float)0.12241068, (float)0.13052619, (float)0.13863297, (float)0.14673047, (float)0.15481816,
(float)0.16289547, (float)0.17096189, (float)0.17901686, (float)0.18705985, (float)0.19509032,
(float)0.20310773, (float)0.21111155, (float)0.21910124, (float)0.22707626, (float)0.23503609,
(float)0.24298018, (float)0.25090801, (float)0.25881905, (float)0.26671276, (float)0.27458862,
(float)0.28244610, (float)0.29028468, (float)0.29810383, (float)0.30590302, (float)0.31368174,
(float)0.32143947, (float)0.32917568, (float)0.33688985, (float)0.34458148, (float)0.35225005,
(float)0.35989504, (float)0.36751594, (float)0.37511224, (float)0.38268343, (float)0.39022901,
(float)0.39774847, (float)0.40524131, (float)0.41270703, (float)0.42014512, (float)0.42755509,
(float)0.43493645, (float)0.44228869, (float)0.44961133, (float)0.45690388, (float)0.46416584,
(float)0.47139674, (float)0.47859608, (float)0.48576339, (float)0.49289819, (float)0.50000000,
(float)0.50706834, (float)0.51410274, (float)0.52110274, (float)0.52806785, (float)0.53499762,
(float)0.54189158, (float)0.54874927, (float)0.55557023, (float)0.56235401, (float)0.56910015,
(float)0.57580819, (float)0.58247770, (float)0.58910822, (float)0.59569930, (float)0.60225052,
(float)0.60876143, (float)0.61523159, (float)0.62166057, (float)0.62804795, (float)0.63439328,
(float)0.64069616, (float)0.64695615, (float)0.65317284, (float)0.65934582, (float)0.66547466,
(float)0.67155895, (float)0.67759830, (float)0.68359230, (float)0.68954054, (float)0.69544264,
(float)0.70129818, (float)0.70710678, (float)0.71286806, (float)0.71858162, (float)0.72424708,
(float)0.72986407, (float)0.73543221, (float)0.74095113, (float)0.74642045, (float)0.75183981,
(float)0.75720885, (float)0.76252720, (float)0.76779452, (float)0.77301045, (float)0.77817464,
(float)0.78328675, (float)0.78834643, (float)0.79335334, (float)0.79830715, (float)0.80320753,
(float)0.80805415, (float)0.81284668, (float)0.81758481, (float)0.82226822, (float)0.82689659,
(float)0.83146961, (float)0.83598698, (float)0.84044840, (float)0.84485357, (float)0.84920218,
(float)0.85349396, (float)0.85772861, (float)0.86190585, (float)0.86602540, (float)0.87008699,
(float)0.87409034, (float)0.87803519, (float)0.88192126, (float)0.88574831, (float)0.88951608,
(float)0.89322430, (float)0.89687274, (float)0.90046115, (float)0.90398929, (float)0.90745693,
(float)0.91086382, (float)0.91420976, (float)0.91749450, (float)0.92071783, (float)0.92387953,
(float)0.92697940, (float)0.93001722, (float)0.93299280, (float)0.93590593, (float)0.93875641,
(float)0.94154407, (float)0.94426870, (float)0.94693013, (float)0.94952818, (float)0.95206268,
(float)0.95453345, (float)0.95694034, (float)0.95928317, (float)0.96156180, (float)0.96377607,
(float)0.96592583, (float)0.96801094, (float)0.97003125, (float)0.97198664, (float)0.97387698,
(float)0.97570213, (float)0.97746197, (float)0.97915640, (float)0.98078528, (float)0.98234852,
(float)0.98384601, (float)0.98527764, (float)0.98664333, (float)0.98794298, (float)0.98917651,
(float)0.99034383, (float)0.99144486, (float)0.99247953, (float)0.99344778, (float)0.99434953,
(float)0.99518473, (float)0.99595331, (float)0.99665524, (float)0.99729046, (float)0.99785892,
(float)0.99836060, (float)0.99879546, (float)0.99916346, (float)0.99946459, (float)0.99969882,
(float)0.99986614, (float)0.99996653, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000,
(float)1.00000000, (float)0.99996653, (float)0.99986614, (float)0.99969882, (float)0.99946459,
(float)0.99916346, (float)0.99879546, (float)0.99836060, (float)0.99785892, (float)0.99729046,
(float)0.99665524, (float)0.99595331, (float)0.99518473, (float)0.99434953, (float)0.99344778,
(float)0.99247953, (float)0.99144486, (float)0.99034383, (float)0.98917651, (float)0.98794298,
(float)0.98664333, (float)0.98527764, (float)0.98384601, (float)0.98234852, (float)0.98078528,
(float)0.97915640, (float)0.97746197, (float)0.97570213, (float)0.97387698, (float)0.97198664,
(float)0.97003125, (float)0.96801094, (float)0.96592583, (float)0.96377607, (float)0.96156180,
(float)0.95928317, (float)0.95694034, (float)0.95453345, (float)0.95206268, (float)0.94952818,
(float)0.94693013, (float)0.94426870, (float)0.94154407, (float)0.93875641, (float)0.93590593,
(float)0.93299280, (float)0.93001722, (float)0.92697940, (float)0.92387953, (float)0.92071783,
(float)0.91749450, (float)0.91420976, (float)0.91086382, (float)0.90745693, (float)0.90398929,
(float)0.90046115, (float)0.89687274, (float)0.89322430, (float)0.88951608, (float)0.88574831,
(float)0.88192126, (float)0.87803519, (float)0.87409034, (float)0.87008699, (float)0.86602540,
(float)0.86190585, (float)0.85772861, (float)0.85349396, (float)0.84920218, (float)0.84485357,
(float)0.84044840, (float)0.83598698, (float)0.83146961, (float)0.82689659, (float)0.82226822,
(float)0.81758481, (float)0.81284668, (float)0.80805415, (float)0.80320753, (float)0.79830715,
(float)0.79335334, (float)0.78834643, (float)0.78328675, (float)0.77817464, (float)0.77301045,
(float)0.76779452, (float)0.76252720, (float)0.75720885, (float)0.75183981, (float)0.74642045,
(float)0.74095113, (float)0.73543221, (float)0.72986407, (float)0.72424708, (float)0.71858162,
(float)0.71286806, (float)0.70710678, (float)0.70129818, (float)0.69544264, (float)0.68954054,
(float)0.68359230, (float)0.67759830, (float)0.67155895, (float)0.66547466, (float)0.65934582,
(float)0.65317284, (float)0.64695615, (float)0.64069616, (float)0.63439328, (float)0.62804795,
(float)0.62166057, (float)0.61523159, (float)0.60876143, (float)0.60225052, (float)0.59569930,
(float)0.58910822, (float)0.58247770, (float)0.57580819, (float)0.56910015, (float)0.56235401,
(float)0.55557023, (float)0.54874927, (float)0.54189158, (float)0.53499762, (float)0.52806785,
(float)0.52110274, (float)0.51410274, (float)0.50706834, (float)0.50000000, (float)0.49289819,
(float)0.48576339, (float)0.47859608, (float)0.47139674, (float)0.46416584, (float)0.45690388,
(float)0.44961133, (float)0.44228869, (float)0.43493645, (float)0.42755509, (float)0.42014512,
(float)0.41270703, (float)0.40524131, (float)0.39774847, (float)0.39022901, (float)0.38268343,
(float)0.37511224, (float)0.36751594, (float)0.35989504, (float)0.35225005, (float)0.34458148,
(float)0.33688985, (float)0.32917568, (float)0.32143947, (float)0.31368174, (float)0.30590302,
(float)0.29810383, (float)0.29028468, (float)0.28244610, (float)0.27458862, (float)0.26671276,
(float)0.25881905, (float)0.25090801, (float)0.24298018, (float)0.23503609, (float)0.22707626,
(float)0.21910124, (float)0.21111155, (float)0.20310773, (float)0.19509032, (float)0.18705985,
(float)0.17901686, (float)0.17096189, (float)0.16289547, (float)0.15481816, (float)0.14673047,
(float)0.13863297, (float)0.13052619, (float)0.12241068, (float)0.11428696, (float)0.10615561,
(float)0.09801714, (float)0.08987211, (float)0.08172107, (float)0.07356456, (float)0.06540313,
(float)0.05723732, (float)0.04906767, (float)0.04089475, (float)0.03271908, (float)0.02454123,
(float)0.01636173, (float)0.00818114
};
// Hanning window: for 15ms at 16kHz with symmetric zeros
static const float kBlocks240w512[512] = {
(float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000,
(float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000,
(float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000,
(float)0.00000000, (float)0.00000000, (float)0.00654494, (float)0.01308960, (float)0.01963369,
(float)0.02617695, (float)0.03271908, (float)0.03925982, (float)0.04579887, (float)0.05233596,
(float)0.05887080, (float)0.06540313, (float)0.07193266, (float)0.07845910, (float)0.08498218,
(float)0.09150162, (float)0.09801714, (float)0.10452846, (float)0.11103531, (float)0.11753740,
(float)0.12403446, (float)0.13052620, (float)0.13701233, (float)0.14349262, (float)0.14996676,
(float)0.15643448, (float)0.16289547, (float)0.16934951, (float)0.17579629, (float)0.18223552,
(float)0.18866697, (float)0.19509032, (float)0.20150533, (float)0.20791170, (float)0.21430916,
(float)0.22069745, (float)0.22707628, (float)0.23344538, (float)0.23980446, (float)0.24615330,
(float)0.25249159, (float)0.25881904, (float)0.26513544, (float)0.27144045, (float)0.27773386,
(float)0.28401536, (float)0.29028466, (float)0.29654160, (float)0.30278578, (float)0.30901700,
(float)0.31523499, (float)0.32143945, (float)0.32763019, (float)0.33380687, (float)0.33996925,
(float)0.34611708, (float)0.35225007, (float)0.35836795, (float)0.36447051, (float)0.37055743,
(float)0.37662852, (float)0.38268346, (float)0.38872197, (float)0.39474389, (float)0.40074885,
(float)0.40673664, (float)0.41270703, (float)0.41865975, (float)0.42459452, (float)0.43051112,
(float)0.43640924, (float)0.44228873, (float)0.44814920, (float)0.45399052, (float)0.45981237,
(float)0.46561453, (float)0.47139674, (float)0.47715878, (float)0.48290035, (float)0.48862126,
(float)0.49432120, (float)0.50000000, (float)0.50565743, (float)0.51129311, (float)0.51690692,
(float)0.52249855, (float)0.52806789, (float)0.53361452, (float)0.53913832, (float)0.54463905,
(float)0.55011642, (float)0.55557024, (float)0.56100029, (float)0.56640625, (float)0.57178795,
(float)0.57714522, (float)0.58247769, (float)0.58778524, (float)0.59306765, (float)0.59832460,
(float)0.60355598, (float)0.60876143, (float)0.61394083, (float)0.61909395, (float)0.62422055,
(float)0.62932038, (float)0.63439333, (float)0.63943899, (float)0.64445734, (float)0.64944810,
(float)0.65441096, (float)0.65934587, (float)0.66425246, (float)0.66913062, (float)0.67398012,
(float)0.67880076, (float)0.68359232, (float)0.68835455, (float)0.69308740, (float)0.69779050,
(float)0.70246369, (float)0.70710677, (float)0.71171963, (float)0.71630198, (float)0.72085363,
(float)0.72537440, (float)0.72986406, (float)0.73432255, (float)0.73874950, (float)0.74314487,
(float)0.74750835, (float)0.75183982, (float)0.75613910, (float)0.76040596, (float)0.76464027,
(float)0.76884186, (float)0.77301043, (float)0.77714598, (float)0.78124821, (float)0.78531694,
(float)0.78935206, (float)0.79335338, (float)0.79732066, (float)0.80125386, (float)0.80515265,
(float)0.80901700, (float)0.81284672, (float)0.81664157, (float)0.82040149, (float)0.82412618,
(float)0.82781565, (float)0.83146966, (float)0.83508795, (float)0.83867061, (float)0.84221727,
(float)0.84572780, (float)0.84920216, (float)0.85264021, (float)0.85604161, (float)0.85940641,
(float)0.86273444, (float)0.86602545, (float)0.86927933, (float)0.87249607, (float)0.87567532,
(float)0.87881714, (float)0.88192129, (float)0.88498765, (float)0.88801610, (float)0.89100653,
(float)0.89395881, (float)0.89687276, (float)0.89974827, (float)0.90258533, (float)0.90538365,
(float)0.90814316, (float)0.91086388, (float)0.91354549, (float)0.91618794, (float)0.91879123,
(float)0.92135513, (float)0.92387950, (float)0.92636442, (float)0.92880958, (float)0.93121493,
(float)0.93358046, (float)0.93590593, (float)0.93819135, (float)0.94043654, (float)0.94264150,
(float)0.94480604, (float)0.94693011, (float)0.94901365, (float)0.95105654, (float)0.95305866,
(float)0.95501995, (float)0.95694035, (float)0.95881975, (float)0.96065807, (float)0.96245527,
(float)0.96421117, (float)0.96592581, (float)0.96759909, (float)0.96923089, (float)0.97082120,
(float)0.97236991, (float)0.97387701, (float)0.97534233, (float)0.97676587, (float)0.97814763,
(float)0.97948742, (float)0.98078531, (float)0.98204112, (float)0.98325491, (float)0.98442656,
(float)0.98555607, (float)0.98664331, (float)0.98768836, (float)0.98869103, (float)0.98965138,
(float)0.99056935, (float)0.99144489, (float)0.99227792, (float)0.99306846, (float)0.99381649,
(float)0.99452192, (float)0.99518472, (float)0.99580491, (float)0.99638247, (float)0.99691731,
(float)0.99740952, (float)0.99785894, (float)0.99826562, (float)0.99862951, (float)0.99895066,
(float)0.99922901, (float)0.99946457, (float)0.99965733, (float)0.99980724, (float)0.99991435,
(float)0.99997860, (float)1.00000000, (float)0.99997860, (float)0.99991435, (float)0.99980724,
(float)0.99965733, (float)0.99946457, (float)0.99922901, (float)0.99895066, (float)0.99862951,
(float)0.99826562, (float)0.99785894, (float)0.99740946, (float)0.99691731, (float)0.99638247,
(float)0.99580491, (float)0.99518472, (float)0.99452192, (float)0.99381644, (float)0.99306846,
(float)0.99227792, (float)0.99144489, (float)0.99056935, (float)0.98965138, (float)0.98869103,
(float)0.98768836, (float)0.98664331, (float)0.98555607, (float)0.98442656, (float)0.98325491,
(float)0.98204112, (float)0.98078525, (float)0.97948742, (float)0.97814757, (float)0.97676587,
(float)0.97534227, (float)0.97387695, (float)0.97236991, (float)0.97082120, (float)0.96923089,
(float)0.96759909, (float)0.96592581, (float)0.96421117, (float)0.96245521, (float)0.96065807,
(float)0.95881969, (float)0.95694029, (float)0.95501995, (float)0.95305860, (float)0.95105648,
(float)0.94901365, (float)0.94693011, (float)0.94480604, (float)0.94264150, (float)0.94043654,
(float)0.93819129, (float)0.93590593, (float)0.93358046, (float)0.93121493, (float)0.92880952,
(float)0.92636436, (float)0.92387950, (float)0.92135507, (float)0.91879123, (float)0.91618794,
(float)0.91354543, (float)0.91086382, (float)0.90814310, (float)0.90538365, (float)0.90258527,
(float)0.89974827, (float)0.89687276, (float)0.89395875, (float)0.89100647, (float)0.88801610,
(float)0.88498759, (float)0.88192123, (float)0.87881714, (float)0.87567532, (float)0.87249595,
(float)0.86927933, (float)0.86602539, (float)0.86273432, (float)0.85940641, (float)0.85604161,
(float)0.85264009, (float)0.84920216, (float)0.84572780, (float)0.84221715, (float)0.83867055,
(float)0.83508795, (float)0.83146954, (float)0.82781565, (float)0.82412612, (float)0.82040137,
(float)0.81664157, (float)0.81284660, (float)0.80901700, (float)0.80515265, (float)0.80125374,
(float)0.79732066, (float)0.79335332, (float)0.78935200, (float)0.78531694, (float)0.78124815,
(float)0.77714586, (float)0.77301049, (float)0.76884180, (float)0.76464021, (float)0.76040596,
(float)0.75613904, (float)0.75183970, (float)0.74750835, (float)0.74314481, (float)0.73874938,
(float)0.73432249, (float)0.72986400, (float)0.72537428, (float)0.72085363, (float)0.71630186,
(float)0.71171951, (float)0.70710677, (float)0.70246363, (float)0.69779032, (float)0.69308734,
(float)0.68835449, (float)0.68359220, (float)0.67880070, (float)0.67398006, (float)0.66913044,
(float)0.66425240, (float)0.65934575, (float)0.65441096, (float)0.64944804, (float)0.64445722,
(float)0.63943905, (float)0.63439327, (float)0.62932026, (float)0.62422055, (float)0.61909389,
(float)0.61394072, (float)0.60876143, (float)0.60355592, (float)0.59832448, (float)0.59306765,
(float)0.58778518, (float)0.58247757, (float)0.57714522, (float)0.57178789, (float)0.56640613,
(float)0.56100023, (float)0.55557019, (float)0.55011630, (float)0.54463905, (float)0.53913826,
(float)0.53361434, (float)0.52806783, (float)0.52249849, (float)0.51690674, (float)0.51129305,
(float)0.50565726, (float)0.50000006, (float)0.49432117, (float)0.48862115, (float)0.48290038,
(float)0.47715873, (float)0.47139663, (float)0.46561456, (float)0.45981231, (float)0.45399037,
(float)0.44814920, (float)0.44228864, (float)0.43640912, (float)0.43051112, (float)0.42459446,
(float)0.41865960, (float)0.41270703, (float)0.40673658, (float)0.40074870, (float)0.39474386,
(float)0.38872188, (float)0.38268328, (float)0.37662849, (float)0.37055734, (float)0.36447033,
(float)0.35836792, (float)0.35224995, (float)0.34611690, (float)0.33996922, (float)0.33380675,
(float)0.32763001, (float)0.32143945, (float)0.31523487, (float)0.30901679, (float)0.30278572,
(float)0.29654145, (float)0.29028472, (float)0.28401530, (float)0.27773371, (float)0.27144048,
(float)0.26513538, (float)0.25881892, (float)0.25249159, (float)0.24615324, (float)0.23980433,
(float)0.23344538, (float)0.22707619, (float)0.22069728, (float)0.21430916, (float)0.20791161,
(float)0.20150517, (float)0.19509031, (float)0.18866688, (float)0.18223536, (float)0.17579627,
(float)0.16934940, (float)0.16289529, (float)0.15643445, (float)0.14996666, (float)0.14349243,
(float)0.13701232, (float)0.13052608, (float)0.12403426, (float)0.11753736, (float)0.11103519,
(float)0.10452849, (float)0.09801710, (float)0.09150149, (float)0.08498220, (float)0.07845904,
(float)0.07193252, (float)0.06540315, (float)0.05887074, (float)0.05233581, (float)0.04579888,
(float)0.03925974, (float)0.03271893, (float)0.02617695, (float)0.01963361, (float)0.01308943,
(float)0.00654493, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000,
(float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000,
(float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000,
(float)0.00000000, (float)0.00000000
};
// Hanning window: for 30ms with 1024 fft with symmetric zeros at 16kHz
static const float kBlocks480w1024[1024] = {
(float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000,
(float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000,
(float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000,
(float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000,
(float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000,
(float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000,
(float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00327249, (float)0.00654494,
(float)0.00981732, (float)0.01308960, (float)0.01636173, (float)0.01963369, (float)0.02290544,
(float)0.02617695, (float)0.02944817, (float)0.03271908, (float)0.03598964, (float)0.03925982,
(float)0.04252957, (float)0.04579887, (float)0.04906768, (float)0.05233596, (float)0.05560368,
(float)0.05887080, (float)0.06213730, (float)0.06540313, (float)0.06866825, (float)0.07193266,
(float)0.07519628, (float)0.07845910, (float)0.08172107, (float)0.08498218, (float)0.08824237,
(float)0.09150162, (float)0.09475989, (float)0.09801714, (float)0.10127335, (float)0.10452846,
(float)0.10778246, (float)0.11103531, (float)0.11428697, (float)0.11753740, (float)0.12078657,
(float)0.12403446, (float)0.12728101, (float)0.13052620, (float)0.13376999, (float)0.13701233,
(float)0.14025325, (float)0.14349262, (float)0.14673047, (float)0.14996676, (float)0.15320145,
(float)0.15643448, (float)0.15966582, (float)0.16289547, (float)0.16612339, (float)0.16934951,
(float)0.17257382, (float)0.17579629, (float)0.17901687, (float)0.18223552, (float)0.18545224,
(float)0.18866697, (float)0.19187967, (float)0.19509032, (float)0.19829889, (float)0.20150533,
(float)0.20470962, (float)0.20791170, (float)0.21111156, (float)0.21430916, (float)0.21750447,
(float)0.22069745, (float)0.22388805, (float)0.22707628, (float)0.23026206, (float)0.23344538,
(float)0.23662618, (float)0.23980446, (float)0.24298020, (float)0.24615330, (float)0.24932377,
(float)0.25249159, (float)0.25565669, (float)0.25881904, (float)0.26197866, (float)0.26513544,
(float)0.26828939, (float)0.27144045, (float)0.27458861, (float)0.27773386, (float)0.28087610,
(float)0.28401536, (float)0.28715158, (float)0.29028466, (float)0.29341471, (float)0.29654160,
(float)0.29966527, (float)0.30278578, (float)0.30590302, (float)0.30901700, (float)0.31212768,
(float)0.31523499, (float)0.31833893, (float)0.32143945, (float)0.32453656, (float)0.32763019,
(float)0.33072028, (float)0.33380687, (float)0.33688986, (float)0.33996925, (float)0.34304500,
(float)0.34611708, (float)0.34918544, (float)0.35225007, (float)0.35531089, (float)0.35836795,
(float)0.36142117, (float)0.36447051, (float)0.36751595, (float)0.37055743, (float)0.37359497,
(float)0.37662852, (float)0.37965801, (float)0.38268346, (float)0.38570479, (float)0.38872197,
(float)0.39173502, (float)0.39474389, (float)0.39774847, (float)0.40074885, (float)0.40374491,
(float)0.40673664, (float)0.40972406, (float)0.41270703, (float)0.41568562, (float)0.41865975,
(float)0.42162940, (float)0.42459452, (float)0.42755508, (float)0.43051112, (float)0.43346250,
(float)0.43640924, (float)0.43935132, (float)0.44228873, (float)0.44522133, (float)0.44814920,
(float)0.45107228, (float)0.45399052, (float)0.45690390, (float)0.45981237, (float)0.46271592,
(float)0.46561453, (float)0.46850815, (float)0.47139674, (float)0.47428030, (float)0.47715878,
(float)0.48003215, (float)0.48290035, (float)0.48576337, (float)0.48862126, (float)0.49147385,
(float)0.49432120, (float)0.49716330, (float)0.50000000, (float)0.50283140, (float)0.50565743,
(float)0.50847799, (float)0.51129311, (float)0.51410276, (float)0.51690692, (float)0.51970553,
(float)0.52249855, (float)0.52528602, (float)0.52806789, (float)0.53084403, (float)0.53361452,
(float)0.53637928, (float)0.53913832, (float)0.54189163, (float)0.54463905, (float)0.54738063,
(float)0.55011642, (float)0.55284631, (float)0.55557024, (float)0.55828828, (float)0.56100029,
(float)0.56370628, (float)0.56640625, (float)0.56910014, (float)0.57178795, (float)0.57446963,
(float)0.57714522, (float)0.57981455, (float)0.58247769, (float)0.58513463, (float)0.58778524,
(float)0.59042960, (float)0.59306765, (float)0.59569931, (float)0.59832460, (float)0.60094351,
(float)0.60355598, (float)0.60616195, (float)0.60876143, (float)0.61135441, (float)0.61394083,
(float)0.61652070, (float)0.61909395, (float)0.62166059, (float)0.62422055, (float)0.62677383,
(float)0.62932038, (float)0.63186020, (float)0.63439333, (float)0.63691956, (float)0.63943899,
(float)0.64195162, (float)0.64445734, (float)0.64695615, (float)0.64944810, (float)0.65193301,
(float)0.65441096, (float)0.65688187, (float)0.65934587, (float)0.66180271, (float)0.66425246,
(float)0.66669512, (float)0.66913062, (float)0.67155898, (float)0.67398012, (float)0.67639405,
(float)0.67880076, (float)0.68120021, (float)0.68359232, (float)0.68597710, (float)0.68835455,
(float)0.69072467, (float)0.69308740, (float)0.69544262, (float)0.69779050, (float)0.70013082,
(float)0.70246369, (float)0.70478904, (float)0.70710677, (float)0.70941699, (float)0.71171963,
(float)0.71401459, (float)0.71630198, (float)0.71858168, (float)0.72085363, (float)0.72311789,
(float)0.72537440, (float)0.72762316, (float)0.72986406, (float)0.73209721, (float)0.73432255,
(float)0.73653996, (float)0.73874950, (float)0.74095118, (float)0.74314487, (float)0.74533057,
(float)0.74750835, (float)0.74967808, (float)0.75183982, (float)0.75399351, (float)0.75613910,
(float)0.75827658, (float)0.76040596, (float)0.76252723, (float)0.76464027, (float)0.76674515,
(float)0.76884186, (float)0.77093029, (float)0.77301043, (float)0.77508241, (float)0.77714598,
(float)0.77920127, (float)0.78124821, (float)0.78328675, (float)0.78531694, (float)0.78733873,
(float)0.78935206, (float)0.79135692, (float)0.79335338, (float)0.79534125, (float)0.79732066,
(float)0.79929149, (float)0.80125386, (float)0.80320752, (float)0.80515265, (float)0.80708915,
(float)0.80901700, (float)0.81093621, (float)0.81284672, (float)0.81474853, (float)0.81664157,
(float)0.81852591, (float)0.82040149, (float)0.82226825, (float)0.82412618, (float)0.82597536,
(float)0.82781565, (float)0.82964706, (float)0.83146966, (float)0.83328325, (float)0.83508795,
(float)0.83688378, (float)0.83867061, (float)0.84044838, (float)0.84221727, (float)0.84397703,
(float)0.84572780, (float)0.84746957, (float)0.84920216, (float)0.85092574, (float)0.85264021,
(float)0.85434544, (float)0.85604161, (float)0.85772866, (float)0.85940641, (float)0.86107504,
(float)0.86273444, (float)0.86438453, (float)0.86602545, (float)0.86765707, (float)0.86927933,
(float)0.87089235, (float)0.87249607, (float)0.87409031, (float)0.87567532, (float)0.87725097,
(float)0.87881714, (float)0.88037390, (float)0.88192129, (float)0.88345921, (float)0.88498765,
(float)0.88650668, (float)0.88801610, (float)0.88951612, (float)0.89100653, (float)0.89248741,
(float)0.89395881, (float)0.89542055, (float)0.89687276, (float)0.89831537, (float)0.89974827,
(float)0.90117162, (float)0.90258533, (float)0.90398932, (float)0.90538365, (float)0.90676826,
(float)0.90814316, (float)0.90950841, (float)0.91086388, (float)0.91220951, (float)0.91354549,
(float)0.91487163, (float)0.91618794, (float)0.91749454, (float)0.91879123, (float)0.92007810,
(float)0.92135513, (float)0.92262226, (float)0.92387950, (float)0.92512691, (float)0.92636442,
(float)0.92759192, (float)0.92880958, (float)0.93001723, (float)0.93121493, (float)0.93240267,
(float)0.93358046, (float)0.93474817, (float)0.93590593, (float)0.93705362, (float)0.93819135,
(float)0.93931901, (float)0.94043654, (float)0.94154406, (float)0.94264150, (float)0.94372880,
(float)0.94480604, (float)0.94587320, (float)0.94693011, (float)0.94797695, (float)0.94901365,
(float)0.95004016, (float)0.95105654, (float)0.95206273, (float)0.95305866, (float)0.95404440,
(float)0.95501995, (float)0.95598525, (float)0.95694035, (float)0.95788521, (float)0.95881975,
(float)0.95974404, (float)0.96065807, (float)0.96156180, (float)0.96245527, (float)0.96333838,
(float)0.96421117, (float)0.96507370, (float)0.96592581, (float)0.96676767, (float)0.96759909,
(float)0.96842021, (float)0.96923089, (float)0.97003126, (float)0.97082120, (float)0.97160077,
(float)0.97236991, (float)0.97312868, (float)0.97387701, (float)0.97461486, (float)0.97534233,
(float)0.97605932, (float)0.97676587, (float)0.97746199, (float)0.97814763, (float)0.97882277,
(float)0.97948742, (float)0.98014158, (float)0.98078531, (float)0.98141843, (float)0.98204112,
(float)0.98265332, (float)0.98325491, (float)0.98384601, (float)0.98442656, (float)0.98499662,
(float)0.98555607, (float)0.98610497, (float)0.98664331, (float)0.98717111, (float)0.98768836,
(float)0.98819500, (float)0.98869103, (float)0.98917651, (float)0.98965138, (float)0.99011570,
(float)0.99056935, (float)0.99101239, (float)0.99144489, (float)0.99186671, (float)0.99227792,
(float)0.99267852, (float)0.99306846, (float)0.99344778, (float)0.99381649, (float)0.99417448,
(float)0.99452192, (float)0.99485862, (float)0.99518472, (float)0.99550015, (float)0.99580491,
(float)0.99609905, (float)0.99638247, (float)0.99665523, (float)0.99691731, (float)0.99716878,
(float)0.99740952, (float)0.99763954, (float)0.99785894, (float)0.99806762, (float)0.99826562,
(float)0.99845290, (float)0.99862951, (float)0.99879545, (float)0.99895066, (float)0.99909520,
(float)0.99922901, (float)0.99935216, (float)0.99946457, (float)0.99956632, (float)0.99965733,
(float)0.99973762, (float)0.99980724, (float)0.99986613, (float)0.99991435, (float)0.99995178,
(float)0.99997860, (float)0.99999464, (float)1.00000000, (float)0.99999464, (float)0.99997860,
(float)0.99995178, (float)0.99991435, (float)0.99986613, (float)0.99980724, (float)0.99973762,
(float)0.99965733, (float)0.99956632, (float)0.99946457, (float)0.99935216, (float)0.99922901,
(float)0.99909520, (float)0.99895066, (float)0.99879545, (float)0.99862951, (float)0.99845290,
(float)0.99826562, (float)0.99806762, (float)0.99785894, (float)0.99763954, (float)0.99740946,
(float)0.99716872, (float)0.99691731, (float)0.99665523, (float)0.99638247, (float)0.99609905,
(float)0.99580491, (float)0.99550015, (float)0.99518472, (float)0.99485862, (float)0.99452192,
(float)0.99417448, (float)0.99381644, (float)0.99344778, (float)0.99306846, (float)0.99267852,
(float)0.99227792, (float)0.99186671, (float)0.99144489, (float)0.99101239, (float)0.99056935,
(float)0.99011564, (float)0.98965138, (float)0.98917651, (float)0.98869103, (float)0.98819494,
(float)0.98768836, (float)0.98717111, (float)0.98664331, (float)0.98610497, (float)0.98555607,
(float)0.98499656, (float)0.98442656, (float)0.98384601, (float)0.98325491, (float)0.98265326,
(float)0.98204112, (float)0.98141843, (float)0.98078525, (float)0.98014158, (float)0.97948742,
(float)0.97882277, (float)0.97814757, (float)0.97746193, (float)0.97676587, (float)0.97605932,
(float)0.97534227, (float)0.97461486, (float)0.97387695, (float)0.97312862, (float)0.97236991,
(float)0.97160077, (float)0.97082120, (float)0.97003126, (float)0.96923089, (float)0.96842015,
(float)0.96759909, (float)0.96676761, (float)0.96592581, (float)0.96507365, (float)0.96421117,
(float)0.96333838, (float)0.96245521, (float)0.96156180, (float)0.96065807, (float)0.95974404,
(float)0.95881969, (float)0.95788515, (float)0.95694029, (float)0.95598525, (float)0.95501995,
(float)0.95404440, (float)0.95305860, (float)0.95206267, (float)0.95105648, (float)0.95004016,
(float)0.94901365, (float)0.94797695, (float)0.94693011, (float)0.94587314, (float)0.94480604,
(float)0.94372880, (float)0.94264150, (float)0.94154406, (float)0.94043654, (float)0.93931895,
(float)0.93819129, (float)0.93705362, (float)0.93590593, (float)0.93474817, (float)0.93358046,
(float)0.93240267, (float)0.93121493, (float)0.93001723, (float)0.92880952, (float)0.92759192,
(float)0.92636436, (float)0.92512691, (float)0.92387950, (float)0.92262226, (float)0.92135507,
(float)0.92007804, (float)0.91879123, (float)0.91749448, (float)0.91618794, (float)0.91487157,
(float)0.91354543, (float)0.91220951, (float)0.91086382, (float)0.90950835, (float)0.90814310,
(float)0.90676820, (float)0.90538365, (float)0.90398932, (float)0.90258527, (float)0.90117157,
(float)0.89974827, (float)0.89831525, (float)0.89687276, (float)0.89542055, (float)0.89395875,
(float)0.89248741, (float)0.89100647, (float)0.88951600, (float)0.88801610, (float)0.88650662,
(float)0.88498759, (float)0.88345915, (float)0.88192123, (float)0.88037384, (float)0.87881714,
(float)0.87725091, (float)0.87567532, (float)0.87409031, (float)0.87249595, (float)0.87089223,
(float)0.86927933, (float)0.86765701, (float)0.86602539, (float)0.86438447, (float)0.86273432,
(float)0.86107504, (float)0.85940641, (float)0.85772860, (float)0.85604161, (float)0.85434544,
(float)0.85264009, (float)0.85092574, (float)0.84920216, (float)0.84746951, (float)0.84572780,
(float)0.84397697, (float)0.84221715, (float)0.84044844, (float)0.83867055, (float)0.83688372,
(float)0.83508795, (float)0.83328319, (float)0.83146954, (float)0.82964706, (float)0.82781565,
(float)0.82597530, (float)0.82412612, (float)0.82226813, (float)0.82040137, (float)0.81852591,
(float)0.81664157, (float)0.81474847, (float)0.81284660, (float)0.81093609, (float)0.80901700,
(float)0.80708915, (float)0.80515265, (float)0.80320752, (float)0.80125374, (float)0.79929143,
(float)0.79732066, (float)0.79534125, (float)0.79335332, (float)0.79135686, (float)0.78935200,
(float)0.78733861, (float)0.78531694, (float)0.78328675, (float)0.78124815, (float)0.77920121,
(float)0.77714586, (float)0.77508223, (float)0.77301049, (float)0.77093029, (float)0.76884180,
(float)0.76674509, (float)0.76464021, (float)0.76252711, (float)0.76040596, (float)0.75827658,
(float)0.75613904, (float)0.75399339, (float)0.75183970, (float)0.74967796, (float)0.74750835,
(float)0.74533057, (float)0.74314481, (float)0.74095106, (float)0.73874938, (float)0.73653996,
(float)0.73432249, (float)0.73209721, (float)0.72986400, (float)0.72762305, (float)0.72537428,
(float)0.72311789, (float)0.72085363, (float)0.71858162, (float)0.71630186, (float)0.71401453,
(float)0.71171951, (float)0.70941705, (float)0.70710677, (float)0.70478898, (float)0.70246363,
(float)0.70013070, (float)0.69779032, (float)0.69544268, (float)0.69308734, (float)0.69072461,
(float)0.68835449, (float)0.68597704, (float)0.68359220, (float)0.68120021, (float)0.67880070,
(float)0.67639399, (float)0.67398006, (float)0.67155886, (float)0.66913044, (float)0.66669512,
(float)0.66425240, (float)0.66180259, (float)0.65934575, (float)0.65688181, (float)0.65441096,
(float)0.65193301, (float)0.64944804, (float)0.64695609, (float)0.64445722, (float)0.64195150,
(float)0.63943905, (float)0.63691956, (float)0.63439327, (float)0.63186014, (float)0.62932026,
(float)0.62677372, (float)0.62422055, (float)0.62166059, (float)0.61909389, (float)0.61652064,
(float)0.61394072, (float)0.61135429, (float)0.60876143, (float)0.60616189, (float)0.60355592,
(float)0.60094339, (float)0.59832448, (float)0.59569913, (float)0.59306765, (float)0.59042960,
(float)0.58778518, (float)0.58513451, (float)0.58247757, (float)0.57981461, (float)0.57714522,
(float)0.57446963, (float)0.57178789, (float)0.56910002, (float)0.56640613, (float)0.56370628,
(float)0.56100023, (float)0.55828822, (float)0.55557019, (float)0.55284619, (float)0.55011630,
(float)0.54738069, (float)0.54463905, (float)0.54189152, (float)0.53913826, (float)0.53637916,
(float)0.53361434, (float)0.53084403, (float)0.52806783, (float)0.52528596, (float)0.52249849,
(float)0.51970541, (float)0.51690674, (float)0.51410276, (float)0.51129305, (float)0.50847787,
(float)0.50565726, (float)0.50283122, (float)0.50000006, (float)0.49716327, (float)0.49432117,
(float)0.49147379, (float)0.48862115, (float)0.48576325, (float)0.48290038, (float)0.48003212,
(float)0.47715873, (float)0.47428021, (float)0.47139663, (float)0.46850798, (float)0.46561456,
(float)0.46271589, (float)0.45981231, (float)0.45690379, (float)0.45399037, (float)0.45107210,
(float)0.44814920, (float)0.44522130, (float)0.44228864, (float)0.43935123, (float)0.43640912,
(float)0.43346232, (float)0.43051112, (float)0.42755505, (float)0.42459446, (float)0.42162928,
(float)0.41865960, (float)0.41568545, (float)0.41270703, (float)0.40972400, (float)0.40673658,
(float)0.40374479, (float)0.40074870, (float)0.39774850, (float)0.39474386, (float)0.39173496,
(float)0.38872188, (float)0.38570464, (float)0.38268328, (float)0.37965804, (float)0.37662849,
(float)0.37359491, (float)0.37055734, (float)0.36751580, (float)0.36447033, (float)0.36142117,
(float)0.35836792, (float)0.35531086, (float)0.35224995, (float)0.34918529, (float)0.34611690,
(float)0.34304500, (float)0.33996922, (float)0.33688980, (float)0.33380675, (float)0.33072016,
(float)0.32763001, (float)0.32453656, (float)0.32143945, (float)0.31833887, (float)0.31523487,
(float)0.31212750, (float)0.30901679, (float)0.30590302, (float)0.30278572, (float)0.29966521,
(float)0.29654145, (float)0.29341453, (float)0.29028472, (float)0.28715155, (float)0.28401530,
(float)0.28087601, (float)0.27773371, (float)0.27458847, (float)0.27144048, (float)0.26828936,
(float)0.26513538, (float)0.26197854, (float)0.25881892, (float)0.25565651, (float)0.25249159,
(float)0.24932374, (float)0.24615324, (float)0.24298008, (float)0.23980433, (float)0.23662600,
(float)0.23344538, (float)0.23026201, (float)0.22707619, (float)0.22388794, (float)0.22069728,
(float)0.21750426, (float)0.21430916, (float)0.21111152, (float)0.20791161, (float)0.20470949,
(float)0.20150517, (float)0.19829892, (float)0.19509031, (float)0.19187963, (float)0.18866688,
(float)0.18545210, (float)0.18223536, (float)0.17901689, (float)0.17579627, (float)0.17257376,
(float)0.16934940, (float)0.16612324, (float)0.16289529, (float)0.15966584, (float)0.15643445,
(float)0.15320137, (float)0.14996666, (float)0.14673033, (float)0.14349243, (float)0.14025325,
(float)0.13701232, (float)0.13376991, (float)0.13052608, (float)0.12728085, (float)0.12403426,
(float)0.12078657, (float)0.11753736, (float)0.11428688, (float)0.11103519, (float)0.10778230,
(float)0.10452849, (float)0.10127334, (float)0.09801710, (float)0.09475980, (float)0.09150149,
(float)0.08824220, (float)0.08498220, (float)0.08172106, (float)0.07845904, (float)0.07519618,
(float)0.07193252, (float)0.06866808, (float)0.06540315, (float)0.06213728, (float)0.05887074,
(float)0.05560357, (float)0.05233581, (float)0.04906749, (float)0.04579888, (float)0.04252954,
(float)0.03925974, (float)0.03598953, (float)0.03271893, (float)0.02944798, (float)0.02617695,
(float)0.02290541, (float)0.01963361, (float)0.01636161, (float)0.01308943, (float)0.00981712,
(float)0.00654493, (float)0.00327244, (float)0.00000000, (float)0.00000000, (float)0.00000000,
(float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000,
(float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000,
(float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000,
(float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000,
(float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000,
(float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000
};
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_WINDOWS_PRIVATE_H_

View File

@ -0,0 +1,112 @@
/*
* 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.
*/
#include "processing_component.h"
#include <cassert>
#include "audio_processing_impl.h"
namespace webrtc {
ProcessingComponent::ProcessingComponent(const AudioProcessingImpl* apm)
: apm_(apm),
initialized_(false),
enabled_(false),
num_handles_(0) {}
ProcessingComponent::~ProcessingComponent() {
assert(initialized_ == false);
}
int ProcessingComponent::Destroy() {
while (!handles_.empty()) {
DestroyHandle(handles_.back());
handles_.pop_back();
}
initialized_ = false;
return apm_->kNoError;
}
int ProcessingComponent::EnableComponent(bool enable) {
if (enable && !enabled_) {
enabled_ = enable; // Must be set before Initialize() is called.
int err = Initialize();
if (err != apm_->kNoError) {
enabled_ = false;
return err;
}
} else {
enabled_ = enable;
}
return apm_->kNoError;
}
bool ProcessingComponent::is_component_enabled() const {
return enabled_;
}
void* ProcessingComponent::handle(int index) const {
assert(index < num_handles_);
return handles_[index];
}
int ProcessingComponent::num_handles() const {
return num_handles_;
}
int ProcessingComponent::Initialize() {
if (!enabled_) {
return apm_->kNoError;
}
num_handles_ = num_handles_required();
if (num_handles_ > static_cast<int>(handles_.size())) {
handles_.resize(num_handles_, NULL);
}
assert(static_cast<int>(handles_.size()) >= num_handles_);
for (int i = 0; i < num_handles_; i++) {
if (handles_[i] == NULL) {
handles_[i] = CreateHandle();
if (handles_[i] == NULL) {
return apm_->kCreationFailedError;
}
}
int err = InitializeHandle(handles_[i]);
if (err != apm_->kNoError) {
return GetHandleError(handles_[i]);
}
}
initialized_ = true;
return Configure();
}
int ProcessingComponent::Configure() {
if (!initialized_) {
return apm_->kNoError;
}
assert(static_cast<int>(handles_.size()) >= num_handles_);
for (int i = 0; i < num_handles_; i++) {
int err = ConfigureHandle(handles_[i]);
if (err != apm_->kNoError) {
return GetHandleError(handles_[i]);
}
}
return apm_->kNoError;
}
} // namespace webrtc

View File

@ -0,0 +1,63 @@
/*
* 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.
*/
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_PROCESSING_COMPONENT_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_PROCESSING_COMPONENT_H_
#include <vector>
#include "audio_processing.h"
namespace webrtc {
class AudioProcessingImpl;
/*template <class T>
class ComponentHandle {
public:
ComponentHandle();
virtual ~ComponentHandle();
virtual int Create() = 0;
virtual T* ptr() const = 0;
};*/
class ProcessingComponent {
public:
explicit ProcessingComponent(const AudioProcessingImpl* apm);
virtual ~ProcessingComponent();
virtual int Initialize();
virtual int Destroy();
virtual int get_version(char* version, int version_len_bytes) const = 0;
protected:
virtual int Configure();
int EnableComponent(bool enable);
bool is_component_enabled() const;
void* handle(int index) const;
int num_handles() const;
private:
virtual void* CreateHandle() const = 0;
virtual int InitializeHandle(void* handle) const = 0;
virtual int ConfigureHandle(void* handle) const = 0;
virtual int DestroyHandle(void* handle) const = 0;
virtual int num_handles_required() const = 0;
virtual int GetHandleError(void* handle) const = 0;
const AudioProcessingImpl* apm_;
std::vector<void*> handles_;
bool initialized_;
bool enabled_;
int num_handles_;
};
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_PROCESSING_COMPONENT_H__

View File

@ -0,0 +1,33 @@
/*
* 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.
*/
#include "splitting_filter.h"
#include "signal_processing_library.h"
namespace webrtc {
void SplittingFilterAnalysis(const WebRtc_Word16* in_data,
WebRtc_Word16* low_band,
WebRtc_Word16* high_band,
WebRtc_Word32* filter_state1,
WebRtc_Word32* filter_state2)
{
WebRtcSpl_AnalysisQMF(in_data, low_band, high_band, filter_state1, filter_state2);
}
void SplittingFilterSynthesis(const WebRtc_Word16* low_band,
const WebRtc_Word16* high_band,
WebRtc_Word16* out_data,
WebRtc_Word32* filt_state1,
WebRtc_Word32* filt_state2)
{
WebRtcSpl_SynthesisQMF(low_band, high_band, out_data, filt_state1, filt_state2);
}
} // namespace webrtc

View File

@ -0,0 +1,63 @@
/*
* 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.
*/
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_SPLITTING_FILTER_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_SPLITTING_FILTER_H_
#include "typedefs.h"
#include "signal_processing_library.h"
namespace webrtc {
/*
* SplittingFilterbank_analysisQMF(...)
*
* Splits a super-wb signal into two subbands: 0-8 kHz and 8-16 kHz.
*
* Input:
* - in_data : super-wb audio signal
*
* Input & Output:
* - filt_state1: Filter state for first all-pass filter
* - filt_state2: Filter state for second all-pass filter
*
* Output:
* - low_band : The signal from the 0-4 kHz band
* - high_band : The signal from the 4-8 kHz band
*/
void SplittingFilterAnalysis(const WebRtc_Word16* in_data,
WebRtc_Word16* low_band,
WebRtc_Word16* high_band,
WebRtc_Word32* filt_state1,
WebRtc_Word32* filt_state2);
/*
* SplittingFilterbank_synthesisQMF(...)
*
* Combines the two subbands (0-8 and 8-16 kHz) into a super-wb signal.
*
* Input:
* - low_band : The signal with the 0-8 kHz band
* - high_band : The signal with the 8-16 kHz band
*
* Input & Output:
* - filt_state1: Filter state for first all-pass filter
* - filt_state2: Filter state for second all-pass filter
*
* Output:
* - out_data : super-wb speech signal
*/
void SplittingFilterSynthesis(const WebRtc_Word16* low_band,
const WebRtc_Word16* high_band,
WebRtc_Word16* out_data,
WebRtc_Word32* filt_state1,
WebRtc_Word32* filt_state2);
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_SPLITTING_FILTER_H_

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- BEGIN_INCLUDE(manifest) -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.native_activity"
android:versionCode="1"
android:versionName="1.0">
<!-- This is the platform API where NativeActivity was introduced. -->
<uses-sdk android:minSdkVersion="8" />
<!-- This .apk has no Java code itself, so set hasCode to false. -->
<application android:label="@string/app_name" android:hasCode="false" android:debuggable="true">
<!-- Our activity is the built-in NativeActivity framework class.
This will take care of integrating with our NDK code. -->
<activity android:name="android.app.NativeActivity"
android:label="@string/app_name"
android:configChanges="orientation|keyboardHidden">
<!-- Tell NativeActivity the name of or .so -->
<meta-data android:name="android.app.lib_name"
android:value="apmtest-activity" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
<!-- END_INCLUDE(manifest) -->

View File

@ -0,0 +1,11 @@
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system use,
# "build.properties", and override values to adapt the script to your
# project structure.
# Project target.
target=android-9

View File

@ -0,0 +1 @@
APP_PLATFORM := android-9

View File

@ -0,0 +1,307 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
//BEGIN_INCLUDE(all)
#include <jni.h>
#include <errno.h>
#include <EGL/egl.h>
#include <GLES/gl.h>
#include <android/sensor.h>
#include <android/log.h>
#include <android_native_app_glue.h>
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "native-activity", __VA_ARGS__))
#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "native-activity", __VA_ARGS__))
/**
* Our saved state data.
*/
struct saved_state {
float angle;
int32_t x;
int32_t y;
};
/**
* Shared state for our app.
*/
struct engine {
struct android_app* app;
ASensorManager* sensorManager;
const ASensor* accelerometerSensor;
ASensorEventQueue* sensorEventQueue;
int animating;
EGLDisplay display;
EGLSurface surface;
EGLContext context;
int32_t width;
int32_t height;
struct saved_state state;
};
/**
* Initialize an EGL context for the current display.
*/
static int engine_init_display(struct engine* engine) {
// initialize OpenGL ES and EGL
/*
* Here specify the attributes of the desired configuration.
* Below, we select an EGLConfig with at least 8 bits per color
* component compatible with on-screen windows
*/
const EGLint attribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_BLUE_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_RED_SIZE, 8,
EGL_NONE
};
EGLint w, h, dummy, format;
EGLint numConfigs;
EGLConfig config;
EGLSurface surface;
EGLContext context;
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglInitialize(display, 0, 0);
/* Here, the application chooses the configuration it desires. In this
* sample, we have a very simplified selection process, where we pick
* the first EGLConfig that matches our criteria */
eglChooseConfig(display, attribs, &config, 1, &numConfigs);
/* EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is
* guaranteed to be accepted by ANativeWindow_setBuffersGeometry().
* As soon as we picked a EGLConfig, we can safely reconfigure the
* ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID. */
eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format);
ANativeWindow_setBuffersGeometry(engine->app->window, 0, 0, format);
surface = eglCreateWindowSurface(display, config, engine->app->window, NULL);
context = eglCreateContext(display, config, NULL, NULL);
if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) {
LOGW("Unable to eglMakeCurrent");
return -1;
}
eglQuerySurface(display, surface, EGL_WIDTH, &w);
eglQuerySurface(display, surface, EGL_HEIGHT, &h);
engine->display = display;
engine->context = context;
engine->surface = surface;
engine->width = w;
engine->height = h;
engine->state.angle = 0;
// Initialize GL state.
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
glEnable(GL_CULL_FACE);
glShadeModel(GL_SMOOTH);
glDisable(GL_DEPTH_TEST);
return 0;
}
/**
* Just the current frame in the display.
*/
static void engine_draw_frame(struct engine* engine) {
if (engine->display == NULL) {
// No display.
return;
}
// Just fill the screen with a color.
glClearColor(((float)engine->state.x)/engine->width, engine->state.angle,
((float)engine->state.y)/engine->height, 1);
glClear(GL_COLOR_BUFFER_BIT);
eglSwapBuffers(engine->display, engine->surface);
}
/**
* Tear down the EGL context currently associated with the display.
*/
static void engine_term_display(struct engine* engine) {
if (engine->display != EGL_NO_DISPLAY) {
eglMakeCurrent(engine->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (engine->context != EGL_NO_CONTEXT) {
eglDestroyContext(engine->display, engine->context);
}
if (engine->surface != EGL_NO_SURFACE) {
eglDestroySurface(engine->display, engine->surface);
}
eglTerminate(engine->display);
}
engine->animating = 0;
engine->display = EGL_NO_DISPLAY;
engine->context = EGL_NO_CONTEXT;
engine->surface = EGL_NO_SURFACE;
}
/**
* Process the next input event.
*/
static int32_t engine_handle_input(struct android_app* app, AInputEvent* event) {
struct engine* engine = (struct engine*)app->userData;
if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) {
engine->animating = 1;
engine->state.x = AMotionEvent_getX(event, 0);
engine->state.y = AMotionEvent_getY(event, 0);
return 1;
}
return 0;
}
/**
* Process the next main command.
*/
static void engine_handle_cmd(struct android_app* app, int32_t cmd) {
struct engine* engine = (struct engine*)app->userData;
switch (cmd) {
case APP_CMD_SAVE_STATE:
// The system has asked us to save our current state. Do so.
engine->app->savedState = malloc(sizeof(struct saved_state));
*((struct saved_state*)engine->app->savedState) = engine->state;
engine->app->savedStateSize = sizeof(struct saved_state);
break;
case APP_CMD_INIT_WINDOW:
// The window is being shown, get it ready.
if (engine->app->window != NULL) {
engine_init_display(engine);
engine_draw_frame(engine);
}
break;
case APP_CMD_TERM_WINDOW:
// The window is being hidden or closed, clean it up.
engine_term_display(engine);
break;
case APP_CMD_GAINED_FOCUS:
// When our app gains focus, we start monitoring the accelerometer.
if (engine->accelerometerSensor != NULL) {
ASensorEventQueue_enableSensor(engine->sensorEventQueue,
engine->accelerometerSensor);
// We'd like to get 60 events per second (in us).
ASensorEventQueue_setEventRate(engine->sensorEventQueue,
engine->accelerometerSensor, (1000L/60)*1000);
}
break;
case APP_CMD_LOST_FOCUS:
// When our app loses focus, we stop monitoring the accelerometer.
// This is to avoid consuming battery while not being used.
if (engine->accelerometerSensor != NULL) {
ASensorEventQueue_disableSensor(engine->sensorEventQueue,
engine->accelerometerSensor);
}
// Also stop animating.
engine->animating = 0;
engine_draw_frame(engine);
break;
}
}
/**
* This is the main entry point of a native application that is using
* android_native_app_glue. It runs in its own thread, with its own
* event loop for receiving input events and doing other things.
*/
void android_main(struct android_app* state) {
struct engine engine;
// Make sure glue isn't stripped.
app_dummy();
memset(&engine, 0, sizeof(engine));
state->userData = &engine;
state->onAppCmd = engine_handle_cmd;
state->onInputEvent = engine_handle_input;
engine.app = state;
// Prepare to monitor accelerometer
engine.sensorManager = ASensorManager_getInstance();
engine.accelerometerSensor = ASensorManager_getDefaultSensor(engine.sensorManager,
ASENSOR_TYPE_ACCELEROMETER);
engine.sensorEventQueue = ASensorManager_createEventQueue(engine.sensorManager,
state->looper, LOOPER_ID_USER, NULL, NULL);
if (state->savedState != NULL) {
// We are starting with a previous saved state; restore from it.
engine.state = *(struct saved_state*)state->savedState;
}
// loop waiting for stuff to do.
while (1) {
// Read all pending events.
int ident;
int events;
struct android_poll_source* source;
// If not animating, we will block forever waiting for events.
// If animating, we loop until all events are read, then continue
// to draw the next frame of animation.
while ((ident=ALooper_pollAll(engine.animating ? 0 : -1, NULL, &events,
(void**)&source)) >= 0) {
// Process this event.
if (source != NULL) {
source->process(state, source);
}
// If a sensor has data, process it now.
if (ident == LOOPER_ID_USER) {
if (engine.accelerometerSensor != NULL) {
ASensorEvent event;
while (ASensorEventQueue_getEvents(engine.sensorEventQueue,
&event, 1) > 0) {
LOGI("accelerometer: x=%f y=%f z=%f",
event.acceleration.x, event.acceleration.y,
event.acceleration.z);
}
}
}
// Check if we are exiting.
if (state->destroyRequested != 0) {
engine_term_display(&engine);
return;
}
}
if (engine.animating) {
// Done with events; draw next animation frame.
engine.state.angle += .01f;
if (engine.state.angle > 1) {
engine.state.angle = 0;
}
// Drawing is throttled to the screen update rate, so there
// is no need to do timing here.
engine_draw_frame(&engine);
}
}
}
//END_INCLUDE(all)

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">apmtest</string>
</resources>

View File

@ -0,0 +1,355 @@
function apmtest(task, testname, filepath, casenumber, legacy)
%APMTEST is a tool to process APM file sets and easily display the output.
% APMTEST(TASK, TESTNAME, CASENUMBER) performs one of several TASKs:
% 'test' Processes the files to produce test output.
% 'list' Prints a list of cases in the test set, preceded by their
% CASENUMBERs.
% 'show' Uses spclab to show the test case specified by the
% CASENUMBER parameter.
%
% using a set of test files determined by TESTNAME:
% 'all' All tests.
% 'apm' The standard APM test set (default).
% 'apmm' The mobile APM test set.
% 'aec' The AEC test set.
% 'aecm' The AECM test set.
% 'agc' The AGC test set.
% 'ns' The NS test set.
% 'vad' The VAD test set.
%
% FILEPATH specifies the path to the test data files.
%
% CASENUMBER can be used to select a single test case. Omit CASENUMBER,
% or set to zero, to use all test cases.
%
if nargin < 5 || isempty(legacy)
% Set to true to run old VQE recordings.
legacy = false;
end
if nargin < 4 || isempty(casenumber)
casenumber = 0;
end
if nargin < 3 || isempty(filepath)
filepath = 'data/';
end
if nargin < 2 || isempty(testname)
testname = 'all';
end
if nargin < 1 || isempty(task)
task = 'test';
end
if ~strcmp(task, 'test') && ~strcmp(task, 'list') && ~strcmp(task, 'show')
error(['TASK ' task ' is not recognized']);
end
if casenumber == 0 && strcmp(task, 'show')
error(['CASENUMBER must be specified for TASK ' task]);
end
inpath = [filepath 'input/'];
outpath = [filepath 'output/'];
refpath = [filepath 'reference/'];
if strcmp(testname, 'all')
tests = {'apm','apmm','aec','aecm','agc','ns','vad'};
else
tests = {testname};
end
if legacy
progname = './test';
else
progname = './process_test';
end
global farFile;
global nearFile;
global eventFile;
global delayFile;
global driftFile;
if legacy
farFile = 'vqeFar.pcm';
nearFile = 'vqeNear.pcm';
eventFile = 'vqeEvent.dat';
delayFile = 'vqeBuf.dat';
driftFile = 'vqeDrift.dat';
else
farFile = 'apm_far.pcm';
nearFile = 'apm_near.pcm';
eventFile = 'apm_event.dat';
delayFile = 'apm_delay.dat';
driftFile = 'apm_drift.dat';
end
simulateMode = false;
nErr = 0;
nCases = 0;
for i=1:length(tests)
simulateMode = false;
if strcmp(tests{i}, 'apm')
testdir = ['apm/'];
outfile = ['out'];
if legacy
opt = ['-ec 1 -agc 2 -nc 2 -vad 3'];
else
opt = ['--no_progress -hpf' ...
' -aec --drift_compensation -agc --fixed_digital' ...
' -ns --ns_moderate -vad'];
end
elseif strcmp(tests{i}, 'apm-swb')
simulateMode = true;
testdir = ['apm-swb/'];
outfile = ['out'];
if legacy
opt = ['-fs 32000 -ec 1 -agc 2 -nc 2'];
else
opt = ['--no_progress -fs 32000 -hpf' ...
' -aec --drift_compensation -agc --adaptive_digital' ...
' -ns --ns_moderate -vad'];
end
elseif strcmp(tests{i}, 'apmm')
testdir = ['apmm/'];
outfile = ['out'];
opt = ['-aec --drift_compensation -agc --fixed_digital -hpf -ns ' ...
'--ns_moderate'];
else
error(['TESTNAME ' tests{i} ' is not recognized']);
end
inpathtest = [inpath testdir];
outpathtest = [outpath testdir];
refpathtest = [refpath testdir];
if ~exist(inpathtest,'dir')
error(['Input directory ' inpathtest ' does not exist']);
end
if ~exist(refpathtest,'dir')
warning(['Reference directory ' refpathtest ' does not exist']);
end
[status, errMsg] = mkdir(outpathtest);
if (status == 0)
error(errMsg);
end
[nErr, nCases] = recurseDir(inpathtest, outpathtest, refpathtest, outfile, ...
progname, opt, simulateMode, nErr, nCases, task, casenumber, legacy);
if strcmp(task, 'test') || strcmp(task, 'show')
system(['rm ' farFile]);
system(['rm ' nearFile]);
if simulateMode == false
system(['rm ' eventFile]);
system(['rm ' delayFile]);
system(['rm ' driftFile]);
end
end
end
if ~strcmp(task, 'list')
if nErr == 0
fprintf(1, '\nAll files are bit-exact to reference\n', nErr);
else
fprintf(1, '\n%d files are NOT bit-exact to reference\n', nErr);
end
end
function [nErrOut, nCases] = recurseDir(inpath, outpath, refpath, ...
outfile, progname, opt, simulateMode, nErr, nCases, task, casenumber, ...
legacy)
global farFile;
global nearFile;
global eventFile;
global delayFile;
global driftFile;
dirs = dir(inpath);
nDirs = 0;
nErrOut = nErr;
for i=3:length(dirs) % skip . and ..
nDirs = nDirs + dirs(i).isdir;
end
if nDirs == 0
nCases = nCases + 1;
if casenumber == nCases || casenumber == 0
if strcmp(task, 'list')
fprintf([num2str(nCases) '. ' outfile '\n'])
else
vadoutfile = ['vad_' outfile '.dat'];
outfile = [outfile '.pcm'];
% Check for VAD test
vadTest = 0;
if ~isempty(findstr(opt, '-vad'))
vadTest = 1;
if legacy
opt = [opt ' ' outpath vadoutfile];
else
opt = [opt ' --vad_out_file ' outpath vadoutfile];
end
end
if exist([inpath 'vqeFar.pcm'])
system(['ln -s -f ' inpath 'vqeFar.pcm ' farFile]);
elseif exist([inpath 'apm_far.pcm'])
system(['ln -s -f ' inpath 'apm_far.pcm ' farFile]);
end
if exist([inpath 'vqeNear.pcm'])
system(['ln -s -f ' inpath 'vqeNear.pcm ' nearFile]);
elseif exist([inpath 'apm_near.pcm'])
system(['ln -s -f ' inpath 'apm_near.pcm ' nearFile]);
end
if exist([inpath 'vqeEvent.dat'])
system(['ln -s -f ' inpath 'vqeEvent.dat ' eventFile]);
elseif exist([inpath 'apm_event.dat'])
system(['ln -s -f ' inpath 'apm_event.dat ' eventFile]);
end
if exist([inpath 'vqeBuf.dat'])
system(['ln -s -f ' inpath 'vqeBuf.dat ' delayFile]);
elseif exist([inpath 'apm_delay.dat'])
system(['ln -s -f ' inpath 'apm_delay.dat ' delayFile]);
end
if exist([inpath 'vqeSkew.dat'])
system(['ln -s -f ' inpath 'vqeSkew.dat ' driftFile]);
elseif exist([inpath 'vqeDrift.dat'])
system(['ln -s -f ' inpath 'vqeDrift.dat ' driftFile]);
elseif exist([inpath 'apm_drift.dat'])
system(['ln -s -f ' inpath 'apm_drift.dat ' driftFile]);
end
if simulateMode == false
command = [progname ' -o ' outpath outfile ' ' opt];
else
if legacy
inputCmd = [' -in ' nearFile];
else
inputCmd = [' -i ' nearFile];
end
if exist([farFile])
if legacy
inputCmd = [' -if ' farFile inputCmd];
else
inputCmd = [' -ir ' farFile inputCmd];
end
end
command = [progname inputCmd ' -o ' outpath outfile ' ' opt];
end
% This prevents MATLAB from using its own C libraries.
shellcmd = ['bash -c "unset LD_LIBRARY_PATH;'];
fprintf([command '\n']);
[status, result] = system([shellcmd command '"']);
fprintf(result);
fprintf(['Reference file: ' refpath outfile '\n']);
if vadTest == 1
equal_to_ref = are_files_equal([outpath vadoutfile], ...
[refpath vadoutfile], ...
'int8');
if ~equal_to_ref
nErr = nErr + 1;
end
end
[equal_to_ref, diffvector] = are_files_equal([outpath outfile], ...
[refpath outfile], ...
'int16');
if ~equal_to_ref
nErr = nErr + 1;
end
if strcmp(task, 'show')
% Assume the last init gives the sample rate of interest.
str_idx = strfind(result, 'Sample rate:');
fs = str2num(result(str_idx(end) + 13:str_idx(end) + 17));
fprintf('Using %d Hz\n', fs);
if exist([farFile])
spclab(fs, farFile, nearFile, [refpath outfile], ...
[outpath outfile], diffvector);
%spclab(fs, diffvector);
else
spclab(fs, nearFile, [refpath outfile], [outpath outfile], ...
diffvector);
%spclab(fs, diffvector);
end
end
end
end
else
for i=3:length(dirs)
if dirs(i).isdir
[nErr, nCases] = recurseDir([inpath dirs(i).name '/'], outpath, ...
refpath,[outfile '_' dirs(i).name], progname, opt, ...
simulateMode, nErr, nCases, task, casenumber, legacy);
end
end
end
nErrOut = nErr;
function [are_equal, diffvector] = ...
are_files_equal(newfile, reffile, precision, diffvector)
are_equal = false;
diffvector = 0;
if ~exist(newfile,'file')
warning(['Output file ' newfile ' does not exist']);
return
end
if ~exist(reffile,'file')
warning(['Reference file ' reffile ' does not exist']);
return
end
fid = fopen(newfile,'rb');
new = fread(fid,inf,precision);
fclose(fid);
fid = fopen(reffile,'rb');
ref = fread(fid,inf,precision);
fclose(fid);
if length(new) ~= length(ref)
warning('Reference is not the same length as output');
minlength = min(length(new), length(ref));
new = new(1:minlength);
ref = ref(1:minlength);
end
diffvector = new - ref;
if isequal(new, ref)
fprintf([newfile ' is bit-exact to reference\n']);
are_equal = true;
else
if isempty(new)
warning([newfile ' is empty']);
return
end
snr = snrseg(new,ref,80);
fprintf('\n');
are_equal = false;
end

View File

@ -0,0 +1,948 @@
/*
* 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.
*/
#include <stdio.h>
#include <string.h>
#ifdef WEBRTC_ANDROID
#include <sys/stat.h>
#endif
#include "gtest/gtest.h"
#include "audio_processing.h"
#include "cpu_features_wrapper.h"
#include "module_common_types.h"
#include "tick_util.h"
#ifdef WEBRTC_ANDROID
#include "external/webrtc/src/modules/audio_processing/debug.pb.h"
#else
#include "webrtc/audio_processing/debug.pb.h"
#endif
using webrtc::AudioFrame;
using webrtc::AudioProcessing;
using webrtc::EchoCancellation;
using webrtc::GainControl;
using webrtc::NoiseSuppression;
using webrtc::TickInterval;
using webrtc::TickTime;
using webrtc::audioproc::Event;
using webrtc::audioproc::Init;
using webrtc::audioproc::ReverseStream;
using webrtc::audioproc::Stream;
namespace {
// Returns true on success, false on error or end-of-file.
bool ReadMessageFromFile(FILE* file,
::google::protobuf::MessageLite* msg) {
// The "wire format" for the size is little-endian.
// Assume process_test is running on a little-endian machine.
int32_t size;
if (fread(&size, sizeof(int32_t), 1, file) != 1) {
return false;
}
if (size <= 0) {
return false;
}
size_t usize = static_cast<size_t>(size);
char array[usize];
if (fread(array, sizeof(char), usize, file) != usize) {
return false;
}
msg->Clear();
return msg->ParseFromArray(array, usize);
}
void PrintStat(const AudioProcessing::Statistic& stat) {
printf("%d, %d, %d\n", stat.average,
stat.maximum,
stat.minimum);
}
void usage() {
printf(
"Usage: process_test [options] [-pb PROTOBUF_FILE]\n"
" [-ir REVERSE_FILE] [-i PRIMARY_FILE] [-o OUT_FILE]\n");
printf(
"process_test is a test application for AudioProcessing.\n\n"
"When a protobuf debug file is available, specify it with -pb.\n"
"Alternately, when -ir or -i is used, the specified files will be\n"
"processed directly in a simulation mode. Otherwise the full set of\n"
"legacy test files is expected to be present in the working directory.\n");
printf("\n");
printf("Options\n");
printf("General configuration (only used for the simulation mode):\n");
printf(" -fs SAMPLE_RATE_HZ\n");
printf(" -ch CHANNELS_IN CHANNELS_OUT\n");
printf(" -rch REVERSE_CHANNELS\n");
printf("\n");
printf("Component configuration:\n");
printf(
"All components are disabled by default. Each block below begins with a\n"
"flag to enable the component with default settings. The subsequent flags\n"
"in the block are used to provide configuration settings.\n");
printf("\n -aec Echo cancellation\n");
printf(" --drift_compensation\n");
printf(" --no_drift_compensation\n");
printf(" --no_echo_metrics\n");
printf(" --no_delay_logging\n");
printf("\n -aecm Echo control mobile\n");
printf(" --aecm_echo_path_in_file FILE\n");
printf(" --aecm_echo_path_out_file FILE\n");
printf("\n -agc Gain control\n");
printf(" --analog\n");
printf(" --adaptive_digital\n");
printf(" --fixed_digital\n");
printf(" --target_level LEVEL\n");
printf(" --compression_gain GAIN\n");
printf(" --limiter\n");
printf(" --no_limiter\n");
printf("\n -hpf High pass filter\n");
printf("\n -ns Noise suppression\n");
printf(" --ns_low\n");
printf(" --ns_moderate\n");
printf(" --ns_high\n");
printf(" --ns_very_high\n");
printf("\n -vad Voice activity detection\n");
printf(" --vad_out_file FILE\n");
printf("\n");
printf("Modifiers:\n");
printf(" --noasm Disable SSE optimization.\n");
printf(" --perf Measure performance.\n");
printf(" --quiet Suppress text output.\n");
printf(" --no_progress Suppress progress.\n");
printf(" --version Print version information and exit.\n");
}
// void function for gtest.
void void_main(int argc, char* argv[]) {
if (argc > 1 && strcmp(argv[1], "--help") == 0) {
usage();
return;
}
if (argc < 2) {
printf("Did you mean to run without arguments?\n");
printf("Try `process_test --help' for more information.\n\n");
}
AudioProcessing* apm = AudioProcessing::Create(0);
ASSERT_TRUE(apm != NULL);
WebRtc_Word8 version[1024];
WebRtc_UWord32 version_bytes_remaining = sizeof(version);
WebRtc_UWord32 version_position = 0;
const char* pb_filename = NULL;
const char* far_filename = NULL;
const char* near_filename = NULL;
const char* out_filename = NULL;
const char* vad_out_filename = NULL;
const char* aecm_echo_path_in_filename = NULL;
const char* aecm_echo_path_out_filename = NULL;
int32_t sample_rate_hz = 16000;
int32_t device_sample_rate_hz = 16000;
int num_capture_input_channels = 1;
int num_capture_output_channels = 1;
int num_render_channels = 1;
int samples_per_channel = sample_rate_hz / 100;
bool simulating = false;
bool perf_testing = false;
bool verbose = true;
bool progress = true;
//bool interleaved = true;
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "-pb") == 0) {
i++;
ASSERT_LT(i, argc) << "Specify protobuf filename after -pb";
pb_filename = argv[i];
} else if (strcmp(argv[i], "-ir") == 0) {
i++;
ASSERT_LT(i, argc) << "Specify filename after -ir";
far_filename = argv[i];
simulating = true;
} else if (strcmp(argv[i], "-i") == 0) {
i++;
ASSERT_LT(i, argc) << "Specify filename after -i";
near_filename = argv[i];
simulating = true;
} else if (strcmp(argv[i], "-o") == 0) {
i++;
ASSERT_LT(i, argc) << "Specify filename after -o";
out_filename = argv[i];
} else if (strcmp(argv[i], "-fs") == 0) {
i++;
ASSERT_LT(i, argc) << "Specify sample rate after -fs";
ASSERT_EQ(1, sscanf(argv[i], "%d", &sample_rate_hz));
samples_per_channel = sample_rate_hz / 100;
ASSERT_EQ(apm->kNoError,
apm->set_sample_rate_hz(sample_rate_hz));
} else if (strcmp(argv[i], "-ch") == 0) {
i++;
ASSERT_LT(i + 1, argc) << "Specify number of channels after -ch";
ASSERT_EQ(1, sscanf(argv[i], "%d", &num_capture_input_channels));
i++;
ASSERT_EQ(1, sscanf(argv[i], "%d", &num_capture_output_channels));
ASSERT_EQ(apm->kNoError,
apm->set_num_channels(num_capture_input_channels,
num_capture_output_channels));
} else if (strcmp(argv[i], "-rch") == 0) {
i++;
ASSERT_LT(i, argc) << "Specify number of channels after -rch";
ASSERT_EQ(1, sscanf(argv[i], "%d", &num_render_channels));
ASSERT_EQ(apm->kNoError,
apm->set_num_reverse_channels(num_render_channels));
} else if (strcmp(argv[i], "-aec") == 0) {
ASSERT_EQ(apm->kNoError, apm->echo_cancellation()->Enable(true));
ASSERT_EQ(apm->kNoError,
apm->echo_cancellation()->enable_metrics(true));
ASSERT_EQ(apm->kNoError,
apm->echo_cancellation()->enable_delay_logging(true));
} else if (strcmp(argv[i], "--drift_compensation") == 0) {
ASSERT_EQ(apm->kNoError, apm->echo_cancellation()->Enable(true));
// TODO(ajm): this is enabled in the VQE test app by default. Investigate
// why it can give better performance despite passing zeros.
ASSERT_EQ(apm->kNoError,
apm->echo_cancellation()->enable_drift_compensation(true));
} else if (strcmp(argv[i], "--no_drift_compensation") == 0) {
ASSERT_EQ(apm->kNoError, apm->echo_cancellation()->Enable(true));
ASSERT_EQ(apm->kNoError,
apm->echo_cancellation()->enable_drift_compensation(false));
} else if (strcmp(argv[i], "--no_echo_metrics") == 0) {
ASSERT_EQ(apm->kNoError, apm->echo_cancellation()->Enable(true));
ASSERT_EQ(apm->kNoError,
apm->echo_cancellation()->enable_metrics(false));
} else if (strcmp(argv[i], "--no_delay_logging") == 0) {
ASSERT_EQ(apm->kNoError, apm->echo_cancellation()->Enable(true));
ASSERT_EQ(apm->kNoError,
apm->echo_cancellation()->enable_delay_logging(false));
} else if (strcmp(argv[i], "-aecm") == 0) {
ASSERT_EQ(apm->kNoError, apm->echo_control_mobile()->Enable(true));
} else if (strcmp(argv[i], "--aecm_echo_path_in_file") == 0) {
i++;
ASSERT_LT(i, argc) << "Specify filename after --aecm_echo_path_in_file";
aecm_echo_path_in_filename = argv[i];
} else if (strcmp(argv[i], "--aecm_echo_path_out_file") == 0) {
i++;
ASSERT_LT(i, argc) << "Specify filename after --aecm_echo_path_out_file";
aecm_echo_path_out_filename = argv[i];
} else if (strcmp(argv[i], "-agc") == 0) {
ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
} else if (strcmp(argv[i], "--analog") == 0) {
ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
ASSERT_EQ(apm->kNoError,
apm->gain_control()->set_mode(GainControl::kAdaptiveAnalog));
} else if (strcmp(argv[i], "--adaptive_digital") == 0) {
ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
ASSERT_EQ(apm->kNoError,
apm->gain_control()->set_mode(GainControl::kAdaptiveDigital));
} else if (strcmp(argv[i], "--fixed_digital") == 0) {
ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
ASSERT_EQ(apm->kNoError,
apm->gain_control()->set_mode(GainControl::kFixedDigital));
} else if (strcmp(argv[i], "--target_level") == 0) {
i++;
int level;
ASSERT_EQ(1, sscanf(argv[i], "%d", &level));
ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
ASSERT_EQ(apm->kNoError,
apm->gain_control()->set_target_level_dbfs(level));
} else if (strcmp(argv[i], "--compression_gain") == 0) {
i++;
int gain;
ASSERT_EQ(1, sscanf(argv[i], "%d", &gain));
ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
ASSERT_EQ(apm->kNoError,
apm->gain_control()->set_compression_gain_db(gain));
} else if (strcmp(argv[i], "--limiter") == 0) {
ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
ASSERT_EQ(apm->kNoError,
apm->gain_control()->enable_limiter(true));
} else if (strcmp(argv[i], "--no_limiter") == 0) {
ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
ASSERT_EQ(apm->kNoError,
apm->gain_control()->enable_limiter(false));
} else if (strcmp(argv[i], "-hpf") == 0) {
ASSERT_EQ(apm->kNoError, apm->high_pass_filter()->Enable(true));
} else if (strcmp(argv[i], "-ns") == 0) {
ASSERT_EQ(apm->kNoError, apm->noise_suppression()->Enable(true));
} else if (strcmp(argv[i], "--ns_low") == 0) {
ASSERT_EQ(apm->kNoError, apm->noise_suppression()->Enable(true));
ASSERT_EQ(apm->kNoError,
apm->noise_suppression()->set_level(NoiseSuppression::kLow));
} else if (strcmp(argv[i], "--ns_moderate") == 0) {
ASSERT_EQ(apm->kNoError, apm->noise_suppression()->Enable(true));
ASSERT_EQ(apm->kNoError,
apm->noise_suppression()->set_level(NoiseSuppression::kModerate));
} else if (strcmp(argv[i], "--ns_high") == 0) {
ASSERT_EQ(apm->kNoError, apm->noise_suppression()->Enable(true));
ASSERT_EQ(apm->kNoError,
apm->noise_suppression()->set_level(NoiseSuppression::kHigh));
} else if (strcmp(argv[i], "--ns_very_high") == 0) {
ASSERT_EQ(apm->kNoError, apm->noise_suppression()->Enable(true));
ASSERT_EQ(apm->kNoError,
apm->noise_suppression()->set_level(NoiseSuppression::kVeryHigh));
} else if (strcmp(argv[i], "-vad") == 0) {
ASSERT_EQ(apm->kNoError, apm->voice_detection()->Enable(true));
} else if (strcmp(argv[i], "--vad_out_file") == 0) {
i++;
ASSERT_LT(i, argc) << "Specify filename after --vad_out_file";
vad_out_filename = argv[i];
} else if (strcmp(argv[i], "--noasm") == 0) {
WebRtc_GetCPUInfo = WebRtc_GetCPUInfoNoASM;
// We need to reinitialize here if components have already been enabled.
ASSERT_EQ(apm->kNoError, apm->Initialize());
} else if (strcmp(argv[i], "--perf") == 0) {
perf_testing = true;
} else if (strcmp(argv[i], "--quiet") == 0) {
verbose = false;
progress = false;
} else if (strcmp(argv[i], "--no_progress") == 0) {
progress = false;
} else if (strcmp(argv[i], "--version") == 0) {
ASSERT_EQ(apm->kNoError, apm->Version(version,
version_bytes_remaining,
version_position));
printf("%s\n", version);
return;
} else if (strcmp(argv[i], "--debug_recording") == 0) {
i++;
ASSERT_LT(i, argc) << "Specify filename after --debug_recording";
ASSERT_EQ(apm->kNoError, apm->StartDebugRecording(argv[i]));
} else {
FAIL() << "Unrecognized argument " << argv[i];
}
}
// If we're reading a protobuf file, ensure a simulation hasn't also
// been requested (which makes no sense...)
ASSERT_FALSE(pb_filename && simulating);
if (verbose) {
printf("Sample rate: %d Hz\n", sample_rate_hz);
printf("Primary channels: %d (in), %d (out)\n",
num_capture_input_channels,
num_capture_output_channels);
printf("Reverse channels: %d \n", num_render_channels);
}
const char far_file_default[] = "apm_far.pcm";
const char near_file_default[] = "apm_near.pcm";
const char out_file_default[] = "out.pcm";
const char event_filename[] = "apm_event.dat";
const char delay_filename[] = "apm_delay.dat";
const char drift_filename[] = "apm_drift.dat";
const char vad_file_default[] = "vad_out.dat";
if (!simulating) {
far_filename = far_file_default;
near_filename = near_file_default;
}
if (!out_filename) {
out_filename = out_file_default;
}
if (!vad_out_filename) {
vad_out_filename = vad_file_default;
}
FILE* pb_file = NULL;
FILE* far_file = NULL;
FILE* near_file = NULL;
FILE* out_file = NULL;
FILE* event_file = NULL;
FILE* delay_file = NULL;
FILE* drift_file = NULL;
FILE* vad_out_file = NULL;
FILE* aecm_echo_path_in_file = NULL;
FILE* aecm_echo_path_out_file = NULL;
if (pb_filename) {
pb_file = fopen(pb_filename, "rb");
ASSERT_TRUE(NULL != pb_file) << "Unable to open protobuf file "
<< pb_filename;
} else {
if (far_filename) {
far_file = fopen(far_filename, "rb");
ASSERT_TRUE(NULL != far_file) << "Unable to open far-end audio file "
<< far_filename;
}
near_file = fopen(near_filename, "rb");
ASSERT_TRUE(NULL != near_file) << "Unable to open near-end audio file "
<< near_filename;
if (!simulating) {
event_file = fopen(event_filename, "rb");
ASSERT_TRUE(NULL != event_file) << "Unable to open event file "
<< event_filename;
delay_file = fopen(delay_filename, "rb");
ASSERT_TRUE(NULL != delay_file) << "Unable to open buffer file "
<< delay_filename;
drift_file = fopen(drift_filename, "rb");
ASSERT_TRUE(NULL != drift_file) << "Unable to open drift file "
<< drift_filename;
}
}
out_file = fopen(out_filename, "wb");
ASSERT_TRUE(NULL != out_file) << "Unable to open output audio file "
<< out_filename;
int near_size_samples = 0;
if (pb_file) {
struct stat st;
stat(pb_filename, &st);
// Crude estimate, but should be good enough.
near_size_samples = st.st_size / 3 / sizeof(int16_t);
} else {
struct stat st;
stat(near_filename, &st);
near_size_samples = st.st_size / sizeof(int16_t);
}
if (apm->voice_detection()->is_enabled()) {
vad_out_file = fopen(vad_out_filename, "wb");
ASSERT_TRUE(NULL != vad_out_file) << "Unable to open VAD output file "
<< vad_out_file;
}
if (aecm_echo_path_in_filename != NULL) {
aecm_echo_path_in_file = fopen(aecm_echo_path_in_filename, "rb");
ASSERT_TRUE(NULL != aecm_echo_path_in_file) << "Unable to open file "
<< aecm_echo_path_in_filename;
const size_t path_size =
apm->echo_control_mobile()->echo_path_size_bytes();
unsigned char echo_path[path_size];
ASSERT_EQ(path_size, fread(echo_path,
sizeof(unsigned char),
path_size,
aecm_echo_path_in_file));
EXPECT_EQ(apm->kNoError,
apm->echo_control_mobile()->SetEchoPath(echo_path, path_size));
fclose(aecm_echo_path_in_file);
aecm_echo_path_in_file = NULL;
}
if (aecm_echo_path_out_filename != NULL) {
aecm_echo_path_out_file = fopen(aecm_echo_path_out_filename, "wb");
ASSERT_TRUE(NULL != aecm_echo_path_out_file) << "Unable to open file "
<< aecm_echo_path_out_filename;
}
size_t read_count = 0;
int reverse_count = 0;
int primary_count = 0;
int near_read_samples = 0;
TickInterval acc_ticks;
AudioFrame far_frame;
far_frame._frequencyInHz = sample_rate_hz;
AudioFrame near_frame;
near_frame._frequencyInHz = sample_rate_hz;
int delay_ms = 0;
int drift_samples = 0;
int capture_level = 127;
int8_t stream_has_voice = 0;
TickTime t0 = TickTime::Now();
TickTime t1 = t0;
WebRtc_Word64 max_time_us = 0;
WebRtc_Word64 max_time_reverse_us = 0;
WebRtc_Word64 min_time_us = 1e6;
WebRtc_Word64 min_time_reverse_us = 1e6;
// TODO(ajm): Ideally we would refactor this block into separate functions,
// but for now we want to share the variables.
if (pb_file) {
Event event_msg;
while (ReadMessageFromFile(pb_file, &event_msg)) {
std::ostringstream trace_stream;
trace_stream << "Processed frames: " << reverse_count << " (reverse), "
<< primary_count << " (primary)";
SCOPED_TRACE(trace_stream.str());
if (event_msg.type() == Event::INIT) {
ASSERT_TRUE(event_msg.has_init());
const Init msg = event_msg.init();
ASSERT_TRUE(msg.has_sample_rate());
ASSERT_EQ(apm->kNoError,
apm->set_sample_rate_hz(msg.sample_rate()));
ASSERT_TRUE(msg.has_device_sample_rate());
ASSERT_EQ(apm->kNoError,
apm->echo_cancellation()->set_device_sample_rate_hz(
msg.device_sample_rate()));
ASSERT_TRUE(msg.has_num_input_channels());
ASSERT_TRUE(msg.has_num_output_channels());
ASSERT_EQ(apm->kNoError,
apm->set_num_channels(msg.num_input_channels(),
msg.num_output_channels()));
ASSERT_TRUE(msg.has_num_reverse_channels());
ASSERT_EQ(apm->kNoError,
apm->set_num_reverse_channels(msg.num_reverse_channels()));
samples_per_channel = msg.sample_rate() / 100;
far_frame._frequencyInHz = msg.sample_rate();
far_frame._payloadDataLengthInSamples =
msg.num_reverse_channels() * samples_per_channel;
near_frame._frequencyInHz = msg.sample_rate();
if (verbose) {
printf("Init at frame: %d (primary), %d (reverse)\n",
primary_count, reverse_count);
printf(" Sample rate: %d Hz\n", sample_rate_hz);
}
} else if (event_msg.type() == Event::REVERSE_STREAM) {
ASSERT_TRUE(event_msg.has_reverse_stream());
const ReverseStream msg = event_msg.reverse_stream();
reverse_count++;
ASSERT_TRUE(msg.has_data());
ASSERT_EQ(sizeof(int16_t) * far_frame._payloadDataLengthInSamples,
msg.data().size());
memcpy(far_frame._payloadData, msg.data().data(), msg.data().size());
if (perf_testing) {
t0 = TickTime::Now();
}
ASSERT_EQ(apm->kNoError,
apm->AnalyzeReverseStream(&far_frame));
if (perf_testing) {
t1 = TickTime::Now();
TickInterval tick_diff = t1 - t0;
acc_ticks += tick_diff;
if (tick_diff.Microseconds() > max_time_reverse_us) {
max_time_reverse_us = tick_diff.Microseconds();
}
if (tick_diff.Microseconds() < min_time_reverse_us) {
min_time_reverse_us = tick_diff.Microseconds();
}
}
} else if (event_msg.type() == Event::STREAM) {
ASSERT_TRUE(event_msg.has_stream());
const Stream msg = event_msg.stream();
primary_count++;
near_frame._audioChannel = apm->num_input_channels();
near_frame._payloadDataLengthInSamples =
apm->num_input_channels() * samples_per_channel;
ASSERT_TRUE(msg.has_input_data());
ASSERT_EQ(sizeof(int16_t) * near_frame._payloadDataLengthInSamples,
msg.input_data().size());
memcpy(near_frame._payloadData,
msg.input_data().data(),
msg.input_data().size());
near_read_samples += near_frame._payloadDataLengthInSamples;
if (progress && primary_count % 100 == 0) {
printf("%.0f%% complete\r",
(near_read_samples * 100.0) / near_size_samples);
fflush(stdout);
}
if (perf_testing) {
t0 = TickTime::Now();
}
ASSERT_EQ(apm->kNoError,
apm->gain_control()->set_stream_analog_level(msg.level()));
ASSERT_EQ(apm->kNoError,
apm->set_stream_delay_ms(msg.delay()));
ASSERT_EQ(apm->kNoError,
apm->echo_cancellation()->set_stream_drift_samples(msg.drift()));
int err = apm->ProcessStream(&near_frame);
if (err == apm->kBadStreamParameterWarning) {
printf("Bad parameter warning. %s\n", trace_stream.str().c_str());
}
ASSERT_TRUE(err == apm->kNoError ||
err == apm->kBadStreamParameterWarning);
capture_level = apm->gain_control()->stream_analog_level();
stream_has_voice =
static_cast<int8_t>(apm->voice_detection()->stream_has_voice());
if (vad_out_file != NULL) {
ASSERT_EQ(1u, fwrite(&stream_has_voice,
sizeof(stream_has_voice),
1,
vad_out_file));
}
if (apm->gain_control()->mode() != GainControl::kAdaptiveAnalog) {
ASSERT_EQ(msg.level(), capture_level);
}
if (perf_testing) {
t1 = TickTime::Now();
TickInterval tick_diff = t1 - t0;
acc_ticks += tick_diff;
if (tick_diff.Microseconds() > max_time_us) {
max_time_us = tick_diff.Microseconds();
}
if (tick_diff.Microseconds() < min_time_us) {
min_time_us = tick_diff.Microseconds();
}
}
ASSERT_EQ(near_frame._payloadDataLengthInSamples,
fwrite(near_frame._payloadData,
sizeof(int16_t),
near_frame._payloadDataLengthInSamples,
out_file));
}
}
ASSERT_TRUE(feof(pb_file));
} else {
enum Events {
kInitializeEvent,
kRenderEvent,
kCaptureEvent,
kResetEventDeprecated
};
int16_t event = 0;
while (simulating || feof(event_file) == 0) {
std::ostringstream trace_stream;
trace_stream << "Processed frames: " << reverse_count << " (reverse), "
<< primary_count << " (primary)";
SCOPED_TRACE(trace_stream.str());
if (simulating) {
if (far_file == NULL) {
event = kCaptureEvent;
} else {
if (event == kRenderEvent) {
event = kCaptureEvent;
} else {
event = kRenderEvent;
}
}
} else {
read_count = fread(&event, sizeof(event), 1, event_file);
if (read_count != 1) {
break;
}
}
if (event == kInitializeEvent || event == kResetEventDeprecated) {
ASSERT_EQ(1u,
fread(&sample_rate_hz, sizeof(sample_rate_hz), 1, event_file));
samples_per_channel = sample_rate_hz / 100;
ASSERT_EQ(1u,
fread(&device_sample_rate_hz,
sizeof(device_sample_rate_hz),
1,
event_file));
ASSERT_EQ(apm->kNoError,
apm->set_sample_rate_hz(sample_rate_hz));
ASSERT_EQ(apm->kNoError,
apm->echo_cancellation()->set_device_sample_rate_hz(
device_sample_rate_hz));
far_frame._frequencyInHz = sample_rate_hz;
near_frame._frequencyInHz = sample_rate_hz;
if (verbose) {
printf("Init at frame: %d (primary), %d (reverse)\n",
primary_count, reverse_count);
printf(" Sample rate: %d Hz\n", sample_rate_hz);
}
} else if (event == kRenderEvent) {
reverse_count++;
far_frame._audioChannel = num_render_channels;
far_frame._payloadDataLengthInSamples =
num_render_channels * samples_per_channel;
read_count = fread(far_frame._payloadData,
sizeof(WebRtc_Word16),
far_frame._payloadDataLengthInSamples,
far_file);
if (simulating) {
if (read_count != far_frame._payloadDataLengthInSamples) {
// Read an equal amount from the near file to avoid errors due to
// not reaching end-of-file.
EXPECT_EQ(0, fseek(near_file, read_count * sizeof(WebRtc_Word16),
SEEK_CUR));
break; // This is expected.
}
} else {
ASSERT_EQ(read_count,
far_frame._payloadDataLengthInSamples);
}
if (perf_testing) {
t0 = TickTime::Now();
}
ASSERT_EQ(apm->kNoError,
apm->AnalyzeReverseStream(&far_frame));
if (perf_testing) {
t1 = TickTime::Now();
TickInterval tick_diff = t1 - t0;
acc_ticks += tick_diff;
if (tick_diff.Microseconds() > max_time_reverse_us) {
max_time_reverse_us = tick_diff.Microseconds();
}
if (tick_diff.Microseconds() < min_time_reverse_us) {
min_time_reverse_us = tick_diff.Microseconds();
}
}
} else if (event == kCaptureEvent) {
primary_count++;
near_frame._audioChannel = num_capture_input_channels;
near_frame._payloadDataLengthInSamples =
num_capture_input_channels * samples_per_channel;
read_count = fread(near_frame._payloadData,
sizeof(WebRtc_Word16),
near_frame._payloadDataLengthInSamples,
near_file);
near_read_samples += read_count;
if (progress && primary_count % 100 == 0) {
printf("%.0f%% complete\r",
(near_read_samples * 100.0) / near_size_samples);
fflush(stdout);
}
if (simulating) {
if (read_count != near_frame._payloadDataLengthInSamples) {
break; // This is expected.
}
delay_ms = 0;
drift_samples = 0;
} else {
ASSERT_EQ(read_count,
near_frame._payloadDataLengthInSamples);
// TODO(ajm): sizeof(delay_ms) for current files?
ASSERT_EQ(1u,
fread(&delay_ms, 2, 1, delay_file));
ASSERT_EQ(1u,
fread(&drift_samples, sizeof(drift_samples), 1, drift_file));
}
if (perf_testing) {
t0 = TickTime::Now();
}
// TODO(ajm): fake an analog gain while simulating.
int capture_level_in = capture_level;
ASSERT_EQ(apm->kNoError,
apm->gain_control()->set_stream_analog_level(capture_level));
ASSERT_EQ(apm->kNoError,
apm->set_stream_delay_ms(delay_ms));
ASSERT_EQ(apm->kNoError,
apm->echo_cancellation()->set_stream_drift_samples(drift_samples));
int err = apm->ProcessStream(&near_frame);
if (err == apm->kBadStreamParameterWarning) {
printf("Bad parameter warning. %s\n", trace_stream.str().c_str());
}
ASSERT_TRUE(err == apm->kNoError ||
err == apm->kBadStreamParameterWarning);
capture_level = apm->gain_control()->stream_analog_level();
stream_has_voice =
static_cast<int8_t>(apm->voice_detection()->stream_has_voice());
if (vad_out_file != NULL) {
ASSERT_EQ(1u, fwrite(&stream_has_voice,
sizeof(stream_has_voice),
1,
vad_out_file));
}
if (apm->gain_control()->mode() != GainControl::kAdaptiveAnalog) {
ASSERT_EQ(capture_level_in, capture_level);
}
if (perf_testing) {
t1 = TickTime::Now();
TickInterval tick_diff = t1 - t0;
acc_ticks += tick_diff;
if (tick_diff.Microseconds() > max_time_us) {
max_time_us = tick_diff.Microseconds();
}
if (tick_diff.Microseconds() < min_time_us) {
min_time_us = tick_diff.Microseconds();
}
}
ASSERT_EQ(near_frame._payloadDataLengthInSamples,
fwrite(near_frame._payloadData,
sizeof(WebRtc_Word16),
near_frame._payloadDataLengthInSamples,
out_file));
}
else {
FAIL() << "Event " << event << " is unrecognized";
}
}
}
printf("100%% complete\r");
if (aecm_echo_path_out_file != NULL) {
const size_t path_size =
apm->echo_control_mobile()->echo_path_size_bytes();
unsigned char echo_path[path_size];
apm->echo_control_mobile()->GetEchoPath(echo_path, path_size);
ASSERT_EQ(path_size, fwrite(echo_path,
sizeof(unsigned char),
path_size,
aecm_echo_path_out_file));
fclose(aecm_echo_path_out_file);
aecm_echo_path_out_file = NULL;
}
if (verbose) {
printf("\nProcessed frames: %d (primary), %d (reverse)\n",
primary_count, reverse_count);
if (apm->echo_cancellation()->are_metrics_enabled()) {
EchoCancellation::Metrics metrics;
apm->echo_cancellation()->GetMetrics(&metrics);
printf("\n--Echo metrics--\n");
printf("(avg, max, min)\n");
printf("ERL: ");
PrintStat(metrics.echo_return_loss);
printf("ERLE: ");
PrintStat(metrics.echo_return_loss_enhancement);
printf("ANLP: ");
PrintStat(metrics.a_nlp);
}
if (apm->echo_cancellation()->is_delay_logging_enabled()) {
int median = 0;
int std = 0;
apm->echo_cancellation()->GetDelayMetrics(&median, &std);
printf("\n--Delay metrics--\n");
printf("Median: %3d\n", median);
printf("Standard deviation: %3d\n", std);
}
}
if (!pb_file) {
int8_t temp_int8;
if (far_file) {
read_count = fread(&temp_int8, sizeof(temp_int8), 1, far_file);
EXPECT_NE(0, feof(far_file)) << "Far-end file not fully processed";
}
read_count = fread(&temp_int8, sizeof(temp_int8), 1, near_file);
EXPECT_NE(0, feof(near_file)) << "Near-end file not fully processed";
if (!simulating) {
read_count = fread(&temp_int8, sizeof(temp_int8), 1, event_file);
EXPECT_NE(0, feof(event_file)) << "Event file not fully processed";
read_count = fread(&temp_int8, sizeof(temp_int8), 1, delay_file);
EXPECT_NE(0, feof(delay_file)) << "Delay file not fully processed";
read_count = fread(&temp_int8, sizeof(temp_int8), 1, drift_file);
EXPECT_NE(0, feof(drift_file)) << "Drift file not fully processed";
}
}
if (perf_testing) {
if (primary_count > 0) {
WebRtc_Word64 exec_time = acc_ticks.Milliseconds();
printf("\nTotal time: %.3f s, file time: %.2f s\n",
exec_time * 0.001, primary_count * 0.01);
printf("Time per frame: %.3f ms (average), %.3f ms (max),"
" %.3f ms (min)\n",
(exec_time * 1.0) / primary_count,
(max_time_us + max_time_reverse_us) / 1000.0,
(min_time_us + min_time_reverse_us) / 1000.0);
} else {
printf("Warning: no capture frames\n");
}
}
AudioProcessing::Destroy(apm);
apm = NULL;
}
} // namespace
int main(int argc, char* argv[])
{
void_main(argc, argv);
// Optional, but removes memory leak noise from Valgrind.
google::protobuf::ShutdownProtobufLibrary();
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,50 @@
syntax = "proto2";
option optimize_for = LITE_RUNTIME;
package webrtc.audioproc;
message Test {
optional int32 num_reverse_channels = 1;
optional int32 num_input_channels = 2;
optional int32 num_output_channels = 3;
optional int32 sample_rate = 4;
message Frame {
}
repeated Frame frame = 5;
optional int32 analog_level_average = 6;
optional int32 max_output_average = 7;
optional int32 has_echo_count = 8;
optional int32 has_voice_count = 9;
optional int32 is_saturated_count = 10;
message Statistic {
optional int32 instant = 1;
optional int32 average = 2;
optional int32 maximum = 3;
optional int32 minimum = 4;
}
message EchoMetrics {
optional Statistic residual_echo_return_loss = 1;
optional Statistic echo_return_loss = 2;
optional Statistic echo_return_loss_enhancement = 3;
optional Statistic a_nlp = 4;
}
optional EchoMetrics echo_metrics = 11;
message DelayMetrics {
optional int32 median = 1;
optional int32 std = 2;
}
optional DelayMetrics delay_metrics = 12;
}
message OutputData {
repeated Test test = 1;
}

View File

@ -0,0 +1,12 @@
noinst_LTLIBRARIES = libapm_util.la
libapm_util_la_SOURCES = delay_estimator_float.c \
delay_estimator_float.h \
delay_estimator.c \
delay_estimator.h \
fft4g.c \
fft4g.h \
ring_buffer.c \
ring_buffer.h
libapm_util_la_CFLAGS = $(AM_CFLAGS) $(COMMON_CFLAGS) \
-I$(top_srcdir)/src/common_audio/signal_processing_library/main/interface

View File

@ -0,0 +1,550 @@
/*
* 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.
*/
#include "delay_estimator.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "signal_processing_library.h"
typedef struct {
// Pointers to mean values of spectrum and bit counts
int32_t* mean_far_spectrum;
int32_t* mean_near_spectrum;
int32_t* mean_bit_counts;
// Arrays only used locally in DelayEstimatorProcess() but whose size
// is determined at run-time.
int32_t* bit_counts;
int32_t* far_spectrum_32;
int32_t* near_spectrum_32;
// Binary history variables
uint32_t* binary_far_history;
// Far end history variables
uint16_t* far_history;
int far_history_pos;
int* far_q_domains;
// Delay histogram variables
int* delay_histogram;
int vad_counter;
// Delay memory
int last_delay;
// Used to enable far end alignment. If it is disabled, only delay values are
// produced
int alignment_enabled;
// Buffer size parameters
int history_size;
int spectrum_size;
} DelayEstimator_t;
// Only bit |kBandFirst| through bit |kBandLast| are processed
// |kBandFirst| - |kBandLast| must be < 32
static const int kBandFirst = 12;
static const int kBandLast = 43;
static __inline uint32_t SetBit(uint32_t in, int32_t pos) {
uint32_t mask = WEBRTC_SPL_LSHIFT_W32(1, pos);
uint32_t out = (in | mask);
return out;
}
// Compares the |binary_vector| with all rows of the |binary_matrix| and counts
// per row the number of times they have the same value.
//
// Inputs:
// - binary_vector : binary "vector" stored in a long
// - binary_matrix : binary "matrix" stored as a vector of long
// - matrix_size : size of binary "matrix"
//
// Output:
// - bit_counts : "Vector" stored as a long, containing for each
// row the number of times the matrix row and the
// input vector have the same value
//
static void BitCountComparison(uint32_t binary_vector,
const uint32_t* binary_matrix,
int matrix_size,
int32_t* bit_counts) {
int n = 0;
uint32_t a = binary_vector;
register uint32_t tmp;
// compare |binary_vector| with all rows of the |binary_matrix|
for (; n < matrix_size; n++) {
a = (binary_vector ^ binary_matrix[n]);
// Returns bit counts in tmp
tmp = a - ((a >> 1) & 033333333333) - ((a >> 2) & 011111111111);
tmp = ((tmp + (tmp >> 3)) & 030707070707);
tmp = (tmp + (tmp >> 6));
tmp = (tmp + (tmp >> 12) + (tmp >> 24)) & 077;
bit_counts[n] = (int32_t) tmp;
}
}
// Computes the binary spectrum by comparing the input |spectrum| with a
// |threshold_spectrum|.
//
// Inputs:
// - spectrum : Spectrum of which the binary spectrum should be
// calculated.
// - threshold_spectrum : Threshold spectrum with which the input
// spectrum is compared.
// Return:
// - out : Binary spectrum
//
static uint32_t BinarySpectrum(int32_t* spectrum, int32_t* threshold_spectrum) {
int k = kBandFirst;
uint32_t out = 0;
for (; k <= kBandLast; k++) {
if (spectrum[k] > threshold_spectrum[k]) {
out = SetBit(out, k - kBandFirst);
}
}
return out;
}
// Calculates the mean recursively.
//
// Inputs:
// - new_value : new additional value
// - factor : factor for smoothing
//
// Input/Output:
// - mean_value : pointer to the mean value that should be updated
//
static void MeanEstimator(const int32_t new_value,
int factor,
int32_t* mean_value) {
int32_t mean_new = *mean_value;
int32_t diff = new_value - mean_new;
// mean_new = mean_value + ((new_value - mean_value) >> factor);
if (diff < 0) {
diff = -WEBRTC_SPL_RSHIFT_W32(-diff, factor);
} else {
diff = WEBRTC_SPL_RSHIFT_W32(diff, factor);
}
mean_new += diff;
*mean_value = mean_new;
}
// Moves the pointer to the next entry and inserts |far_spectrum| and
// corresponding Q-domain in its buffer.
//
// Inputs:
// - self : Pointer to the delay estimation instance
// - far_spectrum : Pointer to the far end spectrum
// - far_q : Q-domain of far end spectrum
//
static void UpdateFarHistory(DelayEstimator_t* self,
uint16_t* far_spectrum,
int far_q) {
// Get new buffer position
self->far_history_pos++;
if (self->far_history_pos >= self->history_size) {
self->far_history_pos = 0;
}
// Update Q-domain buffer
self->far_q_domains[self->far_history_pos] = far_q;
// Update far end spectrum buffer
memcpy(&(self->far_history[self->far_history_pos * self->spectrum_size]),
far_spectrum,
sizeof(uint16_t) * self->spectrum_size);
}
int WebRtc_FreeDelayEstimator(void* handle) {
DelayEstimator_t* self = (DelayEstimator_t*) handle;
if (self == NULL) {
return -1;
}
if (self->mean_far_spectrum != NULL) {
free(self->mean_far_spectrum);
self->mean_far_spectrum = NULL;
}
if (self->mean_near_spectrum != NULL) {
free(self->mean_near_spectrum);
self->mean_near_spectrum = NULL;
}
if (self->mean_bit_counts != NULL) {
free(self->mean_bit_counts);
self->mean_bit_counts = NULL;
}
if (self->bit_counts != NULL) {
free(self->bit_counts);
self->bit_counts = NULL;
}
if (self->far_spectrum_32 != NULL) {
free(self->far_spectrum_32);
self->far_spectrum_32 = NULL;
}
if (self->near_spectrum_32 != NULL) {
free(self->near_spectrum_32);
self->near_spectrum_32 = NULL;
}
if (self->binary_far_history != NULL) {
free(self->binary_far_history);
self->binary_far_history = NULL;
}
if (self->far_history != NULL) {
free(self->far_history);
self->far_history = NULL;
}
if (self->far_q_domains != NULL) {
free(self->far_q_domains);
self->far_q_domains = NULL;
}
if (self->delay_histogram != NULL) {
free(self->delay_histogram);
self->delay_histogram = NULL;
}
free(self);
return 0;
}
int WebRtc_CreateDelayEstimator(void** handle,
int spectrum_size,
int history_size,
int enable_alignment) {
DelayEstimator_t *self = NULL;
// Check if the sub band used in the delay estimation is small enough to
// fit the binary spectra in a uint32.
assert(kBandLast - kBandFirst < 32);
if (spectrum_size < kBandLast) {
return -1;
}
if (history_size < 0) {
return -1;
}
if ((enable_alignment != 0) && (enable_alignment != 1)) {
return -1;
}
self = malloc(sizeof(DelayEstimator_t));
*handle = self;
if (self == NULL) {
return -1;
}
self->mean_far_spectrum = NULL;
self->mean_near_spectrum = NULL;
self->mean_bit_counts = NULL;
self->bit_counts = NULL;
self->far_spectrum_32 = NULL;
self->near_spectrum_32 = NULL;
self->binary_far_history = NULL;
self->far_history = NULL;
self->far_q_domains = NULL;
self->delay_histogram = NULL;
// Allocate memory for spectrum buffers
self->mean_far_spectrum = malloc(spectrum_size * sizeof(int32_t));
if (self->mean_far_spectrum == NULL) {
WebRtc_FreeDelayEstimator(self);
self = NULL;
return -1;
}
self->mean_near_spectrum = malloc(spectrum_size * sizeof(int32_t));
if (self->mean_near_spectrum == NULL) {
WebRtc_FreeDelayEstimator(self);
self = NULL;
return -1;
}
self->mean_bit_counts = malloc(history_size * sizeof(int32_t));
if (self->mean_bit_counts == NULL) {
WebRtc_FreeDelayEstimator(self);
self = NULL;
return -1;
}
self->bit_counts = malloc(history_size * sizeof(int32_t));
if (self->bit_counts == NULL) {
WebRtc_FreeDelayEstimator(self);
self = NULL;
return -1;
}
self->far_spectrum_32 = malloc(spectrum_size * sizeof(int32_t));
if (self->far_spectrum_32 == NULL) {
WebRtc_FreeDelayEstimator(self);
self = NULL;
return -1;
}
self->near_spectrum_32 = malloc(spectrum_size * sizeof(int32_t));
if (self->near_spectrum_32 == NULL) {
WebRtc_FreeDelayEstimator(self);
self = NULL;
return -1;
}
// Allocate memory for history buffers
self->binary_far_history = malloc(history_size * sizeof(uint32_t));
if (self->binary_far_history == NULL) {
WebRtc_FreeDelayEstimator(self);
self = NULL;
return -1;
}
if (enable_alignment) {
self->far_history = malloc(spectrum_size * history_size * sizeof(uint16_t));
if (self->far_history == NULL) {
WebRtc_FreeDelayEstimator(self);
self = NULL;
return -1;
}
self->far_q_domains = malloc(history_size * sizeof(int));
if (self->far_q_domains == NULL) {
WebRtc_FreeDelayEstimator(self);
self = NULL;
return -1;
}
}
self->delay_histogram = malloc(history_size * sizeof(int));
if (self->delay_histogram == NULL) {
WebRtc_FreeDelayEstimator(self);
self = NULL;
return -1;
}
self->spectrum_size = spectrum_size;
self->history_size = history_size;
self->alignment_enabled = enable_alignment;
return 0;
}
int WebRtc_InitDelayEstimator(void* handle) {
DelayEstimator_t* self = (DelayEstimator_t*) handle;
if (self == NULL) {
return -1;
}
// Set averaged far and near end spectra to zero
memset(self->mean_far_spectrum, 0, sizeof(int32_t) * self->spectrum_size);
memset(self->mean_near_spectrum, 0, sizeof(int32_t) * self->spectrum_size);
// Set averaged bit counts to zero
memset(self->mean_bit_counts, 0, sizeof(int32_t) * self->history_size);
memset(self->bit_counts, 0, sizeof(int32_t) * self->history_size);
memset(self->far_spectrum_32, 0, sizeof(int32_t) * self->spectrum_size);
memset(self->near_spectrum_32, 0, sizeof(int32_t) * self->spectrum_size);
// Set far end histories to zero
memset(self->binary_far_history, 0, sizeof(uint32_t) * self->history_size);
if (self->alignment_enabled) {
memset(self->far_history,
0,
sizeof(uint16_t) * self->spectrum_size * self->history_size);
memset(self->far_q_domains, 0, sizeof(int) * self->history_size);
self->far_history_pos = self->history_size;
}
// Set delay histogram to zero
memset(self->delay_histogram, 0, sizeof(int) * self->history_size);
// Set VAD counter to zero
self->vad_counter = 0;
// Set delay memory to zero
self->last_delay = 0;
return 0;
}
int WebRtc_DelayEstimatorProcess(void* handle,
uint16_t* far_spectrum,
uint16_t* near_spectrum,
int spectrum_size,
int far_q,
int vad_value) {
DelayEstimator_t* self = (DelayEstimator_t*) handle;
const int kVadCountThreshold = 25;
const int kMaxHistogram = 600;
int histogram_bin = 0;
int i = 0;
int max_histogram_level = 0;
int min_position = -1;
uint32_t binary_far_spectrum = 0;
uint32_t binary_near_spectrum = 0;
int32_t bit_counts_tmp = 0;
if (self == NULL) {
return -1;
}
if (spectrum_size != self->spectrum_size) {
// Data sizes don't match
return -1;
}
if (far_q > 15) {
// If |far_q| is larger than 15 we cannot guarantee no wrap around
return -1;
}
if (self->alignment_enabled) {
// Update far end history
UpdateFarHistory(self, far_spectrum, far_q);
} // Update the far and near end means
for (i = 0; i < self->spectrum_size; i++) {
self->far_spectrum_32[i] = (int32_t) far_spectrum[i];
MeanEstimator(self->far_spectrum_32[i], 6, &(self->mean_far_spectrum[i]));
self->near_spectrum_32[i] = (int32_t) near_spectrum[i];
MeanEstimator(self->near_spectrum_32[i], 6, &(self->mean_near_spectrum[i]));
}
// Shift binary spectrum history
memmove(&(self->binary_far_history[1]), &(self->binary_far_history[0]),
(self->history_size - 1) * sizeof(uint32_t));
// Get binary spectra
binary_far_spectrum = BinarySpectrum(self->far_spectrum_32,
self->mean_far_spectrum);
binary_near_spectrum = BinarySpectrum(self->near_spectrum_32,
self->mean_near_spectrum);
// Insert new binary spectrum
self->binary_far_history[0] = binary_far_spectrum;
// Compare with delayed spectra
BitCountComparison(binary_near_spectrum,
self->binary_far_history,
self->history_size,
self->bit_counts);
// Smooth bit count curve
for (i = 0; i < self->history_size; i++) {
// Update sum
// |bit_counts| is constrained to [0, 32], meaning we can smooth with a
// factor up to 2^26. We use Q9.
bit_counts_tmp = WEBRTC_SPL_LSHIFT_W32(self->bit_counts[i], 9); // Q9
MeanEstimator(bit_counts_tmp, 9, &(self->mean_bit_counts[i]));
}
// Find minimum position of bit count curve
min_position = (int) WebRtcSpl_MinIndexW32(self->mean_bit_counts,
(int16_t) self->history_size);
// If the far end has been active sufficiently long, begin accumulating a
// histogram of the minimum positions. Search for the maximum bin to
// determine the delay.
if (vad_value == 1) {
if (self->vad_counter >= kVadCountThreshold) {
// Increment the histogram at the current minimum position.
if (self->delay_histogram[min_position] < kMaxHistogram) {
self->delay_histogram[min_position] += 3;
}
self->last_delay = 0;
for (i = 0; i < self->history_size; i++) {
histogram_bin = self->delay_histogram[i];
// Decrement the histogram bin.
if (histogram_bin > 0) {
histogram_bin--;
self->delay_histogram[i] = histogram_bin;
// Select the histogram index corresponding to the maximum bin as the
// delay.
if (histogram_bin > max_histogram_level) {
max_histogram_level = histogram_bin;
self->last_delay = i;
}
}
}
} else {
self->vad_counter++;
}
} else {
self->vad_counter = 0;
}
return self->last_delay;
}
const uint16_t* WebRtc_AlignedFarend(void* handle,
int far_spectrum_size,
int* far_q) {
DelayEstimator_t* self = (DelayEstimator_t*) handle;
int buffer_position = 0;
if (self == NULL) {
return NULL;
}
if (far_spectrum_size != self->spectrum_size) {
return NULL;
}
if (self->alignment_enabled == 0) {
return NULL;
}
// Get buffer position
buffer_position = self->far_history_pos - self->last_delay;
if (buffer_position < 0) {
buffer_position += self->history_size;
}
// Get Q-domain
*far_q = self->far_q_domains[buffer_position];
// Return far end spectrum
return (self->far_history + (buffer_position * far_spectrum_size));
}
int WebRtc_last_delay(void* handle) {
DelayEstimator_t* self = (DelayEstimator_t*) handle;
if (self == NULL) {
return -1;
}
return self->last_delay;
}
int WebRtc_history_size(void* handle) {
DelayEstimator_t* self = (DelayEstimator_t*) handle;
if (self == NULL) {
return -1;
}
return self->history_size;
}
int WebRtc_spectrum_size(void* handle) {
DelayEstimator_t* self = (DelayEstimator_t*) handle;
if (self == NULL) {
return -1;
}
return self->spectrum_size;
}
int WebRtc_is_alignment_enabled(void* handle) {
DelayEstimator_t* self = (DelayEstimator_t*) handle;
if (self == NULL) {
return -1;
}
return self->alignment_enabled;
}

View File

@ -0,0 +1,154 @@
/*
* 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.
*/
// Performs delay estimation on a block by block basis
// The return value is 0 - OK and -1 - Error, unless otherwise stated.
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_H_
#include "typedefs.h"
// Releases the memory allocated by WebRtc_CreateDelayEstimator(...)
// Input:
// - handle : Pointer to the delay estimation instance
//
int WebRtc_FreeDelayEstimator(void* handle);
// Allocates the memory needed by the delay estimation. The memory needs to be
// initialized separately using the WebRtc_InitDelayEstimator(...)
// function.
//
// Inputs:
// - handle : Instance that should be created
// - spectrum_size : Size of the spectrum used both in far end and
// near end. Used to allocate memory for spectrum
// specific buffers.
// - history_size : Size of the far end history used to estimate the
// delay from. Used to allocate memory for history
// specific buffers.
// - enable_alignment : With this mode set to 1, a far end history is
// created, so that the user can retrieve aligned
// far end spectra using
// WebRtc_AlignedFarend(...). Otherwise, only delay
// values are calculated.
//
// Output:
// - handle : Created instance
//
int WebRtc_CreateDelayEstimator(void** handle,
int spectrum_size,
int history_size,
int enable_alignment);
// Initializes the delay estimation instance created with
// WebRtc_CreateDelayEstimator(...)
// Input:
// - handle : Pointer to the delay estimation instance
//
// Output:
// - handle : Initialized instance
//
int WebRtc_InitDelayEstimator(void* handle);
// Estimates and returns the delay between the far end and near end blocks.
// Inputs:
// - handle : Pointer to the delay estimation instance
// - far_spectrum : Pointer to the far end spectrum data
// - near_spectrum : Pointer to the near end spectrum data of the current
// block
// - spectrum_size : The size of the data arrays (same for both far and
// near end)
// - far_q : The Q-domain of the far end data
// - vad_value : The VAD decision of the current block
//
// Output:
// - handle : Updated instance
//
// Return value:
// - delay : >= 0 - Calculated delay value
// -1 - Error
//
int WebRtc_DelayEstimatorProcess(void* handle,
uint16_t* far_spectrum,
uint16_t* near_spectrum,
int spectrum_size,
int far_q,
int vad_value);
// Returns a pointer to the far end spectrum aligned to current near end
// spectrum. The function WebRtc_DelayEstimatorProcess(...) should have been
// called before WebRtc_AlignedFarend(...). Otherwise, you get the pointer to
// the previous frame. The memory is only valid until the next call of
// WebRtc_DelayEstimatorProcess(...).
//
// Inputs:
// - handle : Pointer to the delay estimation instance
// - far_spectrum_size : Size of far_spectrum allocated by the caller
//
// Output:
// - far_q : The Q-domain of the aligned far end spectrum
//
// Return value:
// - far_spectrum : Pointer to the aligned far end spectrum
// NULL - Error
//
const uint16_t* WebRtc_AlignedFarend(void* handle,
int far_spectrum_size,
int* far_q);
// Returns the last calculated delay updated by the function
// WebRtc_DelayEstimatorProcess(...)
//
// Input:
// - handle : Pointer to the delay estimation instance
//
// Return value:
// - delay : >= 0 - Last calculated delay value
// -1 - Error
//
int WebRtc_last_delay(void* handle);
// Returns the history size used in the far end buffers to calculate the delay
// over.
//
// Input:
// - handle : Pointer to the delay estimation instance
//
// Return value:
// - history_size : > 0 - Far end history size
// -1 - Error
//
int WebRtc_history_size(void* handle);
// Returns the fixed spectrum size used in the algorithm.
//
// Input:
// - handle : Pointer to the delay estimation instance
//
// Return value:
// - spectrum_size : > 0 - Spectrum size
// -1 - Error
//
int WebRtc_spectrum_size(void* handle);
// Returns 1 if the far end alignment is enabled and 0 otherwise.
//
// Input:
// - handle : Pointer to the delay estimation instance
//
// Return value:
// - alignment_enabled : 1 - Enabled
// 0 - Disabled
// -1 - Error
//
int WebRtc_is_alignment_enabled(void* handle);
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_H_

View File

@ -0,0 +1,288 @@
/*
* 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.
*/
#include "delay_estimator_float.h"
#include <assert.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "delay_estimator.h"
#include "signal_processing_library.h"
typedef struct {
// Fixed point spectra
uint16_t* far_spectrum_u16;
uint16_t* near_spectrum_u16;
// Far end history variables
float* far_history;
int far_history_pos;
// Fixed point delay estimator
void* fixed_handle;
} DelayEstimatorFloat_t;
// Moves the pointer to the next buffer entry and inserts new far end spectrum.
// Only used when alignment is enabled.
//
// Inputs:
// - self : Pointer to the delay estimation instance
// - far_spectrum : Pointer to the far end spectrum
//
static void UpdateFarHistory(DelayEstimatorFloat_t* self, float* far_spectrum) {
int spectrum_size = WebRtc_spectrum_size(self->fixed_handle);
// Get new buffer position
self->far_history_pos++;
if (self->far_history_pos >= WebRtc_history_size(self->fixed_handle)) {
self->far_history_pos = 0;
}
// Update far end spectrum buffer
memcpy(&(self->far_history[self->far_history_pos * spectrum_size]),
far_spectrum,
sizeof(float) * spectrum_size);
}
int WebRtc_FreeDelayEstimatorFloat(void* handle) {
DelayEstimatorFloat_t* self = (DelayEstimatorFloat_t*) handle;
if (self == NULL) {
return -1;
}
if (self->far_history != NULL) {
free(self->far_history);
self->far_history = NULL;
}
if (self->far_spectrum_u16 != NULL) {
free(self->far_spectrum_u16);
self->far_spectrum_u16 = NULL;
}
if (self->near_spectrum_u16 != NULL) {
free(self->near_spectrum_u16);
self->near_spectrum_u16 = NULL;
}
WebRtc_FreeDelayEstimator(self->fixed_handle);
free(self);
return 0;
}
int WebRtc_CreateDelayEstimatorFloat(void** handle,
int spectrum_size,
int history_size,
int enable_alignment) {
DelayEstimatorFloat_t *self = NULL;
if ((enable_alignment != 0) && (enable_alignment != 1)) {
return -1;
}
self = malloc(sizeof(DelayEstimatorFloat_t));
*handle = self;
if (self == NULL) {
return -1;
}
self->far_history = NULL;
self->far_spectrum_u16 = NULL;
self->near_spectrum_u16 = NULL;
// Create fixed point core delay estimator
if (WebRtc_CreateDelayEstimator(&self->fixed_handle,
spectrum_size,
history_size,
enable_alignment) != 0) {
WebRtc_FreeDelayEstimatorFloat(self);
self = NULL;
return -1;
}
// Allocate memory for far history buffer
if (enable_alignment) {
self->far_history = malloc(spectrum_size * history_size * sizeof(float));
if (self->far_history == NULL) {
WebRtc_FreeDelayEstimatorFloat(self);
self = NULL;
return -1;
}
}
// Allocate memory for fixed point spectra
self->far_spectrum_u16 = malloc(spectrum_size * sizeof(uint16_t));
if (self->far_spectrum_u16 == NULL) {
WebRtc_FreeDelayEstimatorFloat(self);
self = NULL;
return -1;
}
self->near_spectrum_u16 = malloc(spectrum_size * sizeof(uint16_t));
if (self->near_spectrum_u16 == NULL) {
WebRtc_FreeDelayEstimatorFloat(self);
self = NULL;
return -1;
}
return 0;
}
int WebRtc_InitDelayEstimatorFloat(void* handle) {
DelayEstimatorFloat_t* self = (DelayEstimatorFloat_t*) handle;
if (self == NULL) {
return -1;
}
if (WebRtc_InitDelayEstimator(self->fixed_handle) != 0) {
return -1;
}
{
int history_size = WebRtc_history_size(self->fixed_handle);
int spectrum_size = WebRtc_spectrum_size(self->fixed_handle);
if (WebRtc_is_alignment_enabled(self->fixed_handle) == 1) {
// Set far end histories to zero
memset(self->far_history,
0,
sizeof(float) * spectrum_size * history_size);
self->far_history_pos = history_size;
}
// Set fixed point spectra to zero
memset(self->far_spectrum_u16, 0, sizeof(uint16_t) * spectrum_size);
memset(self->near_spectrum_u16, 0, sizeof(uint16_t) * spectrum_size);
}
return 0;
}
int WebRtc_DelayEstimatorProcessFloat(void* handle,
float* far_spectrum,
float* near_spectrum,
int spectrum_size,
int vad_value) {
DelayEstimatorFloat_t* self = (DelayEstimatorFloat_t*) handle;
const float kFftSize = (float) (2 * (spectrum_size - 1));
const float kLogOf2Inverse = 1.4426950f;
float max_value = 0.0f;
float scaling = 0;
int far_q = 0;
int scaling_log = 0;
int i = 0;
if (self == NULL) {
return -1;
}
if (far_spectrum == NULL) {
// Empty far end spectrum
return -1;
}
if (near_spectrum == NULL) {
// Empty near end spectrum
return -1;
}
if (spectrum_size != WebRtc_spectrum_size(self->fixed_handle)) {
// Data sizes don't match
return -1;
}
// Convert floating point spectrum to fixed point
// 1) Find largest value
// 2) Scale largest value to fit in Word16
for (i = 0; i < spectrum_size; ++i) {
if (near_spectrum[i] > max_value) {
max_value = near_spectrum[i];
}
}
// Find the largest possible scaling that is a multiple of two.
// With largest we mean to fit in a Word16.
// TODO(bjornv): I've taken the size of FFT into account, since there is a
// different scaling in float vs fixed point FFTs. I'm not completely sure
// this is necessary.
scaling_log = 14 - (int) (log(max_value / kFftSize + 1) * kLogOf2Inverse);
scaling = (float) (1 << scaling_log) / kFftSize;
for (i = 0; i < spectrum_size; ++i) {
self->near_spectrum_u16[i] = (uint16_t) (near_spectrum[i] * scaling);
}
// Same for far end
max_value = 0.0f;
for (i = 0; i < spectrum_size; ++i) {
if (far_spectrum[i] > max_value) {
max_value = far_spectrum[i];
}
}
// Find the largest possible scaling that is a multiple of two.
// With largest we mean to fit in a Word16.
scaling_log = 14 - (int) (log(max_value / kFftSize + 1) * kLogOf2Inverse);
scaling = (float) (1 << scaling_log) / kFftSize;
for (i = 0; i < spectrum_size; ++i) {
self->far_spectrum_u16[i] = (uint16_t) (far_spectrum[i] * scaling);
}
far_q = (int) scaling_log;
assert(far_q < 16); // Catch too large scaling, which should never be able to
// occur.
if (WebRtc_is_alignment_enabled(self->fixed_handle) == 1) {
// Update far end history
UpdateFarHistory(self, far_spectrum);
}
return WebRtc_DelayEstimatorProcess(self->fixed_handle,
self->far_spectrum_u16,
self->near_spectrum_u16,
spectrum_size,
far_q,
vad_value);
}
const float* WebRtc_AlignedFarendFloat(void* handle, int far_spectrum_size) {
DelayEstimatorFloat_t* self = (DelayEstimatorFloat_t*) handle;
int buffer_pos = 0;
if (self == NULL) {
return NULL;
}
if (far_spectrum_size != WebRtc_spectrum_size(self->fixed_handle)) {
return NULL;
}
if (WebRtc_is_alignment_enabled(self->fixed_handle) != 1) {
return NULL;
}
// Get buffer position
buffer_pos = self->far_history_pos - WebRtc_last_delay(self->fixed_handle);
if (buffer_pos < 0) {
buffer_pos += WebRtc_history_size(self->fixed_handle);
}
// Return pointer to far end spectrum
return (self->far_history + (buffer_pos * far_spectrum_size));
}
int WebRtc_last_delay_float(void* handle) {
DelayEstimatorFloat_t* self = (DelayEstimatorFloat_t*) handle;
if (self == NULL) {
return -1;
}
return WebRtc_last_delay(self->fixed_handle);
}
int WebRtc_is_alignment_enabled_float(void* handle) {
DelayEstimatorFloat_t* self = (DelayEstimatorFloat_t*) handle;
if (self == NULL) {
return -1;
}
return WebRtc_is_alignment_enabled(self->fixed_handle);
}

View File

@ -0,0 +1,125 @@
/*
* 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.
*/
// Performs delay estimation on a block by block basis
// The return value is 0 - OK and -1 - Error, unless otherwise stated.
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_FLOAT_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_FLOAT_H_
// Releases the memory allocated by WebRtc_CreateDelayEstimatorFloat(...)
// Input:
// - handle : Pointer to the delay estimation instance
//
int WebRtc_FreeDelayEstimatorFloat(void* handle);
// Allocates the memory needed by the delay estimation. The memory needs to be
// initialized separately using the WebRtc_InitDelayEstimatorFloat(...)
// function.
//
// Inputs:
// - handle : Instance that should be created
// - spectrum_size : Size of the spectrum used both in far end and
// near end. Used to allocate memory for spectrum
// specific buffers.
// - history_size : Size of the far end history used to estimate the
// delay from. Used to allocate memory for history
// specific buffers.
// - enable_alignment : With this mode set to 1, a far end history is
// created, so that the user can retrieve aligned
// far end spectra using
// WebRtc_AlignedFarendFloat(...). Otherwise, only
// delay values are calculated.
//
// Output:
// - handle : Created instance
//
int WebRtc_CreateDelayEstimatorFloat(void** handle,
int spectrum_size,
int history_size,
int enable_alignment);
// Initializes the delay estimation instance created with
// WebRtc_CreateDelayEstimatorFloat(...)
// Input:
// - handle : Pointer to the delay estimation instance
//
// Output:
// - handle : Initialized instance
//
int WebRtc_InitDelayEstimatorFloat(void* handle);
// Estimates and returns the delay between the far end and near end blocks.
// Inputs:
// - handle : Pointer to the delay estimation instance
// - far_spectrum : Pointer to the far end spectrum data
// - near_spectrum : Pointer to the near end spectrum data of the current
// block
// - spectrum_size : The size of the data arrays (same for both far and
// near end)
// - far_q : The Q-domain of the far end data
// - vad_value : The VAD decision of the current block
//
// Output:
// - handle : Updated instance
//
// Return value:
// - delay : >= 0 - Calculated delay value
// -1 - Error
//
int WebRtc_DelayEstimatorProcessFloat(void* handle,
float* far_spectrum,
float* near_spectrum,
int spectrum_size,
int vad_value);
// Returns a pointer to the far end spectrum aligned to current near end
// spectrum. The function WebRtc_DelayEstimatorProcessFloat(...) should
// have been called before WebRtc_AlignedFarendFloat(...). Otherwise, you get
// the pointer to the previous frame. The memory is only valid until the
// next call of WebRtc_DelayEstimatorProcessFloat(...).
//
// Inputs:
// - handle : Pointer to the delay estimation instance
// - far_spectrum_size : Size of far_spectrum allocated by the caller
//
// Output:
//
// Return value:
// - far_spectrum : Pointer to the aligned far end spectrum
// NULL - Error
//
const float* WebRtc_AlignedFarendFloat(void* handle, int far_spectrum_size);
// Returns the last calculated delay updated by the function
// WebRtcApm_DelayEstimatorProcessFloat(...)
//
// Inputs:
// - handle : Pointer to the delay estimation instance
//
// Return value:
// - delay : >= 0 - Last calculated delay value
// -1 - Error
//
int WebRtc_last_delay_float(void* handle);
// Returns 1 if the far end alignment is enabled and 0 otherwise.
//
// Input:
// - handle : Pointer to the delay estimation instance
//
// Return value:
// - alignment_enabled : 1 - Enabled
// 0 - Disabled
// -1 - Error
//
int WebRtc_is_alignment_enabled_float(void* handle);
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_FLOAT_H_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,18 @@
/*
* 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.
*/
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_UTILITY_FFT4G_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_UTILITY_FFT4G_H_
void rdft(int, int, float *, int *, float *);
void cdft(int, int, float *, int *, float *);
#endif

View File

@ -0,0 +1,239 @@
/*
* 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.
*/
/*
* Provides a generic ring buffer that can be written to and read from with
* arbitrarily sized blocks. The AEC uses this for several different tasks.
*/
#include <stdlib.h>
#include <string.h>
#include "ring_buffer.h"
typedef struct {
int readPos;
int writePos;
int size;
char rwWrap;
bufdata_t *data;
} buf_t;
enum {SAME_WRAP, DIFF_WRAP};
int WebRtcApm_CreateBuffer(void **bufInst, int size)
{
buf_t *buf = NULL;
if (size < 0) {
return -1;
}
buf = malloc(sizeof(buf_t));
*bufInst = buf;
if (buf == NULL) {
return -1;
}
buf->data = malloc(size*sizeof(bufdata_t));
if (buf->data == NULL) {
free(buf);
buf = NULL;
return -1;
}
buf->size = size;
return 0;
}
int WebRtcApm_InitBuffer(void *bufInst)
{
buf_t *buf = (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)
{
buf_t *buf = (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)
{
buf_t *buf = (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)
{
buf_t *buf = (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)
{
buf_t *buf = (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)
{
buf_t *buf = (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 buf_t *buf = (buf_t*)bufInst;
if (buf->rwWrap == SAME_WRAP)
return buf->writePos - buf->readPos;
else
return buf->size - buf->readPos + buf->writePos;
}

View File

@ -0,0 +1,41 @@
/*
* 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.
*/
/*
* Specifies the interface for the AEC generic buffer.
*/
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_UTILITY_RING_BUFFER_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_UTILITY_RING_BUFFER_H_
// Determines buffer datatype
typedef short bufdata_t;
// Unless otherwise specified, functions return 0 on success and -1 on error
int WebRtcApm_CreateBuffer(void **bufInst, int size);
int WebRtcApm_InitBuffer(void *bufInst);
int WebRtcApm_FreeBuffer(void *bufInst);
// Returns number of samples read
int WebRtcApm_ReadBuffer(void *bufInst, bufdata_t *data, int size);
// Returns number of samples written
int WebRtcApm_WriteBuffer(void *bufInst, const bufdata_t *data, int size);
// Returns number of samples flushed
int WebRtcApm_FlushBuffer(void *bufInst, int size);
// Returns number of samples stuffed
int WebRtcApm_StuffBuffer(void *bufInst, int size);
// Returns number of samples in buffer
int WebRtcApm_get_buffer_size(const void *bufInst);
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_UTILITY_RING_BUFFER_H_

View File

@ -0,0 +1,40 @@
# 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.
{
'targets': [
{
'target_name': 'apm_util',
'type': '<(library)',
'dependencies': [
'<(webrtc_root)/common_audio/common_audio.gyp:spl',
],
'direct_dependent_settings': {
'include_dirs': [
'.',
],
},
'sources': [
'delay_estimator_float.c',
'delay_estimator_float.h',
'delay_estimator.c',
'delay_estimator.h',
'fft4g.c',
'fft4g.h',
'ring_buffer.c',
'ring_buffer.h',
],
},
],
}
# Local Variables:
# tab-width:2
# indent-tabs-mode:nil
# End:
# vim: set expandtab tabstop=2 shiftwidth=2:

View File

@ -0,0 +1,202 @@
/*
* 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.
*/
#include "voice_detection_impl.h"
#include <cassert>
#include "critical_section_wrapper.h"
#include "webrtc_vad.h"
#include "audio_processing_impl.h"
#include "audio_buffer.h"
namespace webrtc {
typedef VadInst Handle;
namespace {
WebRtc_Word16 MapSetting(VoiceDetection::Likelihood likelihood) {
switch (likelihood) {
case VoiceDetection::kVeryLowLikelihood:
return 3;
break;
case VoiceDetection::kLowLikelihood:
return 2;
break;
case VoiceDetection::kModerateLikelihood:
return 1;
break;
case VoiceDetection::kHighLikelihood:
return 0;
break;
default:
return -1;
}
}
} // namespace
VoiceDetectionImpl::VoiceDetectionImpl(const AudioProcessingImpl* apm)
: ProcessingComponent(apm),
apm_(apm),
stream_has_voice_(false),
using_external_vad_(false),
likelihood_(kLowLikelihood),
frame_size_ms_(10),
frame_size_samples_(0) {}
VoiceDetectionImpl::~VoiceDetectionImpl() {}
int VoiceDetectionImpl::ProcessCaptureAudio(AudioBuffer* audio) {
if (!is_component_enabled()) {
return apm_->kNoError;
}
if (using_external_vad_) {
using_external_vad_ = false;
return apm_->kNoError;
}
assert(audio->samples_per_split_channel() <= 160);
WebRtc_Word16* mixed_data = audio->low_pass_split_data(0);
if (audio->num_channels() > 1) {
audio->CopyAndMixLowPass(1);
mixed_data = audio->mixed_low_pass_data(0);
}
// TODO(ajm): concatenate data in frame buffer here.
int vad_ret = WebRtcVad_Process(static_cast<Handle*>(handle(0)),
apm_->split_sample_rate_hz(),
mixed_data,
frame_size_samples_);
if (vad_ret == 0) {
stream_has_voice_ = false;
audio->set_activity(AudioFrame::kVadPassive);
} else if (vad_ret == 1) {
stream_has_voice_ = true;
audio->set_activity(AudioFrame::kVadActive);
} else {
return apm_->kUnspecifiedError;
}
return apm_->kNoError;
}
int VoiceDetectionImpl::Enable(bool enable) {
CriticalSectionScoped crit_scoped(*apm_->crit());
return EnableComponent(enable);
}
bool VoiceDetectionImpl::is_enabled() const {
return is_component_enabled();
}
int VoiceDetectionImpl::set_stream_has_voice(bool has_voice) {
using_external_vad_ = true;
stream_has_voice_ = has_voice;
return apm_->kNoError;
}
bool VoiceDetectionImpl::stream_has_voice() const {
// TODO(ajm): enable this assertion?
//assert(using_external_vad_ || is_component_enabled());
return stream_has_voice_;
}
int VoiceDetectionImpl::set_likelihood(VoiceDetection::Likelihood likelihood) {
CriticalSectionScoped crit_scoped(*apm_->crit());
if (MapSetting(likelihood) == -1) {
return apm_->kBadParameterError;
}
likelihood_ = likelihood;
return Configure();
}
VoiceDetection::Likelihood VoiceDetectionImpl::likelihood() const {
return likelihood_;
}
int VoiceDetectionImpl::set_frame_size_ms(int size) {
CriticalSectionScoped crit_scoped(*apm_->crit());
assert(size == 10); // TODO(ajm): remove when supported.
if (size != 10 &&
size != 20 &&
size != 30) {
return apm_->kBadParameterError;
}
frame_size_ms_ = size;
return Initialize();
}
int VoiceDetectionImpl::frame_size_ms() const {
return frame_size_ms_;
}
int VoiceDetectionImpl::Initialize() {
int err = ProcessingComponent::Initialize();
if (err != apm_->kNoError || !is_component_enabled()) {
return err;
}
using_external_vad_ = false;
frame_size_samples_ = frame_size_ms_ * (apm_->split_sample_rate_hz() / 1000);
// TODO(ajm): intialize frame buffer here.
return apm_->kNoError;
}
int VoiceDetectionImpl::get_version(char* version,
int version_len_bytes) const {
if (WebRtcVad_get_version(version, version_len_bytes) != 0) {
return apm_->kBadParameterError;
}
return apm_->kNoError;
}
void* VoiceDetectionImpl::CreateHandle() const {
Handle* handle = NULL;
if (WebRtcVad_Create(&handle) != apm_->kNoError) {
handle = NULL;
} else {
assert(handle != NULL);
}
return handle;
}
int VoiceDetectionImpl::DestroyHandle(void* handle) const {
return WebRtcVad_Free(static_cast<Handle*>(handle));
}
int VoiceDetectionImpl::InitializeHandle(void* handle) const {
return WebRtcVad_Init(static_cast<Handle*>(handle));
}
int VoiceDetectionImpl::ConfigureHandle(void* handle) const {
return WebRtcVad_set_mode(static_cast<Handle*>(handle),
MapSetting(likelihood_));
}
int VoiceDetectionImpl::num_handles_required() const {
return 1;
}
int VoiceDetectionImpl::GetHandleError(void* handle) const {
// The VAD has no get_error() function.
assert(handle != NULL);
return apm_->kUnspecifiedError;
}
} // namespace webrtc

View File

@ -0,0 +1,63 @@
/*
* 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.
*/
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_VOICE_DETECTION_IMPL_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_VOICE_DETECTION_IMPL_H_
#include "audio_processing.h"
#include "processing_component.h"
namespace webrtc {
class AudioProcessingImpl;
class AudioBuffer;
class VoiceDetectionImpl : public VoiceDetection,
public ProcessingComponent {
public:
explicit VoiceDetectionImpl(const AudioProcessingImpl* apm);
virtual ~VoiceDetectionImpl();
int ProcessCaptureAudio(AudioBuffer* audio);
// VoiceDetection implementation.
virtual bool is_enabled() const;
// ProcessingComponent implementation.
virtual int Initialize();
virtual int get_version(char* version, int version_len_bytes) const;
private:
// VoiceDetection implementation.
virtual int Enable(bool enable);
virtual int set_stream_has_voice(bool has_voice);
virtual bool stream_has_voice() const;
virtual int set_likelihood(Likelihood likelihood);
virtual Likelihood likelihood() const;
virtual int set_frame_size_ms(int size);
virtual int frame_size_ms() const;
// ProcessingComponent implementation.
virtual void* CreateHandle() const;
virtual int InitializeHandle(void* handle) const;
virtual int ConfigureHandle(void* handle) const;
virtual int DestroyHandle(void* handle) const;
virtual int num_handles_required() const;
virtual int GetHandleError(void* handle) const;
const AudioProcessingImpl* apm_;
bool stream_has_voice_;
bool using_external_vad_;
Likelihood likelihood_;
int frame_size_ms_;
int frame_size_samples_;
};
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_VOICE_DETECTION_IMPL_H_