Support for Signal calls.

Merge in RedPhone

// FREEBIE
This commit is contained in:
Moxie Marlinspike
2015-09-09 13:54:29 -07:00
parent 3d4ae60d81
commit d83a3d71bc
2585 changed files with 803492 additions and 45 deletions

View File

@@ -0,0 +1,96 @@
# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
#
# Use of this source code is governed by a BSD-style license
# that can be found in the LICENSE file in the root of the source
# tree. An additional intellectual property rights grant can be found
# in the file PATENTS. All contributing project authors may
# be found in the AUTHORS file in the root of the source tree.
#############################
# Build the non-neon library.
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
include $(LOCAL_PATH)/../../../../android-webrtc.mk
LOCAL_ARM_MODE := arm
LOCAL_MODULE_CLASS := STATIC_LIBRARIES
LOCAL_MODULE := libwebrtc_aecm
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := \
echo_control_mobile.c \
aecm_core.c \
aecm_core_c.c
# Flags passed to both C and C++ files.
LOCAL_CFLAGS := $(MY_WEBRTC_COMMON_DEFS)
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/include \
$(LOCAL_PATH)/../utility \
$(LOCAL_PATH)/../../.. \
$(LOCAL_PATH)/../../../common_audio/signal_processing/include \
$(LOCAL_PATH)/../../../system_wrappers/interface \
$(LOCAL_PATH)/../../../..
LOCAL_STATIC_LIBRARIES += libwebrtc_system_wrappers
LOCAL_SHARED_LIBRARIES := \
libcutils \
libdl \
libstlport
ifndef NDK_ROOT
include external/stlport/libstlport.mk
endif
include $(BUILD_STATIC_LIBRARY)
#########################
# Build the neon library.
ifeq ($(WEBRTC_BUILD_NEON_LIBS),true)
include $(CLEAR_VARS)
LOCAL_ARM_MODE := arm
LOCAL_MODULE_CLASS := STATIC_LIBRARIES
LOCAL_MODULE := libwebrtc_aecm_neon
LOCAL_MODULE_TAGS := optional
AECM_ASM_HEADER := $(intermediates)/aecm_core_neon_offsets.h
AECM_ASM_HEADER_DIR := $(intermediates)
# Generate a header file aecm_core_neon_offsets.h which will be included in
# assembly file aecm_core_neon.S, from file aecm_core_neon_offsets.c.
$(AECM_ASM_HEADER): $(LOCAL_PATH)/../../../build/generate_asm_header.py \
$(LOCAL_PATH)/aecm_core_neon_offsets.c
@python $^ --compiler=$(TARGET_CC) --options="$(addprefix -I, \
$(LOCAL_INCLUDES)) $(addprefix -isystem , $(TARGET_C_INCLUDES)) -S" \
--dir=$(AECM_ASM_HEADER_DIR)
LOCAL_GENERATED_SOURCES := $(AECM_ASM_HEADER)
LOCAL_SRC_FILES := aecm_core_neon.S
# Flags passed to both C and C++ files.
LOCAL_CFLAGS := \
$(MY_WEBRTC_COMMON_DEFS) \
-mfpu=neon \
-mfloat-abi=softfp \
-flax-vector-conversions
LOCAL_C_INCLUDES := \
$(AECM_ASM_HEADER_DIR) \
$(LOCAL_PATH)/include \
$(LOCAL_PATH)/../../.. \
$(LOCAL_PATH)/../../../common_audio/signal_processing/include \
$(LOCAL_PATH)/../../../..
LOCAL_INCLUDES := $(LOCAL_C_INCLUDES)
ifndef NDK_ROOT
include external/stlport/libstlport.mk
endif
include $(BUILD_STATIC_LIBRARY)
endif # ifeq ($(WEBRTC_BUILD_NEON_LIBS),true)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,450 @@
/*
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
// Performs echo control (suppression) with fft routines in fixed-point.
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AECM_AECM_CORE_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_AECM_AECM_CORE_H_
#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h"
#include "webrtc/modules/audio_processing/aecm/aecm_defines.h"
#include "webrtc/modules/audio_processing/utility/ring_buffer.h"
#include "webrtc/typedefs.h"
#ifdef _MSC_VER // visual c++
#define ALIGN8_BEG __declspec(align(8))
#define ALIGN8_END
#else // gcc or icc
#define ALIGN8_BEG
#define ALIGN8_END __attribute__((aligned(8)))
#endif
typedef struct {
int16_t real;
int16_t imag;
} complex16_t;
typedef struct {
int farBufWritePos;
int farBufReadPos;
int knownDelay;
int lastKnownDelay;
int firstVAD; // Parameter to control poorly initialized channels
RingBuffer* farFrameBuf;
RingBuffer* nearNoisyFrameBuf;
RingBuffer* nearCleanFrameBuf;
RingBuffer* outFrameBuf;
int16_t farBuf[FAR_BUF_LEN];
int16_t mult;
uint32_t seed;
// Delay estimation variables
void* delay_estimator_farend;
void* delay_estimator;
uint16_t currentDelay;
// Far end history variables
// TODO(bjornv): Replace |far_history| with ring_buffer.
uint16_t far_history[PART_LEN1 * MAX_DELAY];
int far_history_pos;
int far_q_domains[MAX_DELAY];
int16_t nlpFlag;
int16_t fixedDelay;
uint32_t totCount;
int16_t dfaCleanQDomain;
int16_t dfaCleanQDomainOld;
int16_t dfaNoisyQDomain;
int16_t dfaNoisyQDomainOld;
int16_t nearLogEnergy[MAX_BUF_LEN];
int16_t farLogEnergy;
int16_t echoAdaptLogEnergy[MAX_BUF_LEN];
int16_t 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.
int16_t channelStored_buf[PART_LEN1 + 8];
int16_t channelAdapt16_buf[PART_LEN1 + 8];
int32_t channelAdapt32_buf[PART_LEN1 + 8];
int16_t xBuf_buf[PART_LEN2 + 16]; // farend
int16_t dBufClean_buf[PART_LEN2 + 16]; // nearend
int16_t dBufNoisy_buf[PART_LEN2 + 16]; // nearend
int16_t outBuf_buf[PART_LEN + 8];
// Pointers to the above buffers
int16_t *channelStored;
int16_t *channelAdapt16;
int32_t *channelAdapt32;
int16_t *xBuf;
int16_t *dBufClean;
int16_t *dBufNoisy;
int16_t *outBuf;
int32_t echoFilt[PART_LEN1];
int16_t nearFilt[PART_LEN1];
int32_t noiseEst[PART_LEN1];
int noiseEstTooLowCtr[PART_LEN1];
int noiseEstTooHighCtr[PART_LEN1];
int16_t noiseEstCtr;
int16_t cngMode;
int32_t mseAdaptOld;
int32_t mseStoredOld;
int32_t mseThreshold;
int16_t farEnergyMin;
int16_t farEnergyMax;
int16_t farEnergyMaxMin;
int16_t farEnergyVAD;
int16_t farEnergyMSE;
int currentVADValue;
int16_t vadUpdateCount;
int16_t startupState;
int16_t mseChannelCount;
int16_t supGain;
int16_t supGainOld;
int16_t supGainErrParamA;
int16_t supGainErrParamD;
int16_t supGainErrParamDiffAB;
int16_t supGainErrParamDiffBD;
struct RealFFT* real_fft;
#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 int16_t* 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 int16_t * farend,
const int16_t * nearendNoisy,
const int16_t * nearendClean,
int16_t * 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 int16_t * farend,
const int16_t * nearendNoisy,
const int16_t * noisyClean,
int16_t * 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 int16_t * 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,
int16_t * const farend,
const int farLen, const int knownDelay);
// All the functions below are intended to be private
////////////////////////////////////////////////////////////////////////////////
// WebRtcAecm_UpdateFarHistory()
//
// 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
//
void WebRtcAecm_UpdateFarHistory(AecmCore_t* self,
uint16_t* far_spectrum,
int far_q);
////////////////////////////////////////////////////////////////////////////////
// WebRtcAecm_AlignedFarend()
//
// Returns a pointer to the far end spectrum aligned to current near end
// spectrum. The function WebRtc_DelayEstimatorProcessFix(...) should have been
// called before AlignedFarend(...). Otherwise, you get the pointer to the
// previous frame. The memory is only valid until the next call of
// WebRtc_DelayEstimatorProcessFix(...).
//
// Inputs:
// - self : Pointer to the AECM instance.
// - delay : Current delay estimate.
//
// 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* WebRtcAecm_AlignedFarend(AecmCore_t* self,
int* far_q,
int delay);
///////////////////////////////////////////////////////////////////////////////
// WebRtcAecm_CalcSuppressionGain()
//
// This function calculates the suppression gain that is used in the
// Wiener filter.
//
// Inputs:
// - aecm : Pointer to the AECM instance.
//
// Return value:
// - supGain : Suppression gain with which to scale the noise
// level (Q14).
//
int16_t WebRtcAecm_CalcSuppressionGain(AecmCore_t * const aecm);
///////////////////////////////////////////////////////////////////////////////
// WebRtcAecm_CalcEnergies()
//
// This function calculates the log of energies for nearend, farend and
// estimated echoes. There is also an update of energy decision levels,
// i.e. internal VAD.
//
// Inputs:
// - aecm : Pointer to the AECM instance.
// - far_spectrum : Pointer to farend spectrum.
// - far_q : Q-domain of farend spectrum.
// - nearEner : Near end energy for current block in
// Q(aecm->dfaQDomain).
//
// Output:
// - echoEst : Estimated echo in Q(xfa_q+RESOLUTION_CHANNEL16).
//
void WebRtcAecm_CalcEnergies(AecmCore_t * aecm,
const uint16_t* far_spectrum,
const int16_t far_q,
const uint32_t nearEner,
int32_t * echoEst);
///////////////////////////////////////////////////////////////////////////////
// WebRtcAecm_CalcStepSize()
//
// This function calculates the step size used in channel estimation
//
// Inputs:
// - aecm : Pointer to the AECM instance.
//
// Return value:
// - mu : Stepsize in log2(), i.e. number of shifts.
//
int16_t WebRtcAecm_CalcStepSize(AecmCore_t * const aecm);
///////////////////////////////////////////////////////////////////////////////
// WebRtcAecm_UpdateChannel(...)
//
// This function performs channel estimation.
// NLMS and decision on channel storage.
//
// Inputs:
// - aecm : Pointer to the AECM instance.
// - far_spectrum : Absolute value of the farend signal in Q(far_q)
// - far_q : Q-domain of the farend signal
// - dfa : Absolute value of the nearend signal
// (Q[aecm->dfaQDomain])
// - mu : NLMS step size.
// Input/Output:
// - echoEst : Estimated echo in Q(far_q+RESOLUTION_CHANNEL16).
//
void WebRtcAecm_UpdateChannel(AecmCore_t * aecm,
const uint16_t* far_spectrum,
const int16_t far_q,
const uint16_t * const dfa,
const int16_t mu,
int32_t * echoEst);
extern const int16_t WebRtcAecm_kCosTable[];
extern const int16_t WebRtcAecm_kSinTable[];
///////////////////////////////////////////////////////////////////////////////
// Some function pointers, for internal functions shared by ARM NEON and
// generic C code.
//
typedef void (*CalcLinearEnergies)(
AecmCore_t* aecm,
const uint16_t* far_spectrum,
int32_t* echoEst,
uint32_t* far_energy,
uint32_t* echo_energy_adapt,
uint32_t* echo_energy_stored);
extern CalcLinearEnergies WebRtcAecm_CalcLinearEnergies;
typedef void (*StoreAdaptiveChannel)(
AecmCore_t* aecm,
const uint16_t* far_spectrum,
int32_t* echo_est);
extern StoreAdaptiveChannel WebRtcAecm_StoreAdaptiveChannel;
typedef void (*ResetAdaptiveChannel)(AecmCore_t* aecm);
extern ResetAdaptiveChannel WebRtcAecm_ResetAdaptiveChannel;
// For the above function pointers, functions for generic platforms are declared
// and defined as static in file aecm_core.c, while those for ARM Neon platforms
// are declared below and defined in file aecm_core_neon.s.
#if (defined WEBRTC_DETECT_ARM_NEON) || defined (WEBRTC_ARCH_ARM_NEON)
void WebRtcAecm_CalcLinearEnergiesNeon(AecmCore_t* aecm,
const uint16_t* far_spectrum,
int32_t* echo_est,
uint32_t* far_energy,
uint32_t* echo_energy_adapt,
uint32_t* echo_energy_stored);
void WebRtcAecm_StoreAdaptiveChannelNeon(AecmCore_t* aecm,
const uint16_t* far_spectrum,
int32_t* echo_est);
void WebRtcAecm_ResetAdaptiveChannelNeon(AecmCore_t* aecm);
#endif
#if defined(MIPS32_LE)
void WebRtcAecm_CalcLinearEnergies_mips(AecmCore_t* aecm,
const uint16_t* far_spectrum,
int32_t* echo_est,
uint32_t* far_energy,
uint32_t* echo_energy_adapt,
uint32_t* echo_energy_stored);
#if defined(MIPS_DSP_R1_LE)
void WebRtcAecm_StoreAdaptiveChannel_mips(AecmCore_t* aecm,
const uint16_t* far_spectrum,
int32_t* echo_est);
void WebRtcAecm_ResetAdaptiveChannel_mips(AecmCore_t* aecm);
#endif
#endif
#endif

View File

@@ -0,0 +1,790 @@
/*
* Copyright (c) 2013 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 "webrtc/modules/audio_processing/aecm/aecm_core.h"
#include <assert.h>
#include <stddef.h>
#include <stdlib.h>
#include "webrtc/common_audio/signal_processing/include/real_fft.h"
#include "webrtc/modules/audio_processing/aecm/include/echo_control_mobile.h"
#include "webrtc/modules/audio_processing/utility/delay_estimator_wrapper.h"
#include "webrtc/modules/audio_processing/utility/ring_buffer.h"
#include "webrtc/system_wrappers/interface/compile_assert_c.h"
#include "webrtc/system_wrappers/interface/cpu_features_wrapper.h"
#include "webrtc/typedefs.h"
// Square root of Hanning window in Q14.
#if defined(WEBRTC_DETECT_ARM_NEON) || defined(WEBRTC_ARCH_ARM_NEON)
// Table is defined in an ARM assembly file.
extern const ALIGN8_BEG int16_t WebRtcAecm_kSqrtHanning[] ALIGN8_END;
#else
static const ALIGN8_BEG int16_t WebRtcAecm_kSqrtHanning[] ALIGN8_END = {
0, 399, 798, 1196, 1594, 1990, 2386, 2780, 3172,
3562, 3951, 4337, 4720, 5101, 5478, 5853, 6224,
6591, 6954, 7313, 7668, 8019, 8364, 8705, 9040,
9370, 9695, 10013, 10326, 10633, 10933, 11227, 11514,
11795, 12068, 12335, 12594, 12845, 13089, 13325, 13553,
13773, 13985, 14189, 14384, 14571, 14749, 14918, 15079,
15231, 15373, 15506, 15631, 15746, 15851, 15947, 16034,
16111, 16179, 16237, 16286, 16325, 16354, 16373, 16384
};
#endif
#ifdef AECM_WITH_ABS_APPROX
//Q15 alpha = 0.99439986968132 const Factor for magnitude approximation
static const uint16_t kAlpha1 = 32584;
//Q15 beta = 0.12967166976970 const Factor for magnitude approximation
static const uint16_t kBeta1 = 4249;
//Q15 alpha = 0.94234827210087 const Factor for magnitude approximation
static const uint16_t kAlpha2 = 30879;
//Q15 beta = 0.33787806009150 const Factor for magnitude approximation
static const uint16_t kBeta2 = 11072;
//Q15 alpha = 0.82247698684306 const Factor for magnitude approximation
static const uint16_t kAlpha3 = 26951;
//Q15 beta = 0.57762063060713 const Factor for magnitude approximation
static const uint16_t kBeta3 = 18927;
#endif
static const int16_t kNoiseEstQDomain = 15;
static const int16_t kNoiseEstIncCount = 5;
static void ComfortNoise(AecmCore_t* aecm,
const uint16_t* dfa,
complex16_t* out,
const int16_t* lambda);
static void WindowAndFFT(AecmCore_t* aecm,
int16_t* fft,
const int16_t* time_signal,
complex16_t* freq_signal,
int time_signal_scaling) {
int i = 0;
// FFT of signal
for (i = 0; i < PART_LEN; i++) {
// Window time domain signal and insert into real part of
// transformation array |fft|
fft[i] = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT(
(time_signal[i] << time_signal_scaling),
WebRtcAecm_kSqrtHanning[i],
14);
fft[PART_LEN + i] = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT(
(time_signal[i + PART_LEN] << time_signal_scaling),
WebRtcAecm_kSqrtHanning[PART_LEN - i],
14);
}
// Do forward FFT, then take only the first PART_LEN complex samples,
// and change signs of the imaginary parts.
WebRtcSpl_RealForwardFFT(aecm->real_fft, fft, (int16_t*)freq_signal);
for (i = 0; i < PART_LEN; i++) {
freq_signal[i].imag = -freq_signal[i].imag;
}
}
static void InverseFFTAndWindow(AecmCore_t* aecm,
int16_t* fft,
complex16_t* efw,
int16_t* output,
const int16_t* nearendClean)
{
int i, j, outCFFT;
int32_t tmp32no1;
// Reuse |efw| for the inverse FFT output after transferring
// the contents to |fft|.
int16_t* ifft_out = (int16_t*)efw;
// Synthesis
for (i = 1, j = 2; i < PART_LEN; i += 1, j += 2) {
fft[j] = efw[i].real;
fft[j + 1] = -efw[i].imag;
}
fft[0] = efw[0].real;
fft[1] = -efw[0].imag;
fft[PART_LEN2] = efw[PART_LEN].real;
fft[PART_LEN2 + 1] = -efw[PART_LEN].imag;
// Inverse FFT. Keep outCFFT to scale the samples in the next block.
outCFFT = WebRtcSpl_RealInverseFFT(aecm->real_fft, fft, ifft_out);
for (i = 0; i < PART_LEN; i++) {
ifft_out[i] = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(
ifft_out[i], WebRtcAecm_kSqrtHanning[i], 14);
tmp32no1 = WEBRTC_SPL_SHIFT_W32((int32_t)ifft_out[i],
outCFFT - aecm->dfaCleanQDomain);
output[i] = (int16_t)WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX,
tmp32no1 + aecm->outBuf[i],
WEBRTC_SPL_WORD16_MIN);
tmp32no1 = WEBRTC_SPL_MUL_16_16_RSFT(ifft_out[PART_LEN + i],
WebRtcAecm_kSqrtHanning[PART_LEN - i],
14);
tmp32no1 = WEBRTC_SPL_SHIFT_W32(tmp32no1,
outCFFT - aecm->dfaCleanQDomain);
aecm->outBuf[i] = (int16_t)WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX,
tmp32no1,
WEBRTC_SPL_WORD16_MIN);
}
// Copy the current block to the old position
// (aecm->outBuf is shifted elsewhere)
memcpy(aecm->xBuf, aecm->xBuf + PART_LEN, sizeof(int16_t) * PART_LEN);
memcpy(aecm->dBufNoisy,
aecm->dBufNoisy + PART_LEN,
sizeof(int16_t) * PART_LEN);
if (nearendClean != NULL)
{
memcpy(aecm->dBufClean,
aecm->dBufClean + PART_LEN,
sizeof(int16_t) * PART_LEN);
}
}
// Transforms a time domain signal into the frequency domain, outputting the
// complex valued signal, absolute value and sum of absolute values.
//
// time_signal [in] Pointer to time domain signal
// freq_signal_real [out] Pointer to real part of frequency domain array
// freq_signal_imag [out] Pointer to imaginary part of frequency domain
// array
// freq_signal_abs [out] Pointer to absolute value of frequency domain
// array
// freq_signal_sum_abs [out] Pointer to the sum of all absolute values in
// the frequency domain array
// return value The Q-domain of current frequency values
//
static int TimeToFrequencyDomain(AecmCore_t* aecm,
const int16_t* time_signal,
complex16_t* freq_signal,
uint16_t* freq_signal_abs,
uint32_t* freq_signal_sum_abs)
{
int i = 0;
int time_signal_scaling = 0;
int32_t tmp32no1 = 0;
int32_t tmp32no2 = 0;
// In fft_buf, +16 for 32-byte alignment.
int16_t fft_buf[PART_LEN4 + 16];
int16_t *fft = (int16_t *) (((uintptr_t) fft_buf + 31) & ~31);
int16_t tmp16no1;
#ifndef WEBRTC_ARCH_ARM_V7
int16_t tmp16no2;
#endif
#ifdef AECM_WITH_ABS_APPROX
int16_t max_value = 0;
int16_t min_value = 0;
uint16_t alpha = 0;
uint16_t beta = 0;
#endif
#ifdef AECM_DYNAMIC_Q
tmp16no1 = WebRtcSpl_MaxAbsValueW16(time_signal, PART_LEN2);
time_signal_scaling = WebRtcSpl_NormW16(tmp16no1);
#endif
WindowAndFFT(aecm, fft, time_signal, freq_signal, time_signal_scaling);
// Extract imaginary and real part, calculate the magnitude for
// all frequency bins
freq_signal[0].imag = 0;
freq_signal[PART_LEN].imag = 0;
freq_signal_abs[0] = (uint16_t)WEBRTC_SPL_ABS_W16(freq_signal[0].real);
freq_signal_abs[PART_LEN] = (uint16_t)WEBRTC_SPL_ABS_W16(
freq_signal[PART_LEN].real);
(*freq_signal_sum_abs) = (uint32_t)(freq_signal_abs[0]) +
(uint32_t)(freq_signal_abs[PART_LEN]);
for (i = 1; i < PART_LEN; i++)
{
if (freq_signal[i].real == 0)
{
freq_signal_abs[i] = (uint16_t)WEBRTC_SPL_ABS_W16(freq_signal[i].imag);
}
else if (freq_signal[i].imag == 0)
{
freq_signal_abs[i] = (uint16_t)WEBRTC_SPL_ABS_W16(freq_signal[i].real);
}
else
{
// Approximation for magnitude of complex fft output
// magn = sqrt(real^2 + imag^2)
// magn ~= alpha * max(|imag|,|real|) + beta * min(|imag|,|real|)
//
// The parameters alpha and beta are stored in Q15
#ifdef AECM_WITH_ABS_APPROX
tmp16no1 = WEBRTC_SPL_ABS_W16(freq_signal[i].real);
tmp16no2 = WEBRTC_SPL_ABS_W16(freq_signal[i].imag);
if(tmp16no1 > tmp16no2)
{
max_value = tmp16no1;
min_value = tmp16no2;
} else
{
max_value = tmp16no2;
min_value = tmp16no1;
}
// Magnitude in Q(-6)
if ((max_value >> 2) > min_value)
{
alpha = kAlpha1;
beta = kBeta1;
} else if ((max_value >> 1) > min_value)
{
alpha = kAlpha2;
beta = kBeta2;
} else
{
alpha = kAlpha3;
beta = kBeta3;
}
tmp16no1 = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT(max_value, alpha, 15);
tmp16no2 = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT(min_value, beta, 15);
freq_signal_abs[i] = (uint16_t)tmp16no1 + (uint16_t)tmp16no2;
#else
#ifdef WEBRTC_ARCH_ARM_V7
__asm __volatile(
"smulbb %[tmp32no1], %[real], %[real]\n\t"
"smlabb %[tmp32no2], %[imag], %[imag], %[tmp32no1]\n\t"
:[tmp32no1]"+&r"(tmp32no1),
[tmp32no2]"=r"(tmp32no2)
:[real]"r"(freq_signal[i].real),
[imag]"r"(freq_signal[i].imag)
);
#else
tmp16no1 = WEBRTC_SPL_ABS_W16(freq_signal[i].real);
tmp16no2 = WEBRTC_SPL_ABS_W16(freq_signal[i].imag);
tmp32no1 = WEBRTC_SPL_MUL_16_16(tmp16no1, tmp16no1);
tmp32no2 = WEBRTC_SPL_MUL_16_16(tmp16no2, tmp16no2);
tmp32no2 = WebRtcSpl_AddSatW32(tmp32no1, tmp32no2);
#endif // WEBRTC_ARCH_ARM_V7
tmp32no1 = WebRtcSpl_SqrtFloor(tmp32no2);
freq_signal_abs[i] = (uint16_t)tmp32no1;
#endif // AECM_WITH_ABS_APPROX
}
(*freq_signal_sum_abs) += (uint32_t)freq_signal_abs[i];
}
return time_signal_scaling;
}
int WebRtcAecm_ProcessBlock(AecmCore_t * aecm,
const int16_t * farend,
const int16_t * nearendNoisy,
const int16_t * nearendClean,
int16_t * output)
{
int i;
uint32_t xfaSum;
uint32_t dfaNoisySum;
uint32_t dfaCleanSum;
uint32_t echoEst32Gained;
uint32_t tmpU32;
int32_t tmp32no1;
uint16_t xfa[PART_LEN1];
uint16_t dfaNoisy[PART_LEN1];
uint16_t dfaClean[PART_LEN1];
uint16_t* ptrDfaClean = dfaClean;
const uint16_t* far_spectrum_ptr = NULL;
// 32 byte aligned buffers (with +8 or +16).
// TODO (kma): define fft with complex16_t.
int16_t fft_buf[PART_LEN4 + 2 + 16]; // +2 to make a loop safe.
int32_t echoEst32_buf[PART_LEN1 + 8];
int32_t dfw_buf[PART_LEN2 + 8];
int32_t efw_buf[PART_LEN2 + 8];
int16_t* fft = (int16_t*) (((uintptr_t) fft_buf + 31) & ~ 31);
int32_t* echoEst32 = (int32_t*) (((uintptr_t) echoEst32_buf + 31) & ~ 31);
complex16_t* dfw = (complex16_t*) (((uintptr_t) dfw_buf + 31) & ~ 31);
complex16_t* efw = (complex16_t*) (((uintptr_t) efw_buf + 31) & ~ 31);
int16_t hnl[PART_LEN1];
int16_t numPosCoef = 0;
int16_t nlpGain = ONE_Q14;
int delay;
int16_t tmp16no1;
int16_t tmp16no2;
int16_t mu;
int16_t supGain;
int16_t zeros32, zeros16;
int16_t zerosDBufNoisy, zerosDBufClean, zerosXBuf;
int far_q;
int16_t resolutionDiff, qDomainDiff, dfa_clean_q_domain_diff;
const int kMinPrefBand = 4;
const int kMaxPrefBand = 24;
int32_t avgHnl32 = 0;
// Determine startup state. There are three states:
// (0) the first CONV_LEN blocks
// (1) another CONV_LEN blocks
// (2) the rest
if (aecm->startupState < 2)
{
aecm->startupState = (aecm->totCount >= CONV_LEN) +
(aecm->totCount >= CONV_LEN2);
}
// END: Determine startup state
// Buffer near and far end signals
memcpy(aecm->xBuf + PART_LEN, farend, sizeof(int16_t) * PART_LEN);
memcpy(aecm->dBufNoisy + PART_LEN, nearendNoisy, sizeof(int16_t) * PART_LEN);
if (nearendClean != NULL)
{
memcpy(aecm->dBufClean + PART_LEN,
nearendClean,
sizeof(int16_t) * PART_LEN);
}
// Transform far end signal from time domain to frequency domain.
far_q = TimeToFrequencyDomain(aecm,
aecm->xBuf,
dfw,
xfa,
&xfaSum);
// Transform noisy near end signal from time domain to frequency domain.
zerosDBufNoisy = TimeToFrequencyDomain(aecm,
aecm->dBufNoisy,
dfw,
dfaNoisy,
&dfaNoisySum);
aecm->dfaNoisyQDomainOld = aecm->dfaNoisyQDomain;
aecm->dfaNoisyQDomain = (int16_t)zerosDBufNoisy;
if (nearendClean == NULL)
{
ptrDfaClean = dfaNoisy;
aecm->dfaCleanQDomainOld = aecm->dfaNoisyQDomainOld;
aecm->dfaCleanQDomain = aecm->dfaNoisyQDomain;
dfaCleanSum = dfaNoisySum;
} else
{
// Transform clean near end signal from time domain to frequency domain.
zerosDBufClean = TimeToFrequencyDomain(aecm,
aecm->dBufClean,
dfw,
dfaClean,
&dfaCleanSum);
aecm->dfaCleanQDomainOld = aecm->dfaCleanQDomain;
aecm->dfaCleanQDomain = (int16_t)zerosDBufClean;
}
// Get the delay
// Save far-end history and estimate delay
WebRtcAecm_UpdateFarHistory(aecm, xfa, far_q);
if (WebRtc_AddFarSpectrumFix(aecm->delay_estimator_farend,
xfa,
PART_LEN1,
far_q) == -1) {
return -1;
}
delay = WebRtc_DelayEstimatorProcessFix(aecm->delay_estimator,
dfaNoisy,
PART_LEN1,
zerosDBufNoisy);
if (delay == -1)
{
return -1;
}
else if (delay == -2)
{
// If the delay is unknown, we assume zero.
// NOTE: this will have to be adjusted if we ever add lookahead.
delay = 0;
}
if (aecm->fixedDelay >= 0)
{
// Use fixed delay
delay = aecm->fixedDelay;
}
// Get aligned far end spectrum
far_spectrum_ptr = WebRtcAecm_AlignedFarend(aecm, &far_q, delay);
zerosXBuf = (int16_t) far_q;
if (far_spectrum_ptr == NULL)
{
return -1;
}
// Calculate log(energy) and update energy threshold levels
WebRtcAecm_CalcEnergies(aecm,
far_spectrum_ptr,
zerosXBuf,
dfaNoisySum,
echoEst32);
// Calculate stepsize
mu = WebRtcAecm_CalcStepSize(aecm);
// Update counters
aecm->totCount++;
// This is the channel estimation algorithm.
// It is base on NLMS but has a variable step length,
// which was calculated above.
WebRtcAecm_UpdateChannel(aecm,
far_spectrum_ptr,
zerosXBuf,
dfaNoisy,
mu,
echoEst32);
supGain = WebRtcAecm_CalcSuppressionGain(aecm);
// Calculate Wiener filter hnl[]
for (i = 0; i < PART_LEN1; i++)
{
// Far end signal through channel estimate in Q8
// How much can we shift right to preserve resolution
tmp32no1 = echoEst32[i] - aecm->echoFilt[i];
aecm->echoFilt[i] += (tmp32no1 * 50) >> 8;
zeros32 = WebRtcSpl_NormW32(aecm->echoFilt[i]) + 1;
zeros16 = WebRtcSpl_NormW16(supGain) + 1;
if (zeros32 + zeros16 > 16)
{
// Multiplication is safe
// Result in
// Q(RESOLUTION_CHANNEL+RESOLUTION_SUPGAIN+
// aecm->xfaQDomainBuf[diff])
echoEst32Gained = WEBRTC_SPL_UMUL_32_16((uint32_t)aecm->echoFilt[i],
(uint16_t)supGain);
resolutionDiff = 14 - RESOLUTION_CHANNEL16 - RESOLUTION_SUPGAIN;
resolutionDiff += (aecm->dfaCleanQDomain - zerosXBuf);
} else
{
tmp16no1 = 17 - zeros32 - zeros16;
resolutionDiff = 14 + tmp16no1 - RESOLUTION_CHANNEL16 -
RESOLUTION_SUPGAIN;
resolutionDiff += (aecm->dfaCleanQDomain - zerosXBuf);
if (zeros32 > tmp16no1)
{
echoEst32Gained = WEBRTC_SPL_UMUL_32_16((uint32_t)aecm->echoFilt[i],
(uint16_t)WEBRTC_SPL_RSHIFT_W16(
supGain,
tmp16no1)
);
} else
{
// Result in Q-(RESOLUTION_CHANNEL+RESOLUTION_SUPGAIN-16)
echoEst32Gained = WEBRTC_SPL_UMUL_32_16((uint32_t)WEBRTC_SPL_RSHIFT_W32(
aecm->echoFilt[i],
tmp16no1),
(uint16_t)supGain);
}
}
zeros16 = WebRtcSpl_NormW16(aecm->nearFilt[i]);
assert(zeros16 >= 0); // |zeros16| is a norm, hence non-negative.
dfa_clean_q_domain_diff = aecm->dfaCleanQDomain - aecm->dfaCleanQDomainOld;
if (zeros16 < dfa_clean_q_domain_diff && aecm->nearFilt[i]) {
tmp16no1 = aecm->nearFilt[i] << zeros16;
qDomainDiff = zeros16 - dfa_clean_q_domain_diff;
tmp16no2 = ptrDfaClean[i] >> -qDomainDiff;
} else {
tmp16no1 = dfa_clean_q_domain_diff < 0
? aecm->nearFilt[i] >> -dfa_clean_q_domain_diff
: aecm->nearFilt[i] << dfa_clean_q_domain_diff;
qDomainDiff = 0;
tmp16no2 = ptrDfaClean[i];
}
tmp32no1 = (int32_t)(tmp16no2 - tmp16no1);
tmp16no2 = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32no1, 4);
tmp16no2 += tmp16no1;
zeros16 = WebRtcSpl_NormW16(tmp16no2);
if ((tmp16no2) & (-qDomainDiff > zeros16)) {
aecm->nearFilt[i] = WEBRTC_SPL_WORD16_MAX;
} else {
aecm->nearFilt[i] = qDomainDiff < 0 ? tmp16no2 << -qDomainDiff
: tmp16no2 >> qDomainDiff;
}
// Wiener filter coefficients, resulting hnl in Q14
if (echoEst32Gained == 0)
{
hnl[i] = ONE_Q14;
} else if (aecm->nearFilt[i] == 0)
{
hnl[i] = 0;
} else
{
// Multiply the suppression gain
// Rounding
echoEst32Gained += (uint32_t)(aecm->nearFilt[i] >> 1);
tmpU32 = WebRtcSpl_DivU32U16(echoEst32Gained,
(uint16_t)aecm->nearFilt[i]);
// Current resolution is
// Q-(RESOLUTION_CHANNEL+RESOLUTION_SUPGAIN- max(0,17-zeros16- zeros32))
// Make sure we are in Q14
tmp32no1 = (int32_t)WEBRTC_SPL_SHIFT_W32(tmpU32, resolutionDiff);
if (tmp32no1 > ONE_Q14)
{
hnl[i] = 0;
} else if (tmp32no1 < 0)
{
hnl[i] = ONE_Q14;
} else
{
// 1-echoEst/dfa
hnl[i] = ONE_Q14 - (int16_t)tmp32no1;
if (hnl[i] < 0)
{
hnl[i] = 0;
}
}
}
if (hnl[i])
{
numPosCoef++;
}
}
// Only in wideband. Prevent the gain in upper band from being larger than
// in lower band.
if (aecm->mult == 2)
{
// TODO(bjornv): Investigate if the scaling of hnl[i] below can cause
// speech distortion in double-talk.
for (i = 0; i < PART_LEN1; i++)
{
hnl[i] = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT(hnl[i], hnl[i], 14);
}
for (i = kMinPrefBand; i <= kMaxPrefBand; i++)
{
avgHnl32 += (int32_t)hnl[i];
}
assert(kMaxPrefBand - kMinPrefBand + 1 > 0);
avgHnl32 /= (kMaxPrefBand - kMinPrefBand + 1);
for (i = kMaxPrefBand; i < PART_LEN1; i++)
{
if (hnl[i] > (int16_t)avgHnl32)
{
hnl[i] = (int16_t)avgHnl32;
}
}
}
// Calculate NLP gain, result is in Q14
if (aecm->nlpFlag)
{
for (i = 0; i < PART_LEN1; i++)
{
// Truncate values close to zero and one.
if (hnl[i] > NLP_COMP_HIGH)
{
hnl[i] = ONE_Q14;
} else if (hnl[i] < NLP_COMP_LOW)
{
hnl[i] = 0;
}
// Remove outliers
if (numPosCoef < 3)
{
nlpGain = 0;
} else
{
nlpGain = ONE_Q14;
}
// NLP
if ((hnl[i] == ONE_Q14) && (nlpGain == ONE_Q14))
{
hnl[i] = ONE_Q14;
} else
{
hnl[i] = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT(hnl[i], nlpGain, 14);
}
// multiply with Wiener coefficients
efw[i].real = (int16_t)(WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(dfw[i].real,
hnl[i], 14));
efw[i].imag = (int16_t)(WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(dfw[i].imag,
hnl[i], 14));
}
}
else
{
// multiply with Wiener coefficients
for (i = 0; i < PART_LEN1; i++)
{
efw[i].real = (int16_t)(WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(dfw[i].real,
hnl[i], 14));
efw[i].imag = (int16_t)(WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(dfw[i].imag,
hnl[i], 14));
}
}
if (aecm->cngMode == AecmTrue)
{
ComfortNoise(aecm, ptrDfaClean, efw, hnl);
}
InverseFFTAndWindow(aecm, fft, efw, output, nearendClean);
return 0;
}
static void ComfortNoise(AecmCore_t* aecm,
const uint16_t* dfa,
complex16_t* out,
const int16_t* lambda)
{
int16_t i;
int16_t tmp16;
int32_t tmp32;
int16_t randW16[PART_LEN];
int16_t uReal[PART_LEN1];
int16_t uImag[PART_LEN1];
int32_t outLShift32;
int16_t noiseRShift16[PART_LEN1];
int16_t shiftFromNearToNoise = kNoiseEstQDomain - aecm->dfaCleanQDomain;
int16_t minTrackShift;
assert(shiftFromNearToNoise >= 0);
assert(shiftFromNearToNoise < 16);
if (aecm->noiseEstCtr < 100)
{
// Track the minimum more quickly initially.
aecm->noiseEstCtr++;
minTrackShift = 6;
} else
{
minTrackShift = 9;
}
// Estimate noise power.
for (i = 0; i < PART_LEN1; i++)
{
// Shift to the noise domain.
tmp32 = (int32_t)dfa[i];
outLShift32 = WEBRTC_SPL_LSHIFT_W32(tmp32, shiftFromNearToNoise);
if (outLShift32 < aecm->noiseEst[i])
{
// Reset "too low" counter
aecm->noiseEstTooLowCtr[i] = 0;
// Track the minimum.
if (aecm->noiseEst[i] < (1 << minTrackShift))
{
// For small values, decrease noiseEst[i] every
// |kNoiseEstIncCount| block. The regular approach below can not
// go further down due to truncation.
aecm->noiseEstTooHighCtr[i]++;
if (aecm->noiseEstTooHighCtr[i] >= kNoiseEstIncCount)
{
aecm->noiseEst[i]--;
aecm->noiseEstTooHighCtr[i] = 0; // Reset the counter
}
}
else
{
aecm->noiseEst[i] -= ((aecm->noiseEst[i] - outLShift32)
>> minTrackShift);
}
} else
{
// Reset "too high" counter
aecm->noiseEstTooHighCtr[i] = 0;
// Ramp slowly upwards until we hit the minimum again.
if ((aecm->noiseEst[i] >> 19) > 0)
{
// Avoid overflow.
// Multiplication with 2049 will cause wrap around. Scale
// down first and then multiply
aecm->noiseEst[i] >>= 11;
aecm->noiseEst[i] *= 2049;
}
else if ((aecm->noiseEst[i] >> 11) > 0)
{
// Large enough for relative increase
aecm->noiseEst[i] *= 2049;
aecm->noiseEst[i] >>= 11;
}
else
{
// Make incremental increases based on size every
// |kNoiseEstIncCount| block
aecm->noiseEstTooLowCtr[i]++;
if (aecm->noiseEstTooLowCtr[i] >= kNoiseEstIncCount)
{
aecm->noiseEst[i] += (aecm->noiseEst[i] >> 9) + 1;
aecm->noiseEstTooLowCtr[i] = 0; // Reset counter
}
}
}
}
for (i = 0; i < PART_LEN1; i++)
{
tmp32 = WEBRTC_SPL_RSHIFT_W32(aecm->noiseEst[i], shiftFromNearToNoise);
if (tmp32 > 32767)
{
tmp32 = 32767;
aecm->noiseEst[i] = WEBRTC_SPL_LSHIFT_W32(tmp32, shiftFromNearToNoise);
}
noiseRShift16[i] = (int16_t)tmp32;
tmp16 = ONE_Q14 - lambda[i];
noiseRShift16[i] = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT(tmp16,
noiseRShift16[i],
14);
}
// Generate a uniform random array on [0 2^15-1].
WebRtcSpl_RandUArray(randW16, PART_LEN, &aecm->seed);
// Generate noise according to estimated energy.
uReal[0] = 0; // Reject LF noise.
uImag[0] = 0;
for (i = 1; i < PART_LEN1; i++)
{
// Get a random index for the cos and sin tables over [0 359].
tmp16 = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT(359, randW16[i - 1], 15);
// Tables are in Q13.
uReal[i] = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT(noiseRShift16[i],
WebRtcAecm_kCosTable[tmp16],
13);
uImag[i] = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT(-noiseRShift16[i],
WebRtcAecm_kSinTable[tmp16],
13);
}
uImag[PART_LEN] = 0;
for (i = 0; i < PART_LEN1; i++)
{
out[i].real = WebRtcSpl_AddSatW16(out[i].real, uReal[i]);
out[i].imag = WebRtcSpl_AddSatW16(out[i].imag, uImag[i]);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,171 @@
@
@ Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
@
@ Use of this source code is governed by a BSD-style license
@ that can be found in the LICENSE file in the root of the source
@ tree. An additional intellectual property rights grant can be found
@ in the file PATENTS. All contributing project authors may
@ be found in the AUTHORS file in the root of the source tree.
@
@ aecm_core_neon.s
@ This file contains some functions in AECM, optimized for ARM Neon
@ platforms. Reference C code is in file aecm_core.c. Bit-exact.
#include "aecm_core_neon_offsets.h"
#include "webrtc/modules/audio_processing/aecm/aecm_defines.h"
#include "webrtc/system_wrappers/interface/asm_defines.h"
GLOBAL_LABEL WebRtcAecm_kSqrtHanning
GLOBAL_FUNCTION WebRtcAecm_CalcLinearEnergiesNeon
GLOBAL_FUNCTION WebRtcAecm_StoreAdaptiveChannelNeon
GLOBAL_FUNCTION WebRtcAecm_ResetAdaptiveChannelNeon
@ void WebRtcAecm_CalcLinearEnergiesNeon(AecmCore_t* aecm,
@ const uint16_t* far_spectrum,
@ int32_t* echo_est,
@ uint32_t* far_energy,
@ uint32_t* echo_energy_adapt,
@ uint32_t* echo_energy_stored);
.align 2
DEFINE_FUNCTION WebRtcAecm_CalcLinearEnergiesNeon
push {r4-r7}
vmov.i32 q14, #0
vmov.i32 q8, #0
vmov.i32 q9, #0
movw r7, #offset_aecm_channelStored
movw r5, #offset_aecm_channelAdapt16
mov r4, r2
mov r12, #(PART_LEN / 8) @ Loop counter, unrolled by 8.
ldr r6, [r0, r7]
ldr r7, [r0, r5]
LOOP_CALC_LINEAR_ENERGIES:
vld1.16 {d26, d27}, [r1]! @ far_spectrum[i]
vld1.16 {d24, d25}, [r6, :128]! @ &aecm->channelStored[i]
vld1.16 {d0, d1}, [r7, :128]! @ &aecm->channelAdapt16[i]
vaddw.u16 q14, q14, d26
vmull.u16 q10, d26, d24
vmull.u16 q11, d27, d25
vaddw.u16 q14, q14, d27
vmull.u16 q1, d26, d0
vst1.32 {q10, q11}, [r4, :256]! @ &echo_est[i]
vadd.u32 q8, q10
vmull.u16 q2, d27, d1
vadd.u32 q8, q11
vadd.u32 q9, q1
subs r12, #1
vadd.u32 q9, q2
bgt LOOP_CALC_LINEAR_ENERGIES
vadd.u32 d28, d29
vpadd.u32 d28, d28
vmov.32 r12, d28[0]
vadd.u32 d18, d19
vpadd.u32 d18, d18
vmov.32 r5, d18[0] @ echo_energy_adapt_r
vadd.u32 d16, d17
vpadd.u32 d16, d16
ldrh r1, [r1] @ far_spectrum[i]
add r12, r12, r1
str r12, [r3] @ far_energy
vmov.32 r2, d16[0]
ldrsh r12, [r6] @ aecm->channelStored[i]
ldrh r6, [r7] @ aecm->channelAdapt16[i]
mul r0, r12, r1
mla r1, r6, r1, r5
add r2, r2, r0
str r0, [r4] @ echo_est[i]
ldr r4, [sp, #20] @ &echo_energy_stored
str r2, [r4]
ldr r3, [sp, #16] @ &echo_energy_adapt
str r1, [r3]
pop {r4-r7}
bx lr
@ void WebRtcAecm_StoreAdaptiveChannelNeon(AecmCore_t* aecm,
@ const uint16_t* far_spectrum,
@ int32_t* echo_est);
.align 2
DEFINE_FUNCTION WebRtcAecm_StoreAdaptiveChannelNeon
movw r3, #offset_aecm_channelAdapt16
movw r12, #offset_aecm_channelStored
ldr r3, [r0, r3]
ldr r0, [r0, r12]
mov r12, #(PART_LEN / 8) @ Loop counter, unrolled by 8.
LOOP_STORE_ADAPTIVE_CHANNEL:
vld1.16 {d24, d25}, [r3, :128]! @ &aecm->channelAdapt16[i]
vld1.16 {d26, d27}, [r1]! @ &far_spectrum[i]
vst1.16 {d24, d25}, [r0, :128]! @ &aecm->channelStored[i]
vmull.u16 q10, d26, d24
vmull.u16 q11, d27, d25
vst1.16 {q10, q11}, [r2, :256]! @ echo_est[i]
subs r12, #1
bgt LOOP_STORE_ADAPTIVE_CHANNEL
ldrsh r12, [r3]
strh r12, [r0]
ldrh r1, [r1]
mul r3, r1, r12
str r3, [r2]
bx lr
@ void WebRtcAecm_ResetAdaptiveChannelNeon(AecmCore_t* aecm);
.align 2
DEFINE_FUNCTION WebRtcAecm_ResetAdaptiveChannelNeon
movw r1, #offset_aecm_channelAdapt16
movw r2, #offset_aecm_channelAdapt32
movw r3, #offset_aecm_channelStored
ldr r1, [r0, r1] @ &aecm->channelAdapt16[0]
ldr r2, [r0, r2] @ &aecm->channelAdapt32[0]
ldr r0, [r0, r3] @ &aecm->channelStored[0]
mov r3, #(PART_LEN / 8) @ Loop counter, unrolled by 8.
LOOP_RESET_ADAPTIVE_CHANNEL:
vld1.16 {d24, d25}, [r0, :128]!
subs r3, #1
vst1.16 {d24, d25}, [r1, :128]!
vshll.s16 q10, d24, #16
vshll.s16 q11, d25, #16
vst1.16 {q10, q11}, [r2, :256]!
bgt LOOP_RESET_ADAPTIVE_CHANNEL
ldrh r0, [r0]
strh r0, [r1]
mov r0, r0, asl #16
str r0, [r2]
bx lr
@ Square root of Hanning window in Q14.
.align 4
WebRtcAecm_kSqrtHanning:
_WebRtcAecm_kSqrtHanning:
.short 0
.short 399, 798, 1196, 1594, 1990, 2386, 2780, 3172
.short 3562, 3951, 4337, 4720, 5101, 5478, 5853, 6224
.short 6591, 6954, 7313, 7668, 8019, 8364, 8705, 9040
.short 9370, 9695, 10013, 10326, 10633, 10933, 11227, 11514
.short 11795, 12068, 12335, 12594, 12845, 13089, 13325, 13553
.short 13773, 13985, 14189, 14384, 14571, 14749, 14918, 15079
.short 15231, 15373, 15506, 15631, 15746, 15851, 15947, 16034
.short 16111, 16179, 16237, 16286, 16325, 16354, 16373, 16384
@ Square root of Hanning window in Q14. Compared to WebRtcAecm_kSqrtHanning,
@ the order was reversed and one element (0) was removed.
.align 4
kSqrtHanningReversed:
.short 16384, 16373, 16354, 16325, 16286, 16237, 16179, 16111, 16034, 15947
.short 15851, 15746, 15631, 15506, 15373, 15231, 15079, 14918, 14749, 14571
.short 14384, 14189, 13985, 13773, 13553, 13325, 13089, 12845, 12594, 12335
.short 12068, 11795, 11514, 11227, 10933, 10633, 10326, 10013, 9695, 9370
.short 9040, 8705, 8364, 8019, 7668, 7313, 6954, 6591, 6224, 5853, 5478, 5101
.short 4720, 4337, 3951, 3562, 3172, 2780, 2386, 1990, 1594, 1196, 798, 399

View File

@@ -0,0 +1,348 @@
/*
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/modules/audio_processing/aecm/aecm_core.h"
#include <arm_neon.h>
#include <assert.h>
#include "webrtc/common_audio/signal_processing/include/real_fft.h"
// TODO(kma): Re-write the corresponding assembly file, the offset
// generating script and makefile, to replace these C functions.
// Square root of Hanning window in Q14.
const ALIGN8_BEG int16_t WebRtcAecm_kSqrtHanning[] ALIGN8_END = {
0,
399, 798, 1196, 1594, 1990, 2386, 2780, 3172,
3562, 3951, 4337, 4720, 5101, 5478, 5853, 6224,
6591, 6954, 7313, 7668, 8019, 8364, 8705, 9040,
9370, 9695, 10013, 10326, 10633, 10933, 11227, 11514,
11795, 12068, 12335, 12594, 12845, 13089, 13325, 13553,
13773, 13985, 14189, 14384, 14571, 14749, 14918, 15079,
15231, 15373, 15506, 15631, 15746, 15851, 15947, 16034,
16111, 16179, 16237, 16286, 16325, 16354, 16373, 16384
};
// Square root of Hanning window in Q14, in reversed order.
static const ALIGN8_BEG int16_t kSqrtHanningReversed[] ALIGN8_END = {
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_WindowAndFFTNeon(AecmCore_t* aecm,
int16_t* fft,
const int16_t* time_signal,
complex16_t* freq_signal,
int time_signal_scaling) {
int i = 0;
const int16_t* p_time_signal = time_signal;
const int16_t* p_time_signal_offset = &time_signal[PART_LEN];
const int16_t* p_hanning = WebRtcAecm_kSqrtHanning;
const int16_t* p_hanning_reversed = kSqrtHanningReversed;
int16_t* p_fft = fft;
int16_t* p_fft_offset = &fft[PART_LEN2];
assert((uintptr_t)p_time_signal % 8 == 0);
assert((uintptr_t)freq_signal % 32 == 0);
assert((uintptr_t)p_hanning % 8 == 0);
assert((uintptr_t)p_fft % 16 == 0);
__asm __volatile(
"vdup.16 d16, %0\n\t"
"vmov.i16 d21, #0\n\t"
"vmov.i16 d27, #0\n\t"
:
:"r"(time_signal_scaling)
: "d16", "d21", "d27"
);
for (i = 0; i < PART_LEN; i += 4) {
__asm __volatile(
"vld1.16 d0, [%[p_time_signal], :64]!\n\t"
"vld1.16 d22, [%[p_time_signal_offset], :64]!\n\t"
"vld1.16 d17, [%[p_hanning], :64]!\n\t"
"vld1.16 d23, [%[p_hanning_reversed], :64]!\n\t"
"vshl.s16 d18, d0, d16\n\t"
"vshl.s16 d22, d22, d16\n\t"
"vmull.s16 q9, d18, d17\n\t"
"vmull.s16 q12, d22, d23\n\t"
"vshrn.i32 d20, q9, #14\n\t"
"vshrn.i32 d26, q12, #14\n\t"
"vst2.16 {d20, d21}, [%[p_fft], :128]!\n\t"
"vst2.16 {d26, d27}, [%[p_fft_offset], :128]!\n\t"
:[p_time_signal]"+r"(p_time_signal),
[p_time_signal_offset]"+r"(p_time_signal_offset),
[p_hanning]"+r"(p_hanning),
[p_hanning_reversed]"+r"(p_hanning_reversed),
[p_fft]"+r"(p_fft),
[p_fft_offset]"+r"(p_fft_offset)
:
:"d0", "d16", "d17", "d18", "d19", "d20", "d21",
"d22", "d23", "d24", "d25", "d26", "d27"
);
}
// Do forward FFT, then take only the first PART_LEN complex samples,
// and change signs of the imaginary parts.
WebRtcSpl_RealForwardFFT(aecm->real_fft, (int16_t*)fft,
(int16_t*)freq_signal);
for (i = 0; i < PART_LEN; i += 8) {
__asm __volatile(
"vld2.16 {d20, d21, d22, d23}, [%[freq_signal], :256]\n\t"
"vneg.s16 d22, d22\n\t"
"vneg.s16 d23, d23\n\t"
"vst2.16 {d20, d21, d22, d23}, [%[freq_signal], :256]!\n\t"
:[freq_signal]"+r"(freq_signal)
:
: "d20", "d21", "d22", "d23"
);
}
}
void WebRtcAecm_InverseFFTAndWindowNeon(AecmCore_t* aecm,
int16_t* fft,
complex16_t* efw,
int16_t* output,
const int16_t* nearendClean) {
int i, j, outCFFT;
assert((uintptr_t)efw % 32 == 0);
assert((uintptr_t)fft % 16 == 0);
assert((uintptr_t)output% 8 == 0);
assert((uintptr_t)WebRtcAecm_kSqrtHanning % 8 == 0);
assert((uintptr_t)kSqrtHanningReversed % 8 == 0);
assert((uintptr_t)(aecm->outBuf) % 8 == 0);
assert((uintptr_t)(aecm->xBuf) % 32 == 0);
assert((uintptr_t)(aecm->dBufNoisy) % 32 == 0);
assert((uintptr_t)(aecm->dBufClean) % 32 == 0);
// Synthesis
complex16_t* p_efw = efw;
int16_t* p_fft = fft;
int16_t* p_fft_offset = &fft[PART_LEN4 - 6];
for (i = 0, j = 0; i < PART_LEN; i += 4, j += 8) {
// We overwrite two more elements in fft[], but it's ok.
__asm __volatile(
"vld2.16 {q10}, [%[p_efw], :128]!\n\t"
"vmov q11, q10\n\t"
"vneg.s16 d23, d23\n\t"
"vst2.16 {d22, d23}, [%[p_fft], :128]!\n\t"
"vrev64.16 q10, q10\n\t"
"vst2.16 {q10}, [%[p_fft_offset]], %[offset]\n\t"
:[p_efw]"+r"(p_efw),
[p_fft]"+r"(p_fft),
[p_fft_offset]"+r"(p_fft_offset)
:[offset]"r"(-16)
:"d20", "d21", "d22", "d23"
);
}
fft[PART_LEN2] = efw[PART_LEN].real;
fft[PART_LEN2 + 1] = -efw[PART_LEN].imag;
// Inverse FFT. Then take only the real values, and keep outCFFT
// to scale the samples.
outCFFT = WebRtcSpl_RealInverseFFT(aecm->real_fft, fft, (int16_t*)efw);
int32x4_t tmp32x4_2;
__asm __volatile("vdup.32 %q0, %1" : "=w"(tmp32x4_2) : "r"((int32_t)
(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;
//efw[i].real = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(
// efw[i].real, WebRtcAecm_kSqrtHanning[i], 14);
__asm __volatile("vld1.16 %P0, [%1, :64]" : "=w"(tmp16x4_0) : "r"(&efw[i].real));
__asm __volatile("vld1.16 %P0, [%1, :64]" : "=w"(tmp16x4_1) : "r"(&WebRtcAecm_kSqrtHanning[i]));
__asm __volatile("vmull.s16 %q0, %P1, %P2" : "=w"(tmp32x4_0) : "w"(tmp16x4_0), "w"(tmp16x4_1));
__asm __volatile("vrshr.s32 %q0, %q1, #14" : "=w"(tmp32x4_0) : "0"(tmp32x4_0));
//tmp32no1 = WEBRTC_SPL_SHIFT_W32((int32_t)efw[i].real,
// outCFFT - aecm->dfaCleanQDomain);
__asm __volatile("vshl.s32 %q0, %q1, %q2" : "=w"(tmp32x4_0) : "0"(tmp32x4_0), "w"(tmp32x4_2));
//efw[i].real = (int16_t)WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX,
// tmp32no1 + aecm->outBuf[i], WEBRTC_SPL_WORD16_MIN);
// output[i] = efw[i].real;
__asm __volatile("vld1.16 %P0, [%1, :64]" : "=w"(tmp16x4_0) : "r"(&aecm->outBuf[i]));
__asm __volatile("vmovl.s16 %q0, %P1" : "=w"(tmp32x4_1) : "w"(tmp16x4_0));
__asm __volatile("vadd.i32 %q0, %q1" : : "w"(tmp32x4_0), "w"(tmp32x4_1));
__asm __volatile("vqmovn.s32 %P0, %q1" : "=w"(tmp16x4_0) : "w"(tmp32x4_0));
__asm __volatile("vst1.16 %P0, [%1, :64]" : : "w"(tmp16x4_0), "r"(&efw[i].real));
__asm __volatile("vst1.16 %P0, [%1, :64]" : : "w"(tmp16x4_0), "r"(&output[i]));
// tmp32no1 = WEBRTC_SPL_MUL_16_16_RSFT(
// efw[PART_LEN + i].real, WebRtcAecm_kSqrtHanning[PART_LEN - i], 14);
__asm __volatile("vld1.16 %P0, [%1, :64]" : "=w"(tmp16x4_0) : "r"(&efw[PART_LEN + i].real));
__asm __volatile("vld1.16 %P0, [%1, :64]" : "=w"(tmp16x4_1) : "r"(&kSqrtHanningReversed[i]));
__asm __volatile("vmull.s16 %q0, %P1, %P2" : "=w"(tmp32x4_0) : "w"(tmp16x4_0), "w"(tmp16x4_1));
__asm __volatile("vshr.s32 %q0, %q1, #14" : "=w"(tmp32x4_0) : "0"(tmp32x4_0));
// tmp32no1 = WEBRTC_SPL_SHIFT_W32(tmp32no1, outCFFT - aecm->dfaCleanQDomain);
__asm __volatile("vshl.s32 %q0, %q1, %q2" : "=w"(tmp32x4_0) : "0"(tmp32x4_0), "w"(tmp32x4_2));
// aecm->outBuf[i] = (int16_t)WEBRTC_SPL_SAT(
// WEBRTC_SPL_WORD16_MAX, tmp32no1, WEBRTC_SPL_WORD16_MIN);
__asm __volatile("vqmovn.s32 %P0, %q1" : "=w"(tmp16x4_0) : "w"(tmp32x4_0));
__asm __volatile("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 __volatile("vld1.16 {d20, d21, d22, d23}, [%0, :256]" : :
"r"(&aecm->xBuf[i + PART_LEN]) : "q10");
__asm __volatile("vst1.16 {d20, d21, d22, d23}, [%0, :256]" : : "r"(&aecm->xBuf[i]): "q10");
}
for (i = 0; i < PART_LEN; i += 16) {
__asm __volatile("vld1.16 {d20, d21, d22, d23}, [%0, :256]" : :
"r"(&aecm->dBufNoisy[i + PART_LEN]) : "q10");
__asm __volatile("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 __volatile("vld1.16 {d20, d21, d22, d23}, [%0, :256]" : :
"r"(&aecm->dBufClean[i + PART_LEN]) : "q10");
__asm __volatile("vst1.16 {d20, d21, d22, d23}, [%0, :256]" : :
"r"(&aecm->dBufClean[i]): "q10");
}
}
}
void WebRtcAecm_CalcLinearEnergiesNeon(AecmCore_t* aecm,
const uint16_t* far_spectrum,
int32_t* echo_est,
uint32_t* far_energy,
uint32_t* echo_energy_adapt,
uint32_t* echo_energy_stored) {
int i;
register uint32_t far_energy_r;
register uint32_t echo_energy_stored_r;
register uint32_t echo_energy_adapt_r;
assert((uintptr_t)echo_est % 32 == 0);
assert((uintptr_t)(aecm->channelStored) % 16 == 0);
assert((uintptr_t)(aecm->channelAdapt16) % 16 == 0);
assert((uintptr_t)(aecm->channelStored) % 16 == 0);
assert((uintptr_t)(aecm->channelStored) % 16 == 0);
__asm __volatile("vmov.i32 q14, #0" : : : "q14"); // far_energy
__asm __volatile("vmov.i32 q8, #0" : : : "q8"); // echo_energy_stored
__asm __volatile("vmov.i32 q9, #0" : : : "q9"); // echo_energy_adapt
for (i = 0; i < PART_LEN - 7; i += 8) {
// far_energy += (uint32_t)(far_spectrum[i]);
__asm __volatile("vld1.16 {d26, d27}, [%0]" : : "r"(&far_spectrum[i]) : "q13");
__asm __volatile("vaddw.u16 q14, q14, d26" : : : "q14", "q13");
__asm __volatile("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 __volatile("vld1.16 {d24, d25}, [%0, :128]" : : "r"(&aecm->channelStored[i]) : "q12");
__asm __volatile("vmull.u16 q10, d26, d24" : : : "q12", "q13", "q10");
__asm __volatile("vmull.u16 q11, d27, d25" : : : "q12", "q13", "q11");
__asm __volatile("vst1.32 {d20, d21, d22, d23}, [%0, :256]" : : "r"(&echo_est[i]):
"q10", "q11");
// echo_energy_stored += (uint32_t)echoEst[i];
__asm __volatile("vadd.u32 q8, q10" : : : "q10", "q8");
__asm __volatile("vadd.u32 q8, q11" : : : "q11", "q8");
// echo_energy_adapt += WEBRTC_SPL_UMUL_16_16(
// aecm->channelAdapt16[i], far_spectrum[i]);
__asm __volatile("vld1.16 {d24, d25}, [%0, :128]" : : "r"(&aecm->channelAdapt16[i]) : "q12");
__asm __volatile("vmull.u16 q10, d26, d24" : : : "q12", "q13", "q10");
__asm __volatile("vmull.u16 q11, d27, d25" : : : "q12", "q13", "q11");
__asm __volatile("vadd.u32 q9, q10" : : : "q9", "q15");
__asm __volatile("vadd.u32 q9, q11" : : : "q9", "q11");
}
__asm __volatile("vadd.u32 d28, d29" : : : "q14");
__asm __volatile("vpadd.u32 d28, d28" : : : "q14");
__asm __volatile("vmov.32 %0, d28[0]" : "=r"(far_energy_r): : "q14");
__asm __volatile("vadd.u32 d18, d19" : : : "q9");
__asm __volatile("vpadd.u32 d18, d18" : : : "q9");
__asm __volatile("vmov.32 %0, d18[0]" : "=r"(echo_energy_adapt_r): : "q9");
__asm __volatile("vadd.u32 d16, d17" : : : "q8");
__asm __volatile("vpadd.u32 d16, d16" : : : "q8");
__asm __volatile("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 + (uint32_t)echo_est[i];
*far_energy = far_energy_r + (uint32_t)(far_spectrum[i]);
*echo_energy_adapt = echo_energy_adapt_r + WEBRTC_SPL_UMUL_16_16(
aecm->channelAdapt16[i], far_spectrum[i]);
}
void WebRtcAecm_StoreAdaptiveChannelNeon(AecmCore_t* aecm,
const uint16_t* far_spectrum,
int32_t* echo_est) {
int i;
assert((uintptr_t)echo_est % 32 == 0);
assert((uintptr_t)(aecm->channelStored) % 16 == 0);
assert((uintptr_t)(aecm->channelAdapt16) % 16 == 0);
// 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 __volatile("vld1.16 {d26, d27}, [%0]" : : "r"(&far_spectrum[i]) : "q13");
__asm __volatile("vld1.16 {d24, d25}, [%0, :128]" : : "r"(&aecm->channelAdapt16[i]) : "q12");
__asm __volatile("vst1.16 {d24, d25}, [%0, :128]" : : "r"(&aecm->channelStored[i]) : "q12");
__asm __volatile("vmull.u16 q10, d26, d24" : : : "q12", "q13", "q10");
__asm __volatile("vmull.u16 q11, d27, d25" : : : "q12", "q13", "q11");
__asm __volatile("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_ResetAdaptiveChannelNeon(AecmCore_t* aecm) {
int i;
assert((uintptr_t)(aecm->channelStored) % 16 == 0);
assert((uintptr_t)(aecm->channelAdapt16) % 16 == 0);
assert((uintptr_t)(aecm->channelAdapt32) % 32 == 0);
for (i = 0; i < PART_LEN - 7; i += 8) {
// aecm->channelAdapt16[i] = aecm->channelStored[i];
// aecm->channelAdapt32[i] = WEBRTC_SPL_LSHIFT_W32((int32_t)
// aecm->channelStored[i], 16);
__asm __volatile("vld1.16 {d24, d25}, [%0, :128]" : :
"r"(&aecm->channelStored[i]) : "q12");
__asm __volatile("vst1.16 {d24, d25}, [%0, :128]" : :
"r"(&aecm->channelAdapt16[i]) : "q12");
__asm __volatile("vshll.s16 q10, d24, #16" : : : "q12", "q13", "q10");
__asm __volatile("vshll.s16 q11, d25, #16" : : : "q12", "q13", "q11");
__asm __volatile("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(
(int32_t)aecm->channelStored[i], 16);
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/modules/audio_processing/aecm/aecm_core.h"
#include <stddef.h>
// Define offset variables that will be compiled and abstracted to constant
// defines, which will then only be used in ARM assembly code.
int offset_aecm_dfaCleanQDomain = offsetof(AecmCore_t, dfaCleanQDomain);
int offset_aecm_outBuf = offsetof(AecmCore_t, outBuf);
int offset_aecm_xBuf = offsetof(AecmCore_t, xBuf);
int offset_aecm_dBufNoisy = offsetof(AecmCore_t, dBufNoisy);
int offset_aecm_dBufClean = offsetof(AecmCore_t, dBufClean);
int offset_aecm_channelStored = offsetof(AecmCore_t, channelStored);
int offset_aecm_channelAdapt16 = offsetof(AecmCore_t, channelAdapt16);
int offset_aecm_channelAdapt32 = offsetof(AecmCore_t, channelAdapt32);
int offset_aecm_real_fft = offsetof(AecmCore_t, real_fft);

View File

@@ -0,0 +1,87 @@
/*
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AECM_AECM_DEFINES_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_AECM_AECM_DEFINES_H_
#define AECM_DYNAMIC_Q /* Turn on/off dynamic Q-domain. */
/* Algorithm parameters */
#define FRAME_LEN 80 /* Total frame length, 10 ms. */
#define PART_LEN 64 /* Length of partition. */
#define PART_LEN_SHIFT 7 /* Length of (PART_LEN * 2) in base 2. */
#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 */
#define CONV_LEN 512 /* Convergence length used at startup. */
#define CONV_LEN2 (CONV_LEN << 1) /* 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 (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. */
#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 */
#endif

View File

@@ -0,0 +1,726 @@
/*
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/modules/audio_processing/aecm/include/echo_control_mobile.h"
#ifdef AEC_DEBUG
#include <stdio.h>
#endif
#include <stdlib.h>
#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h"
#include "webrtc/modules/audio_processing/aecm/aecm_core.h"
#include "webrtc/modules/audio_processing/utility/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 size_t 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;
int16_t echoMode;
#ifdef AEC_DEBUG
FILE *bufFile;
FILE *delayFile;
FILE *preCompFile;
FILE *postCompFile;
#endif // AEC_DEBUG
// Structures
RingBuffer *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);
int32_t WebRtcAecm_Create(void **aecmInst)
{
aecmob_t *aecm;
if (aecmInst == NULL)
{
return -1;
}
aecm = malloc(sizeof(aecmob_t));
*aecmInst = aecm;
if (aecm == NULL)
{
return -1;
}
WebRtcSpl_Init();
if (WebRtcAecm_CreateCore(&aecm->aecmCore) == -1)
{
WebRtcAecm_Free(aecm);
aecm = NULL;
return -1;
}
aecm->farendBuf = WebRtc_CreateBuffer(kBufSizeSamp,
sizeof(int16_t));
if (!aecm->farendBuf)
{
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;
}
int32_t 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);
WebRtc_FreeBuffer(aecm->farendBuf);
free(aecm);
return 0;
}
int32_t WebRtcAecm_Init(void *aecmInst, int32_t 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 (WebRtc_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;
}
int32_t WebRtcAecm_BufferFarend(void *aecmInst, const int16_t *farend,
int16_t nrOfSamples)
{
aecmob_t *aecm = aecmInst;
int32_t 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);
}
WebRtc_WriteBuffer(aecm->farendBuf, farend, (size_t) nrOfSamples);
return retVal;
}
int32_t WebRtcAecm_Process(void *aecmInst, const int16_t *nearendNoisy,
const int16_t *nearendClean, int16_t *out,
int16_t nrOfSamples, int16_t msInSndCardBuf)
{
aecmob_t *aecm = aecmInst;
int32_t retVal = 0;
short i;
short nmbrOfFilledBuffers;
short nBlocks10ms;
short nFrames;
#ifdef AEC_DEBUG
short msInAECBuf;
#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)
{
if (out != nearendNoisy)
{
memcpy(out, nearendNoisy, sizeof(short) * nrOfSamples);
}
} else if (out != nearendClean)
{
memcpy(out, nearendClean, sizeof(short) * nrOfSamples);
}
nmbrOfFilledBuffers =
(short) WebRtc_available_read(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)
{
WebRtc_MoveReadPtr(aecm->farendBuf,
(int) WebRtc_available_read(aecm->farendBuf)
- (int) 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++)
{
int16_t farend[FRAME_LEN];
const int16_t* farend_ptr = NULL;
nmbrOfFilledBuffers =
(short) WebRtc_available_read(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
WebRtc_ReadBuffer(aecm->farendBuf, (void**) &farend_ptr, farend,
FRAME_LEN);
// Always store the last frame for use when we run out of data
memcpy(&(aecm->farendOld[i][0]), farend_ptr,
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));
farend_ptr = farend;
}
// 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);
}
// Call the AECM
/*WebRtcAecm_ProcessFrame(aecm->aecmCore, farend, &nearend[FRAME_LEN * i],
&out[FRAME_LEN * i], aecm->knownDelay);*/
if (WebRtcAecm_ProcessFrame(aecm->aecmCore,
farend_ptr,
&nearendNoisy[FRAME_LEN * i],
(nearendClean
? &nearendClean[FRAME_LEN * i]
: NULL),
&out[FRAME_LEN * i]) == -1)
return -1;
}
}
#ifdef AEC_DEBUG
msInAECBuf = (short) WebRtc_available_read(aecm->farendBuf) /
(kSampMsNb * aecm->aecmCore->mult);
fwrite(&msInAECBuf, 2, 1, aecm->bufFile);
fwrite(&(aecm->knownDelay), sizeof(aecm->knownDelay), 1, aecm->delayFile);
#endif
return retVal;
}
int32_t 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;
}
int32_t 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;
}
int32_t WebRtcAecm_InitEchoPath(void* aecmInst,
const void* echo_path,
size_t size_bytes)
{
aecmob_t *aecm = aecmInst;
const int16_t* echo_path_ptr = echo_path;
if (aecmInst == NULL) {
return -1;
}
if (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;
}
int32_t WebRtcAecm_GetEchoPath(void* aecmInst,
void* echo_path,
size_t size_bytes)
{
aecmob_t *aecm = aecmInst;
int16_t* echo_path_ptr = echo_path;
if (aecmInst == NULL) {
return -1;
}
if (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(int16_t));
}
int32_t 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, nSampSndCard;
short nSampFar = (short) WebRtc_available_read(aecm->farendBuf);
short diff;
nSampSndCard = msInSndCardBuf * kSampMsNb * aecm->aecmCore->mult;
delayNew = nSampSndCard - nSampFar;
if (delayNew < FRAME_LEN)
{
WebRtc_MoveReadPtr(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 = (int) WebRtc_available_read(aecm->farendBuf);
int nSampSndCard, delayNew, nSampAdd;
const int maxStuffSamp = 10 * FRAME_LEN;
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);
WebRtc_MoveReadPtr(aecm->farendBuf, -nSampAdd);
aecm->delayChange = 1; // the delay needs to be updated
}
return 0;
}

View File

@@ -0,0 +1,232 @@
/*
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AECM_INCLUDE_ECHO_CONTROL_MOBILE_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_AECM_INCLUDE_ECHO_CONTROL_MOBILE_H_
#include <stdlib.h>
#include "webrtc/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 {
int16_t cngMode; // AECM_FALSE, AECM_TRUE (default)
int16_t 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
* -------------------------------------------------------------------
* int32_t return 0: OK
* -1: error
*/
int32_t WebRtcAecm_Create(void **aecmInst);
/*
* This function releases the memory allocated by WebRtcAecm_Create()
*
* Inputs Description
* -------------------------------------------------------------------
* void* aecmInst Pointer to the AECM instance
*
* Outputs Description
* -------------------------------------------------------------------
* int32_t return 0: OK
* -1: error
*/
int32_t WebRtcAecm_Free(void *aecmInst);
/*
* Initializes an AECM instance.
*
* Inputs Description
* -------------------------------------------------------------------
* void* aecmInst Pointer to the AECM instance
* int32_t sampFreq Sampling frequency of data
*
* Outputs Description
* -------------------------------------------------------------------
* int32_t return 0: OK
* -1: error
*/
int32_t WebRtcAecm_Init(void* aecmInst, int32_t sampFreq);
/*
* Inserts an 80 or 160 sample block of data into the farend buffer.
*
* Inputs Description
* -------------------------------------------------------------------
* void* aecmInst Pointer to the AECM instance
* int16_t* farend In buffer containing one frame of
* farend signal
* int16_t nrOfSamples Number of samples in farend buffer
*
* Outputs Description
* -------------------------------------------------------------------
* int32_t return 0: OK
* -1: error
*/
int32_t WebRtcAecm_BufferFarend(void* aecmInst,
const int16_t* farend,
int16_t nrOfSamples);
/*
* Runs the AECM on an 80 or 160 sample blocks of data.
*
* Inputs Description
* -------------------------------------------------------------------
* void* aecmInst Pointer to the AECM instance
* int16_t* nearendNoisy In buffer containing one frame of
* reference nearend+echo signal. If
* noise reduction is active, provide
* the noisy signal here.
* int16_t* 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.
* int16_t nrOfSamples Number of samples in nearend buffer
* int16_t msInSndCardBuf Delay estimate for sound card and
* system buffers
*
* Outputs Description
* -------------------------------------------------------------------
* int16_t* out Out buffer, one frame of processed nearend
* int32_t return 0: OK
* -1: error
*/
int32_t WebRtcAecm_Process(void* aecmInst,
const int16_t* nearendNoisy,
const int16_t* nearendClean,
int16_t* out,
int16_t nrOfSamples,
int16_t 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
* -------------------------------------------------------------------
* int32_t return 0: OK
* -1: error
*/
int32_t 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
* int32_t return 0: OK
* -1: error
*/
int32_t 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
* -------------------------------------------------------------------
* int32_t return 0: OK
* -1: error
*/
int32_t 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
* -------------------------------------------------------------------
* int32_t return 0: OK
* -1: error
*/
int32_t 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
* -------------------------------------------------------------------
* int32_t return 11000-11100: error code
*/
int32_t WebRtcAecm_get_error_code(void *aecmInst);
#ifdef __cplusplus
}
#endif
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AECM_INCLUDE_ECHO_CONTROL_MOBILE_H_