git-svn-id: http://webrtc.googlecode.com/svn/trunk@156 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
2
src/modules/audio_processing/OWNERS
Normal file
2
src/modules/audio_processing/OWNERS
Normal file
@@ -0,0 +1,2 @@
|
||||
ajm@google.com
|
||||
bjornv@google.com
|
||||
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
* 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
|
||||
//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 initilized
|
||||
*
|
||||
* 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 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_ */
|
||||
953
src/modules/audio_processing/aec/main/matlab/fullaec.m
Normal file
953
src/modules/audio_processing/aec/main/matlab/fullaec.m
Normal file
@@ -0,0 +1,953 @@
|
||||
% Partitioned block frequency domain adaptive filtering NLMS and
|
||||
% standard time-domain sample-based NLMS
|
||||
%fid=fopen('aecFar-samsung.pcm', 'rb'); % Load far end
|
||||
fid=fopen('aecFar.pcm', 'rb'); % Load far end
|
||||
%fid=fopen(farFile, 'rb'); % Load far end
|
||||
rrin=fread(fid,inf,'int16');
|
||||
fclose(fid);
|
||||
%rrin=loadsl('data/far_me2.pcm'); % Load far end
|
||||
%fid=fopen('aecNear-samsung.pcm', 'rb'); % Load near end
|
||||
fid=fopen('aecNear.pcm', 'rb'); % Load near end
|
||||
%fid=fopen(nearFile, 'rb'); % Load near end
|
||||
ssin=fread(fid,inf,'int16');
|
||||
%ssin = [zeros(1024,1) ; ssin(1:end-1024)];
|
||||
|
||||
fclose(fid);
|
||||
rand('state',13);
|
||||
fs=16000;
|
||||
mult=fs/8000;
|
||||
%rrin=rrin(fs*0+1:round(fs*120));
|
||||
%ssin=ssin(fs*0+1:round(fs*120));
|
||||
if fs == 8000
|
||||
cohRange = 2:3;
|
||||
elseif fs==16000
|
||||
cohRange = 2;
|
||||
end
|
||||
|
||||
% Flags
|
||||
NLPon=1; % NLP
|
||||
CNon=1; % Comfort noise
|
||||
PLTon=1; % Plotting
|
||||
|
||||
M = 16; % Number of partitions
|
||||
N = 64; % Partition length
|
||||
L = M*N; % Filter length
|
||||
if fs == 8000
|
||||
mufb = 0.6;
|
||||
else
|
||||
mufb = 0.5;
|
||||
end
|
||||
%mufb=1;
|
||||
VADtd=48;
|
||||
alp = 0.1; % Power estimation factor alc = 0.1; % Coherence estimation factor
|
||||
beta = 0.9; % Plotting factor
|
||||
%% Changed a little %%
|
||||
step = 0.3;%0.1875; % Downward step size
|
||||
%%
|
||||
if fs == 8000
|
||||
threshold=2e-6; % DTrob threshold
|
||||
else
|
||||
%threshold=0.7e-6;
|
||||
threshold=1.5e-6; end
|
||||
|
||||
if fs == 8000
|
||||
echoBandRange = ceil(300*2/fs*N):floor(1800*2/fs*N);
|
||||
%echoBandRange = ceil(1500*2/fs*N):floor(2500*2/fs*N);
|
||||
else
|
||||
echoBandRange = ceil(300*2/fs*N):floor(1800*2/fs*N);
|
||||
%echoBandRange = ceil(300*2/fs*N):floor(1800*2/fs*N);
|
||||
end
|
||||
%echoBandRange = ceil(1600*2/fs*N):floor(1900*2/fs*N);
|
||||
%echoBandRange = ceil(2000*2/fs*N):floor(4000*2/fs*N);
|
||||
suppState = 1;
|
||||
transCtr = 0;
|
||||
|
||||
Nt=1;
|
||||
vt=1;
|
||||
|
||||
ramp = 1.0003; % Upward ramp
|
||||
rampd = 0.999; % Downward ramp
|
||||
cvt = 20; % Subband VAD threshold;
|
||||
nnthres = 20; % Noise threshold
|
||||
|
||||
shh=logspace(-1.3,-2.2,N+1)';
|
||||
sh=[shh;flipud(shh(2:end-1))]; % Suppression profile
|
||||
|
||||
len=length(ssin);
|
||||
w=zeros(L,1); % Sample-based TD NLMS
|
||||
WFb=zeros(N+1,M); % Block-based FD NLMS
|
||||
WFbOld=zeros(N+1,M); % Block-based FD NLMS
|
||||
YFb=zeros(N+1,M);
|
||||
erfb=zeros(len,1);
|
||||
erfb3=zeros(len,1);
|
||||
|
||||
ercn=zeros(len,1);
|
||||
zm=zeros(N,1);
|
||||
XFm=zeros(N+1,M);
|
||||
YFm=zeros(N+1,M);
|
||||
pn0=10*ones(N+1,1);
|
||||
pn=zeros(N+1,1);
|
||||
NN=len;
|
||||
Nb=floor(NN/N)-M;
|
||||
erifb=zeros(Nb+1,1)+0.1;
|
||||
erifb3=zeros(Nb+1,1)+0.1;
|
||||
ericn=zeros(Nb+1,1)+0.1;
|
||||
dri=zeros(Nb+1,1)+0.1;
|
||||
start=1;
|
||||
xo=zeros(N,1);
|
||||
do=xo;
|
||||
eo=xo;
|
||||
|
||||
echoBands=zeros(Nb+1,1);
|
||||
cohxdAvg=zeros(Nb+1,1);
|
||||
cohxdSlow=zeros(Nb+1,N+1);
|
||||
cohedSlow=zeros(Nb+1,N+1);
|
||||
%overdriveM=zeros(Nb+1,N+1);
|
||||
cohxdFastAvg=zeros(Nb+1,1);
|
||||
cohxdAvgBad=zeros(Nb+1,1);
|
||||
cohedAvg=zeros(Nb+1,1);
|
||||
cohedFastAvg=zeros(Nb+1,1);
|
||||
hnledAvg=zeros(Nb+1,1);
|
||||
hnlxdAvg=zeros(Nb+1,1);
|
||||
ovrdV=zeros(Nb+1,1);
|
||||
dIdxV=zeros(Nb+1,1);
|
||||
SLxV=zeros(Nb+1,1);
|
||||
hnlSortQV=zeros(Nb+1,1);
|
||||
hnlPrefAvgV=zeros(Nb+1,1);
|
||||
mutInfAvg=zeros(Nb+1,1);
|
||||
%overdrive=zeros(Nb+1,1);
|
||||
hnled = zeros(N+1, 1);
|
||||
weight=zeros(N+1,1);
|
||||
hnlMax = zeros(N+1, 1);
|
||||
hnl = zeros(N+1, 1);
|
||||
overdrive = ones(1, N+1);
|
||||
xfwm=zeros(N+1,M);
|
||||
dfm=zeros(N+1,M);
|
||||
WFbD=ones(N+1,1);
|
||||
|
||||
fbSupp = 0;
|
||||
hnlLocalMin = 1;
|
||||
cohxdLocalMin = 1;
|
||||
hnlLocalMinV=zeros(Nb+1,1);
|
||||
cohxdLocalMinV=zeros(Nb+1,1);
|
||||
hnlMinV=zeros(Nb+1,1);
|
||||
dkEnV=zeros(Nb+1,1);
|
||||
ekEnV=zeros(Nb+1,1);
|
||||
ovrd = 2;
|
||||
ovrdPos = floor((N+1)/4);
|
||||
ovrdSm = 2;
|
||||
hnlMin = 1;
|
||||
minCtr = 0;
|
||||
SeMin = 0;
|
||||
SdMin = 0;
|
||||
SeLocalAvg = 0;
|
||||
SeMinSm = 0;
|
||||
divergeFact = 1;
|
||||
dIdx = 1;
|
||||
hnlMinCtr = 0;
|
||||
hnlNewMin = 0;
|
||||
divergeState = 0;
|
||||
|
||||
Sy=ones(N+1,1);
|
||||
Sym=1e7*ones(N+1,1);
|
||||
|
||||
wins=[0;sqrt(hanning(2*N-1))];
|
||||
ubufn=zeros(2*N,1);
|
||||
ebuf=zeros(2*N,1);
|
||||
ebuf2=zeros(2*N,1);
|
||||
ebuf4=zeros(2*N,1);
|
||||
mbuf=zeros(2*N,1);
|
||||
|
||||
cohedFast = zeros(N+1,1);
|
||||
cohxdFast = zeros(N+1,1);
|
||||
cohxd = zeros(N+1,1);
|
||||
Se = zeros(N+1,1);
|
||||
Sd = zeros(N+1,1);
|
||||
Sx = zeros(N+1,1);
|
||||
SxBad = zeros(N+1,1);
|
||||
Sed = zeros(N+1,1);
|
||||
Sxd = zeros(N+1,1);
|
||||
SxdBad = zeros(N+1,1);
|
||||
hnledp=[];
|
||||
|
||||
cohxdMax = 0;
|
||||
|
||||
%hh=waitbar(0,'Please wait...');
|
||||
progressbar(0);
|
||||
|
||||
%spaces = ' ';
|
||||
%spaces = repmat(spaces, 50, 1);
|
||||
%spaces = ['[' ; spaces ; ']'];
|
||||
%fprintf(1, spaces);
|
||||
%fprintf(1, '\n');
|
||||
|
||||
for kk=1:Nb
|
||||
pos = N * (kk-1) + start;
|
||||
|
||||
% FD block method
|
||||
% ---------------------- Organize data
|
||||
xk = rrin(pos:pos+N-1);
|
||||
dk = ssin(pos:pos+N-1);
|
||||
|
||||
xx = [xo;xk];
|
||||
xo = xk;
|
||||
tmp = fft(xx);
|
||||
XX = tmp(1:N+1);
|
||||
|
||||
dd = [do;dk]; % Overlap
|
||||
do = dk;
|
||||
tmp = fft(dd); % Frequency domain
|
||||
DD = tmp(1:N+1);
|
||||
|
||||
% ------------------------ Power estimation
|
||||
pn0 = (1 - alp) * pn0 + alp * real(XX.* conj(XX));
|
||||
pn = pn0;
|
||||
%pn = (1 - alp) * pn + alp * M * pn0;
|
||||
if (CNon)
|
||||
Yp = real(conj(DD).*DD); % Instantaneous power
|
||||
Sy = (1 - alp) * Sy + alp * Yp; % Averaged power
|
||||
|
||||
mm = min(Sy,Sym);
|
||||
diff = Sym - mm;
|
||||
if (kk>50)
|
||||
Sym = (mm + step*diff) * ramp; % Estimated background noise power
|
||||
end
|
||||
end
|
||||
|
||||
% ---------------------- Filtering
|
||||
XFm(:,1) = XX;
|
||||
for mm=0:(M-1)
|
||||
m=mm+1;
|
||||
YFb(:,m) = XFm(:,m) .* WFb(:,m);
|
||||
end
|
||||
yfk = sum(YFb,2);
|
||||
tmp = [yfk ; flipud(conj(yfk(2:N)))];
|
||||
ykt = real(ifft(tmp));
|
||||
ykfb = ykt(end-N+1:end);
|
||||
|
||||
% ---------------------- Error estimation
|
||||
ekfb = dk - ykfb;
|
||||
%if sum(abs(ekfb)) < sum(abs(dk))
|
||||
%ekfb = dk - ykfb;
|
||||
% erfb(pos:pos+N-1) = ekfb;
|
||||
%else
|
||||
%ekfb = dk;
|
||||
% erfb(pos:pos+N-1) = dk;
|
||||
%end
|
||||
%(kk-1)*(N*2)+1
|
||||
erfb(pos:pos+N-1) = ekfb;
|
||||
tmp = fft([zm;ekfb]); % FD version for cancelling part (overlap-save)
|
||||
Ek = tmp(1:N+1);
|
||||
|
||||
% ------------------------ Adaptation
|
||||
Ek2 = Ek ./(M*pn + 0.001); % Normalized error
|
||||
%Ek2 = Ek ./(pn + 0.001); % Normalized error
|
||||
%Ek2 = Ek ./(100*pn + 0.001); % Normalized error
|
||||
|
||||
absEf = max(abs(Ek2), threshold);
|
||||
absEf = ones(N+1,1)*threshold./absEf;
|
||||
Ek2 = Ek2.*absEf;
|
||||
|
||||
mEk = mufb.*Ek2;
|
||||
PP = conj(XFm).*(ones(M,1) * mEk')';
|
||||
tmp = [PP ; flipud(conj(PP(2:N,:)))];
|
||||
IFPP = real(ifft(tmp));
|
||||
PH = IFPP(1:N,:);
|
||||
tmp = fft([PH;zeros(N,M)]);
|
||||
FPH = tmp(1:N+1,:);
|
||||
WFb = WFb + FPH;
|
||||
|
||||
if mod(kk, 10*mult) == 0
|
||||
WFbEn = sum(real(WFb.*conj(WFb)));
|
||||
%WFbEn = sum(abs(WFb));
|
||||
[tmp, dIdx] = max(WFbEn);
|
||||
|
||||
WFbD = sum(abs(WFb(:, dIdx)),2);
|
||||
%WFbD = WFbD / (mean(WFbD) + 1e-10);
|
||||
WFbD = min(max(WFbD, 0.5), 4);
|
||||
end
|
||||
dIdxV(kk) = dIdx;
|
||||
|
||||
% NLP
|
||||
if (NLPon)
|
||||
|
||||
ee = [eo;ekfb];
|
||||
eo = ekfb;
|
||||
window = wins;
|
||||
if fs == 8000
|
||||
%gamma = 0.88;
|
||||
gamma = 0.9;
|
||||
else
|
||||
%gamma = 0.92;
|
||||
gamma = 0.93;
|
||||
end
|
||||
%gamma = 0.9;
|
||||
|
||||
tmp = fft(xx.*window);
|
||||
xf = tmp(1:N+1);
|
||||
tmp = fft(dd.*window);
|
||||
df = tmp(1:N+1);
|
||||
tmp = fft(ee.*window);
|
||||
ef = tmp(1:N+1);
|
||||
|
||||
xfwm(:,1) = xf;
|
||||
xf = xfwm(:,dIdx);
|
||||
%fprintf(1,'%d: %f\n', kk, xf(4));
|
||||
dfm(:,1) = df;
|
||||
|
||||
SxOld = Sx;
|
||||
|
||||
Se = gamma*Se + (1-gamma)*real(ef.*conj(ef));
|
||||
Sd = gamma*Sd + (1-gamma)*real(df.*conj(df));
|
||||
Sx = gamma*Sx + (1 - gamma)*real(xf.*conj(xf));
|
||||
|
||||
%xRatio = real(xfwm(:,1).*conj(xfwm(:,1))) ./ ...
|
||||
% (real(xfwm(:,2).*conj(xfwm(:,2))) + 1e-10);
|
||||
%xRatio = Sx ./ (SxOld + 1e-10);
|
||||
%SLx = log(1/(N+1)*sum(xRatio)) - 1/(N+1)*sum(log(xRatio));
|
||||
%SLxV(kk) = SLx;
|
||||
|
||||
%freqSm = 0.9;
|
||||
%Sx = filter(freqSm, [1 -(1-freqSm)], Sx);
|
||||
%Sx(end:1) = filter(freqSm, [1 -(1-freqSm)], Sx(end:1));
|
||||
%Se = filter(freqSm, [1 -(1-freqSm)], Se);
|
||||
%Se(end:1) = filter(freqSm, [1 -(1-freqSm)], Se(end:1));
|
||||
%Sd = filter(freqSm, [1 -(1-freqSm)], Sd);
|
||||
%Sd(end:1) = filter(freqSm, [1 -(1-freqSm)], Sd(end:1));
|
||||
|
||||
%SeFast = ef.*conj(ef);
|
||||
%SdFast = df.*conj(df);
|
||||
%SxFast = xf.*conj(xf);
|
||||
%cohedFast = 0.9*cohedFast + 0.1*SeFast ./ (SdFast + 1e-10);
|
||||
%cohedFast(find(cohedFast > 1)) = 1;
|
||||
%cohedFast(find(cohedFast > 1)) = 1 ./ cohedFast(find(cohedFast>1));
|
||||
%cohedFastAvg(kk) = mean(cohedFast(echoBandRange));
|
||||
%cohedFastAvg(kk) = min(cohedFast);
|
||||
|
||||
%cohxdFast = 0.8*cohxdFast + 0.2*log(SdFast ./ (SxFast + 1e-10));
|
||||
%cohxdFastAvg(kk) = mean(cohxdFast(echoBandRange));
|
||||
|
||||
% coherence
|
||||
Sxd = gamma*Sxd + (1 - gamma)*xf.*conj(df);
|
||||
Sed = gamma*Sed + (1-gamma)*ef.*conj(df);
|
||||
|
||||
%Sxd = filter(freqSm, [1 -(1-freqSm)], Sxd);
|
||||
%Sxd(end:1) = filter(freqSm, [1 -(1-freqSm)], Sxd(end:1));
|
||||
%Sed = filter(freqSm, [1 -(1-freqSm)], Sed);
|
||||
%Sed(end:1) = filter(freqSm, [1 -(1-freqSm)], Sed(end:1));
|
||||
|
||||
cohed = real(Sed.*conj(Sed))./(Se.*Sd + 1e-10);
|
||||
%cohedAvg(kk) = mean(cohed(echoBandRange));
|
||||
%cohedAvg(kk) = cohed(6);
|
||||
%cohedAvg(kk) = min(cohed);
|
||||
|
||||
cohxd = real(Sxd.*conj(Sxd))./(Sx.*Sd + 1e-10);
|
||||
%freqSm = 0.5;
|
||||
%cohxd(3:end) = filter(freqSm, [1 -(1-freqSm)], cohxd(3:end));
|
||||
%cohxd(end:3) = filter(freqSm, [1 -(1-freqSm)], cohxd(end:3));
|
||||
%cohxdAvg(kk) = mean(cohxd(echoBandRange));
|
||||
%cohxdAvg(kk) = (cohxd(32));
|
||||
%cohxdAvg(kk) = max(cohxd);
|
||||
|
||||
%xf = xfm(:,dIdx);
|
||||
%SxBad = gamma*SxBad + (1 - gamma)*real(xf.*conj(xf));
|
||||
%SxdBad = gamma*SxdBad + (1 - gamma)*xf.*conj(df);
|
||||
%cohxdBad = real(SxdBad.*conj(SxdBad))./(SxBad.*Sd + 0.01);
|
||||
%cohxdAvgBad(kk) = mean(cohxdBad);
|
||||
|
||||
%for j=1:N+1
|
||||
% mutInf(j) = 0.9*mutInf(j) + 0.1*information(abs(xfm(j,:)), abs(dfm(j,:)));
|
||||
%end
|
||||
%mutInfAvg(kk) = mean(mutInf);
|
||||
|
||||
%hnled = cohedFast;
|
||||
%xIdx = find(cohxd > 1 - cohed);
|
||||
%hnled(xIdx) = 1 - cohxd(xIdx);
|
||||
%hnled = 1 - max(cohxd, 1-cohedFast);
|
||||
hnled = min(1 - cohxd, cohed);
|
||||
%hnled = 1 - cohxd;
|
||||
%hnled = max(1 - (cohxd + (1-cohedFast)), 0);
|
||||
%hnled = 1 - max(cohxd, 1-cohed);
|
||||
|
||||
if kk > 1
|
||||
cohxdSlow(kk,:) = 0.99*cohxdSlow(kk-1,:) + 0.01*cohxd';
|
||||
cohedSlow(kk,:) = 0.99*cohedSlow(kk-1,:) + 0.01*(1-cohed)';
|
||||
end
|
||||
|
||||
|
||||
if 0
|
||||
%if kk > 50
|
||||
%idx = find(hnled > 0.3);
|
||||
hnlMax = hnlMax*0.9999;
|
||||
%hnlMax(idx) = max(hnlMax(idx), hnled(idx));
|
||||
hnlMax = max(hnlMax, hnled);
|
||||
%overdrive(idx) = max(log(hnlMax(idx))/log(0.99), 1);
|
||||
avgHnl = mean(hnlMax(echoBandRange));
|
||||
if avgHnl > 0.3
|
||||
overdrive = max(log(avgHnl)/log(0.99), 1);
|
||||
end
|
||||
weight(4:end) = max(hnlMax) - hnlMax(4:end);
|
||||
end
|
||||
|
||||
|
||||
|
||||
%[hg, gidx] = max(hnled);
|
||||
%fnrg = Sx(gidx) / (Sd(gidx) + 1e-10);
|
||||
|
||||
%[tmp, bidx] = find((Sx / Sd + 1e-10) > fnrg);
|
||||
%hnled(bidx) = hg;
|
||||
|
||||
|
||||
%cohed1 = mean(cohed(cohRange)); % range depends on bandwidth
|
||||
%cohed1 = cohed1^2;
|
||||
%echoBands(kk) = length(find(cohed(echoBandRange) < 0.25))/length(echoBandRange);
|
||||
|
||||
%if (fbSupp == 0)
|
||||
% if (echoBands(kk) > 0.8)
|
||||
% fbSupp = 1;
|
||||
% end
|
||||
%else
|
||||
% if (echoBands(kk) < 0.6)
|
||||
% fbSupp = 0;
|
||||
% end
|
||||
%end
|
||||
%overdrive(kk) = 7.5*echoBands(kk) + 0.5;
|
||||
|
||||
% Factor by which to weight other bands
|
||||
%if (cohed1 < 0.1)
|
||||
% w = 0.8 - cohed1*10*0.4;
|
||||
%else
|
||||
% w = 0.4;
|
||||
%end
|
||||
|
||||
% Weight coherence subbands
|
||||
%hnled = w*cohed1 + (1 - w)*cohed;
|
||||
%hnled = (hnled).^2;
|
||||
%cohed(floor(N/2):end) = cohed(floor(N/2):end).^2;
|
||||
%if fbSupp == 1
|
||||
% cohed = zeros(size(cohed));
|
||||
%end
|
||||
%cohed = cohed.^overdrive(kk);
|
||||
|
||||
%hnled = gamma*hnled + (1 - gamma)*cohed;
|
||||
% Additional hf suppression
|
||||
%hnledp = [hnledp ; mean(hnled)];
|
||||
%hnled(floor(N/2):end) = hnled(floor(N/2):end).^2;
|
||||
%ef = ef.*((weight*(min(1 - hnled)).^2 + (1 - weight).*(1 - hnled)).^2);
|
||||
|
||||
cohedMean = mean(cohed(echoBandRange));
|
||||
%aggrFact = 4*(1-mean(hnled(echoBandRange))) + 1;
|
||||
%[hnlSort, hnlSortIdx] = sort(hnled(echoBandRange));
|
||||
[hnlSort, hnlSortIdx] = sort(1-cohxd(echoBandRange));
|
||||
[xSort, xSortIdx] = sort(Sx);
|
||||
%aggrFact = (1-mean(hnled(echoBandRange)));
|
||||
%hnlSortQ = hnlSort(qIdx);
|
||||
hnlSortQ = mean(1 - cohxd(echoBandRange));
|
||||
%hnlSortQ = mean(1 - cohxd);
|
||||
|
||||
[hnlSort2, hnlSortIdx2] = sort(hnled(echoBandRange));
|
||||
%[hnlSort2, hnlSortIdx2] = sort(hnled);
|
||||
hnlQuant = 0.75;
|
||||
hnlQuantLow = 0.5;
|
||||
qIdx = floor(hnlQuant*length(hnlSort2));
|
||||
qIdxLow = floor(hnlQuantLow*length(hnlSort2));
|
||||
hnlPrefAvg = hnlSort2(qIdx);
|
||||
hnlPrefAvgLow = hnlSort2(qIdxLow);
|
||||
%hnlPrefAvgLow = mean(hnled);
|
||||
%hnlPrefAvg = max(hnlSort2);
|
||||
%hnlPrefAvgLow = min(hnlSort2);
|
||||
|
||||
%hnlPref = hnled(echoBandRange);
|
||||
%hnlPrefAvg = mean(hnlPref(xSortIdx((0.5*length(xSortIdx)):end)));
|
||||
|
||||
%hnlPrefAvg = min(hnlPrefAvg, hnlSortQ);
|
||||
|
||||
%hnlSortQIdx = hnlSortIdx(qIdx);
|
||||
%SeQ = Se(qIdx + echoBandRange(1) - 1);
|
||||
%SdQ = Sd(qIdx + echoBandRange(1) - 1);
|
||||
%SeQ = Se(qIdxLow + echoBandRange(1) - 1);
|
||||
%SdQ = Sd(qIdxLow + echoBandRange(1) - 1);
|
||||
%propLow = length(find(hnlSort < 0.1))/length(hnlSort);
|
||||
%aggrFact = min((1 - hnlSortQ)/2, 0.5);
|
||||
%aggrTerm = 1/aggrFact;
|
||||
|
||||
%hnlg = mean(hnled(echoBandRange));
|
||||
%hnlg = hnlSortQ;
|
||||
%if suppState == 0
|
||||
% if hnlg < 0.05
|
||||
% suppState = 2;
|
||||
% transCtr = 0;
|
||||
% elseif hnlg < 0.75
|
||||
% suppState = 1;
|
||||
% transCtr = 0;
|
||||
% end
|
||||
%elseif suppState == 1
|
||||
% if hnlg > 0.8
|
||||
% suppState = 0;
|
||||
% transCtr = 0;
|
||||
% elseif hnlg < 0.05
|
||||
% suppState = 2;
|
||||
% transCtr = 0;
|
||||
% end
|
||||
%else
|
||||
% if hnlg > 0.8
|
||||
% suppState = 0;
|
||||
% transCtr = 0;
|
||||
% elseif hnlg > 0.25
|
||||
% suppState = 1;
|
||||
% transCtr = 0;
|
||||
% end
|
||||
%end
|
||||
%if kk > 50
|
||||
|
||||
if cohedMean > 0.98 & hnlSortQ > 0.9
|
||||
%if suppState == 1
|
||||
% hnled = 0.5*hnled + 0.5*cohed;
|
||||
% %hnlSortQ = 0.5*hnlSortQ + 0.5*cohedMean;
|
||||
% hnlPrefAvg = 0.5*hnlPrefAvg + 0.5*cohedMean;
|
||||
%else
|
||||
% hnled = cohed;
|
||||
% %hnlSortQ = cohedMean;
|
||||
% hnlPrefAvg = cohedMean;
|
||||
%end
|
||||
suppState = 0;
|
||||
elseif cohedMean < 0.95 | hnlSortQ < 0.8
|
||||
%if suppState == 0
|
||||
% hnled = 0.5*hnled + 0.5*cohed;
|
||||
% %hnlSortQ = 0.5*hnlSortQ + 0.5*cohedMean;
|
||||
% hnlPrefAvg = 0.5*hnlPrefAvg + 0.5*cohedMean;
|
||||
%end
|
||||
suppState = 1;
|
||||
end
|
||||
|
||||
if hnlSortQ < cohxdLocalMin & hnlSortQ < 0.75
|
||||
cohxdLocalMin = hnlSortQ;
|
||||
end
|
||||
|
||||
if cohxdLocalMin == 1
|
||||
ovrd = 3;
|
||||
hnled = 1-cohxd;
|
||||
hnlPrefAvg = hnlSortQ;
|
||||
hnlPrefAvgLow = hnlSortQ;
|
||||
end
|
||||
|
||||
if suppState == 0
|
||||
hnled = cohed;
|
||||
hnlPrefAvg = cohedMean;
|
||||
hnlPrefAvgLow = cohedMean;
|
||||
end
|
||||
|
||||
%if hnlPrefAvg < hnlLocalMin & hnlPrefAvg < 0.6
|
||||
if hnlPrefAvgLow < hnlLocalMin & hnlPrefAvgLow < 0.6
|
||||
%hnlLocalMin = hnlPrefAvg;
|
||||
%hnlMin = hnlPrefAvg;
|
||||
hnlLocalMin = hnlPrefAvgLow;
|
||||
hnlMin = hnlPrefAvgLow;
|
||||
hnlNewMin = 1;
|
||||
hnlMinCtr = 0;
|
||||
%if hnlMinCtr == 0
|
||||
% hnlMinCtr = hnlMinCtr + 1;
|
||||
%else
|
||||
% hnlMinCtr = 0;
|
||||
% hnlMin = hnlLocalMin;
|
||||
%SeLocalMin = SeQ;
|
||||
%SdLocalMin = SdQ;
|
||||
%SeLocalAvg = 0;
|
||||
%minCtr = 0;
|
||||
% ovrd = max(log(0.0001)/log(hnlMin), 2);
|
||||
%divergeFact = hnlLocalMin;
|
||||
end
|
||||
|
||||
if hnlNewMin == 1
|
||||
hnlMinCtr = hnlMinCtr + 1;
|
||||
end
|
||||
if hnlMinCtr == 2
|
||||
hnlNewMin = 0;
|
||||
hnlMinCtr = 0;
|
||||
%ovrd = max(log(0.0001)/log(hnlMin), 2);
|
||||
ovrd = max(log(0.00001)/(log(hnlMin + 1e-10) + 1e-10), 3);
|
||||
%ovrd = max(log(0.00000001)/(log(hnlMin + 1e-10) + 1e-10), 5);
|
||||
%ovrd = max(log(0.0001)/log(hnlPrefAvg), 2);
|
||||
%ovrd = max(log(0.001)/log(hnlMin), 2);
|
||||
end
|
||||
hnlLocalMin = min(hnlLocalMin + 0.0008/mult, 1);
|
||||
cohxdLocalMin = min(cohxdLocalMin + 0.0004/mult, 1);
|
||||
%divergeFact = hnlSortQ;
|
||||
|
||||
|
||||
%if minCtr > 0 & hnlLocalMin < 1
|
||||
% hnlMin = hnlLocalMin;
|
||||
% %SeMin = 0.9*SeMin + 0.1*sqrt(SeLocalMin);
|
||||
% SdMin = sqrt(SdLocalMin);
|
||||
% %SeMin = sqrt(SeLocalMin)*hnlSortQ;
|
||||
% SeMin = sqrt(SeLocalMin);
|
||||
% %ovrd = log(100/SeMin)/log(hnlSortQ);
|
||||
% %ovrd = log(100/SeMin)/log(hnlSortQ);
|
||||
% ovrd = log(0.01)/log(hnlMin);
|
||||
% ovrd = max(ovrd, 2);
|
||||
% ovrdPos = hnlSortQIdx;
|
||||
% %ovrd = max(ovrd, 1);
|
||||
% %SeMin = sqrt(SeLocalAvg/5);
|
||||
% minCtr = 0;
|
||||
%else
|
||||
% %SeLocalMin = 0.9*SeLocalMin +0.1*SeQ;
|
||||
% SeLocalAvg = SeLocalAvg + SeQ;
|
||||
% minCtr = minCtr + 1;
|
||||
%end
|
||||
|
||||
if ovrd < ovrdSm
|
||||
ovrdSm = 0.99*ovrdSm + 0.01*ovrd;
|
||||
else
|
||||
ovrdSm = 0.9*ovrdSm + 0.1*ovrd;
|
||||
end
|
||||
%end
|
||||
|
||||
%ekEn = sum(real(ekfb.^2));
|
||||
%dkEn = sum(real(dk.^2));
|
||||
ekEn = sum(Se);
|
||||
dkEn = sum(Sd);
|
||||
|
||||
if divergeState == 0
|
||||
if ekEn > dkEn
|
||||
ef = df;
|
||||
divergeState = 1;
|
||||
%hnlPrefAvg = hnlSortQ;
|
||||
%hnled = (1 - cohxd);
|
||||
end
|
||||
else
|
||||
%if ekEn*1.1 < dkEn
|
||||
%if ekEn*1.26 < dkEn
|
||||
if ekEn*1.05 < dkEn
|
||||
divergeState = 0;
|
||||
else
|
||||
ef = df;
|
||||
end
|
||||
end
|
||||
|
||||
if ekEn > dkEn*19.95
|
||||
WFb=zeros(N+1,M); % Block-based FD NLMS
|
||||
end
|
||||
|
||||
ekEnV(kk) = ekEn;
|
||||
dkEnV(kk) = dkEn;
|
||||
|
||||
hnlLocalMinV(kk) = hnlLocalMin;
|
||||
cohxdLocalMinV(kk) = cohxdLocalMin;
|
||||
hnlMinV(kk) = hnlMin;
|
||||
%cohxdMaxLocal = max(cohxdSlow(kk,:));
|
||||
%if kk > 50
|
||||
%cohxdMaxLocal = 1-hnlSortQ;
|
||||
%if cohxdMaxLocal > 0.5
|
||||
% %if cohxdMaxLocal > cohxdMax
|
||||
% odScale = max(log(cohxdMaxLocal)/log(0.95), 1);
|
||||
% %overdrive(7:end) = max(log(cohxdSlow(kk,7:end))/log(0.9), 1);
|
||||
% cohxdMax = cohxdMaxLocal;
|
||||
% end
|
||||
%end
|
||||
%end
|
||||
%cohxdMax = cohxdMax*0.999;
|
||||
|
||||
%overdriveM(kk,:) = max(overdrive, 1);
|
||||
%aggrFact = 0.25;
|
||||
aggrFact = 0.3;
|
||||
%aggrFact = 0.5*propLow;
|
||||
%if fs == 8000
|
||||
% wCurve = [0 ; 0 ; aggrFact*sqrt(linspace(0,1,N-1))' + 0.1];
|
||||
%else
|
||||
% wCurve = [0; 0; 0; aggrFact*sqrt(linspace(0,1,N-2))' + 0.1];
|
||||
%end
|
||||
wCurve = [0; aggrFact*sqrt(linspace(0,1,N))' + 0.1];
|
||||
% For sync with C
|
||||
%if fs == 8000
|
||||
% wCurve = wCurve(2:end);
|
||||
%else
|
||||
% wCurve = wCurve(1:end-1);
|
||||
%end
|
||||
%weight = aggrFact*(sqrt(linspace(0,1,N+1)'));
|
||||
%weight = aggrFact*wCurve;
|
||||
weight = wCurve;
|
||||
%weight = aggrFact*ones(N+1,1);
|
||||
%weight = zeros(N+1,1);
|
||||
%hnled = weight.*min(hnled) + (1 - weight).*hnled;
|
||||
%hnled = weight.*min(mean(hnled(echoBandRange)), hnled) + (1 - weight).*hnled;
|
||||
%hnled = weight.*min(hnlSortQ, hnled) + (1 - weight).*hnled;
|
||||
|
||||
%hnlSortQV(kk) = mean(hnled);
|
||||
%hnlPrefAvgV(kk) = mean(hnled(echoBandRange));
|
||||
|
||||
hnled = weight.*min(hnlPrefAvg, hnled) + (1 - weight).*hnled;
|
||||
|
||||
%od = aggrFact*(sqrt(linspace(0,1,N+1)') + aggrTerm);
|
||||
%od = 4*(sqrt(linspace(0,1,N+1)') + 1/4);
|
||||
|
||||
%ovrdFact = (ovrdSm - 1) / sqrt(ovrdPos/(N+1));
|
||||
%ovrdFact = ovrdSm / sqrt(echoBandRange(floor(length(echoBandRange)/2))/(N+1));
|
||||
%od = ovrdFact*sqrt(linspace(0,1,N+1))' + 1;
|
||||
%od = ovrdSm*ones(N+1,1).*abs(WFb(:,dIdx))/(max(abs(WFb(:,dIdx)))+1e-10);
|
||||
|
||||
%od = ovrdSm*ones(N+1,1);
|
||||
%od = ovrdSm*WFbD.*(sqrt(linspace(0,1,N+1))' + 1);
|
||||
|
||||
od = ovrdSm*(sqrt(linspace(0,1,N+1))' + 1);
|
||||
%od = 4*(sqrt(linspace(0,1,N+1))' + 1);
|
||||
|
||||
%od = 2*ones(N+1,1);
|
||||
%od = 2*ones(N+1,1);
|
||||
%sshift = ((1-hnled)*2-1).^3+1;
|
||||
sshift = ones(N+1,1);
|
||||
|
||||
hnled = hnled.^(od.*sshift);
|
||||
|
||||
%if hnlg > 0.75
|
||||
%if (suppState ~= 0)
|
||||
% transCtr = 0;
|
||||
%end
|
||||
% suppState = 0;
|
||||
%elseif hnlg < 0.6 & hnlg > 0.2
|
||||
% suppState = 1;
|
||||
%elseif hnlg < 0.1
|
||||
%hnled = zeros(N+1, 1);
|
||||
%if (suppState ~= 2)
|
||||
% transCtr = 0;
|
||||
%end
|
||||
% suppState = 2;
|
||||
%else
|
||||
% if (suppState ~= 2)
|
||||
% transCtr = 0;
|
||||
% end
|
||||
% suppState = 2;
|
||||
%end
|
||||
%if suppState == 0
|
||||
% hnled = ones(N+1, 1);
|
||||
%elseif suppState == 2
|
||||
% hnled = zeros(N+1, 1);
|
||||
%end
|
||||
%hnled(find(hnled < 0.1)) = 0;
|
||||
%hnled = hnled.^2;
|
||||
%if transCtr < 5
|
||||
%hnl = 0.75*hnl + 0.25*hnled;
|
||||
% transCtr = transCtr + 1;
|
||||
%else
|
||||
hnl = hnled;
|
||||
%end
|
||||
%hnled(find(hnled < 0.05)) = 0;
|
||||
ef = ef.*(hnl);
|
||||
|
||||
%ef = ef.*(min(1 - cohxd, cohed).^2);
|
||||
%ef = ef.*((1-cohxd).^2);
|
||||
|
||||
ovrdV(kk) = ovrdSm;
|
||||
%ovrdV(kk) = dIdx;
|
||||
%ovrdV(kk) = divergeFact;
|
||||
%hnledAvg(kk) = 1-mean(1-cohedFast(echoBandRange));
|
||||
hnledAvg(kk) = 1-mean(1-cohed(echoBandRange));
|
||||
hnlxdAvg(kk) = 1-mean(cohxd(echoBandRange));
|
||||
%hnlxdAvg(kk) = cohxd(5);
|
||||
%hnlSortQV(kk) = mean(hnled);
|
||||
hnlSortQV(kk) = hnlPrefAvgLow;
|
||||
hnlPrefAvgV(kk) = hnlPrefAvg;
|
||||
%hnlAvg(kk) = propLow;
|
||||
%ef(N/2:end) = 0;
|
||||
%ner = (sum(Sd) ./ (sum(Se.*(hnl.^2)) + 1e-10));
|
||||
|
||||
% Comfort noise
|
||||
if (CNon)
|
||||
snn=sqrt(Sym);
|
||||
snn(1)=0; % Reject LF noise
|
||||
Un=snn.*exp(j*2*pi.*[0;rand(N-1,1);0]);
|
||||
|
||||
% Weight comfort noise by suppression
|
||||
Un = sqrt(1-hnled.^2).*Un;
|
||||
Fmix = ef + Un;
|
||||
else
|
||||
Fmix = ef;
|
||||
end
|
||||
|
||||
% Overlap and add in time domain for smoothness
|
||||
tmp = [Fmix ; flipud(conj(Fmix(2:N)))];
|
||||
mixw = wins.*real(ifft(tmp));
|
||||
mola = mbuf(end-N+1:end) + mixw(1:N);
|
||||
mbuf = mixw;
|
||||
ercn(pos:pos+N-1) = mola;
|
||||
end % NLPon
|
||||
|
||||
% Filter update
|
||||
%Ek2 = Ek ./(12*pn + 0.001); % Normalized error
|
||||
%Ek2 = Ek2 * divergeFact;
|
||||
%Ek2 = Ek ./(pn + 0.001); % Normalized error
|
||||
%Ek2 = Ek ./(100*pn + 0.001); % Normalized error
|
||||
|
||||
%divergeIdx = find(abs(Ek) > abs(DD));
|
||||
%divergeIdx = find(Se > Sd);
|
||||
%threshMod = threshold*ones(N+1,1);
|
||||
%if length(divergeIdx) > 0
|
||||
%if sum(abs(Ek)) > sum(abs(DD))
|
||||
%WFb(divergeIdx,:) = WFb(divergeIdx,:) .* repmat(sqrt(Sd(divergeIdx)./(Se(divergeIdx)+1e-10))),1,M);
|
||||
%Ek2(divergeIdx) = Ek2(divergeIdx) .* sqrt(Sd(divergeIdx)./(Se(divergeIdx)+1e-10));
|
||||
%Ek2(divergeIdx) = Ek2(divergeIdx) .* abs(DD(divergeIdx))./(abs(Ek(divergeIdx))+1e-10);
|
||||
%WFb(divergeIdx,:) = WFbOld(divergeIdx,:);
|
||||
%WFb = WFbOld;
|
||||
%threshMod(divergeIdx) = threshMod(divergeIdx) .* abs(DD(divergeIdx))./(abs(Ek(divergeIdx))+1e-10);
|
||||
% threshMod(divergeIdx) = threshMod(divergeIdx) .* sqrt(Sd(divergeIdx)./(Se(divergeIdx)+1e-10));
|
||||
%end
|
||||
|
||||
%absEf = max(abs(Ek2), threshold);
|
||||
%absEf = ones(N+1,1)*threshold./absEf;
|
||||
%absEf = max(abs(Ek2), threshMod);
|
||||
%absEf = threshMod./absEf;
|
||||
%Ek2 = Ek2.*absEf;
|
||||
|
||||
%if sum(Se) <= sum(Sd)
|
||||
|
||||
% mEk = mufb.*Ek2;
|
||||
% PP = conj(XFm).*(ones(M,1) * mEk')';
|
||||
% tmp = [PP ; flipud(conj(PP(2:N,:)))];
|
||||
% IFPP = real(ifft(tmp));
|
||||
% PH = IFPP(1:N,:);
|
||||
% tmp = fft([PH;zeros(N,M)]);
|
||||
% FPH = tmp(1:N+1,:);
|
||||
% %WFbOld = WFb;
|
||||
% WFb = WFb + FPH;
|
||||
|
||||
%else
|
||||
% WF = WFbOld;
|
||||
%end
|
||||
|
||||
% Shift old FFTs
|
||||
%for m=M:-1:2
|
||||
% XFm(:,m) = XFm(:,m-1);
|
||||
% YFm(:,m) = YFm(:,m-1);
|
||||
%end
|
||||
XFm(:,2:end) = XFm(:,1:end-1);
|
||||
YFm(:,2:end) = YFm(:,1:end-1);
|
||||
xfwm(:,2:end) = xfwm(:,1:end-1);
|
||||
dfm(:,2:end) = dfm(:,1:end-1);
|
||||
|
||||
%if mod(kk, floor(Nb/50)) == 0
|
||||
% fprintf(1, '.');
|
||||
%end
|
||||
|
||||
if mod(kk, floor(Nb/100)) == 0
|
||||
%if mod(kk, floor(Nb/500)) == 0
|
||||
progressbar(kk/Nb);
|
||||
%figure(5)
|
||||
%plot(abs(WFb));
|
||||
%legend('1','2','3','4','5','6','7','8','9','10','11','12');
|
||||
%title(kk*N/fs);
|
||||
%figure(6)
|
||||
%plot(WFbD);
|
||||
%figure(6)
|
||||
%plot(threshMod)
|
||||
%if length(divergeIdx) > 0
|
||||
% plot(abs(DD))
|
||||
% hold on
|
||||
% plot(abs(Ek), 'r')
|
||||
% hold off
|
||||
%plot(min(sqrt(Sd./(Se+1e-10)),1))
|
||||
%axis([0 N 0 1]);
|
||||
%end
|
||||
%figure(6)
|
||||
%plot(cohedFast);
|
||||
%axis([1 N+1 0 1]);
|
||||
%plot(WFbEn);
|
||||
|
||||
%figure(7)
|
||||
%plot(weight);
|
||||
%plot([cohxd 1-cohed]);
|
||||
%plot([cohxd 1-cohed 1-cohedFast hnled]);
|
||||
%plot([cohxd cohxdFast/max(cohxdFast)]);
|
||||
%legend('cohxd', '1-cohed', '1-cohedFast');
|
||||
%axis([1 65 0 1]);
|
||||
%pause(0.5);
|
||||
%overdrive
|
||||
end
|
||||
end
|
||||
progressbar(1);
|
||||
|
||||
%figure(2);
|
||||
%plot([feat(:,1) feat(:,2)+1 feat(:,3)+2 mfeat+3]);
|
||||
%plot([feat(:,1) mfeat+1]);
|
||||
|
||||
%figure(3);
|
||||
%plot(10*log10([dri erifb erifb3 ericn]));
|
||||
%legend('Near-end','Error','Post NLP','Final',4);
|
||||
% Compensate for delay
|
||||
%ercn=[ercn(N+1:end);zeros(N,1)];
|
||||
%ercn_=[ercn_(N+1:end);zeros(N,1)];
|
||||
|
||||
%figure(11);
|
||||
%plot(cohxdSlow);
|
||||
|
||||
%figure(12);
|
||||
%surf(cohxdSlow);
|
||||
%shading interp;
|
||||
|
||||
%figure(13);
|
||||
%plot(overdriveM);
|
||||
|
||||
%figure(14);
|
||||
%surf(overdriveM);
|
||||
%shading interp;
|
||||
|
||||
figure(10);
|
||||
t = (0:Nb)*N/fs;
|
||||
rrinSubSamp = rrin(N*(1:(Nb+1)));
|
||||
plot(t, rrinSubSamp/max(abs(rrinSubSamp)),'b');
|
||||
hold on
|
||||
plot(t, hnledAvg, 'r');
|
||||
plot(t, hnlxdAvg, 'g');
|
||||
plot(t, hnlSortQV, 'y');
|
||||
plot(t, hnlLocalMinV, 'k');
|
||||
plot(t, cohxdLocalMinV, 'c');
|
||||
plot(t, hnlPrefAvgV, 'm');
|
||||
%plot(t, cohxdAvg, 'r');
|
||||
%plot(cohxdFastAvg, 'r');
|
||||
%plot(cohxdAvgBad, 'k');
|
||||
%plot(t, cohedAvg, 'k');
|
||||
%plot(t, 1-cohedFastAvg, 'k');
|
||||
%plot(ssin(N*(1:floor(length(ssin)/N)))/max(abs(ssin)));
|
||||
%plot(echoBands,'r');
|
||||
%plot(overdrive, 'g');
|
||||
%plot(erfb(N*(1:floor(length(erfb)/N)))/max(abs(erfb)));
|
||||
hold off
|
||||
tightx;
|
||||
|
||||
figure(11)
|
||||
plot(t, ovrdV);
|
||||
tightx;
|
||||
%plot(mfeat,'r');
|
||||
%plot(1-cohxyp_,'r');
|
||||
%plot(Hnlxydp,'y');
|
||||
%plot(hnledp,'k');
|
||||
%plot(Hnlxydp, 'c');
|
||||
%plot(ccohpd_,'k');
|
||||
%plot(supplot_, 'g');
|
||||
%plot(ones(length(mfeat),1)*rr1_, 'k');
|
||||
%plot(ones(length(mfeat),1)*rr2_, 'k');
|
||||
%plot(N*(1:length(feat)), feat);
|
||||
%plot(Sep_,'r');
|
||||
%axis([1 floor(length(erfb)/N) -1 1])
|
||||
%hold off
|
||||
%plot(10*log10([Se_, Sx_, Seu_, real(sf_.*conj(sf_))]));
|
||||
%legend('Se','Sx','Seu','S');
|
||||
%figure(5)
|
||||
%plot([ercn ercn_]);
|
||||
|
||||
figure(12)
|
||||
plot(t, dIdxV);
|
||||
%plot(t, SLxV);
|
||||
tightx;
|
||||
|
||||
%figure(13)
|
||||
%plot(t, [ekEnV dkEnV]);
|
||||
%plot(t, dkEnV./(ekEnV+1e-10));
|
||||
%tightx;
|
||||
|
||||
%close(hh);
|
||||
%spclab(fs,ssin,erfb,ercn,'outxd.pcm');
|
||||
%spclab(fs,rrin,ssin,erfb,1.78*ercn,'vqeOut-1.pcm');
|
||||
%spclab(fs,erfb,'aecOutLp.pcm');
|
||||
%spclab(fs,rrin,ssin,erfb,1.78*ercn,'aecOut25.pcm','vqeOut-1.pcm');
|
||||
%spclab(fs,rrin,ssin,erfb,ercn,'aecOut-mba.pcm');
|
||||
%spclab(fs,rrin,ssin,erfb,ercn,'aecOut.pcm');
|
||||
%spclab(fs, ssin, erfb, ercn, 'out0.pcm');
|
||||
54
src/modules/audio_processing/aec/main/source/Android.mk
Normal file
54
src/modules/audio_processing/aec/main/source/Android.mk
Normal 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.
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE_CLASS := STATIC_LIBRARIES
|
||||
LOCAL_MODULE := libwebrtc_aec
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_GENERATED_SOURCES :=
|
||||
LOCAL_SRC_FILES := \
|
||||
echo_cancellation.c \
|
||||
aec_core.c \
|
||||
aec_rdft.c \
|
||||
resampler.c
|
||||
|
||||
# Flags passed to both C and C++ files.
|
||||
MY_CFLAGS :=
|
||||
MY_CFLAGS_C :=
|
||||
MY_DEFS := '-DNO_TCMALLOC' \
|
||||
'-DNO_HEAPCHECKER' \
|
||||
'-DWEBRTC_TARGET_PC' \
|
||||
'-DWEBRTC_LINUX' \
|
||||
'-DWEBRTC_THREAD_RR' \
|
||||
'-DWEBRTC_ANDROID' \
|
||||
'-DANDROID'
|
||||
LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS)
|
||||
|
||||
# Include paths placed before CFLAGS/CPPFLAGS
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../../.. \
|
||||
$(LOCAL_PATH)/../interface \
|
||||
$(LOCAL_PATH)/../../../utility \
|
||||
$(LOCAL_PATH)/../../../../../common_audio/signal_processing_library/main/interface
|
||||
|
||||
# Flags passed to only C++ (and not C) files.
|
||||
LOCAL_CPPFLAGS :=
|
||||
|
||||
LOCAL_LDFLAGS :=
|
||||
|
||||
LOCAL_STATIC_LIBRARIES :=
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := libcutils \
|
||||
libdl \
|
||||
libstlport
|
||||
LOCAL_ADDITIONAL_DEPENDENCIES :=
|
||||
|
||||
include external/stlport/libstlport.mk
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
49
src/modules/audio_processing/aec/main/source/aec.gyp
Normal file
49
src/modules/audio_processing/aec/main/source/aec.gyp
Normal file
@@ -0,0 +1,49 @@
|
||||
# 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.
|
||||
|
||||
{
|
||||
'includes': [
|
||||
'../../../../../common_settings.gypi',
|
||||
],
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'aec',
|
||||
'type': '<(library)',
|
||||
'dependencies': [
|
||||
'../../../../../common_audio/signal_processing_library/main/source/spl.gyp:spl',
|
||||
'../../../utility/util.gyp:apm_util'
|
||||
],
|
||||
'include_dirs': [
|
||||
'../interface',
|
||||
],
|
||||
'direct_dependent_settings': {
|
||||
'include_dirs': [
|
||||
'../interface',
|
||||
],
|
||||
},
|
||||
'sources': [
|
||||
'../interface/echo_cancellation.h',
|
||||
'echo_cancellation.c',
|
||||
'aec_core.c',
|
||||
'aec_core_sse2.c',
|
||||
'aec_rdft.h',
|
||||
'aec_rdft.c',
|
||||
'aec_rdft_sse2.c',
|
||||
'aec_core.h',
|
||||
'resampler.c',
|
||||
'resampler.h',
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
# Local Variables:
|
||||
# tab-width:2
|
||||
# indent-tabs-mode:nil
|
||||
# End:
|
||||
# vim: set expandtab tabstop=2 shiftwidth=2:
|
||||
1456
src/modules/audio_processing/aec/main/source/aec_core.c
Normal file
1456
src/modules/audio_processing/aec/main/source/aec_core.c
Normal file
File diff suppressed because it is too large
Load Diff
193
src/modules/audio_processing/aec/main/source/aec_core.h
Normal file
193
src/modules/audio_processing/aec/main/source/aec_core.h
Normal file
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* 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 "typedefs.h"
|
||||
#include "signal_processing_library.h"
|
||||
|
||||
//#define G167 // for running G167 tests
|
||||
//#define UNCONSTR // time-unconstrained filter
|
||||
//#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
|
||||
|
||||
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;
|
||||
#ifdef FFTW
|
||||
float fftR[PART_LEN2];
|
||||
fftw_complex fftC[PART_LEN2];
|
||||
fftw_plan fftPlan, ifftPlan;
|
||||
|
||||
fftw_complex xfBuf[NR_PART * PART_LEN1];
|
||||
fftw_complex wfBuf[NR_PART * PART_LEN1];
|
||||
fftw_complex sde[PART_LEN1];
|
||||
#else
|
||||
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
|
||||
#endif
|
||||
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;
|
||||
|
||||
// Toggles for G.167 testing
|
||||
#ifdef G167
|
||||
short adaptToggle; // Filter adaptation
|
||||
short nlpToggle; // Nonlinear processing
|
||||
short cnToggle; // Comfort noise
|
||||
#endif
|
||||
|
||||
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
|
||||
|
||||
#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;
|
||||
#define IP_LEN PART_LEN // this must be at least ceil(2 + sqrt(PART_LEN))
|
||||
#define W_LEN PART_LEN
|
||||
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_
|
||||
|
||||
435
src/modules/audio_processing/aec/main/source/aec_core_sse2.c
Normal file
435
src/modules/audio_processing/aec/main/source/aec_core_sse2.c
Normal file
@@ -0,0 +1,435 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#if defined(__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;
|
||||
}
|
||||
|
||||
#ifdef UNCONSTR
|
||||
for (j = 0; j < PART_LEN1; j++) {
|
||||
aec->wfBuf[pos + j][0] += MulRe(aec->xfBuf[xPos + j][0],
|
||||
-aec->xfBuf[xPos + j][1],
|
||||
ef[j][0], ef[j][1]);
|
||||
aec->wfBuf[pos + j][1] += MulIm(aec->xfBuf[xPos + j][0],
|
||||
-aec->xfBuf[xPos + j][1],
|
||||
ef[j][0], ef[j][1]);
|
||||
}
|
||||
#else
|
||||
// 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;
|
||||
}
|
||||
#endif // UNCONSTR
|
||||
}
|
||||
}
|
||||
|
||||
#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
|
||||
|
||||
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 = (__m128)_mm_srli_epi32((__m128i)two_n,
|
||||
shift_exponent_into_top_mantissa);
|
||||
const __m128 n_0 = _mm_or_ps(
|
||||
(__m128)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 = (__m128)_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 //__SSE2__
|
||||
522
src/modules/audio_processing/aec/main/source/aec_rdft.c
Normal file
522
src/modules/audio_processing/aec/main/source/aec_rdft.c
Normal file
@@ -0,0 +1,522 @@
|
||||
/*
|
||||
* 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 <math.h>
|
||||
|
||||
#include "aec_rdft.h"
|
||||
#include "system_wrappers/interface/cpu_features_wrapper.h"
|
||||
|
||||
float rdft_w[64];
|
||||
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() {
|
||||
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);
|
||||
}
|
||||
|
||||
static void makect_32() {
|
||||
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(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];
|
||||
wk2i = rdft_w[k1 + 1];
|
||||
wk1r = rdft_w[k2];
|
||||
wk1i = rdft_w[k2 + 1];
|
||||
wk3r = wk1r - 2 * wk2i * wk1i;
|
||||
wk3i = 2 * wk2i * wk1r - wk1i;
|
||||
x0r = a[j] + a[j + 2];
|
||||
x0i = a[j + 1] + a[j + 3];
|
||||
x1r = a[j] - 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] = 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 = wk1r - 2 * wk2r * wk1i;
|
||||
wk3i = 2 * wk2r * wk1r - wk1i;
|
||||
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(int l, float *a) {
|
||||
const int n = 128;
|
||||
int j, j1, j2, j3, k, k1, k2, m, m2;
|
||||
float wk1r, wk1i, wk2r, wk2i, wk3r, wk3i;
|
||||
float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;
|
||||
|
||||
m = l << 2;
|
||||
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;
|
||||
}
|
||||
wk1r = rdft_w[2];
|
||||
for (j = m; j < l + m; 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] = x2i - x0i;
|
||||
a[j2 + 1] = x0r - x2r;
|
||||
x0r = x1r - x3i;
|
||||
x0i = x1i + x3r;
|
||||
a[j1] = wk1r * (x0r - x0i);
|
||||
a[j1 + 1] = wk1r * (x0r + x0i);
|
||||
x0r = x3i + x1r;
|
||||
x0i = x3r - x1i;
|
||||
a[j3] = 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];
|
||||
wk2i = rdft_w[k1 + 1];
|
||||
wk1r = rdft_w[k2];
|
||||
wk1i = rdft_w[k2 + 1];
|
||||
wk3r = wk1r - 2 * wk2i * wk1i;
|
||||
wk3i = 2 * wk2i * wk1r - wk1i;
|
||||
for (j = k; j < l + k; 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;
|
||||
x0r -= x2r;
|
||||
x0i -= x2i;
|
||||
a[j2] = wk2r * x0r - wk2i * x0i;
|
||||
a[j2 + 1] = wk2r * x0i + wk2i * x0r;
|
||||
x0r = x1r - x3i;
|
||||
x0i = x1i + x3r;
|
||||
a[j1] = wk1r * x0r - wk1i * x0i;
|
||||
a[j1 + 1] = wk1r * x0i + wk1i * x0r;
|
||||
x0r = x1r + x3i;
|
||||
x0i = x1i - x3r;
|
||||
a[j3] = wk3r * x0r - wk3i * x0i;
|
||||
a[j3 + 1] = wk3r * x0i + wk3i * x0r;
|
||||
}
|
||||
wk1r = rdft_w[k2 + 2];
|
||||
wk1i = rdft_w[k2 + 3];
|
||||
wk3r = wk1r - 2 * wk2r * wk1i;
|
||||
wk3i = 2 * wk2r * wk1r - wk1i;
|
||||
for (j = k + m; j < l + (k + m); 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;
|
||||
x0r -= x2r;
|
||||
x0i -= x2i;
|
||||
a[j2] = -wk2i * x0r - wk2r * x0i;
|
||||
a[j2 + 1] = -wk2i * x0i + wk2r * x0r;
|
||||
x0r = x1r - x3i;
|
||||
x0i = x1i + x3r;
|
||||
a[j1] = wk1r * x0r - wk1i * x0i;
|
||||
a[j1 + 1] = wk1r * x0i + wk1i * x0r;
|
||||
x0r = x1r + x3i;
|
||||
x0i = x1i - x3r;
|
||||
a[j3] = 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(8, 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(8, 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;
|
||||
int nw;
|
||||
float xi;
|
||||
|
||||
nw = ip[0];
|
||||
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;
|
||||
int nw;
|
||||
float xi;
|
||||
|
||||
nw = ip[0];
|
||||
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 rftfsub_128;
|
||||
rft_sub_128_t rftbsub_128;
|
||||
|
||||
void aec_rdft_init(void) {
|
||||
rftfsub_128 = rftfsub_128_C;
|
||||
rftbsub_128 = rftbsub_128_C;
|
||||
if (WebRtc_GetCPUInfo(kSSE2)) {
|
||||
#if defined(__SSE2__)
|
||||
aec_rdft_init_sse2();
|
||||
#endif
|
||||
}
|
||||
// init library constants.
|
||||
makewt_32();
|
||||
makect_32();
|
||||
}
|
||||
23
src/modules/audio_processing/aec/main/source/aec_rdft.h
Normal file
23
src/modules/audio_processing/aec/main/source/aec_rdft.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// constants shared by all paths (C, SSE2).
|
||||
extern float rdft_w[64];
|
||||
|
||||
// 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;
|
||||
|
||||
// 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);
|
||||
209
src/modules/audio_processing/aec/main/source/aec_rdft_sse2.c
Normal file
209
src/modules/audio_processing/aec/main/source/aec_rdft_sse2.c
Normal file
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* 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 <emmintrin.h>
|
||||
|
||||
#include "aec_rdft.h"
|
||||
|
||||
#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
|
||||
|
||||
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.
|
||||
// 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) {
|
||||
rftfsub_128 = rftfsub_128_SSE2;
|
||||
rftbsub_128 = rftbsub_128_SSE2;
|
||||
}
|
||||
821
src/modules/audio_processing/aec/main/source/echo_cancellation.c
Normal file
821
src/modules/audio_processing/aec/main/source/echo_cancellation.c
Normal file
@@ -0,0 +1,821 @@
|
||||
/*
|
||||
* 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "echo_cancellation.h"
|
||||
#include "aec_core.h"
|
||||
#include "ring_buffer.h"
|
||||
#include "resampler.h"
|
||||
#ifdef AEC_DEBUG
|
||||
#include <stdio.h>
|
||||
#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 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 initilisation 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;
|
||||
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
235
src/modules/audio_processing/aec/main/source/resampler.c
Normal file
235
src/modules/audio_processing/aec/main/source/resampler.c
Normal file
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* 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,
|
||||
const int size,
|
||||
const 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 yAvg = 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;
|
||||
yAvg = y / n;
|
||||
denom = x2 - xAvg*x;
|
||||
|
||||
if (denom != 0) {
|
||||
skew = (xy - xAvg*y) / denom;
|
||||
}
|
||||
|
||||
*skewEst = skew;
|
||||
return 0;
|
||||
}
|
||||
32
src/modules/audio_processing/aec/main/source/resampler.h
Normal file
32
src/modules/audio_processing/aec/main/source/resampler.h
Normal 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_
|
||||
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* 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
|
||||
* WebRtc_Word32 scSampFreq Soundcard sampling frequency
|
||||
*
|
||||
* Outputs Description
|
||||
* -------------------------------------------------------------------
|
||||
* WebRtc_Word32 return 0: OK
|
||||
* -1: error
|
||||
*/
|
||||
WebRtc_Word32 WebRtcAecm_Init(void* aecmInst,
|
||||
WebRtc_Word32 sampFreq,
|
||||
WebRtc_Word32 scSampFreq);
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
/*
|
||||
* 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_ */
|
||||
447
src/modules/audio_processing/aecm/main/matlab/compsup.m
Normal file
447
src/modules/audio_processing/aecm/main/matlab/compsup.m
Normal file
@@ -0,0 +1,447 @@
|
||||
function [emicrophone,aaa]=compsup(microphone,TheFarEnd,avtime,samplingfreq);
|
||||
% microphone = microphone signal
|
||||
% aaa = nonlinearity input variable
|
||||
% TheFarEnd = far end signal
|
||||
% avtime = interval to compute suppression from (seconds)
|
||||
% samplingfreq = sampling frequency
|
||||
|
||||
%if(nargin==6)
|
||||
% fprintf(1,'suppress has received a delay sequence\n');
|
||||
%end
|
||||
|
||||
|
||||
Ap500=[ 1.00, -4.95, 9.801, -9.70299, 4.80298005, -0.9509900499];
|
||||
Bp500=[ 0.662743088639636, -2.5841655608125, 3.77668102146288, -2.45182477425154, 0.596566274575251, 0.0];
|
||||
|
||||
|
||||
Ap200=[ 1.00, -4.875, 9.50625, -9.26859375, 4.518439453125, -0.881095693359375];
|
||||
Bp200=[ 0.862545460994275, -3.2832804496114, 4.67892032308828, -2.95798023879133, 0.699796870041299, 0.0];
|
||||
|
||||
maxDelay=0.4; %[s]
|
||||
histLen=1; %[s]
|
||||
|
||||
|
||||
% CONSTANTS THAT YOU CAN EXPERIMENT WITH
|
||||
A_GAIN=10.0; % for the suppress case
|
||||
oversampling = 2; % must be power of 2; minimum is 2; 4 works
|
||||
% fine for support=64, but for support=128,
|
||||
% 8 gives better results.
|
||||
support=64; %512 % fft support (frequency resolution; at low
|
||||
% settings you can hear more distortion
|
||||
% (e.g. pitch that is left-over from far-end))
|
||||
% 128 works well, 64 is ok)
|
||||
|
||||
lowlevel = mean(abs(microphone))*0.0001;
|
||||
|
||||
G_ol = 0; % Use overlapping sets of estimates
|
||||
|
||||
% ECHO SUPPRESSION SPECIFIC PARAMETERS
|
||||
suppress_overdrive=1.0; % overdrive factor for suppression 1.4 is good
|
||||
gamma_echo=1.0; % same as suppress_overdrive but at different place
|
||||
de_echo_bound=0.0;
|
||||
mLim=10; % rank of matrix G
|
||||
%limBW = 1; % use bandwidth-limited response for G
|
||||
if mLim > (support/2+1)
|
||||
error('mLim in suppress.m too large\n');
|
||||
end
|
||||
|
||||
|
||||
dynrange=1.0000e-004;
|
||||
|
||||
% other, constants
|
||||
hsupport = support/2;
|
||||
hsupport1 = hsupport+1;
|
||||
factor = 2 / oversampling;
|
||||
updatel = support/oversampling;
|
||||
win=sqrt(designwindow(0,support));
|
||||
estLen = round(avtime * samplingfreq/updatel)
|
||||
|
||||
runningfmean =0.0;
|
||||
|
||||
mLim = floor(hsupport1/2);
|
||||
V = sqrt(2/hsupport1)*cos(pi/hsupport1*(repmat((0:hsupport1-1) + 0.5, mLim, 1).* ...
|
||||
repmat((0:mLim-1)' + 0.5, 1, hsupport1)));
|
||||
|
||||
fprintf(1,'updatel is %5.3f s\n', updatel/samplingfreq);
|
||||
|
||||
|
||||
|
||||
bandfirst=8; bandlast=25;
|
||||
dosmooth=0; % to get rid of wavy bin counts (can be worse or better)
|
||||
|
||||
% compute some constants
|
||||
blockLen = support/oversampling;
|
||||
maxDelayb = floor(samplingfreq*maxDelay/updatel); % in blocks
|
||||
histLenb = floor(samplingfreq*histLen/updatel); % in blocks
|
||||
|
||||
x0=TheFarEnd;
|
||||
y0=microphone;
|
||||
|
||||
|
||||
%input
|
||||
tlength=min([length(microphone),length(TheFarEnd)]);
|
||||
updateno=floor(tlength/updatel);
|
||||
tlength=updatel*updateno;
|
||||
updateno = updateno - oversampling + 1;
|
||||
|
||||
TheFarEnd =TheFarEnd(1:tlength);
|
||||
microphone =microphone(1:tlength);
|
||||
|
||||
TheFarEnd =[zeros(hsupport,1);TheFarEnd(1:tlength)];
|
||||
microphone =[zeros(hsupport,1);microphone(1:tlength)];
|
||||
|
||||
|
||||
% signal length
|
||||
n = min([floor(length(x0)/support)*support,floor(length(y0)/support)*support]);
|
||||
nb = n/blockLen - oversampling + 1; % in blocks
|
||||
|
||||
% initialize space
|
||||
win = sqrt([0 ; hanning(support-1)]);
|
||||
sxAll2 = zeros(hsupport1,nb);
|
||||
syAll2 = zeros(hsupport1,nb);
|
||||
|
||||
z500=zeros(5,maxDelayb+1);
|
||||
z200=zeros(5,hsupport1);
|
||||
|
||||
bxspectrum=uint32(zeros(nb,1));
|
||||
bxhist=uint32(zeros(maxDelayb+1,1));
|
||||
byspectrum=uint32(zeros(nb,1));
|
||||
bcount=zeros(1+maxDelayb,nb);
|
||||
fcount=zeros(1+maxDelayb,nb);
|
||||
fout=zeros(1+maxDelayb,nb);
|
||||
delay=zeros(nb,1);
|
||||
tdelay=zeros(nb,1);
|
||||
nlgains=zeros(nb,1);
|
||||
|
||||
% create space (mainly for debugging)
|
||||
emicrophone=zeros(tlength,1);
|
||||
femicrophone=complex(zeros(hsupport1,updateno));
|
||||
thefilter=zeros(hsupport1,updateno);
|
||||
thelimiter=ones(hsupport1,updateno);
|
||||
fTheFarEnd=complex(zeros(hsupport1,updateno));
|
||||
afTheFarEnd=zeros(hsupport1,updateno);
|
||||
fmicrophone=complex(zeros(hsupport1,updateno));
|
||||
afmicrophone=zeros(hsupport1,updateno);
|
||||
|
||||
G = zeros(hsupport1, hsupport1);
|
||||
zerovec = zeros(hsupport1,1);
|
||||
zeromat = zeros(hsupport1);
|
||||
|
||||
% Reset sums
|
||||
mmxs_a = zerovec;
|
||||
mmys_a = zerovec;
|
||||
s2xs_a = zerovec;
|
||||
s2ys_a = zerovec;
|
||||
Rxxs_a = zeromat;
|
||||
Ryxs_a = zeromat;
|
||||
count_a = 1;
|
||||
|
||||
mmxs_b = zerovec;
|
||||
mmys_b = zerovec;
|
||||
s2xs_b = zerovec;
|
||||
s2ys_b = zerovec;
|
||||
Rxxs_b = zeromat;
|
||||
Ryxs_b = zeromat;
|
||||
count_b = 1;
|
||||
|
||||
nog=0;
|
||||
|
||||
aaa=zeros(size(TheFarEnd));
|
||||
|
||||
% loop over signal blocks
|
||||
fprintf(1,'.. Suppression; averaging G over %5.1f seconds; file length %5.1f seconds ..\n',avtime, length(microphone)/samplingfreq);
|
||||
fprintf(1,'.. SUPPRESSING ONLY AFTER %5.1f SECONDS! ..\n',avtime);
|
||||
fprintf(1,'.. 20 seconds is good ..\n');
|
||||
hh = waitbar_j(0,'Please wait...');
|
||||
|
||||
|
||||
for i=1:updateno
|
||||
|
||||
sb = (i-1)*updatel + 1;
|
||||
se=sb+support-1;
|
||||
|
||||
% analysis FFTs
|
||||
temp=fft(win .* TheFarEnd(sb:se));
|
||||
fTheFarEnd(:,i)=temp(1:hsupport1);
|
||||
xf=fTheFarEnd(:,i);
|
||||
afTheFarEnd(:,i)= abs(fTheFarEnd(:,i));
|
||||
|
||||
temp=win .* microphone(sb:se);
|
||||
|
||||
temp=fft(win .* microphone(sb:se));
|
||||
fmicrophone(:,i)=temp(1:hsupport1);
|
||||
yf=fmicrophone(:,i);
|
||||
afmicrophone(:,i)= abs(fmicrophone(:,i));
|
||||
|
||||
|
||||
ener_orig = afmicrophone(:,i)'*afmicrophone(:,i);
|
||||
if( ener_orig == 0)
|
||||
afmicrophone(:,i)=lowlevel*ones(size(afmicrophone(:,i)));
|
||||
end
|
||||
|
||||
|
||||
% use log domain (showed improved performance)
|
||||
xxf= sqrt(real(xf.*conj(xf))+1e-20);
|
||||
yyf= sqrt(real(yf.*conj(yf))+1e-20);
|
||||
sxAll2(:,i) = 20*log10(xxf);
|
||||
syAll2(:,i) = 20*log10(yyf);
|
||||
|
||||
mD=min(i-1,maxDelayb);
|
||||
xthreshold = sum(sxAll2(:,i-mD:i),2)/(maxDelayb+1);
|
||||
|
||||
[yout, z200] = filter(Bp200,Ap200,syAll2(:,i),z200,2);
|
||||
yout=yout/(maxDelayb+1);
|
||||
ythreshold = mean(syAll2(:,i-mD:i),2);
|
||||
|
||||
|
||||
bxspectrum(i)=getBspectrum(sxAll2(:,i),xthreshold,bandfirst,bandlast);
|
||||
byspectrum(i)=getBspectrum(syAll2(:,i),yout,bandfirst,bandlast);
|
||||
|
||||
bxhist(end-mD:end)=bxspectrum(i-mD:i);
|
||||
|
||||
bcount(:,i)=hisser2( ...
|
||||
byspectrum(i),flipud(bxhist),bandfirst,bandlast);
|
||||
|
||||
|
||||
[fout(:,i), z500] = filter(Bp500,Ap500,bcount(:,i),z500,2);
|
||||
fcount(:,i)=sum(bcount(:,max(1,i-histLenb+1):i),2); % using the history range
|
||||
fout(:,i)=round(fout(:,i));
|
||||
[value,delay(i)]=min(fout(:,i),[],1);
|
||||
tdelay(i)=(delay(i)-1)*support/(samplingfreq*oversampling);
|
||||
|
||||
% compensate
|
||||
|
||||
idel = max(i - delay(i) + 1,1);
|
||||
|
||||
|
||||
% echo suppression
|
||||
|
||||
noisyspec = afmicrophone(:,i);
|
||||
|
||||
% Estimate G using covariance matrices
|
||||
|
||||
% Cumulative estimates
|
||||
xx = afTheFarEnd(:,idel);
|
||||
yy = afmicrophone(:,i);
|
||||
|
||||
% Means
|
||||
mmxs_a = mmxs_a + xx;
|
||||
mmys_a = mmys_a + yy;
|
||||
if (G_ol)
|
||||
mmxs_b = mmxs_b + xx;
|
||||
mmys_b = mmys_b + yy;
|
||||
mmy = mean([mmys_a/count_a mmys_b/count_b],2);
|
||||
mmx = mean([mmxs_a/count_a mmxs_b/count_b],2);
|
||||
else
|
||||
mmx = mmxs_a/count_a;
|
||||
mmy = mmys_a/count_a;
|
||||
end
|
||||
count_a = count_a + 1;
|
||||
count_b = count_b + 1;
|
||||
|
||||
% Mean removal
|
||||
xxm = xx - mmx;
|
||||
yym = yy - mmy;
|
||||
|
||||
% Variances
|
||||
s2xs_a = s2xs_a + xxm .* xxm;
|
||||
s2ys_a = s2ys_a + yym .* yym;
|
||||
s2xs_b = s2xs_b + xxm .* xxm;
|
||||
s2ys_b = s2ys_b + yym .* yym;
|
||||
|
||||
% Correlation matrices
|
||||
Rxxs_a = Rxxs_a + xxm * xxm';
|
||||
Ryxs_a = Ryxs_a + yym * xxm';
|
||||
Rxxs_b = Rxxs_b + xxm * xxm';
|
||||
Ryxs_b = Ryxs_b + yym * xxm';
|
||||
|
||||
|
||||
% Gain matrix A
|
||||
|
||||
if mod(i, estLen) == 0
|
||||
|
||||
|
||||
% Cumulative based estimates
|
||||
Rxxf = Rxxs_a / (estLen - 1);
|
||||
Ryxf = Ryxs_a / (estLen - 1);
|
||||
|
||||
% Variance normalization
|
||||
s2x2 = s2xs_a / (estLen - 1);
|
||||
s2x2 = sqrt(s2x2);
|
||||
% Sx = diag(max(s2x2,dynrange*max(s2x2)));
|
||||
Sx = diag(s2x2);
|
||||
if (sum(s2x2) > 0)
|
||||
iSx = inv(Sx);
|
||||
else
|
||||
iSx= Sx + 0.01;
|
||||
end
|
||||
|
||||
s2y2 = s2ys_a / (estLen - 1);
|
||||
s2y2 = sqrt(s2y2);
|
||||
% Sy = diag(max(s2y2,dynrange*max(s2y2)));
|
||||
Sy = diag(s2y2);
|
||||
iSy = inv(Sy);
|
||||
rx = iSx * Rxxf * iSx;
|
||||
ryx = iSy * Ryxf * iSx;
|
||||
|
||||
|
||||
|
||||
dbd= 7; % Us less than the full matrix
|
||||
|
||||
% k x m
|
||||
% Bandlimited structure on G
|
||||
LSEon = 0; % Default is using MMSE
|
||||
if (LSEon)
|
||||
ryx = ryx*rx;
|
||||
rx = rx*rx;
|
||||
end
|
||||
p = dbd-1;
|
||||
gaj = min(min(hsupport1,2*p+1),min([p+(1:hsupport1); hsupport1+p+1-(1:hsupport1)]));
|
||||
cgaj = [0 cumsum(gaj)];
|
||||
|
||||
G3 = zeros(hsupport1);
|
||||
for kk=1:hsupport1
|
||||
ki = max(0,kk-p-1);
|
||||
if (sum(sum(rx(ki+1:ki+gaj(kk),ki+1:ki+gaj(kk))))>0)
|
||||
G3(kk,ki+1:ki+gaj(kk)) = ryx(kk,ki+1:ki+gaj(kk))/rx(ki+1:ki+gaj(kk),ki+1:ki+gaj(kk));
|
||||
else
|
||||
G3(kk,ki+1:ki+gaj(kk)) = ryx(kk,ki+1:ki+gaj(kk));
|
||||
end
|
||||
end
|
||||
% End Bandlimited structure
|
||||
|
||||
G = G3;
|
||||
G(abs(G)<0.01)=0;
|
||||
G = suppress_overdrive * Sy * G * iSx;
|
||||
|
||||
if 1
|
||||
figure(32); mi=2;
|
||||
surf(max(min(G,mi),-mi)); view(2)
|
||||
title('Unscaled Masked Limited-bandwidth G');
|
||||
end
|
||||
pause(0.05);
|
||||
|
||||
% Reset sums
|
||||
mmxs_a = zerovec;
|
||||
mmys_a = zerovec;
|
||||
s2xs_a = zerovec;
|
||||
s2ys_a = zerovec;
|
||||
Rxxs_a = zeromat;
|
||||
Ryxs_a = zeromat;
|
||||
count_a = 1;
|
||||
|
||||
end
|
||||
|
||||
if (G_ol)
|
||||
% Gain matrix B
|
||||
|
||||
if ((mod((i-estLen/2), estLen) == 0) & i>estLen)
|
||||
|
||||
|
||||
% Cumulative based estimates
|
||||
Rxxf = Rxxs_b / (estLen - 1);
|
||||
Ryxf = Ryxs_b / (estLen - 1);
|
||||
|
||||
% Variance normalization
|
||||
s2x2 = s2xs_b / (estLen - 1);
|
||||
s2x2 = sqrt(s2x2);
|
||||
Sx = diag(max(s2x2,dynrange*max(s2x2)));
|
||||
iSx = inv(Sx);
|
||||
s2y2 = s2ys_b / (estLen - 1);
|
||||
s2y2 = sqrt(s2y2);
|
||||
Sy = diag(max(s2y2,dynrange*max(s2y2)));
|
||||
iSy = inv(Sy);
|
||||
rx = iSx * Rxxf * iSx;
|
||||
ryx = iSy * Ryxf * iSx;
|
||||
|
||||
|
||||
% Bandlimited structure on G
|
||||
LSEon = 0; % Default is using MMSE
|
||||
if (LSEon)
|
||||
ryx = ryx*rx;
|
||||
rx = rx*rx;
|
||||
end
|
||||
p = dbd-1;
|
||||
gaj = min(min(hsupport1,2*p+1),min([p+(1:hsupport1); hsupport1+p+1-(1:hsupport1)]));
|
||||
cgaj = [0 cumsum(gaj)];
|
||||
|
||||
G3 = zeros(hsupport1);
|
||||
for kk=1:hsupport1
|
||||
ki = max(0,kk-p-1);
|
||||
G3(kk,ki+1:ki+gaj(kk)) = ryx(kk,ki+1:ki+gaj(kk))/rx(ki+1:ki+gaj(kk),ki+1:ki+gaj(kk));
|
||||
end
|
||||
% End Bandlimited structure
|
||||
|
||||
G = G3;
|
||||
G(abs(G)<0.01)=0;
|
||||
G = suppress_overdrive * Sy * G * iSx;
|
||||
|
||||
if 1
|
||||
figure(32); mi=2;
|
||||
surf(max(min(G,mi),-mi)); view(2)
|
||||
title('Unscaled Masked Limited-bandwidth G');
|
||||
end
|
||||
pause(0.05);
|
||||
|
||||
|
||||
% Reset sums
|
||||
mmxs_b = zerovec;
|
||||
mmys_b = zerovec;
|
||||
s2xs_b = zerovec;
|
||||
s2ys_b = zerovec;
|
||||
Rxxs_b = zeromat;
|
||||
Ryxs_b = zeromat;
|
||||
count_b = 1;
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
FECestimate2 = G*afTheFarEnd(:,idel);
|
||||
|
||||
% compute Wiener filter and suppressor function
|
||||
thefilter(:,i) = (noisyspec - gamma_echo*FECestimate2) ./ noisyspec;
|
||||
ix0 = find(thefilter(:,i)<de_echo_bound); % bounding trick 1
|
||||
thefilter(ix0,i) = de_echo_bound; % bounding trick 2
|
||||
ix0 = find(thefilter(:,i)>1); % bounding in reasonable range
|
||||
thefilter(ix0,i) = 1;
|
||||
|
||||
% NONLINEARITY
|
||||
nl_alpha=0.8; % memory; seems not very critical
|
||||
nlSeverity=0.3; % nonlinearity severity: 0 does nothing; 1 suppresses all
|
||||
thefmean=mean(thefilter(8:16,i));
|
||||
if (thefmean<1)
|
||||
disp('');
|
||||
end
|
||||
runningfmean = nl_alpha*runningfmean + (1-nl_alpha)*thefmean;
|
||||
aaa(sb+20+1:sb+20+updatel)=10000*runningfmean* ones(updatel,1); % debug
|
||||
slope0=1.0/(1.0-nlSeverity); %
|
||||
thegain = max(0.0,min(1.0,slope0*(runningfmean-nlSeverity)));
|
||||
% END NONLINEARITY
|
||||
thefilter(:,i) = thegain*thefilter(:,i);
|
||||
|
||||
|
||||
% Wiener filtering
|
||||
femicrophone(:,i) = fmicrophone(:,i) .* thefilter(:,i);
|
||||
thelimiter(:,i) = (noisyspec - A_GAIN*FECestimate2) ./ noisyspec;
|
||||
index = find(thelimiter(:,i)>1.0);
|
||||
thelimiter(index,i) = 1.0;
|
||||
index = find(thelimiter(:,i)<0.0);
|
||||
thelimiter(index,i) = 0.0;
|
||||
|
||||
if (rem(i,floor(updateno/20))==0)
|
||||
fprintf(1,'.');
|
||||
end
|
||||
if mod(i,50)==0
|
||||
waitbar_j(i/updateno,hh);
|
||||
end
|
||||
|
||||
|
||||
% reconstruction; first make spectrum odd
|
||||
temp=[femicrophone(:,i);flipud(conj(femicrophone(2:hsupport,i)))];
|
||||
emicrophone(sb:se) = emicrophone(sb:se) + factor * win .* real(ifft(temp));
|
||||
|
||||
end
|
||||
fprintf(1,'\n');
|
||||
|
||||
close(hh);
|
||||
22
src/modules/audio_processing/aecm/main/matlab/getBspectrum.m
Normal file
22
src/modules/audio_processing/aecm/main/matlab/getBspectrum.m
Normal file
@@ -0,0 +1,22 @@
|
||||
function bspectrum=getBspectrum(ps,threshold,bandfirst,bandlast)
|
||||
% function bspectrum=getBspectrum(ps,threshold,bandfirst,bandlast)
|
||||
% compute binary spectrum using threshold spectrum as pivot
|
||||
% bspectrum = binary spectrum (binary)
|
||||
% ps=current power spectrum (float)
|
||||
% threshold=threshold spectrum (float)
|
||||
% bandfirst = first band considered
|
||||
% bandlast = last band considered
|
||||
|
||||
% initialization stuff
|
||||
if( length(ps)<bandlast | bandlast>32 | length(ps)~=length(threshold))
|
||||
error('BinDelayEst:spectrum:invalid','Dimensionality error');
|
||||
end
|
||||
|
||||
% get current binary spectrum
|
||||
diff = ps - threshold;
|
||||
bspectrum=uint32(0);
|
||||
for(i=bandfirst:bandlast)
|
||||
if( diff(i)>0 )
|
||||
bspectrum = bitset(bspectrum,i);
|
||||
end
|
||||
end
|
||||
21
src/modules/audio_processing/aecm/main/matlab/hisser2.m
Normal file
21
src/modules/audio_processing/aecm/main/matlab/hisser2.m
Normal file
@@ -0,0 +1,21 @@
|
||||
function bcount=hisser2(bs,bsr,bandfirst,bandlast)
|
||||
% function bcount=hisser(bspectrum,bandfirst,bandlast)
|
||||
% histogram for the binary spectra
|
||||
% bcount= array of bit counts
|
||||
% bs=binary spectrum (one int32 number each)
|
||||
% bsr=reference binary spectra (one int32 number each)
|
||||
% blockSize = histogram over blocksize blocks
|
||||
% bandfirst = first band considered
|
||||
% bandlast = last band considered
|
||||
|
||||
% weight all delays equally
|
||||
maxDelay = length(bsr);
|
||||
|
||||
% compute counts (two methods; the first works better and is operational)
|
||||
bcount=zeros(maxDelay,1);
|
||||
for(i=1:maxDelay)
|
||||
% the delay should have low count for low-near&high-far and high-near&low-far
|
||||
bcount(i)= sum(bitget(bitxor(bs,bsr(i)),bandfirst:bandlast));
|
||||
% the delay should have low count for low-near&high-far (works less well)
|
||||
% bcount(i)= sum(bitget(bitand(bsr(i),bitxor(bs,bsr(i))),bandfirst:bandlast));
|
||||
end
|
||||
19
src/modules/audio_processing/aecm/main/matlab/main2.m
Normal file
19
src/modules/audio_processing/aecm/main/matlab/main2.m
Normal file
@@ -0,0 +1,19 @@
|
||||
|
||||
fid=fopen('aecfar.pcm'); far=fread(fid,'short'); fclose(fid);
|
||||
fid=fopen('aecnear.pcm'); mic=fread(fid,'short'); fclose(fid);
|
||||
|
||||
%fid=fopen('QA1far.pcm'); far=fread(fid,'short'); fclose(fid);
|
||||
%fid=fopen('QA1near.pcm'); mic=fread(fid,'short'); fclose(fid);
|
||||
|
||||
start=0 * 8000+1;
|
||||
stop= 30 * 8000;
|
||||
microphone=mic(start:stop);
|
||||
TheFarEnd=far(start:stop);
|
||||
avtime=1;
|
||||
|
||||
% 16000 to make it compatible with the C-version
|
||||
[emicrophone,tdel]=compsup(microphone,TheFarEnd,avtime,16000);
|
||||
|
||||
spclab(8000,TheFarEnd,microphone,emicrophone);
|
||||
|
||||
|
||||
269
src/modules/audio_processing/aecm/main/matlab/matlab/AECMobile.m
Normal file
269
src/modules/audio_processing/aecm/main/matlab/matlab/AECMobile.m
Normal file
@@ -0,0 +1,269 @@
|
||||
function [femicrophone, aecmStructNew, enerNear, enerFar] = AECMobile(fmicrophone, afTheFarEnd, setupStruct, aecmStruct)
|
||||
global NEARENDFFT;
|
||||
global F;
|
||||
|
||||
aecmStructNew = aecmStruct;
|
||||
|
||||
% Magnitude spectrum of near end signal
|
||||
afmicrophone = abs(fmicrophone);
|
||||
%afmicrophone = NEARENDFFT(setupStruct.currentBlock,:)'/2^F(setupStruct.currentBlock,end);
|
||||
% Near end energy level
|
||||
ener_orig = afmicrophone'*afmicrophone;
|
||||
if( ener_orig == 0)
|
||||
lowlevel = 0.01;
|
||||
afmicrophone = lowlevel*ones(size(afmicrophone));
|
||||
end
|
||||
%adiff = max(abs(afmicrophone - afTheFarEnd));
|
||||
%if (adiff > 0)
|
||||
% disp([setupStruct.currentBlock adiff])
|
||||
%end
|
||||
|
||||
% Store the near end energy
|
||||
%aecmStructNew.enerNear(setupStruct.currentBlock) = log(afmicrophone'*afmicrophone);
|
||||
aecmStructNew.enerNear(setupStruct.currentBlock) = log(sum(afmicrophone));
|
||||
% Store the far end energy
|
||||
%aecmStructNew.enerFar(setupStruct.currentBlock) = log(afTheFarEnd'*afTheFarEnd);
|
||||
aecmStructNew.enerFar(setupStruct.currentBlock) = log(sum(afTheFarEnd));
|
||||
|
||||
% Update subbands (We currently use all frequency bins, hence .useSubBand is turned off)
|
||||
if aecmStructNew.useSubBand
|
||||
internalIndex = 1;
|
||||
for kk=1:setupStruct.subBandLength+1
|
||||
ySubBand(kk) = mean(afmicrophone(internalIndex:internalIndex+setupStruct.numInBand(kk)-1).^aecmStructNew.bandFactor);
|
||||
xSubBand(kk) = mean(afTheFarEnd(internalIndex:internalIndex+setupStruct.numInBand(kk)-1).^aecmStructNew.bandFactor);
|
||||
internalIndex = internalIndex + setupStruct.numInBand(kk);
|
||||
end
|
||||
else
|
||||
ySubBand = afmicrophone.^aecmStructNew.bandFactor;
|
||||
xSubBand = afTheFarEnd.^aecmStructNew.bandFactor;
|
||||
end
|
||||
|
||||
% Estimated echo energy
|
||||
if (aecmStructNew.bandFactor == 1)
|
||||
%aecmStructNew.enerEcho(setupStruct.currentBlock) = log((aecmStructNew.H.*xSubBand)'*(aecmStructNew.H.*xSubBand));
|
||||
%aecmStructNew.enerEchoStored(setupStruct.currentBlock) = log((aecmStructNew.HStored.*xSubBand)'*(aecmStructNew.HStored.*xSubBand));
|
||||
aecmStructNew.enerEcho(setupStruct.currentBlock) = log(sum(aecmStructNew.H.*xSubBand));
|
||||
aecmStructNew.enerEchoStored(setupStruct.currentBlock) = log(sum(aecmStructNew.HStored.*xSubBand));
|
||||
elseif (aecmStructNew.bandFactor == 2)
|
||||
aecmStructNew.enerEcho(setupStruct.currentBlock) = log(aecmStructNew.H'*xSubBand);
|
||||
aecmStructNew.enerEchoStored(setupStruct.currentBlock) = log(aecmStructNew.HStored'*xSubBand);
|
||||
end
|
||||
|
||||
% Last 100 blocks of data, used for plotting
|
||||
n100 = max(1,setupStruct.currentBlock-99):setupStruct.currentBlock;
|
||||
enerError = aecmStructNew.enerNear(n100)-aecmStructNew.enerEcho(n100);
|
||||
enerErrorStored = aecmStructNew.enerNear(n100)-aecmStructNew.enerEchoStored(n100);
|
||||
|
||||
% Store the far end sub band. This is needed if we use LSE instead of NLMS
|
||||
aecmStructNew.X = [xSubBand aecmStructNew.X(:,1:end-1)];
|
||||
|
||||
% Update energy levels, which control the VAD
|
||||
if ((aecmStructNew.enerFar(setupStruct.currentBlock) < aecmStructNew.energyMin) & (aecmStructNew.enerFar(setupStruct.currentBlock) >= aecmStruct.FAR_ENERGY_MIN))
|
||||
aecmStructNew.energyMin = aecmStructNew.enerFar(setupStruct.currentBlock);
|
||||
%aecmStructNew.energyMin = max(aecmStructNew.energyMin,12);
|
||||
aecmStructNew.energyMin = max(aecmStructNew.energyMin,aecmStruct.FAR_ENERGY_MIN);
|
||||
aecmStructNew.energyLevel = (aecmStructNew.energyMax-aecmStructNew.energyMin)*aecmStructNew.energyThres+aecmStructNew.energyMin;
|
||||
aecmStructNew.energyLevelMSE = (aecmStructNew.energyMax-aecmStructNew.energyMin)*aecmStructNew.energyThresMSE+aecmStructNew.energyMin;
|
||||
end
|
||||
if (aecmStructNew.enerFar(setupStruct.currentBlock) > aecmStructNew.energyMax)
|
||||
aecmStructNew.energyMax = aecmStructNew.enerFar(setupStruct.currentBlock);
|
||||
aecmStructNew.energyLevel = (aecmStructNew.energyMax-aecmStructNew.energyMin)*aecmStructNew.energyThres+aecmStructNew.energyMin;
|
||||
aecmStructNew.energyLevelMSE = (aecmStructNew.energyMax-aecmStructNew.energyMin)*aecmStructNew.energyThresMSE+aecmStructNew.energyMin;
|
||||
end
|
||||
|
||||
% Calculate current energy error in near end (estimated echo vs. near end)
|
||||
dE = aecmStructNew.enerNear(setupStruct.currentBlock)-aecmStructNew.enerEcho(setupStruct.currentBlock);
|
||||
|
||||
%%%%%%%%
|
||||
% Calculate step size used in LMS algorithm, based on current far end energy and near end energy error (dE)
|
||||
%%%%%%%%
|
||||
if setupStruct.stepSize_flag
|
||||
[mu, aecmStructNew] = calcStepSize(aecmStructNew.enerFar(setupStruct.currentBlock), dE, aecmStructNew, setupStruct.currentBlock, 1);
|
||||
else
|
||||
mu = 0.25;
|
||||
end
|
||||
aecmStructNew.muLog(setupStruct.currentBlock) = mu; % Store the step size
|
||||
|
||||
% Estimate Echo Spectral Shape
|
||||
[U, aecmStructNew.H] = fallerEstimator(ySubBand,aecmStructNew.X,aecmStructNew.H,mu);
|
||||
|
||||
%%%%%
|
||||
% Determine if we should store or restore the channel
|
||||
%%%%%
|
||||
if ((setupStruct.currentBlock <= aecmStructNew.convLength) | (~setupStruct.channelUpdate_flag))
|
||||
aecmStructNew.HStored = aecmStructNew.H; % Store what you have after startup
|
||||
elseif ((setupStruct.currentBlock > aecmStructNew.convLength) & (setupStruct.channelUpdate_flag))
|
||||
if ((aecmStructNew.enerFar(setupStruct.currentBlock) < aecmStructNew.energyLevelMSE) & (aecmStructNew.enerFar(setupStruct.currentBlock-1) >= aecmStructNew.energyLevelMSE))
|
||||
xxx = aecmStructNew.countMseH;
|
||||
if (xxx > 20)
|
||||
mseStored = mean(abs(aecmStructNew.enerEchoStored(setupStruct.currentBlock-xxx:setupStruct.currentBlock-1)-aecmStructNew.enerNear(setupStruct.currentBlock-xxx:setupStruct.currentBlock-1)));
|
||||
mseLatest = mean(abs(aecmStructNew.enerEcho(setupStruct.currentBlock-xxx:setupStruct.currentBlock-1)-aecmStructNew.enerNear(setupStruct.currentBlock-xxx:setupStruct.currentBlock-1)));
|
||||
%fprintf('Stored: %4f Latest: %4f\n', mseStored, mseLatest) % Uncomment if you want to display the MSE values
|
||||
if ((mseStored < 0.8*mseLatest) & (aecmStructNew.mseHStoredOld < 0.8*aecmStructNew.mseHLatestOld))
|
||||
aecmStructNew.H = aecmStructNew.HStored;
|
||||
fprintf('Restored H at block %d\n',setupStruct.currentBlock)
|
||||
elseif (((0.8*mseStored > mseLatest) & (mseLatest < aecmStructNew.mseHThreshold) & (aecmStructNew.mseHLatestOld < aecmStructNew.mseHThreshold)) | (mseStored == Inf))
|
||||
aecmStructNew.HStored = aecmStructNew.H;
|
||||
fprintf('Stored new H at block %d\n',setupStruct.currentBlock)
|
||||
end
|
||||
aecmStructNew.mseHStoredOld = mseStored;
|
||||
aecmStructNew.mseHLatestOld = mseLatest;
|
||||
end
|
||||
elseif ((aecmStructNew.enerFar(setupStruct.currentBlock) >= aecmStructNew.energyLevelMSE) & (aecmStructNew.enerFar(setupStruct.currentBlock-1) < aecmStructNew.energyLevelMSE))
|
||||
aecmStructNew.countMseH = 1;
|
||||
elseif (aecmStructNew.enerFar(setupStruct.currentBlock) >= aecmStructNew.energyLevelMSE)
|
||||
aecmStructNew.countMseH = aecmStructNew.countMseH + 1;
|
||||
end
|
||||
end
|
||||
|
||||
%%%%%
|
||||
% Check delay (calculate the delay offset (if we can))
|
||||
% The algorithm is not tuned and should be used with care. It runs separately from Bastiaan's algorithm.
|
||||
%%%%%
|
||||
yyy = 31; % Correlation buffer length (currently unfortunately hard coded)
|
||||
dxxx = 25; % Maximum offset (currently unfortunately hard coded)
|
||||
if (setupStruct.currentBlock > aecmStructNew.convLength)
|
||||
if (aecmStructNew.enerFar(setupStruct.currentBlock-(yyy+2*dxxx-1):setupStruct.currentBlock) > aecmStructNew.energyLevelMSE)
|
||||
for xxx = -dxxx:dxxx
|
||||
aecmStructNew.delayLatestS(xxx+dxxx+1) = sum(sign(aecmStructNew.enerEcho(setupStruct.currentBlock-(yyy+dxxx-xxx)+1:setupStruct.currentBlock+xxx-dxxx)-mean(aecmStructNew.enerEcho(setupStruct.currentBlock-(yyy++dxxx-xxx)+1:setupStruct.currentBlock+xxx-dxxx))).*sign(aecmStructNew.enerNear(setupStruct.currentBlock-yyy-dxxx+1:setupStruct.currentBlock-dxxx)-mean(aecmStructNew.enerNear(setupStruct.currentBlock-yyy-dxxx+1:setupStruct.currentBlock-dxxx))));
|
||||
end
|
||||
aecmStructNew.newDelayCurve = 1;
|
||||
end
|
||||
end
|
||||
if ((setupStruct.currentBlock > 2*aecmStructNew.convLength) & ~rem(setupStruct.currentBlock,yyy*2) & aecmStructNew.newDelayCurve)
|
||||
[maxV,maxP] = max(aecmStructNew.delayLatestS);
|
||||
if ((maxP > 2) & (maxP < 2*dxxx))
|
||||
maxVLeft = aecmStructNew.delayLatestS(max(1,maxP-4));
|
||||
maxVRight = aecmStructNew.delayLatestS(min(2*dxxx+1,maxP+4));
|
||||
%fprintf('Max %d, Left %d, Right %d\n',maxV,maxVLeft,maxVRight) % Uncomment if you want to see max value
|
||||
if ((maxV > 24) & (maxVLeft < maxV - 10) & (maxVRight < maxV - 10))
|
||||
aecmStructNew.feedbackDelay = maxP-dxxx-1;
|
||||
aecmStructNew.newDelayCurve = 0;
|
||||
aecmStructNew.feedbackDelayUpdate = 1;
|
||||
fprintf('Feedback Update at block %d\n',setupStruct.currentBlock)
|
||||
end
|
||||
end
|
||||
end
|
||||
% End of "Check delay"
|
||||
%%%%%%%%
|
||||
|
||||
%%%%%
|
||||
% Calculate suppression gain, based on far end energy and near end energy error (dE)
|
||||
if (setupStruct.supGain_flag)
|
||||
[gamma_echo, aecmStructNew.cntIn, aecmStructNew.cntOut] = calcFilterGain(aecmStructNew.enerFar(setupStruct.currentBlock), dE, aecmStructNew, setupStruct.currentBlock, aecmStructNew.convLength, aecmStructNew.cntIn, aecmStructNew.cntOut);
|
||||
else
|
||||
gamma_echo = 1;
|
||||
end
|
||||
aecmStructNew.gammaLog(setupStruct.currentBlock) = gamma_echo; % Store the gain
|
||||
gamma_use = gamma_echo;
|
||||
|
||||
% Use the stored channel
|
||||
U = aecmStructNew.HStored.*xSubBand;
|
||||
|
||||
% compute Wiener filter and suppressor function
|
||||
Iy = find(ySubBand);
|
||||
subBandFilter = zeros(size(ySubBand));
|
||||
if (aecmStructNew.bandFactor == 2)
|
||||
subBandFilter(Iy) = (1 - gamma_use*sqrt(U(Iy)./ySubBand(Iy))); % For Faller
|
||||
else
|
||||
subBandFilter(Iy) = (1 - gamma_use*(U(Iy)./ySubBand(Iy))); % For COV
|
||||
end
|
||||
ix0 = find(subBandFilter < 0); % bounding trick 1
|
||||
subBandFilter(ix0) = 0;
|
||||
ix0 = find(subBandFilter > 1); % bounding trick 1
|
||||
subBandFilter(ix0) = 1;
|
||||
|
||||
% Interpolate back to normal frequency bins if we use sub bands
|
||||
if aecmStructNew.useSubBand
|
||||
thefilter = interp1(setupStruct.centerFreq,subBandFilter,linspace(0,setupStruct.samplingfreq/2,setupStruct.hsupport1)','nearest');
|
||||
testfilter = interp1(setupStruct.centerFreq,subBandFilter,linspace(0,setupStruct.samplingfreq/2,1000),'nearest');
|
||||
thefilter(end) = subBandFilter(end);
|
||||
|
||||
internalIndex = 1;
|
||||
for kk=1:setupStruct.subBandLength+1
|
||||
internalIndex:internalIndex+setupStruct.numInBand(kk)-1;
|
||||
thefilter(internalIndex:internalIndex+setupStruct.numInBand(kk)-1) = subBandFilter(kk);
|
||||
internalIndex = internalIndex + setupStruct.numInBand(kk);
|
||||
end
|
||||
else
|
||||
thefilter = subBandFilter;
|
||||
testfilter = subBandFilter;
|
||||
end
|
||||
|
||||
% Bound the filter
|
||||
ix0 = find(thefilter < setupStruct.de_echo_bound); % bounding trick 1
|
||||
thefilter(ix0) = setupStruct.de_echo_bound; % bounding trick 2
|
||||
ix0 = find(thefilter > 1); % bounding in reasonable range
|
||||
thefilter(ix0) = 1;
|
||||
|
||||
%%%%
|
||||
% NLP
|
||||
%%%%
|
||||
thefmean = mean(thefilter(8:16));
|
||||
if (thefmean < 1)
|
||||
disp('');
|
||||
end
|
||||
aecmStructNew.runningfmean = setupStruct.nl_alpha*aecmStructNew.runningfmean + (1-setupStruct.nl_alpha)*thefmean;
|
||||
slope0 = 1.0/(1.0 - setupStruct.nlSeverity); %
|
||||
thegain = max(0.0, min(1.0, slope0*(aecmStructNew.runningfmean - setupStruct.nlSeverity)));
|
||||
if ~setupStruct.nlp_flag
|
||||
thegain = 1;
|
||||
end
|
||||
% END NONLINEARITY
|
||||
thefilter = thegain*thefilter;
|
||||
|
||||
%%%%
|
||||
% The suppression
|
||||
%%%%
|
||||
femicrophone = fmicrophone .* thefilter;
|
||||
% Store the output energy (used for plotting)
|
||||
%aecmStructNew.enerOut(setupStruct.currentBlock) = log(abs(femicrophone)'*abs(femicrophone));
|
||||
aecmStructNew.enerOut(setupStruct.currentBlock) = log(sum(abs(femicrophone)));
|
||||
|
||||
if aecmStructNew.plotIt
|
||||
figure(13)
|
||||
subplot(311)
|
||||
%plot(n100,enerFar(n100),'b-',n100,enerNear(n100),'k--',n100,enerEcho(n100),'r-',[n100(1) n100(end)],[1 1]*vadThNew,'b:',[n100(1) n100(end)],[1 1]*((energyMax-energyMin)/4+energyMin),'r-.',[n100(1) n100(end)],[1 1]*vadNearThNew,'g:',[n100(1) n100(end)],[1 1]*energyMax,'r-.',[n100(1) n100(end)],[1 1]*energyMin,'r-.','LineWidth',2)
|
||||
plot(n100,aecmStructNew.enerFar(n100),'b-',n100,aecmStructNew.enerNear(n100),'k--',n100,aecmStructNew.enerOut(n100),'r-.',n100,aecmStructNew.enerEcho(n100),'r-',n100,aecmStructNew.enerEchoStored(n100),'c-',[n100(1) n100(end)],[1 1]*((aecmStructNew.energyMax-aecmStructNew.energyMin)/4+aecmStructNew.energyMin),'g-.',[n100(1) n100(end)],[1 1]*aecmStructNew.energyMax,'g-.',[n100(1) n100(end)],[1 1]*aecmStructNew.energyMin,'g-.','LineWidth',2)
|
||||
%title(['Frame ',int2str(i),' av ',int2str(setupStruct.updateno),' State = ',int2str(speechState),' \mu = ',num2str(mu)])
|
||||
title(['\gamma = ',num2str(gamma_echo),' \mu = ',num2str(mu)])
|
||||
subplot(312)
|
||||
%plot(n100,enerError,'b-',[n100(1) n100(end)],[1 1]*vadNearTh,'r:',[n100(1) n100(end)],[-1.5 -1.5]*vadNearTh,'r:','LineWidth',2)
|
||||
%plot(n100,enerError,'b-',[n100(1) n100(end)],[1 1],'r:',[n100(1) n100(end)],[-2 -2],'r:','LineWidth',2)
|
||||
plot(n100,enerError,'b-',n100,enerErrorStored,'c-',[n100(1) n100(end)],[1 1]*aecmStructNew.varMean,'k--',[n100(1) n100(end)],[1 1],'r:',[n100(1) n100(end)],[-2 -2],'r:','LineWidth',2)
|
||||
% Plot mu
|
||||
%plot(n100,log2(aecmStructNew.muLog(n100)),'b-','LineWidth',2)
|
||||
%plot(n100,log2(aecmStructNew.HGain(n100)),'b-',[n100(1) n100(end)],[1 1]*log2(sum(aecmStructNew.HStored)),'r:','LineWidth',2)
|
||||
title(['Block ',int2str(setupStruct.currentBlock),' av ',int2str(setupStruct.updateno)])
|
||||
subplot(313)
|
||||
%plot(n100,enerVar(n100),'b-',[n100(1) n100(end)],[1 1],'r:',[n100(1) n100(end)],[-2 -2],'r:','LineWidth',2)
|
||||
%plot(n100,enerVar(n100),'b-','LineWidth',2)
|
||||
% Plot correlation curve
|
||||
|
||||
%plot(-25:25,aecmStructNew.delayStored/max(aecmStructNew.delayStored),'c-',-25:25,aecmStructNew.delayLatest/max(aecmStructNew.delayLatest),'r-',-25:25,(max(aecmStructNew.delayStoredS)-aecmStructNew.delayStoredS)/(max(aecmStructNew.delayStoredS)-min(aecmStructNew.delayStoredS)),'c:',-25:25,(max(aecmStructNew.delayLatestS)-aecmStructNew.delayLatestS)/(max(aecmStructNew.delayLatestS)-min(aecmStructNew.delayLatestS)),'r:','LineWidth',2)
|
||||
%plot(-25:25,aecmStructNew.delayStored,'c-',-25:25,aecmStructNew.delayLatest,'r-',-25:25,(max(aecmStructNew.delayStoredS)-aecmStructNew.delayStoredS)/(max(aecmStructNew.delayStoredS)-min(aecmStructNew.delayStoredS)),'c:',-25:25,(max(aecmStructNew.delayLatestS)-aecmStructNew.delayLatestS)/(max(aecmStructNew.delayLatestS)-min(aecmStructNew.delayLatestS)),'r:','LineWidth',2)
|
||||
%plot(-25:25,aecmStructNew.delayLatest,'r-',-25:25,(50-aecmStructNew.delayLatestS)/100,'r:','LineWidth',2)
|
||||
plot(-25:25,aecmStructNew.delayLatestS,'r:','LineWidth',2)
|
||||
%plot(-25:25,aecmStructNew.delayStored,'c-',-25:25,aecmStructNew.delayLatest,'r-','LineWidth',2)
|
||||
plot(0:32,aecmStruct.HStored,'bo-','LineWidth',2)
|
||||
%title(['\gamma | In = ',int2str(aecmStructNew.muStruct.countInInterval),' | Out High = ',int2str(aecmStructNew.muStruct.countOutHighInterval),' | Out Low = ',int2str(aecmStructNew.muStruct.countOutLowInterval)])
|
||||
pause(1)
|
||||
%if ((setupStruct.currentBlock == 860) | (setupStruct.currentBlock == 420) | (setupStruct.currentBlock == 960))
|
||||
if 0%(setupStruct.currentBlock == 960)
|
||||
figure(60)
|
||||
plot(n100,aecmStructNew.enerNear(n100),'k--',n100,aecmStructNew.enerEcho(n100),'k:','LineWidth',2)
|
||||
legend('Near End','Estimated Echo')
|
||||
title('Signal Energy witH offset compensation')
|
||||
figure(61)
|
||||
subplot(211)
|
||||
stem(sign(aecmStructNew.enerNear(n100)-mean(aecmStructNew.enerNear(n100))))
|
||||
title('Near End Energy Pattern (around mean value)')
|
||||
subplot(212)
|
||||
stem(sign(aecmStructNew.enerEcho(n100)-mean(aecmStructNew.enerEcho(n100))))
|
||||
title('Estimated Echo Energy Pattern (around mean value)')
|
||||
pause
|
||||
end
|
||||
drawnow%,pause
|
||||
elseif ~rem(setupStruct.currentBlock,100)
|
||||
fprintf('Block %d of %d\n',setupStruct.currentBlock,setupStruct.updateno)
|
||||
end
|
||||
98
src/modules/audio_processing/aecm/main/matlab/matlab/align.m
Normal file
98
src/modules/audio_processing/aecm/main/matlab/matlab/align.m
Normal file
@@ -0,0 +1,98 @@
|
||||
function [delayStructNew] = align(xf, yf, delayStruct, i, trueDelay);
|
||||
|
||||
%%%%%%%
|
||||
% Bastiaan's algorithm copied
|
||||
%%%%%%%
|
||||
Ap500 = [1.00, -4.95, 9.801, -9.70299, 4.80298005, -0.9509900499];
|
||||
Bp500 = [0.662743088639636, -2.5841655608125, 3.77668102146288, -2.45182477425154, 0.596566274575251, 0.0];
|
||||
Ap200 = [1.00, -4.875, 9.50625, -9.26859375, 4.518439453125, -0.881095693359375];
|
||||
Bp200 = [0.862545460994275, -3.2832804496114, 4.67892032308828, -2.95798023879133, 0.699796870041299, 0.0];
|
||||
|
||||
oldMethod = 1; % Turn on or off the old method. The new one is Bastiaan's August 2008 updates
|
||||
THReSHoLD = 2.0; % ADJUSTABLE threshold factor; 4.0 seems good
|
||||
%%%%%%%%%%%%%%%%%%%
|
||||
% use log domain (showed improved performance)
|
||||
xxf = sqrt(real(xf.*conj(xf))+1e-20);
|
||||
yyf = sqrt(real(yf.*conj(yf))+1e-20);
|
||||
delayStruct.sxAll2(:,i) = 20*log10(xxf);
|
||||
delayStruct.syAll2(:,i) = 20*log10(yyf);
|
||||
|
||||
mD = min(i-1,delayStruct.maxDelayb);
|
||||
if oldMethod
|
||||
factor = 1.0;
|
||||
histLenb = 250;
|
||||
xthreshold = factor*median(delayStruct.sxAll2(:,i-mD:i),2);
|
||||
ythreshold = factor*median(delayStruct.syAll2(:,i-mD:i),2);
|
||||
else
|
||||
xthreshold = sum(delayStruct.sxAll2(:,i-mD:i),2)/(delayStruct.maxDelayb+1);
|
||||
|
||||
[yout, delayStruct.z200] = filter(Bp200, Ap200, delayStruct.syAll2(:,i), delayStruct.z200, 2);
|
||||
yout = yout/(delayStruct.maxDelayb+1);
|
||||
ythreshold = mean(delayStruct.syAll2(:,i-mD:i),2);
|
||||
ythreshold = yout;
|
||||
end
|
||||
|
||||
delayStruct.bxspectrum(i) = getBspectrum(delayStruct.sxAll2(:,i), xthreshold, delayStruct.bandfirst, delayStruct.bandlast);
|
||||
delayStruct.byspectrum(i) = getBspectrum(delayStruct.syAll2(:,i), ythreshold, delayStruct.bandfirst, delayStruct.bandlast);
|
||||
|
||||
delayStruct.bxhist(end-mD:end) = delayStruct.bxspectrum(i-mD:i);
|
||||
|
||||
delayStruct.bcount(:,i) = hisser2(delayStruct.byspectrum(i), flipud(delayStruct.bxhist), delayStruct.bandfirst, delayStruct.bandlast);
|
||||
[delayStruct.fout(:,i), delayStruct.z500] = filter(Bp500, Ap500, delayStruct.bcount(:,i), delayStruct.z500, 2);
|
||||
if oldMethod
|
||||
%delayStruct.new(:,i) = sum(delayStruct.bcount(:,max(1,i-histLenb+1):i),2); % using the history range
|
||||
tmpVec = [delayStruct.fout(1,i)*ones(2,1); delayStruct.fout(:,i); delayStruct.fout(end,i)*ones(2,1)]; % using the history range
|
||||
tmpVec = filter(ones(1,5), 1, tmpVec);
|
||||
delayStruct.new(:,i) = tmpVec(5:end);
|
||||
%delayStruct.new(:,i) = delayStruct.fout(:,i); % using the history range
|
||||
else
|
||||
[delayStruct.fout(:,i), delayStruct.z500] = filter(Bp500, Ap500, delayStruct.bcount(:,i), delayStruct.z500, 2);
|
||||
% NEW CODE
|
||||
delayStruct.new(:,i) = filter([-1,-2,1,4,1,-2,-1], 1, delayStruct.fout(:,i)); %remv smth component
|
||||
delayStruct.new(1:end-3,i) = delayStruct.new(1+3:end,i);
|
||||
delayStruct.new(1:6,i) = 0.0;
|
||||
delayStruct.new(end-6:end,i) = 0.0; % ends are no good
|
||||
end
|
||||
[valuen, tempdelay] = min(delayStruct.new(:,i)); % find minimum
|
||||
if oldMethod
|
||||
threshold = valuen + (max(delayStruct.new(:,i)) - valuen)/4;
|
||||
thIndex = find(delayStruct.new(:,i) <= threshold);
|
||||
if (i > 1)
|
||||
delayDiff = abs(delayStruct.delay(i-1)-tempdelay+1);
|
||||
if (delayStruct.oneGoodEstimate & (max(diff(thIndex)) > 1) & (delayDiff < 10))
|
||||
% We consider this minimum to be significant, hence update the delay
|
||||
delayStruct.delay(i) = tempdelay;
|
||||
elseif (~delayStruct.oneGoodEstimate & (max(diff(thIndex)) > 1))
|
||||
delayStruct.delay(i) = tempdelay;
|
||||
if (i > histLenb)
|
||||
delayStruct.oneGoodEstimate = 1;
|
||||
end
|
||||
else
|
||||
delayStruct.delay(i) = delayStruct.delay(i-1);
|
||||
end
|
||||
else
|
||||
delayStruct.delay(i) = tempdelay;
|
||||
end
|
||||
else
|
||||
threshold = THReSHoLD*std(delayStruct.new(:,i)); % set updata threshold
|
||||
if ((-valuen > threshold) | (i < delayStruct.smlength)) % see if you want to update delay
|
||||
delayStruct.delay(i) = tempdelay;
|
||||
else
|
||||
delayStruct.delay(i) = delayStruct.delay(i-1);
|
||||
end
|
||||
% END NEW CODE
|
||||
end
|
||||
delayStructNew = delayStruct;
|
||||
|
||||
% administrative and plotting stuff
|
||||
if( 0)
|
||||
figure(10);
|
||||
plot([1:length(delayStructNew.new(:,i))],delayStructNew.new(:,i),trueDelay*[1 1],[min(delayStructNew.new(:,i)),max(delayStructNew.new(:,i))],'r',[1 length(delayStructNew.new(:,i))],threshold*[1 1],'r:', 'LineWidth',2);
|
||||
%plot([1:length(delayStructNew.bcount(:,i))],delayStructNew.bcount(:,i),trueDelay*[1 1],[min(delayStructNew.bcount(:,i)),max(delayStructNew.bcount(:,i))],'r','LineWidth',2);
|
||||
%plot([thedelay,thedelay],[min(fcount(:,i)),max(fcount(:,i))],'r');
|
||||
%title(sprintf('bin count and known delay at time %5.1f s\n',(i-1)*(support/(fs*oversampling))));
|
||||
title(delayStructNew.oneGoodEstimate)
|
||||
xlabel('delay in frames');
|
||||
%hold off;
|
||||
drawnow
|
||||
end
|
||||
@@ -0,0 +1,88 @@
|
||||
function [gam, cntIn2, cntOut2] = calcFilterGain(energy, dE, aecmStruct, t, T, cntIn, cntOut)
|
||||
|
||||
defaultLevel = 1.2;
|
||||
cntIn2 = cntIn;
|
||||
cntOut2 = cntOut;
|
||||
if (t < T)
|
||||
gam = 1;
|
||||
else
|
||||
dE1 = -5;
|
||||
dE2 = 1;
|
||||
gamMid = 0.2;
|
||||
gam = max(0,min((energy - aecmStruct.energyMin)/(aecmStruct.energyLevel - aecmStruct.energyMin), 1-(1-gamMid)*(aecmStruct.energyMax-energy)/(aecmStruct.energyMax-aecmStruct.energyLevel)));
|
||||
|
||||
dEOffset = -0.5;
|
||||
dEWidth = 1.5;
|
||||
%gam2 = max(1,2-((dE-dEOffset)/(dE2-dEOffset)).^2);
|
||||
gam2 = 1+(abs(dE-dEOffset)<(dE2-dEOffset));
|
||||
|
||||
gam = gam*gam2;
|
||||
|
||||
|
||||
if (energy < aecmStruct.energyLevel)
|
||||
gam = 0;
|
||||
else
|
||||
gam = defaultLevel;
|
||||
end
|
||||
dEVec = aecmStruct.enerNear(t-63:t)-aecmStruct.enerEcho(t-63:t);
|
||||
%dEVec = aecmStruct.enerNear(t-20:t)-aecmStruct.enerEcho(t-20:t);
|
||||
numCross = 0;
|
||||
currentState = 0;
|
||||
for ii=1:64
|
||||
if (currentState == 0)
|
||||
currentState = (dEVec(ii) > dE2) - (dEVec(ii) < -2);
|
||||
elseif ((currentState == 1) & (dEVec(ii) < -2))
|
||||
numCross = numCross + 1;
|
||||
currentState = -1;
|
||||
elseif ((currentState == -1) & (dEVec(ii) > dE2))
|
||||
numCross = numCross + 1;
|
||||
currentState = 1;
|
||||
end
|
||||
end
|
||||
gam = max(0, gam - numCross/25);
|
||||
gam = 1;
|
||||
|
||||
ener_A = 1;
|
||||
ener_B = 0.8;
|
||||
ener_C = aecmStruct.energyLevel + (aecmStruct.energyMax-aecmStruct.energyLevel)/5;
|
||||
dE_A = 4;%2;
|
||||
dE_B = 3.6;%1.8;
|
||||
dE_C = 0.9*dEWidth;
|
||||
dE_D = 1;
|
||||
timeFactorLength = 10;
|
||||
ddE = abs(dE-dEOffset);
|
||||
if (energy < aecmStruct.energyLevel)
|
||||
gam = 0;
|
||||
else
|
||||
gam = 1;
|
||||
gam2 = max(0, min(ener_B*(energy-aecmStruct.energyLevel)/(ener_C-aecmStruct.energyLevel), ener_B+(ener_A-ener_B)*(energy-ener_C)/(aecmStruct.energyMax-ener_C)));
|
||||
if (ddE < dEWidth)
|
||||
% Update counters
|
||||
cntIn2 = cntIn2 + 1;
|
||||
if (cntIn2 > 2)
|
||||
cntOut2 = 0;
|
||||
end
|
||||
gam3 = max(dE_D, min(dE_A-(dE_A-dE_B)*(ddE/dE_C), dE_D+(dE_B-dE_D)*(dEWidth-ddE)/(dEWidth-dE_C)));
|
||||
gam3 = dE_A;
|
||||
else
|
||||
% Update counters
|
||||
cntOut2 = cntOut2 + 1;
|
||||
if (cntOut2 > 2)
|
||||
cntIn2 = 0;
|
||||
end
|
||||
%gam2 = 1;
|
||||
gam3 = dE_D;
|
||||
end
|
||||
timeFactor = min(1, cntIn2/timeFactorLength);
|
||||
gam = gam*(1-timeFactor) + timeFactor*gam2*gam3;
|
||||
end
|
||||
%gam = gam/floor(numCross/2+1);
|
||||
end
|
||||
if isempty(gam)
|
||||
numCross
|
||||
timeFactor
|
||||
cntIn2
|
||||
cntOut2
|
||||
gam2
|
||||
gam3
|
||||
end
|
||||
@@ -0,0 +1,105 @@
|
||||
function [mu, aecmStructNew] = calcStepSize(energy, dE, aecmStruct, t, logscale)
|
||||
|
||||
if (nargin < 4)
|
||||
t = 1;
|
||||
logscale = 1;
|
||||
elseif (nargin == 4)
|
||||
logscale = 1;
|
||||
end
|
||||
T = aecmStruct.convLength;
|
||||
|
||||
if logscale
|
||||
currentMuMax = aecmStruct.MU_MIN + (aecmStruct.MU_MAX-aecmStruct.MU_MIN)*min(t,T)/T;
|
||||
if (aecmStruct.energyMin >= aecmStruct.energyMax)
|
||||
mu = aecmStruct.MU_MIN;
|
||||
else
|
||||
mu = (energy - aecmStruct.energyMin)/(aecmStruct.energyMax - aecmStruct.energyMin)*(currentMuMax-aecmStruct.MU_MIN) + aecmStruct.MU_MIN;
|
||||
end
|
||||
mu = 2^mu;
|
||||
if (energy < aecmStruct.energyLevel)
|
||||
mu = 0;
|
||||
end
|
||||
else
|
||||
muMin = 0;
|
||||
muMax = 0.5;
|
||||
currentMuMax = muMin + (muMax-muMin)*min(t,T)/T;
|
||||
if (aecmStruct.energyMin >= aecmStruct.energyMax)
|
||||
mu = muMin;
|
||||
else
|
||||
mu = (energy - aecmStruct.energyMin)/(aecmStruct.energyMax - aecmStruct.energyMin)*(currentMuMax-muMin) + muMin;
|
||||
end
|
||||
end
|
||||
dE2 = 1;
|
||||
dEOffset = -0.5;
|
||||
offBoost = 5;
|
||||
if (mu > 0)
|
||||
if (abs(dE-aecmStruct.ENERGY_DEV_OFFSET) > aecmStruct.ENERGY_DEV_TOL)
|
||||
aecmStruct.muStruct.countInInterval = 0;
|
||||
else
|
||||
aecmStruct.muStruct.countInInterval = aecmStruct.muStruct.countInInterval + 1;
|
||||
end
|
||||
if (dE < aecmStruct.ENERGY_DEV_OFFSET - aecmStruct.ENERGY_DEV_TOL)
|
||||
aecmStruct.muStruct.countOutLowInterval = aecmStruct.muStruct.countOutLowInterval + 1;
|
||||
else
|
||||
aecmStruct.muStruct.countOutLowInterval = 0;
|
||||
end
|
||||
if (dE > aecmStruct.ENERGY_DEV_OFFSET + aecmStruct.ENERGY_DEV_TOL)
|
||||
aecmStruct.muStruct.countOutHighInterval = aecmStruct.muStruct.countOutHighInterval + 1;
|
||||
else
|
||||
aecmStruct.muStruct.countOutHighInterval = 0;
|
||||
end
|
||||
end
|
||||
muVar = 2^min(-3,5/50*aecmStruct.muStruct.countInInterval-3);
|
||||
muOff = 2^max(offBoost,min(0,offBoost*(aecmStruct.muStruct.countOutLowInterval-aecmStruct.muStruct.minOutLowInterval)/(aecmStruct.muStruct.maxOutLowInterval-aecmStruct.muStruct.minOutLowInterval)));
|
||||
|
||||
muLow = 1/64;
|
||||
muVar = 1;
|
||||
if (t < 2*T)
|
||||
muDT = 1;
|
||||
muVar = 1;
|
||||
mdEVec = 0;
|
||||
numCross = 0;
|
||||
else
|
||||
muDT = min(1,max(muLow,1-(1-muLow)*(dE-aecmStruct.ENERGY_DEV_OFFSET)/aecmStruct.ENERGY_DEV_TOL));
|
||||
dEVec = aecmStruct.enerNear(t-63:t)-aecmStruct.enerEcho(t-63:t);
|
||||
%dEVec = aecmStruct.enerNear(t-20:t)-aecmStruct.enerEcho(t-20:t);
|
||||
numCross = 0;
|
||||
currentState = 0;
|
||||
for ii=1:64
|
||||
if (currentState == 0)
|
||||
currentState = (dEVec(ii) > dE2) - (dEVec(ii) < -2);
|
||||
elseif ((currentState == 1) & (dEVec(ii) < -2))
|
||||
numCross = numCross + 1;
|
||||
currentState = -1;
|
||||
elseif ((currentState == -1) & (dEVec(ii) > dE2))
|
||||
numCross = numCross + 1;
|
||||
currentState = 1;
|
||||
end
|
||||
end
|
||||
|
||||
%logicDEVec = (dEVec > dE2) - (dEVec < -2);
|
||||
%numCross = sum(abs(diff(logicDEVec)));
|
||||
%mdEVec = mean(abs(dEVec-dEOffset));
|
||||
%mdEVec = mean(abs(dEVec-mean(dEVec)));
|
||||
%mdEVec = max(dEVec)-min(dEVec);
|
||||
%if (mdEVec > 4)%1.5)
|
||||
% muVar = 0;
|
||||
%end
|
||||
muVar = 2^(-floor(numCross/2));
|
||||
muVar = 2^(-numCross);
|
||||
end
|
||||
%muVar = 1;
|
||||
|
||||
|
||||
% if (eStd > (dE2-dEOffset))
|
||||
% muVar = 1/8;
|
||||
% else
|
||||
% muVar = 1;
|
||||
% end
|
||||
|
||||
%mu = mu*muDT*muVar*muOff;
|
||||
mu = mu*muDT*muVar;
|
||||
mu = min(mu,0.25);
|
||||
aecmStructNew = aecmStruct;
|
||||
%aecmStructNew.varMean = mdEVec;
|
||||
aecmStructNew.varMean = numCross;
|
||||
@@ -0,0 +1,42 @@
|
||||
function [U, Hnew] = fallerEstimator(Y, X, H, mu)
|
||||
|
||||
% Near end signal is stacked frame by frame columnwise in matrix Y and far end in X
|
||||
%
|
||||
% Possible estimation procedures are
|
||||
% 1) LSE
|
||||
% 2) NLMS
|
||||
% 3) Separated numerator and denomerator filters
|
||||
regParam = 1;
|
||||
[numFreqs, numFrames] = size(Y);
|
||||
[numFreqs, Q] = size(X);
|
||||
U = zeros(numFreqs, 1);
|
||||
|
||||
if ((nargin == 3) | (nargin == 5))
|
||||
dtd = 0;
|
||||
end
|
||||
if (nargin == 4)
|
||||
dtd = H;
|
||||
end
|
||||
Emax = 7;
|
||||
dEH = Emax-sum(sum(H));
|
||||
nu = 2*mu;
|
||||
% if (nargin < 5)
|
||||
% H = zeros(numFreqs, Q);
|
||||
% for kk = 1:numFreqs
|
||||
% Xmatrix = hankel(X(kk,1:Q),X(kk,Q:end));
|
||||
% y = Y(kk,1:end-Q+1)';
|
||||
% H(kk,:) = (y'*Xmatrix')*inv(Xmatrix*Xmatrix'+regParam);
|
||||
% U(kk,1) = H(kk,:)*Xmatrix(:,1);
|
||||
% end
|
||||
% else
|
||||
for kk = 1:numFreqs
|
||||
x = X(kk,1:Q)';
|
||||
y = Y(kk,1);
|
||||
Htmp = mu*(y-H(kk,:)*x)/(x'*x+regParam)*x;
|
||||
%Htmp = (mu*(y-H(kk,:)*x)/(x'*x+regParam) - nu/dEH)*x;
|
||||
H(kk,:) = H(kk,:) + Htmp';
|
||||
U(kk,1) = H(kk,:)*x;
|
||||
end
|
||||
% end
|
||||
|
||||
Hnew = H;
|
||||
@@ -0,0 +1,22 @@
|
||||
function bspectrum=getBspectrum(ps,threshold,bandfirst,bandlast)
|
||||
% function bspectrum=getBspectrum(ps,threshold,bandfirst,bandlast)
|
||||
% compute binary spectrum using threshold spectrum as pivot
|
||||
% bspectrum = binary spectrum (binary)
|
||||
% ps=current power spectrum (float)
|
||||
% threshold=threshold spectrum (float)
|
||||
% bandfirst = first band considered
|
||||
% bandlast = last band considered
|
||||
|
||||
% initialization stuff
|
||||
if( length(ps)<bandlast | bandlast>32 | length(ps)~=length(threshold))
|
||||
error('BinDelayEst:spectrum:invalid','Dimensionality error');
|
||||
end
|
||||
|
||||
% get current binary spectrum
|
||||
diff = ps - threshold;
|
||||
bspectrum=uint32(0);
|
||||
for(i=bandfirst:bandlast)
|
||||
if( diff(i)>0 )
|
||||
bspectrum = bitset(bspectrum,i);
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,21 @@
|
||||
function bcount=hisser2(bs,bsr,bandfirst,bandlast)
|
||||
% function bcount=hisser(bspectrum,bandfirst,bandlast)
|
||||
% histogram for the binary spectra
|
||||
% bcount= array of bit counts
|
||||
% bs=binary spectrum (one int32 number each)
|
||||
% bsr=reference binary spectra (one int32 number each)
|
||||
% blockSize = histogram over blocksize blocks
|
||||
% bandfirst = first band considered
|
||||
% bandlast = last band considered
|
||||
|
||||
% weight all delays equally
|
||||
maxDelay = length(bsr);
|
||||
|
||||
% compute counts (two methods; the first works better and is operational)
|
||||
bcount=zeros(maxDelay,1);
|
||||
for(i=1:maxDelay)
|
||||
% the delay should have low count for low-near&high-far and high-near&low-far
|
||||
bcount(i)= sum(bitget(bitxor(bs,bsr(i)),bandfirst:bandlast));
|
||||
% the delay should have low count for low-near&high-far (works less well)
|
||||
% bcount(i)= sum(bitget(bitand(bsr(i),bitxor(bs,bsr(i))),bandfirst:bandlast));
|
||||
end
|
||||
@@ -0,0 +1,283 @@
|
||||
useHTC = 1; % Set this if you want to run a single file and set file names below. Otherwise use simEnvironment to run from several scenarios in a row
|
||||
delayCompensation_flag = 0; % Set this flag to one if you want to turn on the delay compensation/enhancement
|
||||
global FARENDFFT;
|
||||
global NEARENDFFT;
|
||||
global F;
|
||||
|
||||
if useHTC
|
||||
% fid=fopen('./htcTouchHd/nb/aecFar.pcm'); xFar=fread(fid,'short'); fclose(fid);
|
||||
% fid=fopen('./htcTouchHd/nb/aecNear.pcm'); yNear=fread(fid,'short'); fclose(fid);
|
||||
% fid=fopen('./samsungBlackjack/nb/aecFar.pcm'); xFar=fread(fid,'short'); fclose(fid);
|
||||
% fid=fopen('./samsungBlackjack/nb/aecNear.pcm'); yNear=fread(fid,'short'); fclose(fid);
|
||||
% fid=fopen('aecFarPoor.pcm'); xFar=fread(fid,'short'); fclose(fid);
|
||||
% fid=fopen('aecNearPoor.pcm'); yNear=fread(fid,'short'); fclose(fid);
|
||||
% fid=fopen('out_aes.pcm'); outAES=fread(fid,'short'); fclose(fid);
|
||||
fid=fopen('aecFar4.pcm'); xFar=fread(fid,'short'); fclose(fid);
|
||||
fid=fopen('aecNear4.pcm'); yNear=fread(fid,'short'); fclose(fid);
|
||||
yNearSpeech = zeros(size(xFar));
|
||||
fs = 8000;
|
||||
frameSize = 64;
|
||||
% frameSize = 128;
|
||||
fs = 16000;
|
||||
% frameSize = 256;
|
||||
%F = load('fftValues.txt');
|
||||
%FARENDFFT = F(:,1:33);
|
||||
%NEARENDFFT = F(:,34:66);
|
||||
|
||||
else
|
||||
loadFileFar = [speakerType, '_s_',scenario,'_far_b.wav'];
|
||||
[xFar,fs,nbits] = wavread(loadFileFar);
|
||||
xFar = xFar*2^(nbits-1);
|
||||
loadFileNear = [speakerType, '_s_',scenario,'_near_b.wav'];
|
||||
[yNear,fs,nbits] = wavread(loadFileNear);
|
||||
yNear = yNear*2^(nbits-1);
|
||||
loadFileNearSpeech = [speakerType, '_s_',scenario,'_nearSpeech_b.wav'];
|
||||
[yNearSpeech,fs,nbits] = wavread(loadFileNearSpeech);
|
||||
yNearSpeech = yNearSpeech*2^(nbits-1);
|
||||
frameSize = 256;
|
||||
end
|
||||
|
||||
dtRegions = [];
|
||||
|
||||
% General settings for the AECM
|
||||
setupStruct = struct(...
|
||||
'stepSize_flag', 1,... % This flag turns on the step size calculation. If turned off, mu = 0.25.
|
||||
'supGain_flag', 0,... % This flag turns on the suppression gain calculation. If turned off, gam = 1.
|
||||
'channelUpdate_flag', 0,... % This flag turns on the channel update. If turned off, H is updated for convLength and then kept constant.
|
||||
'nlp_flag', 0,... % Turn on/off NLP
|
||||
'withVAD_flag', 0,... % Turn on/off NLP
|
||||
'useSubBand', 0,... % Set to 1 if to use subBands
|
||||
'useDelayEstimation', 1,... % Set to 1 if to use delay estimation
|
||||
'support', frameSize,... % # of samples per frame
|
||||
'samplingfreq',fs,... % Sampling frequency
|
||||
'oversampling', 2,... % Overlap between blocks/frames
|
||||
'updatel', 0,... % # of samples between blocks
|
||||
'hsupport1', 0,... % # of bins in frequency domain
|
||||
'factor', 0,... % synthesis window amplification
|
||||
'tlength', 0,... % # of samples of entire file
|
||||
'updateno', 0,... % # of updates
|
||||
'nb', 1,... % # of blocks
|
||||
'currentBlock', 0,... %
|
||||
'win', zeros(frameSize,1),...% Window to apply for fft and synthesis
|
||||
'avtime', 1,... % Time (in sec.) to perform averaging
|
||||
'estLen', 0,... % Averaging in # of blocks
|
||||
'A_GAIN', 10.0,... %
|
||||
'suppress_overdrive', 1.0,... % overdrive factor for suppression 1.4 is good
|
||||
'gamma_echo', 1.0,... % same as suppress_overdrive but at different place
|
||||
'de_echo_bound', 0.0,... %
|
||||
'nl_alpha', 0.4,... % memory; seems not very critical
|
||||
'nlSeverity', 0.2,... % nonlinearity severity: 0 does nothing; 1 suppresses all
|
||||
'numInBand', [],... % # of frequency bins in resp. subBand
|
||||
'centerFreq', [],... % Center frequency of resp. subBand
|
||||
'dtRegions', dtRegions,... % Regions where we have DT
|
||||
'subBandLength', frameSize/2);%All bins
|
||||
%'subBandLength', 11); %Something's wrong when subBandLength even
|
||||
%'nl_alpha', 0.8,... % memory; seems not very critical
|
||||
|
||||
delayStruct = struct(...
|
||||
'bandfirst', 8,...
|
||||
'bandlast', 25,...
|
||||
'smlength', 600,...
|
||||
'maxDelay', 0.4,...
|
||||
'oneGoodEstimate', 0,...
|
||||
'delayAdjust', 0,...
|
||||
'maxDelayb', 0);
|
||||
% More parameters in delayStruct are constructed in "updateSettings" below
|
||||
|
||||
% Make struct settings
|
||||
[setupStruct, delayStruct] = updateSettings(yNear, xFar, setupStruct, delayStruct);
|
||||
setupStruct.numInBand = ones(setupStruct.hsupport1,1);
|
||||
|
||||
Q = 1; % Time diversity in channel
|
||||
% General settings for the step size calculation
|
||||
muStruct = struct(...
|
||||
'countInInterval', 0,...
|
||||
'countOutHighInterval', 0,...
|
||||
'countOutLowInterval', 0,...
|
||||
'minInInterval', 50,...
|
||||
'minOutHighInterval', 10,...
|
||||
'minOutLowInterval', 10,...
|
||||
'maxOutLowInterval', 50);
|
||||
% General settings for the AECM
|
||||
aecmStruct = struct(...
|
||||
'plotIt', 0,... % Set to 0 to turn off plotting
|
||||
'useSubBand', 0,...
|
||||
'bandFactor', 1,...
|
||||
'H', zeros(setupStruct.subBandLength+1,Q),...
|
||||
'HStored', zeros(setupStruct.subBandLength+1,Q),...
|
||||
'X', zeros(setupStruct.subBandLength+1,Q),...
|
||||
'energyThres', 0.28,...
|
||||
'energyThresMSE', 0.4,...
|
||||
'energyMin', inf,...
|
||||
'energyMax', -inf,...
|
||||
'energyLevel', 0,...
|
||||
'energyLevelMSE', 0,...
|
||||
'convLength', 100,...
|
||||
'gammaLog', ones(setupStruct.updateno,1),...
|
||||
'muLog', ones(setupStruct.updateno,1),...
|
||||
'enerFar', zeros(setupStruct.updateno,1),...
|
||||
'enerNear', zeros(setupStruct.updateno,1),...
|
||||
'enerEcho', zeros(setupStruct.updateno,1),...
|
||||
'enerEchoStored', zeros(setupStruct.updateno,1),...
|
||||
'enerOut', zeros(setupStruct.updateno,1),...
|
||||
'runningfmean', 0,...
|
||||
'muStruct', muStruct,...
|
||||
'varMean', 0,...
|
||||
'countMseH', 0,...
|
||||
'mseHThreshold', 1.1,...
|
||||
'mseHStoredOld', inf,...
|
||||
'mseHLatestOld', inf,...
|
||||
'delayLatestS', zeros(1,51),...
|
||||
'feedbackDelay', 0,...
|
||||
'feedbackDelayUpdate', 0,...
|
||||
'cntIn', 0,...
|
||||
'cntOut', 0,...
|
||||
'FAR_ENERGY_MIN', 1,...
|
||||
'ENERGY_DEV_OFFSET', 0.5,...
|
||||
'ENERGY_DEV_TOL', 1.5,...
|
||||
'MU_MIN', -16,...
|
||||
'MU_MAX', -2,...
|
||||
'newDelayCurve', 0);
|
||||
|
||||
% Adjust speech signals
|
||||
xFar = [zeros(setupStruct.hsupport1-1,1);xFar(1:setupStruct.tlength)];
|
||||
yNear = [zeros(setupStruct.hsupport1-1,1);yNear(1:setupStruct.tlength)];
|
||||
yNearSpeech = [zeros(setupStruct.hsupport1-1,1);yNearSpeech(1:setupStruct.tlength)];
|
||||
xFar = xFar(1:setupStruct.tlength);
|
||||
yNear = yNear(1:setupStruct.tlength);
|
||||
|
||||
% Set figure settings
|
||||
if aecmStruct.plotIt
|
||||
figure(13)
|
||||
set(gcf,'doublebuffer','on')
|
||||
end
|
||||
%%%%%%%%%%
|
||||
% Here starts the algorithm
|
||||
% Dividing into frames and then estimating the near end speech
|
||||
%%%%%%%%%%
|
||||
fTheFarEnd = complex(zeros(setupStruct.hsupport1,1));
|
||||
afTheFarEnd = zeros(setupStruct.hsupport1,setupStruct.updateno+1);
|
||||
fFar = zeros(setupStruct.hsupport1,setupStruct.updateno+1);
|
||||
fmicrophone = complex(zeros(setupStruct.hsupport1,1));
|
||||
afmicrophone = zeros(setupStruct.hsupport1,setupStruct.updateno+1);
|
||||
fNear = zeros(setupStruct.hsupport1,setupStruct.updateno+1);
|
||||
femicrophone = complex(zeros(setupStruct.hsupport1,1));
|
||||
emicrophone = zeros(setupStruct.tlength,1);
|
||||
|
||||
if (setupStruct.useDelayEstimation == 2)
|
||||
delSamples = [1641 1895 2032 1895 2311 2000 2350 2222 NaN 2332 2330 2290 2401 2415 NaN 2393 2305 2381 2398];
|
||||
delBlocks = round(delSamples/setupStruct.updatel);
|
||||
delStarts = floor([25138 46844 105991 169901 195739 218536 241803 333905 347703 362660 373753 745135 765887 788078 806257 823835 842443 860139 881869]/setupStruct.updatel);
|
||||
else
|
||||
delStarts = [];
|
||||
end
|
||||
|
||||
for i=1:setupStruct.updateno
|
||||
setupStruct.currentBlock = i;
|
||||
|
||||
sb = (i-1)*setupStruct.updatel + 1;
|
||||
se = sb + setupStruct.support - 1;
|
||||
|
||||
%%%%%%%
|
||||
% Analysis FFTs
|
||||
%%%%%%%
|
||||
% Far end signal
|
||||
temp = fft(setupStruct.win .* xFar(sb:se))/frameSize;
|
||||
fTheFarEnd = temp(1:setupStruct.hsupport1);
|
||||
afTheFarEnd(:,i) = abs(fTheFarEnd);
|
||||
fFar(:,i) = fTheFarEnd;
|
||||
% Near end signal
|
||||
temp = fft(setupStruct.win .* yNear(sb:se))/frameSize;%,pause
|
||||
fmicrophone = temp(1:setupStruct.hsupport1);
|
||||
afmicrophone(:,i) = abs(fmicrophone);
|
||||
fNear(:,i) = fmicrophone;
|
||||
%abs(fmicrophone),pause
|
||||
% The true near end speaker (if we have such info)
|
||||
temp = fft(setupStruct.win .* yNearSpeech(sb:se));
|
||||
aftrueSpeech = abs(temp(1:setupStruct.hsupport1));
|
||||
|
||||
if(i == 1000)
|
||||
%break;
|
||||
end
|
||||
|
||||
% Perform delay estimation
|
||||
if (setupStruct.useDelayEstimation == 1)
|
||||
% Delay Estimation
|
||||
delayStruct = align(fTheFarEnd, fmicrophone, delayStruct, i);
|
||||
%delayStruct.delay(i) = 39;%19;
|
||||
idel = max(i - delayStruct.delay(i) + 1,1);
|
||||
|
||||
if delayCompensation_flag
|
||||
% If we have a new delay estimate from Bastiaan's alg. update the offset
|
||||
if (delayStruct.delay(i) ~= delayStruct.delay(max(1,i-1)))
|
||||
delayStruct.delayAdjust = delayStruct.delayAdjust + delayStruct.delay(i) - delayStruct.delay(i-1);
|
||||
end
|
||||
% Store the compensated delay
|
||||
delayStruct.delayNew(i) = delayStruct.delay(i) - delayStruct.delayAdjust;
|
||||
if (delayStruct.delayNew(i) < 1)
|
||||
% Something's wrong
|
||||
pause,break
|
||||
end
|
||||
% Compensate with the offset estimate
|
||||
idel = idel + delayStruct.delayAdjust;
|
||||
end
|
||||
if 0%aecmStruct.plotIt
|
||||
figure(1)
|
||||
plot(1:i,delayStruct.delay(1:i),'k:',1:i,delayStruct.delayNew(1:i),'k--','LineWidth',2),drawnow
|
||||
end
|
||||
elseif (setupStruct.useDelayEstimation == 2)
|
||||
% Use "manual delay"
|
||||
delIndex = find(delStarts<i);
|
||||
if isempty(delIndex)
|
||||
idel = i;
|
||||
else
|
||||
idel = i - delBlocks(max(delIndex));
|
||||
if isnan(idel)
|
||||
idel = i - delBlocks(max(delIndex)-1);
|
||||
end
|
||||
end
|
||||
else
|
||||
% No delay estimation
|
||||
%idel = max(i - 18, 1);
|
||||
idel = max(i - 50, 1);
|
||||
end
|
||||
|
||||
%%%%%%%%
|
||||
% This is the AECM algorithm
|
||||
%
|
||||
% Output is the new frequency domain signal (hopefully) echo compensated
|
||||
%%%%%%%%
|
||||
[femicrophone, aecmStruct] = AECMobile(fmicrophone, afTheFarEnd(:,idel), setupStruct, aecmStruct);
|
||||
%[femicrophone, aecmStruct] = AECMobile(fmicrophone, FARENDFFT(idel,:)'/2^F(idel,end-1), setupStruct, aecmStruct);
|
||||
|
||||
if aecmStruct.feedbackDelayUpdate
|
||||
% If the feedback tells us there is a new offset out there update the enhancement
|
||||
delayStruct.delayAdjust = delayStruct.delayAdjust + aecmStruct.feedbackDelay;
|
||||
aecmStruct.feedbackDelayUpdate = 0;
|
||||
end
|
||||
|
||||
% reconstruction; first make spectrum odd
|
||||
temp = [femicrophone; flipud(conj(femicrophone(2:(setupStruct.hsupport1-1))))];
|
||||
emicrophone(sb:se) = emicrophone(sb:se) + setupStruct.factor * setupStruct.win .* real(ifft(temp))*frameSize;
|
||||
if max(isnan(emicrophone(sb:se)))
|
||||
% Something's wrong with the output at block i
|
||||
i
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if useHTC
|
||||
fid=fopen('aecOutMatlabC.pcm','w');fwrite(fid,int16(emicrophone),'short');fclose(fid);
|
||||
%fid=fopen('farendFFT.txt','w');fwrite(fid,int16(afTheFarEnd(:)),'short');fclose(fid);
|
||||
%fid=fopen('farendFFTreal.txt','w');fwrite(fid,int16(imag(fFar(:))),'short');fclose(fid);
|
||||
%fid=fopen('farendFFTimag.txt','w');fwrite(fid,int16(real(fFar(:))),'short');fclose(fid);
|
||||
%fid=fopen('nearendFFT.txt','w');fwrite(fid,int16(afmicrophone(:)),'short');fclose(fid);
|
||||
%fid=fopen('nearendFFTreal.txt','w');fwrite(fid,int16(real(fNear(:))),'short');fclose(fid);
|
||||
%fid=fopen('nearendFFTimag.txt','w');fwrite(fid,int16(imag(fNear(:))),'short');fclose(fid);
|
||||
end
|
||||
if useHTC
|
||||
%spclab(setupStruct.samplingfreq,xFar,yNear,emicrophone)
|
||||
else
|
||||
spclab(setupStruct.samplingfreq,xFar,yNear,emicrophone,yNearSpeech)
|
||||
end
|
||||
@@ -0,0 +1,15 @@
|
||||
speakerType = 'fm';
|
||||
%for k=2:5
|
||||
%for k=[2 4 5]
|
||||
for k=3
|
||||
scenario = int2str(k);
|
||||
fprintf('Current scenario: %d\n',k)
|
||||
mainProgram
|
||||
%saveFile = [speakerType, '_s_',scenario,'_delayEst_v2_vad_man.wav'];
|
||||
%wavwrite(emic,fs,nbits,saveFile);
|
||||
%saveFile = ['P:\Engineering_share\BjornV\AECM\',speakerType, '_s_',scenario,'_delayEst_v2_vad_man.pcm'];
|
||||
%saveFile = [speakerType, '_s_',scenario,'_adaptMu_adaptGamma_withVar_gammFilt_HSt.pcm'];
|
||||
saveFile = ['scenario_',scenario,'_090417_backupH_nlp.pcm'];
|
||||
fid=fopen(saveFile,'w');fwrite(fid,int16(emicrophone),'short');fclose(fid);
|
||||
%pause
|
||||
end
|
||||
@@ -0,0 +1,94 @@
|
||||
function [setupStructNew, delayStructNew] = updateSettings(microphone, TheFarEnd, setupStruct, delayStruct);
|
||||
|
||||
% other, constants
|
||||
setupStruct.hsupport1 = setupStruct.support/2 + 1;
|
||||
setupStruct.factor = 2 / setupStruct.oversampling;
|
||||
setupStruct.updatel = setupStruct.support/setupStruct.oversampling;
|
||||
setupStruct.estLen = round(setupStruct.avtime * setupStruct.samplingfreq/setupStruct.updatel);
|
||||
|
||||
% compute some constants
|
||||
blockLen = setupStruct.support/setupStruct.oversampling;
|
||||
delayStruct.maxDelayb = floor(setupStruct.samplingfreq*delayStruct.maxDelay/setupStruct.updatel); % in blocks
|
||||
|
||||
%input
|
||||
tlength = min([length(microphone),length(TheFarEnd)]);
|
||||
updateno = floor(tlength/setupStruct.updatel);
|
||||
setupStruct.tlength = setupStruct.updatel*updateno;
|
||||
setupStruct.updateno = updateno - setupStruct.oversampling + 1;
|
||||
|
||||
% signal length
|
||||
n = floor(min([length(TheFarEnd), length(microphone)])/setupStruct.support)*setupStruct.support;
|
||||
setupStruct.nb = n/blockLen - setupStruct.oversampling + 1; % in blocks
|
||||
|
||||
setupStruct.win = sqrt([0 ; hanning(setupStruct.support-1)]);
|
||||
|
||||
% Construct filterbank in Bark-scale
|
||||
|
||||
K = setupStruct.subBandLength; %Something's wrong when K even
|
||||
erbs = 21.4*log10(0.00437*setupStruct.samplingfreq/2+1);
|
||||
fe = (10.^((0:K)'*erbs/K/21.4)-1)/0.00437;
|
||||
setupStruct.centerFreq = fe;
|
||||
H = diag(ones(1,K-1))+diag(ones(1,K-2),-1);
|
||||
Hinv = inv(H);
|
||||
aty = 2*Hinv(end,:)*fe(2:end-1);
|
||||
boundary = aty - (setupStruct.samplingfreq/2 + fe(end-1))/2;
|
||||
if rem(K,2)
|
||||
x1 = min([fe(2)/2, -boundary]);
|
||||
else
|
||||
x1 = max([0, boundary]);
|
||||
end
|
||||
%x1
|
||||
g = fe(2:end-1);
|
||||
g(1) = g(1) - x1/2;
|
||||
x = 2*Hinv*g;
|
||||
x = [x1;x];
|
||||
%figure(42), clf
|
||||
xy = zeros((K+1)*4,1);
|
||||
yy = zeros((K+1)*4,1);
|
||||
xy(1:4) = [fe(1) fe(1) x(1) x(1)]';
|
||||
yy(1:4) = [0 1 1 0]'/x(1);
|
||||
for kk=2:K
|
||||
xy((kk-1)*4+(1:4)) = [x(kk-1) x(kk-1) x(kk) x(kk)]';
|
||||
yy((kk-1)*4+(1:4)) = [0 1 1 0]'/(x(kk)-x(kk-1));
|
||||
end
|
||||
xy(end-3:end) = [x(K) x(K) fe(end) fe(end)]';
|
||||
yy(end-3:end) = [0 1 1 0]'/(fe(end)*2-2*x(K));
|
||||
%plot(xy,yy,'LineWidth',2)
|
||||
%fill(xy,yy,'y')
|
||||
|
||||
x = [0;x];
|
||||
xk = x*setupStruct.hsupport1/setupStruct.samplingfreq*2;
|
||||
%setupStruct.erbBoundaries = xk;
|
||||
numInBand = zeros(length(xk),1);
|
||||
xh = (0:setupStruct.hsupport1-1);
|
||||
|
||||
for kk=1:length(xk)
|
||||
if (kk==length(xk))
|
||||
numInBand(kk) = length(find(xh>=xk(kk)));
|
||||
else
|
||||
numInBand(kk) = length(intersect(find(xh>=xk(kk)),find(xh<xk(kk+1))));
|
||||
end
|
||||
end
|
||||
setupStruct.numInBand = numInBand;
|
||||
|
||||
setupStructNew = setupStruct;
|
||||
|
||||
delayStructNew = struct(...
|
||||
'sxAll2',zeros(setupStructNew.hsupport1,setupStructNew.nb),...
|
||||
'syAll2',zeros(setupStructNew.hsupport1,setupStructNew.nb),...
|
||||
'z200',zeros(5,setupStructNew.hsupport1),...
|
||||
'z500',zeros(5,delayStruct.maxDelayb+1),...
|
||||
'bxspectrum',uint32(zeros(setupStructNew.nb,1)),...
|
||||
'byspectrum',uint32(zeros(setupStructNew.nb,1)),...
|
||||
'bandfirst',delayStruct.bandfirst,'bandlast',delayStruct.bandlast,...
|
||||
'bxhist',uint32(zeros(delayStruct.maxDelayb+1,1)),...
|
||||
'bcount',zeros(1+delayStruct.maxDelayb,setupStructNew.nb),...
|
||||
'fout',zeros(1+delayStruct.maxDelayb,setupStructNew.nb),...
|
||||
'new',zeros(1+delayStruct.maxDelayb,setupStructNew.nb),...
|
||||
'smlength',delayStruct.smlength,...
|
||||
'maxDelay', delayStruct.maxDelay,...
|
||||
'maxDelayb', delayStruct.maxDelayb,...
|
||||
'oneGoodEstimate', 0,...
|
||||
'delayAdjust', 0,...
|
||||
'delayNew',zeros(setupStructNew.nb,1),...
|
||||
'delay',zeros(setupStructNew.nb,1));
|
||||
234
src/modules/audio_processing/aecm/main/matlab/waitbar_j.m
Normal file
234
src/modules/audio_processing/aecm/main/matlab/waitbar_j.m
Normal file
@@ -0,0 +1,234 @@
|
||||
function fout = waitbar_j(x,whichbar, varargin)
|
||||
%WAITBAR Display wait bar.
|
||||
% H = WAITBAR(X,'title', property, value, property, value, ...)
|
||||
% creates and displays a waitbar of fractional length X. The
|
||||
% handle to the waitbar figure is returned in H.
|
||||
% X should be between 0 and 1. Optional arguments property and
|
||||
% value allow to set corresponding waitbar figure properties.
|
||||
% Property can also be an action keyword 'CreateCancelBtn', in
|
||||
% which case a cancel button will be added to the figure, and
|
||||
% the passed value string will be executed upon clicking on the
|
||||
% cancel button or the close figure button.
|
||||
%
|
||||
% WAITBAR(X) will set the length of the bar in the most recently
|
||||
% created waitbar window to the fractional length X.
|
||||
%
|
||||
% WAITBAR(X,H) will set the length of the bar in waitbar H
|
||||
% to the fractional length X.
|
||||
%
|
||||
% WAITBAR(X,H,'updated title') will update the title text in
|
||||
% the waitbar figure, in addition to setting the fractional
|
||||
% length to X.
|
||||
%
|
||||
% WAITBAR is typically used inside a FOR loop that performs a
|
||||
% lengthy computation. A sample usage is shown below:
|
||||
%
|
||||
% h = waitbar(0,'Please wait...');
|
||||
% for i=1:100,
|
||||
% % computation here %
|
||||
% waitbar(i/100,h)
|
||||
% end
|
||||
% close(h)
|
||||
|
||||
% Clay M. Thompson 11-9-92
|
||||
% Vlad Kolesnikov 06-7-99
|
||||
% Copyright 1984-2001 The MathWorks, Inc.
|
||||
% $Revision: 1.22 $ $Date: 2001/04/15 12:03:29 $
|
||||
|
||||
if nargin>=2
|
||||
if ischar(whichbar)
|
||||
type=2; %we are initializing
|
||||
name=whichbar;
|
||||
elseif isnumeric(whichbar)
|
||||
type=1; %we are updating, given a handle
|
||||
f=whichbar;
|
||||
else
|
||||
error(['Input arguments of type ' class(whichbar) ' not valid.'])
|
||||
end
|
||||
elseif nargin==1
|
||||
f = findobj(allchild(0),'flat','Tag','TMWWaitbar');
|
||||
|
||||
if isempty(f)
|
||||
type=2;
|
||||
name='Waitbar';
|
||||
else
|
||||
type=1;
|
||||
f=f(1);
|
||||
end
|
||||
else
|
||||
error('Input arguments not valid.');
|
||||
end
|
||||
|
||||
x = max(0,min(100*x,100));
|
||||
|
||||
switch type
|
||||
case 1, % waitbar(x) update
|
||||
p = findobj(f,'Type','patch');
|
||||
l = findobj(f,'Type','line');
|
||||
if isempty(f) | isempty(p) | isempty(l),
|
||||
error('Couldn''t find waitbar handles.');
|
||||
end
|
||||
xpatch = get(p,'XData');
|
||||
xpatch = [0 x x 0];
|
||||
set(p,'XData',xpatch)
|
||||
xline = get(l,'XData');
|
||||
set(l,'XData',xline);
|
||||
|
||||
if nargin>2,
|
||||
% Update waitbar title:
|
||||
hAxes = findobj(f,'type','axes');
|
||||
hTitle = get(hAxes,'title');
|
||||
set(hTitle,'string',varargin{1});
|
||||
end
|
||||
|
||||
case 2, % waitbar(x,name) initialize
|
||||
vertMargin = 0;
|
||||
if nargin > 2,
|
||||
% we have optional arguments: property-value pairs
|
||||
if rem (nargin, 2 ) ~= 0
|
||||
error( 'Optional initialization arguments must be passed in pairs' );
|
||||
end
|
||||
end
|
||||
|
||||
oldRootUnits = get(0,'Units');
|
||||
|
||||
set(0, 'Units', 'points');
|
||||
screenSize = get(0,'ScreenSize');
|
||||
|
||||
axFontSize=get(0,'FactoryAxesFontSize');
|
||||
|
||||
pointsPerPixel = 72/get(0,'ScreenPixelsPerInch');
|
||||
|
||||
width = 360 * pointsPerPixel;
|
||||
height = 75 * pointsPerPixel;
|
||||
pos = [screenSize(3)/2-width/2 screenSize(4)/2-height/2 width height];
|
||||
|
||||
%pos= [501.75 589.5 393.75 52.5];
|
||||
f = figure(...
|
||||
'Units', 'points', ...
|
||||
'BusyAction', 'queue', ...
|
||||
'Position', pos, ...
|
||||
'Resize','on', ...
|
||||
'CreateFcn','', ...
|
||||
'NumberTitle','off', ...
|
||||
'IntegerHandle','off', ...
|
||||
'MenuBar', 'none', ...
|
||||
'Tag','TMWWaitbar',...
|
||||
'Interruptible', 'off', ...
|
||||
'Visible','on');
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%
|
||||
% set figure properties as passed to the fcn
|
||||
% pay special attention to the 'cancel' request
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
if nargin > 2,
|
||||
propList = varargin(1:2:end);
|
||||
valueList = varargin(2:2:end);
|
||||
cancelBtnCreated = 0;
|
||||
for ii = 1:length( propList )
|
||||
try
|
||||
if strcmp(lower(propList{ii}), 'createcancelbtn' ) & ~cancelBtnCreated
|
||||
cancelBtnHeight = 23 * pointsPerPixel;
|
||||
cancelBtnWidth = 60 * pointsPerPixel;
|
||||
newPos = pos;
|
||||
vertMargin = vertMargin + cancelBtnHeight;
|
||||
newPos(4) = newPos(4)+vertMargin;
|
||||
callbackFcn = [valueList{ii}];
|
||||
set( f, 'Position', newPos, 'CloseRequestFcn', callbackFcn );
|
||||
cancelButt = uicontrol('Parent',f, ...
|
||||
'Units','points', ...
|
||||
'Callback',callbackFcn, ...
|
||||
'ButtonDownFcn', callbackFcn, ...
|
||||
'Enable','on', ...
|
||||
'Interruptible','off', ...
|
||||
'Position', [pos(3)-cancelBtnWidth*1.4, 7, ...
|
||||
cancelBtnWidth, cancelBtnHeight], ...
|
||||
'String','Cancel', ...
|
||||
'Tag','TMWWaitbarCancelButton');
|
||||
cancelBtnCreated = 1;
|
||||
else
|
||||
% simply set the prop/value pair of the figure
|
||||
set( f, propList{ii}, valueList{ii});
|
||||
end
|
||||
catch
|
||||
disp ( ['Warning: could not set property ''' propList{ii} ''' with value ''' num2str(valueList{ii}) '''' ] );
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
|
||||
colormap([]);
|
||||
|
||||
axNorm=[.05 .3 .9 .2];
|
||||
% axNorm=[1 1 1 1];
|
||||
axPos=axNorm.*[pos(3:4),pos(3:4)] + [0 vertMargin 0 0];
|
||||
|
||||
h = axes('XLim',[0 100],...
|
||||
'YLim',[0 1],...
|
||||
'Box','on', ...
|
||||
'Units','Points',...
|
||||
'FontSize', axFontSize,...
|
||||
'Position',axPos,...
|
||||
'XTickMode','manual',...
|
||||
'YTickMode','manual',...
|
||||
'XTick',[],...
|
||||
'YTick',[],...
|
||||
'XTickLabelMode','manual',...
|
||||
'XTickLabel',[],...
|
||||
'YTickLabelMode','manual',...
|
||||
'YTickLabel',[]);
|
||||
|
||||
tHandle=title(name);
|
||||
tHandle=get(h,'title');
|
||||
oldTitleUnits=get(tHandle,'Units');
|
||||
set(tHandle,...
|
||||
'Units', 'points',...
|
||||
'String', name);
|
||||
|
||||
tExtent=get(tHandle,'Extent');
|
||||
set(tHandle,'Units',oldTitleUnits);
|
||||
|
||||
titleHeight=tExtent(4)+axPos(2)+axPos(4)+5;
|
||||
if titleHeight>pos(4)
|
||||
pos(4)=titleHeight;
|
||||
pos(2)=screenSize(4)/2-pos(4)/2;
|
||||
figPosDirty=logical(1);
|
||||
else
|
||||
figPosDirty=logical(0);
|
||||
end
|
||||
|
||||
if tExtent(3)>pos(3)*1.10;
|
||||
pos(3)=min(tExtent(3)*1.10,screenSize(3));
|
||||
pos(1)=screenSize(3)/2-pos(3)/2;
|
||||
|
||||
axPos([1,3])=axNorm([1,3])*pos(3);
|
||||
set(h,'Position',axPos);
|
||||
|
||||
figPosDirty=logical(1);
|
||||
end
|
||||
|
||||
if figPosDirty
|
||||
set(f,'Position',pos);
|
||||
end
|
||||
|
||||
xpatch = [0 x x 0];
|
||||
ypatch = [0 0 1 1];
|
||||
xline = [100 0 0 100 100];
|
||||
yline = [0 0 1 1 0];
|
||||
|
||||
p = patch(xpatch,ypatch,'r','EdgeColor','r','EraseMode','none');
|
||||
l = line(xline,yline,'EraseMode','none');
|
||||
set(l,'Color',get(gca,'XColor'));
|
||||
|
||||
|
||||
set(f,'HandleVisibility','callback','visible','on', 'resize','off');
|
||||
|
||||
set(0, 'Units', oldRootUnits);
|
||||
end % case
|
||||
drawnow;
|
||||
|
||||
if nargout==1,
|
||||
fout = f;
|
||||
end
|
||||
52
src/modules/audio_processing/aecm/main/source/Android.mk
Normal file
52
src/modules/audio_processing/aecm/main/source/Android.mk
Normal file
@@ -0,0 +1,52 @@
|
||||
# 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.
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_ARM_MODE := arm
|
||||
LOCAL_MODULE_CLASS := STATIC_LIBRARIES
|
||||
LOCAL_MODULE := libwebrtc_aecm
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_GENERATED_SOURCES :=
|
||||
LOCAL_SRC_FILES := echo_control_mobile.c \
|
||||
aecm_core.c
|
||||
|
||||
# Flags passed to both C and C++ files.
|
||||
MY_CFLAGS :=
|
||||
MY_CFLAGS_C :=
|
||||
MY_DEFS := '-DNO_TCMALLOC' \
|
||||
'-DNO_HEAPCHECKER' \
|
||||
'-DWEBRTC_TARGET_PC' \
|
||||
'-DWEBRTC_LINUX' \
|
||||
'-DWEBRTC_THREAD_RR' \
|
||||
'-DWEBRTC_ANDROID' \
|
||||
'-DANDROID'
|
||||
LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS)
|
||||
|
||||
# Include paths placed before CFLAGS/CPPFLAGS
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../../.. \
|
||||
$(LOCAL_PATH)/../interface \
|
||||
$(LOCAL_PATH)/../../../utility \
|
||||
$(LOCAL_PATH)/../../../../../common_audio/signal_processing_library/main/interface
|
||||
|
||||
# Flags passed to only C++ (and not C) files.
|
||||
LOCAL_CPPFLAGS :=
|
||||
|
||||
LOCAL_LDFLAGS :=
|
||||
|
||||
LOCAL_STATIC_LIBRARIES :=
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := libcutils \
|
||||
libdl \
|
||||
libstlport
|
||||
LOCAL_ADDITIONAL_DEPENDENCIES :=
|
||||
|
||||
include external/stlport/libstlport.mk
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
43
src/modules/audio_processing/aecm/main/source/aecm.gyp
Normal file
43
src/modules/audio_processing/aecm/main/source/aecm.gyp
Normal file
@@ -0,0 +1,43 @@
|
||||
# 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.
|
||||
|
||||
{
|
||||
'includes': [
|
||||
'../../../../../common_settings.gypi',
|
||||
],
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'aecm',
|
||||
'type': '<(library)',
|
||||
'dependencies': [
|
||||
'../../../../../common_audio/signal_processing_library/main/source/spl.gyp:spl',
|
||||
'../../../utility/util.gyp: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',
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
# Local Variables:
|
||||
# tab-width:2
|
||||
# indent-tabs-mode:nil
|
||||
# End:
|
||||
# vim: set expandtab tabstop=2 shiftwidth=2:
|
||||
2452
src/modules/audio_processing/aecm/main/source/aecm_core.c
Normal file
2452
src/modules/audio_processing/aecm/main/source/aecm_core.c
Normal file
File diff suppressed because it is too large
Load Diff
313
src/modules/audio_processing/aecm/main/source/aecm_core.h
Normal file
313
src/modules/audio_processing/aecm/main/source/aecm_core.h
Normal file
@@ -0,0 +1,313 @@
|
||||
/*
|
||||
* 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"
|
||||
#include "typedefs.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 511 // Difference between max and min should be at least 2 (Q8)
|
||||
#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
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int farBufWritePos;
|
||||
int farBufReadPos;
|
||||
int knownDelay;
|
||||
int lastKnownDelay;
|
||||
|
||||
void *farFrameBuf;
|
||||
void *nearNoisyFrameBuf;
|
||||
void *nearCleanFrameBuf;
|
||||
void *outFrameBuf;
|
||||
|
||||
WebRtc_Word16 xBuf[PART_LEN2]; // farend
|
||||
WebRtc_Word16 dBufClean[PART_LEN2]; // nearend
|
||||
WebRtc_Word16 dBufNoisy[PART_LEN2]; // nearend
|
||||
WebRtc_Word16 outBuf[PART_LEN];
|
||||
|
||||
WebRtc_Word16 farBuf[FAR_BUF_LEN];
|
||||
|
||||
WebRtc_Word16 mult;
|
||||
WebRtc_UWord32 seed;
|
||||
|
||||
// Delay estimation variables
|
||||
WebRtc_UWord16 medianYlogspec[PART_LEN1];
|
||||
WebRtc_UWord16 medianXlogspec[PART_LEN1];
|
||||
WebRtc_UWord16 medianBCount[MAX_DELAY];
|
||||
WebRtc_UWord16 xfaHistory[PART_LEN1][MAX_DELAY];
|
||||
WebRtc_Word16 delHistoryPos;
|
||||
WebRtc_UWord32 bxHistory[MAX_DELAY];
|
||||
WebRtc_UWord16 currentDelay;
|
||||
WebRtc_UWord16 previousDelay;
|
||||
WebRtc_Word16 delayAdjust;
|
||||
|
||||
WebRtc_Word16 nlpFlag;
|
||||
WebRtc_Word16 fixedDelay;
|
||||
|
||||
WebRtc_UWord32 totCount;
|
||||
|
||||
WebRtc_Word16 xfaQDomainBuf[MAX_DELAY];
|
||||
WebRtc_Word16 dfaCleanQDomain;
|
||||
WebRtc_Word16 dfaCleanQDomainOld;
|
||||
WebRtc_Word16 dfaNoisyQDomain;
|
||||
WebRtc_Word16 dfaNoisyQDomainOld;
|
||||
|
||||
WebRtc_Word16 nearLogEnergy[MAX_BUF_LEN];
|
||||
WebRtc_Word16 farLogEnergy[MAX_BUF_LEN];
|
||||
WebRtc_Word16 echoAdaptLogEnergy[MAX_BUF_LEN];
|
||||
WebRtc_Word16 echoStoredLogEnergy[MAX_BUF_LEN];
|
||||
|
||||
WebRtc_Word16 channelAdapt16[PART_LEN1];
|
||||
WebRtc_Word32 channelAdapt32[PART_LEN1];
|
||||
WebRtc_Word16 channelStored[PART_LEN1];
|
||||
WebRtc_Word32 echoFilt[PART_LEN1];
|
||||
WebRtc_Word16 nearFilt[PART_LEN1];
|
||||
WebRtc_Word32 noiseEst[PART_LEN1];
|
||||
WebRtc_Word16 noiseEstQDomain[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;
|
||||
WebRtc_Word16 currentVADValue;
|
||||
WebRtc_Word16 vadUpdateCount;
|
||||
|
||||
WebRtc_Word16 delayHistogram[MAX_DELAY];
|
||||
WebRtc_Word16 delayVadCount;
|
||||
WebRtc_Word16 maxDelayHistIdx;
|
||||
WebRtc_Word16 lastMinPos;
|
||||
|
||||
WebRtc_Word16 startupState;
|
||||
WebRtc_Word16 mseChannelCount;
|
||||
WebRtc_Word16 delayCount;
|
||||
WebRtc_Word16 newDelayCorrData;
|
||||
WebRtc_Word16 lastDelayUpdateCount;
|
||||
WebRtc_Word16 delayCorrelation[CORR_BUF_LEN];
|
||||
WebRtc_Word16 supGain;
|
||||
WebRtc_Word16 supGainOld;
|
||||
WebRtc_Word16 delayOffsetFlag;
|
||||
|
||||
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, int delayOffsetFlag);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// 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 :
|
||||
//
|
||||
//
|
||||
void WebRtcAecm_ProcessFrame(AecmCore_t * const aecm, const WebRtc_Word16 * const farend,
|
||||
const WebRtc_Word16 * const nearendNoisy,
|
||||
const WebRtc_Word16 * const nearendClean,
|
||||
WebRtc_Word16 * const 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 :
|
||||
//
|
||||
//
|
||||
void WebRtcAecm_ProcessBlock(AecmCore_t * const aecm, const WebRtc_Word16 * const farend,
|
||||
const WebRtc_Word16 * const nearendNoisy,
|
||||
const WebRtc_Word16 * const noisyClean,
|
||||
WebRtc_Word16 * const 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);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,733 @@
|
||||
/*
|
||||
* 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, WebRtc_Word32 scSampFreq)
|
||||
{
|
||||
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;
|
||||
|
||||
if (scSampFreq < 1 || scSampFreq > 96000)
|
||||
{
|
||||
aecm->lastError = AECM_BAD_PARAMETER_ERROR;
|
||||
return -1;
|
||||
}
|
||||
aecm->scSampFreq = scSampFreq;
|
||||
|
||||
// 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)
|
||||
{
|
||||
WebRtcAecm_ProcessFrame(aecm->aecmCore, farend, &nearendNoisy[FRAME_LEN * i],
|
||||
NULL, &out[FRAME_LEN * i]);
|
||||
} else
|
||||
{
|
||||
WebRtcAecm_ProcessFrame(aecm->aecmCore, farend, &nearendNoisy[FRAME_LEN * i],
|
||||
&nearendClean[FRAME_LEN * i], &out[FRAME_LEN * i]);
|
||||
}
|
||||
|
||||
#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_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;
|
||||
}
|
||||
273
src/modules/audio_processing/agc/main/interface/gain_control.h
Normal file
273
src/modules/audio_processing/agc/main/interface/gain_control.h
Normal 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_
|
||||
32
src/modules/audio_processing/agc/main/matlab/getGains.m
Normal file
32
src/modules/audio_processing/agc/main/matlab/getGains.m
Normal file
@@ -0,0 +1,32 @@
|
||||
% Outputs a file for testing purposes.
|
||||
%
|
||||
% Adjust the following parameters to suit. Their purpose becomes more clear on
|
||||
% viewing the gain plots.
|
||||
% MaxGain: Max gain in dB
|
||||
% MinGain: Min gain at overload (0 dBov) in dB
|
||||
% CompRatio: Compression ratio, essentially determines the slope of the gain
|
||||
% function between the max and min gains
|
||||
% Knee: The smoothness of the transition to max gain (smaller is smoother)
|
||||
MaxGain = 5; MinGain = 0; CompRatio = 3; Knee = 1;
|
||||
|
||||
% Compute gains
|
||||
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);
|
||||
|
||||
% Save gains to file
|
||||
fid = fopen('gains', 'wb');
|
||||
if fid == -1
|
||||
error(sprintf('Unable to open file %s', filename));
|
||||
return
|
||||
end
|
||||
fwrite(fid, gains, 'int32');
|
||||
fclose(fid);
|
||||
|
||||
% Plotting
|
||||
in = 10*log10(lvl); out = 20*log10(gains/65536);
|
||||
subplot(121); plot(in, out); axis([-60, 0, -5, 30]); grid on; xlabel('Input (dB)'); ylabel('Gain (dB)');
|
||||
subplot(122); plot(in, in+out); axis([-60, 0, -60, 10]); grid on; xlabel('Input (dB)'); ylabel('Output (dB)');
|
||||
zoom on;
|
||||
46
src/modules/audio_processing/agc/main/source/Android.mk
Normal file
46
src/modules/audio_processing/agc/main/source/Android.mk
Normal file
@@ -0,0 +1,46 @@
|
||||
# This file is generated by gyp; do not edit. This means you!
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_ARM_MODE := arm
|
||||
LOCAL_MODULE_CLASS := STATIC_LIBRARIES
|
||||
LOCAL_MODULE := libwebrtc_agc
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_GENERATED_SOURCES :=
|
||||
LOCAL_SRC_FILES := analog_agc.c \
|
||||
digital_agc.c
|
||||
|
||||
# Flags passed to both C and C++ files.
|
||||
MY_CFLAGS :=
|
||||
MY_CFLAGS_C :=
|
||||
MY_DEFS := '-DNO_TCMALLOC' \
|
||||
'-DNO_HEAPCHECKER' \
|
||||
'-DWEBRTC_TARGET_PC' \
|
||||
'-DWEBRTC_LINUX' \
|
||||
'-DWEBRTC_THREAD_RR' \
|
||||
'-DWEBRTC_ANDROID' \
|
||||
'-DANDROID'
|
||||
LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS)
|
||||
|
||||
# Include paths placed before CFLAGS/CPPFLAGS
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../../.. \
|
||||
$(LOCAL_PATH)/../interface \
|
||||
$(LOCAL_PATH)/../../../../../common_audio/signal_processing_library/main/interface
|
||||
|
||||
# Flags passed to only C++ (and not C) files.
|
||||
LOCAL_CPPFLAGS :=
|
||||
LOCAL_LDFLAGS :=
|
||||
|
||||
LOCAL_STATIC_LIBRARIES :=
|
||||
# Duplicate the static libraries to fix circular references
|
||||
LOCAL_STATIC_LIBRARIES += $(LOCAL_STATIC_LIBRARIES)
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := libcutils \
|
||||
libdl \
|
||||
libstlport
|
||||
LOCAL_ADDITIONAL_DEPENDENCIES :=
|
||||
|
||||
include external/stlport/libstlport.mk
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
43
src/modules/audio_processing/agc/main/source/agc.gyp
Normal file
43
src/modules/audio_processing/agc/main/source/agc.gyp
Normal file
@@ -0,0 +1,43 @@
|
||||
# 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.
|
||||
|
||||
{
|
||||
'includes': [
|
||||
'../../../../../common_settings.gypi', # Common settings
|
||||
],
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'agc',
|
||||
'type': '<(library)',
|
||||
'dependencies': [
|
||||
'../../../../../common_audio/signal_processing_library/main/source/spl.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',
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
# Local Variables:
|
||||
# tab-width:2
|
||||
# indent-tabs-mode:nil
|
||||
# End:
|
||||
# vim: set expandtab tabstop=2 shiftwidth=2:
|
||||
1700
src/modules/audio_processing/agc/main/source/analog_agc.c
Normal file
1700
src/modules/audio_processing/agc/main/source/analog_agc.c
Normal file
File diff suppressed because it is too large
Load Diff
133
src/modules/audio_processing/agc/main/source/analog_agc.h
Normal file
133
src/modules/audio_processing/agc/main/source/analog_agc.h
Normal 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_
|
||||
780
src/modules/audio_processing/agc/main/source/digital_agc.c
Normal file
780
src/modules/audio_processing/agc/main/source/digital_agc.c
Normal file
@@ -0,0 +1,780 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
memcpy(out, in_near, 10 * L * sizeof(WebRtc_Word16));
|
||||
if (FS == 32000)
|
||||
{
|
||||
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;
|
||||
WebRtc_Word16 *buf1_ptr;
|
||||
|
||||
// process in 10 sub frames of 1 ms (to save on memory)
|
||||
nrg = 0;
|
||||
buf1_ptr = &buf1[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
|
||||
}
|
||||
76
src/modules/audio_processing/agc/main/source/digital_agc.h
Normal file
76
src/modules/audio_processing/agc/main/source/digital_agc.h
Normal 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_
|
||||
60
src/modules/audio_processing/main/apm_tests.gyp
Normal file
60
src/modules/audio_processing/main/apm_tests.gyp
Normal file
@@ -0,0 +1,60 @@
|
||||
# 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.
|
||||
|
||||
{
|
||||
'includes': [
|
||||
'../../../common_settings.gypi',
|
||||
],
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'unit_test',
|
||||
'type': 'executable',
|
||||
'dependencies': [
|
||||
'source/apm.gyp:audio_processing',
|
||||
'../../../system_wrappers/source/system_wrappers.gyp:system_wrappers',
|
||||
'../../../common_audio/signal_processing_library/main/source/spl.gyp:spl',
|
||||
|
||||
'../../../../testing/gtest.gyp:gtest',
|
||||
'../../../../testing/gtest.gyp:gtest_main',
|
||||
'../../../../third_party/protobuf/protobuf.gyp:protobuf_lite',
|
||||
],
|
||||
'include_dirs': [
|
||||
'../../../../testing/gtest/include',
|
||||
],
|
||||
'sources': [
|
||||
'test/unit_test/unit_test.cc',
|
||||
'test/unit_test/audio_processing_unittest.pb.cc',
|
||||
'test/unit_test/audio_processing_unittest.pb.h',
|
||||
],
|
||||
},
|
||||
{
|
||||
'target_name': 'process_test',
|
||||
'type': 'executable',
|
||||
'dependencies': [
|
||||
'source/apm.gyp:audio_processing',
|
||||
'../../../system_wrappers/source/system_wrappers.gyp:system_wrappers',
|
||||
|
||||
'../../../../testing/gtest.gyp:gtest',
|
||||
'../../../../testing/gtest.gyp:gtest_main',
|
||||
],
|
||||
'include_dirs': [
|
||||
'../../../../testing/gtest/include',
|
||||
],
|
||||
'sources': [
|
||||
'test/process_test/process_test.cc',
|
||||
],
|
||||
},
|
||||
|
||||
],
|
||||
}
|
||||
|
||||
# Local Variables:
|
||||
# tab-width:2
|
||||
# indent-tabs-mode:nil
|
||||
# End:
|
||||
# vim: set expandtab tabstop=2 shiftwidth=2:
|
||||
564
src/modules/audio_processing/main/interface/audio_processing.h
Normal file
564
src/modules/audio_processing/main/interface/audio_processing.h
Normal file
@@ -0,0 +1,564 @@
|
||||
/*
|
||||
* 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 "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;
|
||||
|
||||
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;
|
||||
|
||||
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.
|
||||
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.
|
||||
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_
|
||||
72
src/modules/audio_processing/main/source/Android.mk
Normal file
72
src/modules/audio_processing/main/source/Android.mk
Normal file
@@ -0,0 +1,72 @@
|
||||
# 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.
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_ARM_MODE := arm
|
||||
LOCAL_MODULE := libwebrtc_apm
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_CPP_EXTENSION := .cc
|
||||
LOCAL_GENERATED_SOURCES :=
|
||||
LOCAL_SRC_FILES := audio_buffer.cc \
|
||||
audio_processing_impl.cc \
|
||||
echo_cancellation_impl.cc \
|
||||
echo_control_mobile_impl.cc \
|
||||
gain_control_impl.cc \
|
||||
high_pass_filter_impl.cc \
|
||||
level_estimator_impl.cc \
|
||||
noise_suppression_impl.cc \
|
||||
splitting_filter.cc \
|
||||
processing_component.cc \
|
||||
voice_detection_impl.cc
|
||||
|
||||
# Flags passed to both C and C++ files.
|
||||
MY_CFLAGS :=
|
||||
MY_CFLAGS_C :=
|
||||
MY_DEFS := '-DNO_TCMALLOC' \
|
||||
'-DNO_HEAPCHECKER' \
|
||||
'-DWEBRTC_TARGET_PC' \
|
||||
'-DWEBRTC_LINUX' \
|
||||
'-DWEBRTC_THREAD_RR' \
|
||||
'-DWEBRTC_ANDROID' \
|
||||
'-DANDROID' \
|
||||
'-DWEBRTC_NS_FIXED'
|
||||
# floating point
|
||||
# -DWEBRTC_NS_FLOAT'
|
||||
LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS)
|
||||
|
||||
# Include paths placed before CFLAGS/CPPFLAGS
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../.. \
|
||||
$(LOCAL_PATH)/../interface \
|
||||
$(LOCAL_PATH)/../../../interface \
|
||||
$(LOCAL_PATH)/../../../../system_wrappers/interface \
|
||||
$(LOCAL_PATH)/../../aec/main/interface \
|
||||
$(LOCAL_PATH)/../../aecm/main/interface \
|
||||
$(LOCAL_PATH)/../../agc/main/interface \
|
||||
$(LOCAL_PATH)/../../ns/main/interface \
|
||||
$(LOCAL_PATH)/../../../../common_audio/signal_processing_library/main/interface \
|
||||
$(LOCAL_PATH)/../../../../common_audio/vad/main/interface
|
||||
|
||||
# Flags passed to only C++ (and not C) files.
|
||||
LOCAL_CPPFLAGS :=
|
||||
|
||||
LOCAL_LDFLAGS :=
|
||||
|
||||
LOCAL_STATIC_LIBRARIES :=
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := libcutils \
|
||||
libdl \
|
||||
libstlport
|
||||
|
||||
LOCAL_ADDITIONAL_DEPENDENCIES :=
|
||||
|
||||
include external/stlport/libstlport.mk
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
77
src/modules/audio_processing/main/source/apm.gyp
Normal file
77
src/modules/audio_processing/main/source/apm.gyp
Normal file
@@ -0,0 +1,77 @@
|
||||
# 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.
|
||||
|
||||
{
|
||||
'includes': [
|
||||
'../../../../common_settings.gypi', # Common settings
|
||||
],
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'audio_processing',
|
||||
'type': '<(library)',
|
||||
'conditions': [
|
||||
['prefer_fixed_point==1', {
|
||||
'dependencies': ['../../ns/main/source/ns.gyp:ns_fix'],
|
||||
'defines': ['WEBRTC_NS_FIXED'],
|
||||
}, { # else: prefer_fixed_point==0
|
||||
'dependencies': ['../../ns/main/source/ns.gyp:ns'],
|
||||
'defines': ['WEBRTC_NS_FLOAT'],
|
||||
}],
|
||||
],
|
||||
'dependencies': [
|
||||
'../../../../system_wrappers/source/system_wrappers.gyp:system_wrappers',
|
||||
'../../aec/main/source/aec.gyp:aec',
|
||||
'../../aecm/main/source/aecm.gyp:aecm',
|
||||
'../../agc/main/source/agc.gyp:agc',
|
||||
'../../../../common_audio/signal_processing_library/main/source/spl.gyp:spl',
|
||||
'../../../../common_audio/vad/main/source/vad.gyp:vad',
|
||||
],
|
||||
'include_dirs': [
|
||||
'../interface',
|
||||
'../../../interface',
|
||||
],
|
||||
'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',
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
# Local Variables:
|
||||
# tab-width:2
|
||||
# indent-tabs-mode:nil
|
||||
# End:
|
||||
# vim: set expandtab tabstop=2 shiftwidth=2:
|
||||
278
src/modules/audio_processing/main/source/audio_buffer.cc
Normal file
278
src/modules/audio_processing/main/source/audio_buffer.cc
Normal 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.
|
||||
*/
|
||||
|
||||
#include "audio_buffer.h"
|
||||
|
||||
#include "module_common_types.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(am): check range of input parameters?
|
||||
AudioBuffer::AudioBuffer(WebRtc_Word32 max_num_channels,
|
||||
WebRtc_Word32 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),
|
||||
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(WebRtc_Word32 channel) const {
|
||||
assert(channel >= 0 && channel < num_channels_);
|
||||
if (data_ != NULL) {
|
||||
return data_;
|
||||
}
|
||||
|
||||
return channels_[channel].data;
|
||||
}
|
||||
|
||||
WebRtc_Word16* AudioBuffer::low_pass_split_data(WebRtc_Word32 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(WebRtc_Word32 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(WebRtc_Word32 channel) const {
|
||||
assert(channel >= 0 && channel < num_mixed_low_pass_channels_);
|
||||
|
||||
return mixed_low_pass_channels_[channel].data;
|
||||
}
|
||||
|
||||
WebRtc_Word16* AudioBuffer::low_pass_reference(WebRtc_Word32 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(WebRtc_Word32 channel) const {
|
||||
assert(channel >= 0 && channel < num_channels_);
|
||||
return split_channels_[channel].analysis_filter_state1;
|
||||
}
|
||||
|
||||
WebRtc_Word32* AudioBuffer::analysis_filter_state2(WebRtc_Word32 channel) const {
|
||||
assert(channel >= 0 && channel < num_channels_);
|
||||
return split_channels_[channel].analysis_filter_state2;
|
||||
}
|
||||
|
||||
WebRtc_Word32* AudioBuffer::synthesis_filter_state1(WebRtc_Word32 channel) const {
|
||||
assert(channel >= 0 && channel < num_channels_);
|
||||
return split_channels_[channel].synthesis_filter_state1;
|
||||
}
|
||||
|
||||
WebRtc_Word32* AudioBuffer::synthesis_filter_state2(WebRtc_Word32 channel) const {
|
||||
assert(channel >= 0 && channel < num_channels_);
|
||||
return split_channels_[channel].synthesis_filter_state2;
|
||||
}
|
||||
|
||||
WebRtc_Word32 AudioBuffer::num_channels() const {
|
||||
return num_channels_;
|
||||
}
|
||||
|
||||
WebRtc_Word32 AudioBuffer::samples_per_channel() const {
|
||||
return samples_per_channel_;
|
||||
}
|
||||
|
||||
WebRtc_Word32 AudioBuffer::samples_per_split_channel() const {
|
||||
return samples_per_split_channel_;
|
||||
}
|
||||
|
||||
// TODO(ajm): Do deinterleaving and mixing in one step?
|
||||
void AudioBuffer::DeinterleaveFrom(AudioFrame* audioFrame) {
|
||||
assert(audioFrame->_audioChannel <= max_num_channels_);
|
||||
assert(audioFrame->_payloadDataLengthInSamples == samples_per_channel_);
|
||||
|
||||
num_channels_ = audioFrame->_audioChannel;
|
||||
num_mixed_channels_ = 0;
|
||||
num_mixed_low_pass_channels_ = 0;
|
||||
reference_copied_ = false;
|
||||
|
||||
if (num_channels_ == 1) {
|
||||
// We can get away with a pointer assignment in this case.
|
||||
data_ = audioFrame->_payloadData;
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_channels_; i++) {
|
||||
WebRtc_Word16* deinterleaved = channels_[i].data;
|
||||
WebRtc_Word16* interleaved = audioFrame->_payloadData;
|
||||
WebRtc_Word32 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* audioFrame) const {
|
||||
assert(audioFrame->_audioChannel == num_channels_);
|
||||
assert(audioFrame->_payloadDataLengthInSamples == samples_per_channel_);
|
||||
|
||||
if (num_channels_ == 1) {
|
||||
if (num_mixed_channels_ == 1) {
|
||||
memcpy(audioFrame->_payloadData,
|
||||
channels_[0].data,
|
||||
sizeof(WebRtc_Word16) * samples_per_channel_);
|
||||
} else {
|
||||
// These should point to the same buffer in this case.
|
||||
assert(data_ == audioFrame->_payloadData);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_channels_; i++) {
|
||||
WebRtc_Word16* deinterleaved = channels_[i].data;
|
||||
WebRtc_Word16* interleaved = audioFrame->_payloadData;
|
||||
WebRtc_Word32 interleaved_idx = i;
|
||||
for (int j = 0; j < samples_per_channel_; j++) {
|
||||
interleaved[interleaved_idx] = deinterleaved[j];
|
||||
interleaved_idx += num_channels_;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(ajm): would be good to support the no-mix case with pointer assignment.
|
||||
// TODO(ajm): handle mixing to multiple channels?
|
||||
void AudioBuffer::Mix(WebRtc_Word32 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(WebRtc_Word32 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
|
||||
68
src/modules/audio_processing/main/source/audio_buffer.h
Normal file
68
src/modules/audio_processing/main/source/audio_buffer.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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 "typedefs.h"
|
||||
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
struct AudioChannel;
|
||||
struct SplitAudioChannel;
|
||||
class AudioFrame;
|
||||
|
||||
class AudioBuffer {
|
||||
public:
|
||||
AudioBuffer(WebRtc_Word32 max_num_channels, WebRtc_Word32 samples_per_channel);
|
||||
virtual ~AudioBuffer();
|
||||
|
||||
WebRtc_Word32 num_channels() const;
|
||||
WebRtc_Word32 samples_per_channel() const;
|
||||
WebRtc_Word32 samples_per_split_channel() const;
|
||||
|
||||
WebRtc_Word16* data(WebRtc_Word32 channel) const;
|
||||
WebRtc_Word16* low_pass_split_data(WebRtc_Word32 channel) const;
|
||||
WebRtc_Word16* high_pass_split_data(WebRtc_Word32 channel) const;
|
||||
WebRtc_Word16* mixed_low_pass_data(WebRtc_Word32 channel) const;
|
||||
WebRtc_Word16* low_pass_reference(WebRtc_Word32 channel) const;
|
||||
|
||||
WebRtc_Word32* analysis_filter_state1(WebRtc_Word32 channel) const;
|
||||
WebRtc_Word32* analysis_filter_state2(WebRtc_Word32 channel) const;
|
||||
WebRtc_Word32* synthesis_filter_state1(WebRtc_Word32 channel) const;
|
||||
WebRtc_Word32* synthesis_filter_state2(WebRtc_Word32 channel) const;
|
||||
|
||||
void DeinterleaveFrom(AudioFrame* audioFrame);
|
||||
void InterleaveTo(AudioFrame* audioFrame) const;
|
||||
void Mix(WebRtc_Word32 num_mixed_channels);
|
||||
void CopyAndMixLowPass(WebRtc_Word32 num_mixed_channels);
|
||||
void CopyLowPassToReference();
|
||||
|
||||
private:
|
||||
const WebRtc_Word32 max_num_channels_;
|
||||
WebRtc_Word32 num_channels_;
|
||||
WebRtc_Word32 num_mixed_channels_;
|
||||
WebRtc_Word32 num_mixed_low_pass_channels_;
|
||||
const WebRtc_Word32 samples_per_channel_;
|
||||
WebRtc_Word32 samples_per_split_channel_;
|
||||
bool reference_copied_;
|
||||
|
||||
WebRtc_Word16* data_;
|
||||
// TODO(ajm): Prefer to make these vectors if permitted...
|
||||
AudioChannel* channels_;
|
||||
SplitAudioChannel* split_channels_;
|
||||
// TODO(ajm): 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_
|
||||
@@ -0,0 +1,636 @@
|
||||
/*
|
||||
* 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 <cassert>
|
||||
|
||||
#include "module_common_types.h"
|
||||
|
||||
#include "critical_section_wrapper.h"
|
||||
#include "file_wrapper.h"
|
||||
|
||||
#include "audio_buffer.h"
|
||||
#include "echo_cancellation_impl.h"
|
||||
#include "echo_control_mobile_impl.h"
|
||||
#include "high_pass_filter_impl.h"
|
||||
#include "gain_control_impl.h"
|
||||
#include "level_estimator_impl.h"
|
||||
#include "noise_suppression_impl.h"
|
||||
#include "processing_component.h"
|
||||
#include "splitting_filter.h"
|
||||
#include "voice_detection_impl.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
|
||||
enum Events {
|
||||
kInitializeEvent,
|
||||
kRenderEvent,
|
||||
kCaptureEvent
|
||||
};
|
||||
|
||||
const char kMagicNumber[] = "#!vqetrace1.2";
|
||||
} // namespace
|
||||
|
||||
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),
|
||||
debug_file_(FileWrapper::Create()),
|
||||
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_render_input_channels_(1),
|
||||
num_capture_input_channels_(1),
|
||||
num_capture_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();
|
||||
}
|
||||
|
||||
if (debug_file_->Open()) {
|
||||
debug_file_->CloseFile();
|
||||
}
|
||||
delete debug_file_;
|
||||
debug_file_ = NULL;
|
||||
|
||||
delete crit_;
|
||||
crit_ = NULL;
|
||||
|
||||
if (render_audio_ != NULL) {
|
||||
delete render_audio_;
|
||||
render_audio_ = NULL;
|
||||
}
|
||||
|
||||
if (capture_audio_ != NULL) {
|
||||
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_render_input_channels_,
|
||||
samples_per_channel_);
|
||||
capture_audio_ = new AudioBuffer(num_capture_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;
|
||||
}
|
||||
}
|
||||
|
||||
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_render_input_channels_ = channels;
|
||||
|
||||
return InitializeLocked();
|
||||
}
|
||||
|
||||
int AudioProcessingImpl::num_reverse_channels() const {
|
||||
return num_render_input_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_capture_input_channels_ = input_channels;
|
||||
num_capture_output_channels_ = output_channels;
|
||||
|
||||
return InitializeLocked();
|
||||
}
|
||||
|
||||
int AudioProcessingImpl::num_input_channels() const {
|
||||
return num_capture_input_channels_;
|
||||
}
|
||||
|
||||
int AudioProcessingImpl::num_output_channels() const {
|
||||
return num_capture_output_channels_;
|
||||
}
|
||||
|
||||
int AudioProcessingImpl::ProcessStream(AudioFrame* frame) {
|
||||
CriticalSectionScoped crit_scoped(*crit_);
|
||||
int err = kNoError;
|
||||
|
||||
if (frame == NULL) {
|
||||
return kNullPointerError;
|
||||
}
|
||||
|
||||
if (frame->_frequencyInHz !=
|
||||
static_cast<WebRtc_UWord32>(sample_rate_hz_)) {
|
||||
return kBadSampleRateError;
|
||||
}
|
||||
|
||||
if (frame->_audioChannel != num_capture_input_channels_) {
|
||||
return kBadNumberChannelsError;
|
||||
}
|
||||
|
||||
if (frame->_payloadDataLengthInSamples != samples_per_channel_) {
|
||||
return kBadDataLengthError;
|
||||
}
|
||||
|
||||
if (debug_file_->Open()) {
|
||||
WebRtc_UWord8 event = kCaptureEvent;
|
||||
if (!debug_file_->Write(&event, sizeof(event))) {
|
||||
return kFileError;
|
||||
}
|
||||
|
||||
if (!debug_file_->Write(&frame->_frequencyInHz,
|
||||
sizeof(frame->_frequencyInHz))) {
|
||||
return kFileError;
|
||||
}
|
||||
|
||||
if (!debug_file_->Write(&frame->_audioChannel,
|
||||
sizeof(frame->_audioChannel))) {
|
||||
return kFileError;
|
||||
}
|
||||
|
||||
if (!debug_file_->Write(&frame->_payloadDataLengthInSamples,
|
||||
sizeof(frame->_payloadDataLengthInSamples))) {
|
||||
return kFileError;
|
||||
}
|
||||
|
||||
if (!debug_file_->Write(frame->_payloadData,
|
||||
sizeof(WebRtc_Word16) * frame->_payloadDataLengthInSamples *
|
||||
frame->_audioChannel)) {
|
||||
return kFileError;
|
||||
}
|
||||
}
|
||||
|
||||
capture_audio_->DeinterleaveFrom(frame);
|
||||
|
||||
// TODO(ajm): experiment with mixing and AEC placement.
|
||||
if (num_capture_output_channels_ < num_capture_input_channels_) {
|
||||
capture_audio_->Mix(num_capture_output_channels_);
|
||||
|
||||
frame->_audioChannel = num_capture_output_channels_;
|
||||
}
|
||||
|
||||
if (sample_rate_hz_ == kSampleRate32kHz) {
|
||||
for (int i = 0; i < num_capture_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_capture_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);
|
||||
|
||||
return kNoError;
|
||||
}
|
||||
|
||||
int AudioProcessingImpl::AnalyzeReverseStream(AudioFrame* frame) {
|
||||
CriticalSectionScoped crit_scoped(*crit_);
|
||||
int err = kNoError;
|
||||
|
||||
if (frame == NULL) {
|
||||
return kNullPointerError;
|
||||
}
|
||||
|
||||
if (frame->_frequencyInHz !=
|
||||
static_cast<WebRtc_UWord32>(sample_rate_hz_)) {
|
||||
return kBadSampleRateError;
|
||||
}
|
||||
|
||||
if (frame->_audioChannel != num_render_input_channels_) {
|
||||
return kBadNumberChannelsError;
|
||||
}
|
||||
|
||||
if (frame->_payloadDataLengthInSamples != samples_per_channel_) {
|
||||
return kBadDataLengthError;
|
||||
}
|
||||
|
||||
if (debug_file_->Open()) {
|
||||
WebRtc_UWord8 event = kRenderEvent;
|
||||
if (!debug_file_->Write(&event, sizeof(event))) {
|
||||
return kFileError;
|
||||
}
|
||||
|
||||
if (!debug_file_->Write(&frame->_frequencyInHz,
|
||||
sizeof(frame->_frequencyInHz))) {
|
||||
return kFileError;
|
||||
}
|
||||
|
||||
if (!debug_file_->Write(&frame->_audioChannel,
|
||||
sizeof(frame->_audioChannel))) {
|
||||
return kFileError;
|
||||
}
|
||||
|
||||
if (!debug_file_->Write(&frame->_payloadDataLengthInSamples,
|
||||
sizeof(frame->_payloadDataLengthInSamples))) {
|
||||
return kFileError;
|
||||
}
|
||||
|
||||
if (!debug_file_->Write(frame->_payloadData,
|
||||
sizeof(WebRtc_Word16) * frame->_payloadDataLengthInSamples *
|
||||
frame->_audioChannel)) {
|
||||
return kFileError;
|
||||
}
|
||||
}
|
||||
|
||||
render_audio_->DeinterleaveFrom(frame);
|
||||
|
||||
// TODO(ajm): turn the splitting filter into a component?
|
||||
if (sample_rate_hz_ == kSampleRate32kHz) {
|
||||
for (int i = 0; i < num_render_input_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]) {
|
||||
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;
|
||||
}
|
||||
|
||||
if (debug_file_->WriteText("%s\n", kMagicNumber) == -1) {
|
||||
debug_file_->CloseFile();
|
||||
return kFileError;
|
||||
}
|
||||
|
||||
// TODO(ajm): should we do this? If so, we need the number of channels etc.
|
||||
// Record the default sample rate.
|
||||
WebRtc_UWord8 event = kInitializeEvent;
|
||||
if (!debug_file_->Write(&event, sizeof(event))) {
|
||||
return kFileError;
|
||||
}
|
||||
|
||||
if (!debug_file_->Write(&sample_rate_hz_, sizeof(sample_rate_hz_))) {
|
||||
return kFileError;
|
||||
}
|
||||
|
||||
return kNoError;
|
||||
}
|
||||
|
||||
int AudioProcessingImpl::StopDebugRecording() {
|
||||
CriticalSectionScoped crit_scoped(*crit_);
|
||||
// We just return if recording hasn't started.
|
||||
if (debug_file_->Open()) {
|
||||
if (debug_file_->CloseFile() == -1) {
|
||||
return kFileError;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
WebRtc_Word8 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;
|
||||
}
|
||||
} // namespace webrtc
|
||||
109
src/modules/audio_processing/main/source/audio_processing_impl.h
Normal file
109
src/modules/audio_processing/main/source/audio_processing_impl.h
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* 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 "audio_processing.h"
|
||||
|
||||
namespace webrtc {
|
||||
class CriticalSectionWrapper;
|
||||
class FileWrapper;
|
||||
|
||||
class AudioBuffer;
|
||||
class EchoCancellationImpl;
|
||||
class EchoControlMobileImpl;
|
||||
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 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_;
|
||||
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_render_input_channels_;
|
||||
int num_capture_input_channels_;
|
||||
int num_capture_output_channels_;
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_AUDIO_PROCESSING_IMPL_H_
|
||||
@@ -0,0 +1,348 @@
|
||||
/*
|
||||
* 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) {}
|
||||
|
||||
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::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_;
|
||||
|
||||
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
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
// 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 device_sample_rate_hz() const;
|
||||
virtual int set_stream_drift_samples(int drift);
|
||||
virtual int stream_drift_samples() const;
|
||||
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);
|
||||
|
||||
// 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_;
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_ECHO_CANCELLATION_IMPL_H_
|
||||
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* 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 "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_BAD_PARAMETER_ERROR:
|
||||
return AudioProcessing::kBadParameterError;
|
||||
case AECM_BAD_PARAMETER_WARNING:
|
||||
return AudioProcessing::kBadStreamParameterWarning;
|
||||
default:
|
||||
// AECMOBFIX_UNSPECIFIED_ERROR
|
||||
// AECMOBFIX_UNINITIALIZED_ERROR
|
||||
// AECMOBFIX_NULL_POINTER_ERROR
|
||||
return AudioProcessing::kUnspecifiedError;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
EchoControlMobileImpl::EchoControlMobileImpl(const AudioProcessingImpl* apm)
|
||||
: ProcessingComponent(apm),
|
||||
apm_(apm),
|
||||
routing_mode_(kSpeakerphone),
|
||||
comfort_noise_enabled_(true) {}
|
||||
|
||||
EchoControlMobileImpl::~EchoControlMobileImpl() {}
|
||||
|
||||
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::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 {
|
||||
return WebRtcAecm_Init(static_cast<Handle*>(handle),
|
||||
apm_->sample_rate_hz(),
|
||||
48000); // Dummy value. This isn't actually
|
||||
// required by AECM.
|
||||
}
|
||||
|
||||
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
|
||||
@@ -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_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;
|
||||
|
||||
// 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_;
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_ECHO_CONTROL_MOBILE_IMPL_H_
|
||||
391
src/modules/audio_processing/main/source/gain_control_impl.cc
Normal file
391
src/modules/audio_processing/main/source/gain_control_impl.cc
Normal 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
|
||||
80
src/modules/audio_processing/main/source/gain_control_impl.h
Normal file
80
src/modules/audio_processing/main/source/gain_control_impl.h
Normal 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;
|
||||
|
||||
private:
|
||||
// GainControl implementation.
|
||||
virtual int Enable(bool enable);
|
||||
virtual int set_stream_analog_level(int level);
|
||||
virtual int stream_analog_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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
182
src/modules/audio_processing/main/source/level_estimator_impl.cc
Normal file
182
src/modules/audio_processing/main/source/level_estimator_impl.cc
Normal 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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
|
||||
@@ -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_
|
||||
112
src/modules/audio_processing/main/source/processing_component.cc
Normal file
112
src/modules/audio_processing/main/source/processing_component.cc
Normal 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
|
||||
@@ -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__
|
||||
33
src/modules/audio_processing/main/source/splitting_filter.cc
Normal file
33
src/modules/audio_processing/main/source/splitting_filter.cc
Normal 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
|
||||
63
src/modules/audio_processing/main/source/splitting_filter.h
Normal file
63
src/modules/audio_processing/main/source/splitting_filter.h
Normal 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_
|
||||
202
src/modules/audio_processing/main/source/voice_detection_impl.cc
Normal file
202
src/modules/audio_processing/main/source/voice_detection_impl.cc
Normal 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_val;
|
||||
vad_ret_val = WebRtcVad_Process(static_cast<Handle*>(handle(0)),
|
||||
apm_->split_sample_rate_hz(),
|
||||
mixed_data,
|
||||
frame_size_samples_);
|
||||
|
||||
if (vad_ret_val == 0) {
|
||||
stream_has_voice_ = false;
|
||||
} else if (vad_ret_val == 1) {
|
||||
stream_has_voice_ = true;
|
||||
} 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
|
||||
@@ -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_
|
||||
@@ -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) -->
|
||||
@@ -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
|
||||
@@ -0,0 +1,26 @@
|
||||
# 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.
|
||||
#
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := apmtest-activity
|
||||
LOCAL_SRC_FILES := main.c
|
||||
LOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv1_CM
|
||||
LOCAL_STATIC_LIBRARIES := android_native_app_glue
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
$(call import-module,android/native_app_glue)
|
||||
@@ -0,0 +1 @@
|
||||
APP_PLATFORM := android-9
|
||||
@@ -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)
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">apmtest</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,48 @@
|
||||
# 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.
|
||||
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
# apm test app
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
LOCAL_CPP_EXTENSION := .cc
|
||||
LOCAL_SRC_FILES:= \
|
||||
process_test.cc
|
||||
|
||||
# Flags passed to both C and C++ files.
|
||||
LOCAL_CFLAGS := \
|
||||
'-DWEBRTC_TARGET_PC' \
|
||||
'-DWEBRTC_LINUX' \
|
||||
'-DWEBRTC_THREAD_RR' \
|
||||
'-DWEBRTC_ANDROID' \
|
||||
'-DANDROID'
|
||||
|
||||
LOCAL_CPPFLAGS :=
|
||||
LOCAL_LDFLAGS :=
|
||||
LOCAL_C_INCLUDES := \
|
||||
external/gtest/include \
|
||||
$(LOCAL_PATH)/../../../../../system_wrappers/interface \
|
||||
$(LOCAL_PATH)/../../interface \
|
||||
$(LOCAL_PATH)/../../../../interface \
|
||||
$(LOCAL_PATH)/../../../../..
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
libgtest
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := \
|
||||
libutils \
|
||||
libstlport \
|
||||
libwebrtc_audio_preprocessing
|
||||
|
||||
LOCAL_MODULE:= webrtc_apm_process_test
|
||||
|
||||
include external/stlport/libstlport.mk
|
||||
include $(BUILD_EXECUTABLE)
|
||||
360
src/modules/audio_processing/main/test/process_test/apmtest.m
Normal file
360
src/modules/audio_processing/main/test/process_test/apmtest.m
Normal file
@@ -0,0 +1,360 @@
|
||||
function apmtest(task, testname, 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.
|
||||
%
|
||||
% CASENUMBER can be used to select a single test case. Omit CASENUMBER,
|
||||
% or set to zero, to use all test cases.
|
||||
%
|
||||
|
||||
if nargin < 4
|
||||
% Set to true to run old VQE recordings.
|
||||
legacy = false;
|
||||
end
|
||||
|
||||
if nargin < 3
|
||||
casenumber = 0;
|
||||
end
|
||||
|
||||
if nargin < 2
|
||||
task = 'test';
|
||||
end
|
||||
|
||||
if nargin < 1
|
||||
testname = 'all';
|
||||
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
|
||||
|
||||
filepath = 'data/';
|
||||
inpath = [filepath 'input/'];
|
||||
outpath = [filepath 'output/'];
|
||||
refpath = [filepath 'reference/'];
|
||||
|
||||
% Temporary
|
||||
if legacy
|
||||
refpath = [filepath 'output/'];
|
||||
outpath = [filepath 'reference/'];
|
||||
end
|
||||
|
||||
if strcmp(testname, 'all')
|
||||
tests = {'apm','apmm','aec','aecm','agc','ns','vad'};
|
||||
else
|
||||
tests = {testname};
|
||||
end
|
||||
|
||||
if legacy
|
||||
progname = '/usr/local/google/p4/dev/depot/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
|
||||
|
||||
inpath = [inpath testdir];
|
||||
outpath = [outpath testdir];
|
||||
refpath = [refpath testdir];
|
||||
|
||||
if ~exist(inpath,'dir')
|
||||
error(['Input directory ' inpath ' does not exist']);
|
||||
end
|
||||
|
||||
if ~exist(refpath,'dir')
|
||||
warning(['Reference directory ' refpath ' does not exist']);
|
||||
end
|
||||
|
||||
[status, errMsg] = mkdir(outpath);
|
||||
if (status == 0)
|
||||
error(errMsg);
|
||||
end
|
||||
|
||||
[nErr, nCases] = recurseDir(inpath, outpath, refpath, 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.day'])
|
||||
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.day'])
|
||||
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
|
||||
|
||||
if vadTest == 1
|
||||
spclab([refpath vadoutfile], [outpath vadoutfile]);
|
||||
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
|
||||
@@ -0,0 +1,628 @@
|
||||
/*
|
||||
* 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 "tick_util.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "module_common_types.h"
|
||||
|
||||
#include "audio_processing.h"
|
||||
|
||||
#include "cpu_features_wrapper.h"
|
||||
|
||||
using webrtc::AudioFrame;
|
||||
using webrtc::TickInterval;
|
||||
using webrtc::TickTime;
|
||||
|
||||
using webrtc::AudioProcessing;
|
||||
using webrtc::GainControl;
|
||||
using webrtc::NoiseSuppression;
|
||||
|
||||
void usage() {
|
||||
printf(
|
||||
"Usage: process_test [options] [-ir REVERSE_FILE] [-i PRIMARY_FILE]\n");
|
||||
printf(
|
||||
" [-o OUT_FILE]\n");
|
||||
printf(
|
||||
"process_test is a test application for AudioProcessing.\n\n"
|
||||
"When -ir or -i is specified the files will be processed directly in a\n"
|
||||
"simulation mode. Otherwise the full set of test files is expected to be\n"
|
||||
"present in the working directory.\n");
|
||||
printf("\n");
|
||||
printf("Options\n");
|
||||
printf("General configuration:\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("\n -aecm Echo control mobile\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");
|
||||
printf("\n");
|
||||
printf("Modifiers:\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* far_filename = NULL;
|
||||
const char* near_filename = NULL;
|
||||
const char* out_filename = NULL;
|
||||
const char* vad_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], "-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));
|
||||
|
||||
} else if (strcmp(argv[i], "-noasm") == 0) {
|
||||
WebRtc_GetCPUInfo = WebRtc_GetCPUInfoNoASM;
|
||||
|
||||
} 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], "-aecm") == 0) {
|
||||
ASSERT_EQ(apm->kNoError, apm->echo_control_mobile()->Enable(true));
|
||||
|
||||
} 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], "--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 {
|
||||
FAIL() << "Unrecognized argument " << argv[i];
|
||||
}
|
||||
}
|
||||
|
||||
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 == NULL) {
|
||||
out_filename = out_file_default;
|
||||
}
|
||||
|
||||
if (vad_out_filename == NULL) {
|
||||
vad_out_filename = vad_file_default;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (far_filename != NULL) {
|
||||
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;
|
||||
struct stat st;
|
||||
stat(near_filename, &st);
|
||||
int near_size_samples = st.st_size / sizeof(int16_t);
|
||||
|
||||
out_file = fopen(out_filename, "wb");
|
||||
ASSERT_TRUE(NULL != out_file) << "Unable to open output audio file "
|
||||
<< out_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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
enum Events {
|
||||
kInitializeEvent,
|
||||
kRenderEvent,
|
||||
kCaptureEvent,
|
||||
kResetEventDeprecated
|
||||
};
|
||||
int16_t event = 0;
|
||||
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;
|
||||
|
||||
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 (fread(&event, sizeof(event), 1, event_file) != 1) {
|
||||
// break; // This is expected.
|
||||
//}
|
||||
}
|
||||
|
||||
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) {
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
printf("\nProcessed frames: %d (primary), %d (reverse)\n",
|
||||
primary_count, reverse_count);
|
||||
}
|
||||
|
||||
int8_t temp_int8;
|
||||
if (far_file != NULL) {
|
||||
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;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
void_main(argc, argv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
49
src/modules/audio_processing/main/test/unit_test/Android.mk
Normal file
49
src/modules/audio_processing/main/test/unit_test/Android.mk
Normal file
@@ -0,0 +1,49 @@
|
||||
# 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.
|
||||
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
# apm test app
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
LOCAL_CPP_EXTENSION := .cc
|
||||
LOCAL_SRC_FILES:= \
|
||||
unit_test.cc
|
||||
|
||||
# Flags passed to both C and C++ files.
|
||||
LOCAL_CFLAGS := \
|
||||
'-DWEBRTC_TARGET_PC' \
|
||||
'-DWEBRTC_LINUX' \
|
||||
'-DWEBRTC_THREAD_RR' \
|
||||
'-DWEBRTC_ANDROID' \
|
||||
'-DANDROID'
|
||||
|
||||
LOCAL_CPPFLAGS :=
|
||||
LOCAL_LDFLAGS :=
|
||||
LOCAL_C_INCLUDES := \
|
||||
external/gtest/include \
|
||||
$(LOCAL_PATH)/../../../../../system_wrappers/interface \
|
||||
$(LOCAL_PATH)/../../../../../common_audio/signal_processing_library/main/interface \
|
||||
$(LOCAL_PATH)/../../interface \
|
||||
$(LOCAL_PATH)/../../../../interface \
|
||||
$(LOCAL_PATH)/../../../../..
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
libgtest
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := \
|
||||
libutils \
|
||||
libstlport \
|
||||
libwebrtc_audio_preprocessing
|
||||
|
||||
LOCAL_MODULE:= webrtc_apm_unit_test
|
||||
|
||||
include external/stlport/libstlport.mk
|
||||
include $(BUILD_EXECUTABLE)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,862 @@
|
||||
// Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
// source: audio_processing_unittest.proto
|
||||
|
||||
#ifndef PROTOBUF_audio_5fprocessing_5funittest_2eproto__INCLUDED
|
||||
#define PROTOBUF_audio_5fprocessing_5funittest_2eproto__INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <google/protobuf/stubs/common.h>
|
||||
|
||||
#if GOOGLE_PROTOBUF_VERSION < 2004000
|
||||
#error This file was generated by a newer version of protoc which is
|
||||
#error incompatible with your Protocol Buffer headers. Please update
|
||||
#error your headers.
|
||||
#endif
|
||||
#if 2004000 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
|
||||
#error This file was generated by an older version of protoc which is
|
||||
#error incompatible with your Protocol Buffer headers. Please
|
||||
#error regenerate this file with a newer version of protoc.
|
||||
#endif
|
||||
|
||||
#include <google/protobuf/generated_message_util.h>
|
||||
#include <google/protobuf/repeated_field.h>
|
||||
#include <google/protobuf/extension_set.h>
|
||||
// @@protoc_insertion_point(includes)
|
||||
|
||||
namespace audio_processing_unittest {
|
||||
|
||||
// Internal implementation detail -- do not call these.
|
||||
void protobuf_AddDesc_audio_5fprocessing_5funittest_2eproto();
|
||||
void protobuf_AssignDesc_audio_5fprocessing_5funittest_2eproto();
|
||||
void protobuf_ShutdownFile_audio_5fprocessing_5funittest_2eproto();
|
||||
|
||||
class Test;
|
||||
class Test_Statistic;
|
||||
class Test_EchoMetrics;
|
||||
class OutputData;
|
||||
|
||||
// ===================================================================
|
||||
|
||||
class Test_Statistic : public ::google::protobuf::MessageLite {
|
||||
public:
|
||||
Test_Statistic();
|
||||
virtual ~Test_Statistic();
|
||||
|
||||
Test_Statistic(const Test_Statistic& from);
|
||||
|
||||
inline Test_Statistic& operator=(const Test_Statistic& from) {
|
||||
CopyFrom(from);
|
||||
return *this;
|
||||
}
|
||||
|
||||
static const Test_Statistic& default_instance();
|
||||
|
||||
void Swap(Test_Statistic* other);
|
||||
|
||||
// implements Message ----------------------------------------------
|
||||
|
||||
Test_Statistic* New() const;
|
||||
void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
|
||||
void CopyFrom(const Test_Statistic& from);
|
||||
void MergeFrom(const Test_Statistic& from);
|
||||
void Clear();
|
||||
bool IsInitialized() const;
|
||||
|
||||
int ByteSize() const;
|
||||
bool MergePartialFromCodedStream(
|
||||
::google::protobuf::io::CodedInputStream* input);
|
||||
void SerializeWithCachedSizes(
|
||||
::google::protobuf::io::CodedOutputStream* output) const;
|
||||
int GetCachedSize() const { return _cached_size_; }
|
||||
private:
|
||||
void SharedCtor();
|
||||
void SharedDtor();
|
||||
void SetCachedSize(int size) const;
|
||||
public:
|
||||
|
||||
::std::string GetTypeName() const;
|
||||
|
||||
// nested types ----------------------------------------------------
|
||||
|
||||
// accessors -------------------------------------------------------
|
||||
|
||||
// optional int32 instant = 1;
|
||||
inline bool has_instant() const;
|
||||
inline void clear_instant();
|
||||
static const int kInstantFieldNumber = 1;
|
||||
inline ::google::protobuf::int32 instant() const;
|
||||
inline void set_instant(::google::protobuf::int32 value);
|
||||
|
||||
// optional int32 average = 2;
|
||||
inline bool has_average() const;
|
||||
inline void clear_average();
|
||||
static const int kAverageFieldNumber = 2;
|
||||
inline ::google::protobuf::int32 average() const;
|
||||
inline void set_average(::google::protobuf::int32 value);
|
||||
|
||||
// optional int32 maximum = 3;
|
||||
inline bool has_maximum() const;
|
||||
inline void clear_maximum();
|
||||
static const int kMaximumFieldNumber = 3;
|
||||
inline ::google::protobuf::int32 maximum() const;
|
||||
inline void set_maximum(::google::protobuf::int32 value);
|
||||
|
||||
// optional int32 minimum = 4;
|
||||
inline bool has_minimum() const;
|
||||
inline void clear_minimum();
|
||||
static const int kMinimumFieldNumber = 4;
|
||||
inline ::google::protobuf::int32 minimum() const;
|
||||
inline void set_minimum(::google::protobuf::int32 value);
|
||||
|
||||
// @@protoc_insertion_point(class_scope:audio_processing_unittest.Test.Statistic)
|
||||
private:
|
||||
inline void set_has_instant();
|
||||
inline void clear_has_instant();
|
||||
inline void set_has_average();
|
||||
inline void clear_has_average();
|
||||
inline void set_has_maximum();
|
||||
inline void clear_has_maximum();
|
||||
inline void set_has_minimum();
|
||||
inline void clear_has_minimum();
|
||||
|
||||
::google::protobuf::int32 instant_;
|
||||
::google::protobuf::int32 average_;
|
||||
::google::protobuf::int32 maximum_;
|
||||
::google::protobuf::int32 minimum_;
|
||||
|
||||
mutable int _cached_size_;
|
||||
::google::protobuf::uint32 _has_bits_[(4 + 31) / 32];
|
||||
|
||||
friend void protobuf_AddDesc_audio_5fprocessing_5funittest_2eproto();
|
||||
friend void protobuf_AssignDesc_audio_5fprocessing_5funittest_2eproto();
|
||||
friend void protobuf_ShutdownFile_audio_5fprocessing_5funittest_2eproto();
|
||||
|
||||
void InitAsDefaultInstance();
|
||||
static Test_Statistic* default_instance_;
|
||||
};
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
class Test_EchoMetrics : public ::google::protobuf::MessageLite {
|
||||
public:
|
||||
Test_EchoMetrics();
|
||||
virtual ~Test_EchoMetrics();
|
||||
|
||||
Test_EchoMetrics(const Test_EchoMetrics& from);
|
||||
|
||||
inline Test_EchoMetrics& operator=(const Test_EchoMetrics& from) {
|
||||
CopyFrom(from);
|
||||
return *this;
|
||||
}
|
||||
|
||||
static const Test_EchoMetrics& default_instance();
|
||||
|
||||
void Swap(Test_EchoMetrics* other);
|
||||
|
||||
// implements Message ----------------------------------------------
|
||||
|
||||
Test_EchoMetrics* New() const;
|
||||
void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
|
||||
void CopyFrom(const Test_EchoMetrics& from);
|
||||
void MergeFrom(const Test_EchoMetrics& from);
|
||||
void Clear();
|
||||
bool IsInitialized() const;
|
||||
|
||||
int ByteSize() const;
|
||||
bool MergePartialFromCodedStream(
|
||||
::google::protobuf::io::CodedInputStream* input);
|
||||
void SerializeWithCachedSizes(
|
||||
::google::protobuf::io::CodedOutputStream* output) const;
|
||||
int GetCachedSize() const { return _cached_size_; }
|
||||
private:
|
||||
void SharedCtor();
|
||||
void SharedDtor();
|
||||
void SetCachedSize(int size) const;
|
||||
public:
|
||||
|
||||
::std::string GetTypeName() const;
|
||||
|
||||
// nested types ----------------------------------------------------
|
||||
|
||||
// accessors -------------------------------------------------------
|
||||
|
||||
// optional .audio_processing_unittest.Test.Statistic residualEchoReturnLoss = 1;
|
||||
inline bool has_residualechoreturnloss() const;
|
||||
inline void clear_residualechoreturnloss();
|
||||
static const int kResidualEchoReturnLossFieldNumber = 1;
|
||||
inline const ::audio_processing_unittest::Test_Statistic& residualechoreturnloss() const;
|
||||
inline ::audio_processing_unittest::Test_Statistic* mutable_residualechoreturnloss();
|
||||
inline ::audio_processing_unittest::Test_Statistic* release_residualechoreturnloss();
|
||||
|
||||
// optional .audio_processing_unittest.Test.Statistic echoReturnLoss = 2;
|
||||
inline bool has_echoreturnloss() const;
|
||||
inline void clear_echoreturnloss();
|
||||
static const int kEchoReturnLossFieldNumber = 2;
|
||||
inline const ::audio_processing_unittest::Test_Statistic& echoreturnloss() const;
|
||||
inline ::audio_processing_unittest::Test_Statistic* mutable_echoreturnloss();
|
||||
inline ::audio_processing_unittest::Test_Statistic* release_echoreturnloss();
|
||||
|
||||
// optional .audio_processing_unittest.Test.Statistic echoReturnLossEnhancement = 3;
|
||||
inline bool has_echoreturnlossenhancement() const;
|
||||
inline void clear_echoreturnlossenhancement();
|
||||
static const int kEchoReturnLossEnhancementFieldNumber = 3;
|
||||
inline const ::audio_processing_unittest::Test_Statistic& echoreturnlossenhancement() const;
|
||||
inline ::audio_processing_unittest::Test_Statistic* mutable_echoreturnlossenhancement();
|
||||
inline ::audio_processing_unittest::Test_Statistic* release_echoreturnlossenhancement();
|
||||
|
||||
// optional .audio_processing_unittest.Test.Statistic aNlp = 4;
|
||||
inline bool has_anlp() const;
|
||||
inline void clear_anlp();
|
||||
static const int kANlpFieldNumber = 4;
|
||||
inline const ::audio_processing_unittest::Test_Statistic& anlp() const;
|
||||
inline ::audio_processing_unittest::Test_Statistic* mutable_anlp();
|
||||
inline ::audio_processing_unittest::Test_Statistic* release_anlp();
|
||||
|
||||
// @@protoc_insertion_point(class_scope:audio_processing_unittest.Test.EchoMetrics)
|
||||
private:
|
||||
inline void set_has_residualechoreturnloss();
|
||||
inline void clear_has_residualechoreturnloss();
|
||||
inline void set_has_echoreturnloss();
|
||||
inline void clear_has_echoreturnloss();
|
||||
inline void set_has_echoreturnlossenhancement();
|
||||
inline void clear_has_echoreturnlossenhancement();
|
||||
inline void set_has_anlp();
|
||||
inline void clear_has_anlp();
|
||||
|
||||
::audio_processing_unittest::Test_Statistic* residualechoreturnloss_;
|
||||
::audio_processing_unittest::Test_Statistic* echoreturnloss_;
|
||||
::audio_processing_unittest::Test_Statistic* echoreturnlossenhancement_;
|
||||
::audio_processing_unittest::Test_Statistic* anlp_;
|
||||
|
||||
mutable int _cached_size_;
|
||||
::google::protobuf::uint32 _has_bits_[(4 + 31) / 32];
|
||||
|
||||
friend void protobuf_AddDesc_audio_5fprocessing_5funittest_2eproto();
|
||||
friend void protobuf_AssignDesc_audio_5fprocessing_5funittest_2eproto();
|
||||
friend void protobuf_ShutdownFile_audio_5fprocessing_5funittest_2eproto();
|
||||
|
||||
void InitAsDefaultInstance();
|
||||
static Test_EchoMetrics* default_instance_;
|
||||
};
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
class Test : public ::google::protobuf::MessageLite {
|
||||
public:
|
||||
Test();
|
||||
virtual ~Test();
|
||||
|
||||
Test(const Test& from);
|
||||
|
||||
inline Test& operator=(const Test& from) {
|
||||
CopyFrom(from);
|
||||
return *this;
|
||||
}
|
||||
|
||||
static const Test& default_instance();
|
||||
|
||||
void Swap(Test* other);
|
||||
|
||||
// implements Message ----------------------------------------------
|
||||
|
||||
Test* New() const;
|
||||
void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
|
||||
void CopyFrom(const Test& from);
|
||||
void MergeFrom(const Test& from);
|
||||
void Clear();
|
||||
bool IsInitialized() const;
|
||||
|
||||
int ByteSize() const;
|
||||
bool MergePartialFromCodedStream(
|
||||
::google::protobuf::io::CodedInputStream* input);
|
||||
void SerializeWithCachedSizes(
|
||||
::google::protobuf::io::CodedOutputStream* output) const;
|
||||
int GetCachedSize() const { return _cached_size_; }
|
||||
private:
|
||||
void SharedCtor();
|
||||
void SharedDtor();
|
||||
void SetCachedSize(int size) const;
|
||||
public:
|
||||
|
||||
::std::string GetTypeName() const;
|
||||
|
||||
// nested types ----------------------------------------------------
|
||||
|
||||
typedef Test_Statistic Statistic;
|
||||
typedef Test_EchoMetrics EchoMetrics;
|
||||
|
||||
// accessors -------------------------------------------------------
|
||||
|
||||
// optional int32 numReverseChannels = 1;
|
||||
inline bool has_numreversechannels() const;
|
||||
inline void clear_numreversechannels();
|
||||
static const int kNumReverseChannelsFieldNumber = 1;
|
||||
inline ::google::protobuf::int32 numreversechannels() const;
|
||||
inline void set_numreversechannels(::google::protobuf::int32 value);
|
||||
|
||||
// optional int32 numChannels = 2;
|
||||
inline bool has_numchannels() const;
|
||||
inline void clear_numchannels();
|
||||
static const int kNumChannelsFieldNumber = 2;
|
||||
inline ::google::protobuf::int32 numchannels() const;
|
||||
inline void set_numchannels(::google::protobuf::int32 value);
|
||||
|
||||
// optional int32 sampleRate = 3;
|
||||
inline bool has_samplerate() const;
|
||||
inline void clear_samplerate();
|
||||
static const int kSampleRateFieldNumber = 3;
|
||||
inline ::google::protobuf::int32 samplerate() const;
|
||||
inline void set_samplerate(::google::protobuf::int32 value);
|
||||
|
||||
// optional int32 hasEchoCount = 4;
|
||||
inline bool has_hasechocount() const;
|
||||
inline void clear_hasechocount();
|
||||
static const int kHasEchoCountFieldNumber = 4;
|
||||
inline ::google::protobuf::int32 hasechocount() const;
|
||||
inline void set_hasechocount(::google::protobuf::int32 value);
|
||||
|
||||
// optional int32 hasVoiceCount = 5;
|
||||
inline bool has_hasvoicecount() const;
|
||||
inline void clear_hasvoicecount();
|
||||
static const int kHasVoiceCountFieldNumber = 5;
|
||||
inline ::google::protobuf::int32 hasvoicecount() const;
|
||||
inline void set_hasvoicecount(::google::protobuf::int32 value);
|
||||
|
||||
// optional int32 isSaturatedCount = 6;
|
||||
inline bool has_issaturatedcount() const;
|
||||
inline void clear_issaturatedcount();
|
||||
static const int kIsSaturatedCountFieldNumber = 6;
|
||||
inline ::google::protobuf::int32 issaturatedcount() const;
|
||||
inline void set_issaturatedcount(::google::protobuf::int32 value);
|
||||
|
||||
// optional .audio_processing_unittest.Test.EchoMetrics echoMetrics = 7;
|
||||
inline bool has_echometrics() const;
|
||||
inline void clear_echometrics();
|
||||
static const int kEchoMetricsFieldNumber = 7;
|
||||
inline const ::audio_processing_unittest::Test_EchoMetrics& echometrics() const;
|
||||
inline ::audio_processing_unittest::Test_EchoMetrics* mutable_echometrics();
|
||||
inline ::audio_processing_unittest::Test_EchoMetrics* release_echometrics();
|
||||
|
||||
// @@protoc_insertion_point(class_scope:audio_processing_unittest.Test)
|
||||
private:
|
||||
inline void set_has_numreversechannels();
|
||||
inline void clear_has_numreversechannels();
|
||||
inline void set_has_numchannels();
|
||||
inline void clear_has_numchannels();
|
||||
inline void set_has_samplerate();
|
||||
inline void clear_has_samplerate();
|
||||
inline void set_has_hasechocount();
|
||||
inline void clear_has_hasechocount();
|
||||
inline void set_has_hasvoicecount();
|
||||
inline void clear_has_hasvoicecount();
|
||||
inline void set_has_issaturatedcount();
|
||||
inline void clear_has_issaturatedcount();
|
||||
inline void set_has_echometrics();
|
||||
inline void clear_has_echometrics();
|
||||
|
||||
::google::protobuf::int32 numreversechannels_;
|
||||
::google::protobuf::int32 numchannels_;
|
||||
::google::protobuf::int32 samplerate_;
|
||||
::google::protobuf::int32 hasechocount_;
|
||||
::google::protobuf::int32 hasvoicecount_;
|
||||
::google::protobuf::int32 issaturatedcount_;
|
||||
::audio_processing_unittest::Test_EchoMetrics* echometrics_;
|
||||
|
||||
mutable int _cached_size_;
|
||||
::google::protobuf::uint32 _has_bits_[(7 + 31) / 32];
|
||||
|
||||
friend void protobuf_AddDesc_audio_5fprocessing_5funittest_2eproto();
|
||||
friend void protobuf_AssignDesc_audio_5fprocessing_5funittest_2eproto();
|
||||
friend void protobuf_ShutdownFile_audio_5fprocessing_5funittest_2eproto();
|
||||
|
||||
void InitAsDefaultInstance();
|
||||
static Test* default_instance_;
|
||||
};
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
class OutputData : public ::google::protobuf::MessageLite {
|
||||
public:
|
||||
OutputData();
|
||||
virtual ~OutputData();
|
||||
|
||||
OutputData(const OutputData& from);
|
||||
|
||||
inline OutputData& operator=(const OutputData& from) {
|
||||
CopyFrom(from);
|
||||
return *this;
|
||||
}
|
||||
|
||||
static const OutputData& default_instance();
|
||||
|
||||
void Swap(OutputData* other);
|
||||
|
||||
// implements Message ----------------------------------------------
|
||||
|
||||
OutputData* New() const;
|
||||
void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
|
||||
void CopyFrom(const OutputData& from);
|
||||
void MergeFrom(const OutputData& from);
|
||||
void Clear();
|
||||
bool IsInitialized() const;
|
||||
|
||||
int ByteSize() const;
|
||||
bool MergePartialFromCodedStream(
|
||||
::google::protobuf::io::CodedInputStream* input);
|
||||
void SerializeWithCachedSizes(
|
||||
::google::protobuf::io::CodedOutputStream* output) const;
|
||||
int GetCachedSize() const { return _cached_size_; }
|
||||
private:
|
||||
void SharedCtor();
|
||||
void SharedDtor();
|
||||
void SetCachedSize(int size) const;
|
||||
public:
|
||||
|
||||
::std::string GetTypeName() const;
|
||||
|
||||
// nested types ----------------------------------------------------
|
||||
|
||||
// accessors -------------------------------------------------------
|
||||
|
||||
// repeated .audio_processing_unittest.Test test = 1;
|
||||
inline int test_size() const;
|
||||
inline void clear_test();
|
||||
static const int kTestFieldNumber = 1;
|
||||
inline const ::audio_processing_unittest::Test& test(int index) const;
|
||||
inline ::audio_processing_unittest::Test* mutable_test(int index);
|
||||
inline ::audio_processing_unittest::Test* add_test();
|
||||
inline const ::google::protobuf::RepeatedPtrField< ::audio_processing_unittest::Test >&
|
||||
test() const;
|
||||
inline ::google::protobuf::RepeatedPtrField< ::audio_processing_unittest::Test >*
|
||||
mutable_test();
|
||||
|
||||
// @@protoc_insertion_point(class_scope:audio_processing_unittest.OutputData)
|
||||
private:
|
||||
|
||||
::google::protobuf::RepeatedPtrField< ::audio_processing_unittest::Test > test_;
|
||||
|
||||
mutable int _cached_size_;
|
||||
::google::protobuf::uint32 _has_bits_[(1 + 31) / 32];
|
||||
|
||||
friend void protobuf_AddDesc_audio_5fprocessing_5funittest_2eproto();
|
||||
friend void protobuf_AssignDesc_audio_5fprocessing_5funittest_2eproto();
|
||||
friend void protobuf_ShutdownFile_audio_5fprocessing_5funittest_2eproto();
|
||||
|
||||
void InitAsDefaultInstance();
|
||||
static OutputData* default_instance_;
|
||||
};
|
||||
// ===================================================================
|
||||
|
||||
|
||||
// ===================================================================
|
||||
|
||||
// Test_Statistic
|
||||
|
||||
// optional int32 instant = 1;
|
||||
inline bool Test_Statistic::has_instant() const {
|
||||
return (_has_bits_[0] & 0x00000001u) != 0;
|
||||
}
|
||||
inline void Test_Statistic::set_has_instant() {
|
||||
_has_bits_[0] |= 0x00000001u;
|
||||
}
|
||||
inline void Test_Statistic::clear_has_instant() {
|
||||
_has_bits_[0] &= ~0x00000001u;
|
||||
}
|
||||
inline void Test_Statistic::clear_instant() {
|
||||
instant_ = 0;
|
||||
clear_has_instant();
|
||||
}
|
||||
inline ::google::protobuf::int32 Test_Statistic::instant() const {
|
||||
return instant_;
|
||||
}
|
||||
inline void Test_Statistic::set_instant(::google::protobuf::int32 value) {
|
||||
set_has_instant();
|
||||
instant_ = value;
|
||||
}
|
||||
|
||||
// optional int32 average = 2;
|
||||
inline bool Test_Statistic::has_average() const {
|
||||
return (_has_bits_[0] & 0x00000002u) != 0;
|
||||
}
|
||||
inline void Test_Statistic::set_has_average() {
|
||||
_has_bits_[0] |= 0x00000002u;
|
||||
}
|
||||
inline void Test_Statistic::clear_has_average() {
|
||||
_has_bits_[0] &= ~0x00000002u;
|
||||
}
|
||||
inline void Test_Statistic::clear_average() {
|
||||
average_ = 0;
|
||||
clear_has_average();
|
||||
}
|
||||
inline ::google::protobuf::int32 Test_Statistic::average() const {
|
||||
return average_;
|
||||
}
|
||||
inline void Test_Statistic::set_average(::google::protobuf::int32 value) {
|
||||
set_has_average();
|
||||
average_ = value;
|
||||
}
|
||||
|
||||
// optional int32 maximum = 3;
|
||||
inline bool Test_Statistic::has_maximum() const {
|
||||
return (_has_bits_[0] & 0x00000004u) != 0;
|
||||
}
|
||||
inline void Test_Statistic::set_has_maximum() {
|
||||
_has_bits_[0] |= 0x00000004u;
|
||||
}
|
||||
inline void Test_Statistic::clear_has_maximum() {
|
||||
_has_bits_[0] &= ~0x00000004u;
|
||||
}
|
||||
inline void Test_Statistic::clear_maximum() {
|
||||
maximum_ = 0;
|
||||
clear_has_maximum();
|
||||
}
|
||||
inline ::google::protobuf::int32 Test_Statistic::maximum() const {
|
||||
return maximum_;
|
||||
}
|
||||
inline void Test_Statistic::set_maximum(::google::protobuf::int32 value) {
|
||||
set_has_maximum();
|
||||
maximum_ = value;
|
||||
}
|
||||
|
||||
// optional int32 minimum = 4;
|
||||
inline bool Test_Statistic::has_minimum() const {
|
||||
return (_has_bits_[0] & 0x00000008u) != 0;
|
||||
}
|
||||
inline void Test_Statistic::set_has_minimum() {
|
||||
_has_bits_[0] |= 0x00000008u;
|
||||
}
|
||||
inline void Test_Statistic::clear_has_minimum() {
|
||||
_has_bits_[0] &= ~0x00000008u;
|
||||
}
|
||||
inline void Test_Statistic::clear_minimum() {
|
||||
minimum_ = 0;
|
||||
clear_has_minimum();
|
||||
}
|
||||
inline ::google::protobuf::int32 Test_Statistic::minimum() const {
|
||||
return minimum_;
|
||||
}
|
||||
inline void Test_Statistic::set_minimum(::google::protobuf::int32 value) {
|
||||
set_has_minimum();
|
||||
minimum_ = value;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
// Test_EchoMetrics
|
||||
|
||||
// optional .audio_processing_unittest.Test.Statistic residualEchoReturnLoss = 1;
|
||||
inline bool Test_EchoMetrics::has_residualechoreturnloss() const {
|
||||
return (_has_bits_[0] & 0x00000001u) != 0;
|
||||
}
|
||||
inline void Test_EchoMetrics::set_has_residualechoreturnloss() {
|
||||
_has_bits_[0] |= 0x00000001u;
|
||||
}
|
||||
inline void Test_EchoMetrics::clear_has_residualechoreturnloss() {
|
||||
_has_bits_[0] &= ~0x00000001u;
|
||||
}
|
||||
inline void Test_EchoMetrics::clear_residualechoreturnloss() {
|
||||
if (residualechoreturnloss_ != NULL) residualechoreturnloss_->::audio_processing_unittest::Test_Statistic::Clear();
|
||||
clear_has_residualechoreturnloss();
|
||||
}
|
||||
inline const ::audio_processing_unittest::Test_Statistic& Test_EchoMetrics::residualechoreturnloss() const {
|
||||
return residualechoreturnloss_ != NULL ? *residualechoreturnloss_ : *default_instance_->residualechoreturnloss_;
|
||||
}
|
||||
inline ::audio_processing_unittest::Test_Statistic* Test_EchoMetrics::mutable_residualechoreturnloss() {
|
||||
set_has_residualechoreturnloss();
|
||||
if (residualechoreturnloss_ == NULL) residualechoreturnloss_ = new ::audio_processing_unittest::Test_Statistic;
|
||||
return residualechoreturnloss_;
|
||||
}
|
||||
inline ::audio_processing_unittest::Test_Statistic* Test_EchoMetrics::release_residualechoreturnloss() {
|
||||
clear_has_residualechoreturnloss();
|
||||
::audio_processing_unittest::Test_Statistic* temp = residualechoreturnloss_;
|
||||
residualechoreturnloss_ = NULL;
|
||||
return temp;
|
||||
}
|
||||
|
||||
// optional .audio_processing_unittest.Test.Statistic echoReturnLoss = 2;
|
||||
inline bool Test_EchoMetrics::has_echoreturnloss() const {
|
||||
return (_has_bits_[0] & 0x00000002u) != 0;
|
||||
}
|
||||
inline void Test_EchoMetrics::set_has_echoreturnloss() {
|
||||
_has_bits_[0] |= 0x00000002u;
|
||||
}
|
||||
inline void Test_EchoMetrics::clear_has_echoreturnloss() {
|
||||
_has_bits_[0] &= ~0x00000002u;
|
||||
}
|
||||
inline void Test_EchoMetrics::clear_echoreturnloss() {
|
||||
if (echoreturnloss_ != NULL) echoreturnloss_->::audio_processing_unittest::Test_Statistic::Clear();
|
||||
clear_has_echoreturnloss();
|
||||
}
|
||||
inline const ::audio_processing_unittest::Test_Statistic& Test_EchoMetrics::echoreturnloss() const {
|
||||
return echoreturnloss_ != NULL ? *echoreturnloss_ : *default_instance_->echoreturnloss_;
|
||||
}
|
||||
inline ::audio_processing_unittest::Test_Statistic* Test_EchoMetrics::mutable_echoreturnloss() {
|
||||
set_has_echoreturnloss();
|
||||
if (echoreturnloss_ == NULL) echoreturnloss_ = new ::audio_processing_unittest::Test_Statistic;
|
||||
return echoreturnloss_;
|
||||
}
|
||||
inline ::audio_processing_unittest::Test_Statistic* Test_EchoMetrics::release_echoreturnloss() {
|
||||
clear_has_echoreturnloss();
|
||||
::audio_processing_unittest::Test_Statistic* temp = echoreturnloss_;
|
||||
echoreturnloss_ = NULL;
|
||||
return temp;
|
||||
}
|
||||
|
||||
// optional .audio_processing_unittest.Test.Statistic echoReturnLossEnhancement = 3;
|
||||
inline bool Test_EchoMetrics::has_echoreturnlossenhancement() const {
|
||||
return (_has_bits_[0] & 0x00000004u) != 0;
|
||||
}
|
||||
inline void Test_EchoMetrics::set_has_echoreturnlossenhancement() {
|
||||
_has_bits_[0] |= 0x00000004u;
|
||||
}
|
||||
inline void Test_EchoMetrics::clear_has_echoreturnlossenhancement() {
|
||||
_has_bits_[0] &= ~0x00000004u;
|
||||
}
|
||||
inline void Test_EchoMetrics::clear_echoreturnlossenhancement() {
|
||||
if (echoreturnlossenhancement_ != NULL) echoreturnlossenhancement_->::audio_processing_unittest::Test_Statistic::Clear();
|
||||
clear_has_echoreturnlossenhancement();
|
||||
}
|
||||
inline const ::audio_processing_unittest::Test_Statistic& Test_EchoMetrics::echoreturnlossenhancement() const {
|
||||
return echoreturnlossenhancement_ != NULL ? *echoreturnlossenhancement_ : *default_instance_->echoreturnlossenhancement_;
|
||||
}
|
||||
inline ::audio_processing_unittest::Test_Statistic* Test_EchoMetrics::mutable_echoreturnlossenhancement() {
|
||||
set_has_echoreturnlossenhancement();
|
||||
if (echoreturnlossenhancement_ == NULL) echoreturnlossenhancement_ = new ::audio_processing_unittest::Test_Statistic;
|
||||
return echoreturnlossenhancement_;
|
||||
}
|
||||
inline ::audio_processing_unittest::Test_Statistic* Test_EchoMetrics::release_echoreturnlossenhancement() {
|
||||
clear_has_echoreturnlossenhancement();
|
||||
::audio_processing_unittest::Test_Statistic* temp = echoreturnlossenhancement_;
|
||||
echoreturnlossenhancement_ = NULL;
|
||||
return temp;
|
||||
}
|
||||
|
||||
// optional .audio_processing_unittest.Test.Statistic aNlp = 4;
|
||||
inline bool Test_EchoMetrics::has_anlp() const {
|
||||
return (_has_bits_[0] & 0x00000008u) != 0;
|
||||
}
|
||||
inline void Test_EchoMetrics::set_has_anlp() {
|
||||
_has_bits_[0] |= 0x00000008u;
|
||||
}
|
||||
inline void Test_EchoMetrics::clear_has_anlp() {
|
||||
_has_bits_[0] &= ~0x00000008u;
|
||||
}
|
||||
inline void Test_EchoMetrics::clear_anlp() {
|
||||
if (anlp_ != NULL) anlp_->::audio_processing_unittest::Test_Statistic::Clear();
|
||||
clear_has_anlp();
|
||||
}
|
||||
inline const ::audio_processing_unittest::Test_Statistic& Test_EchoMetrics::anlp() const {
|
||||
return anlp_ != NULL ? *anlp_ : *default_instance_->anlp_;
|
||||
}
|
||||
inline ::audio_processing_unittest::Test_Statistic* Test_EchoMetrics::mutable_anlp() {
|
||||
set_has_anlp();
|
||||
if (anlp_ == NULL) anlp_ = new ::audio_processing_unittest::Test_Statistic;
|
||||
return anlp_;
|
||||
}
|
||||
inline ::audio_processing_unittest::Test_Statistic* Test_EchoMetrics::release_anlp() {
|
||||
clear_has_anlp();
|
||||
::audio_processing_unittest::Test_Statistic* temp = anlp_;
|
||||
anlp_ = NULL;
|
||||
return temp;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
// Test
|
||||
|
||||
// optional int32 numReverseChannels = 1;
|
||||
inline bool Test::has_numreversechannels() const {
|
||||
return (_has_bits_[0] & 0x00000001u) != 0;
|
||||
}
|
||||
inline void Test::set_has_numreversechannels() {
|
||||
_has_bits_[0] |= 0x00000001u;
|
||||
}
|
||||
inline void Test::clear_has_numreversechannels() {
|
||||
_has_bits_[0] &= ~0x00000001u;
|
||||
}
|
||||
inline void Test::clear_numreversechannels() {
|
||||
numreversechannels_ = 0;
|
||||
clear_has_numreversechannels();
|
||||
}
|
||||
inline ::google::protobuf::int32 Test::numreversechannels() const {
|
||||
return numreversechannels_;
|
||||
}
|
||||
inline void Test::set_numreversechannels(::google::protobuf::int32 value) {
|
||||
set_has_numreversechannels();
|
||||
numreversechannels_ = value;
|
||||
}
|
||||
|
||||
// optional int32 numChannels = 2;
|
||||
inline bool Test::has_numchannels() const {
|
||||
return (_has_bits_[0] & 0x00000002u) != 0;
|
||||
}
|
||||
inline void Test::set_has_numchannels() {
|
||||
_has_bits_[0] |= 0x00000002u;
|
||||
}
|
||||
inline void Test::clear_has_numchannels() {
|
||||
_has_bits_[0] &= ~0x00000002u;
|
||||
}
|
||||
inline void Test::clear_numchannels() {
|
||||
numchannels_ = 0;
|
||||
clear_has_numchannels();
|
||||
}
|
||||
inline ::google::protobuf::int32 Test::numchannels() const {
|
||||
return numchannels_;
|
||||
}
|
||||
inline void Test::set_numchannels(::google::protobuf::int32 value) {
|
||||
set_has_numchannels();
|
||||
numchannels_ = value;
|
||||
}
|
||||
|
||||
// optional int32 sampleRate = 3;
|
||||
inline bool Test::has_samplerate() const {
|
||||
return (_has_bits_[0] & 0x00000004u) != 0;
|
||||
}
|
||||
inline void Test::set_has_samplerate() {
|
||||
_has_bits_[0] |= 0x00000004u;
|
||||
}
|
||||
inline void Test::clear_has_samplerate() {
|
||||
_has_bits_[0] &= ~0x00000004u;
|
||||
}
|
||||
inline void Test::clear_samplerate() {
|
||||
samplerate_ = 0;
|
||||
clear_has_samplerate();
|
||||
}
|
||||
inline ::google::protobuf::int32 Test::samplerate() const {
|
||||
return samplerate_;
|
||||
}
|
||||
inline void Test::set_samplerate(::google::protobuf::int32 value) {
|
||||
set_has_samplerate();
|
||||
samplerate_ = value;
|
||||
}
|
||||
|
||||
// optional int32 hasEchoCount = 4;
|
||||
inline bool Test::has_hasechocount() const {
|
||||
return (_has_bits_[0] & 0x00000008u) != 0;
|
||||
}
|
||||
inline void Test::set_has_hasechocount() {
|
||||
_has_bits_[0] |= 0x00000008u;
|
||||
}
|
||||
inline void Test::clear_has_hasechocount() {
|
||||
_has_bits_[0] &= ~0x00000008u;
|
||||
}
|
||||
inline void Test::clear_hasechocount() {
|
||||
hasechocount_ = 0;
|
||||
clear_has_hasechocount();
|
||||
}
|
||||
inline ::google::protobuf::int32 Test::hasechocount() const {
|
||||
return hasechocount_;
|
||||
}
|
||||
inline void Test::set_hasechocount(::google::protobuf::int32 value) {
|
||||
set_has_hasechocount();
|
||||
hasechocount_ = value;
|
||||
}
|
||||
|
||||
// optional int32 hasVoiceCount = 5;
|
||||
inline bool Test::has_hasvoicecount() const {
|
||||
return (_has_bits_[0] & 0x00000010u) != 0;
|
||||
}
|
||||
inline void Test::set_has_hasvoicecount() {
|
||||
_has_bits_[0] |= 0x00000010u;
|
||||
}
|
||||
inline void Test::clear_has_hasvoicecount() {
|
||||
_has_bits_[0] &= ~0x00000010u;
|
||||
}
|
||||
inline void Test::clear_hasvoicecount() {
|
||||
hasvoicecount_ = 0;
|
||||
clear_has_hasvoicecount();
|
||||
}
|
||||
inline ::google::protobuf::int32 Test::hasvoicecount() const {
|
||||
return hasvoicecount_;
|
||||
}
|
||||
inline void Test::set_hasvoicecount(::google::protobuf::int32 value) {
|
||||
set_has_hasvoicecount();
|
||||
hasvoicecount_ = value;
|
||||
}
|
||||
|
||||
// optional int32 isSaturatedCount = 6;
|
||||
inline bool Test::has_issaturatedcount() const {
|
||||
return (_has_bits_[0] & 0x00000020u) != 0;
|
||||
}
|
||||
inline void Test::set_has_issaturatedcount() {
|
||||
_has_bits_[0] |= 0x00000020u;
|
||||
}
|
||||
inline void Test::clear_has_issaturatedcount() {
|
||||
_has_bits_[0] &= ~0x00000020u;
|
||||
}
|
||||
inline void Test::clear_issaturatedcount() {
|
||||
issaturatedcount_ = 0;
|
||||
clear_has_issaturatedcount();
|
||||
}
|
||||
inline ::google::protobuf::int32 Test::issaturatedcount() const {
|
||||
return issaturatedcount_;
|
||||
}
|
||||
inline void Test::set_issaturatedcount(::google::protobuf::int32 value) {
|
||||
set_has_issaturatedcount();
|
||||
issaturatedcount_ = value;
|
||||
}
|
||||
|
||||
// optional .audio_processing_unittest.Test.EchoMetrics echoMetrics = 7;
|
||||
inline bool Test::has_echometrics() const {
|
||||
return (_has_bits_[0] & 0x00000040u) != 0;
|
||||
}
|
||||
inline void Test::set_has_echometrics() {
|
||||
_has_bits_[0] |= 0x00000040u;
|
||||
}
|
||||
inline void Test::clear_has_echometrics() {
|
||||
_has_bits_[0] &= ~0x00000040u;
|
||||
}
|
||||
inline void Test::clear_echometrics() {
|
||||
if (echometrics_ != NULL) echometrics_->::audio_processing_unittest::Test_EchoMetrics::Clear();
|
||||
clear_has_echometrics();
|
||||
}
|
||||
inline const ::audio_processing_unittest::Test_EchoMetrics& Test::echometrics() const {
|
||||
return echometrics_ != NULL ? *echometrics_ : *default_instance_->echometrics_;
|
||||
}
|
||||
inline ::audio_processing_unittest::Test_EchoMetrics* Test::mutable_echometrics() {
|
||||
set_has_echometrics();
|
||||
if (echometrics_ == NULL) echometrics_ = new ::audio_processing_unittest::Test_EchoMetrics;
|
||||
return echometrics_;
|
||||
}
|
||||
inline ::audio_processing_unittest::Test_EchoMetrics* Test::release_echometrics() {
|
||||
clear_has_echometrics();
|
||||
::audio_processing_unittest::Test_EchoMetrics* temp = echometrics_;
|
||||
echometrics_ = NULL;
|
||||
return temp;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
// OutputData
|
||||
|
||||
// repeated .audio_processing_unittest.Test test = 1;
|
||||
inline int OutputData::test_size() const {
|
||||
return test_.size();
|
||||
}
|
||||
inline void OutputData::clear_test() {
|
||||
test_.Clear();
|
||||
}
|
||||
inline const ::audio_processing_unittest::Test& OutputData::test(int index) const {
|
||||
return test_.Get(index);
|
||||
}
|
||||
inline ::audio_processing_unittest::Test* OutputData::mutable_test(int index) {
|
||||
return test_.Mutable(index);
|
||||
}
|
||||
inline ::audio_processing_unittest::Test* OutputData::add_test() {
|
||||
return test_.Add();
|
||||
}
|
||||
inline const ::google::protobuf::RepeatedPtrField< ::audio_processing_unittest::Test >&
|
||||
OutputData::test() const {
|
||||
return test_;
|
||||
}
|
||||
inline ::google::protobuf::RepeatedPtrField< ::audio_processing_unittest::Test >*
|
||||
OutputData::mutable_test() {
|
||||
return &test_;
|
||||
}
|
||||
|
||||
|
||||
// @@protoc_insertion_point(namespace_scope)
|
||||
|
||||
} // namespace audio_processing_unittest
|
||||
|
||||
// @@protoc_insertion_point(global_scope)
|
||||
|
||||
#endif // PROTOBUF_audio_5fprocessing_5funittest_2eproto__INCLUDED
|
||||
@@ -0,0 +1,33 @@
|
||||
package audio_processing_unittest;
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
|
||||
message Test {
|
||||
optional int32 numReverseChannels = 1;
|
||||
optional int32 numChannels = 2;
|
||||
optional int32 sampleRate = 3;
|
||||
|
||||
optional int32 hasEchoCount = 4;
|
||||
optional int32 hasVoiceCount = 5;
|
||||
optional int32 isSaturatedCount = 6;
|
||||
|
||||
message Statistic {
|
||||
optional int32 instant = 1;
|
||||
optional int32 average = 2;
|
||||
optional int32 maximum = 3;
|
||||
optional int32 minimum = 4;
|
||||
}
|
||||
|
||||
message EchoMetrics {
|
||||
optional Statistic residualEchoReturnLoss = 1;
|
||||
optional Statistic echoReturnLoss = 2;
|
||||
optional Statistic echoReturnLossEnhancement = 3;
|
||||
optional Statistic aNlp = 4;
|
||||
}
|
||||
|
||||
optional EchoMetrics echoMetrics = 7;
|
||||
}
|
||||
|
||||
message OutputData {
|
||||
repeated Test test = 1;
|
||||
}
|
||||
|
||||
881
src/modules/audio_processing/main/test/unit_test/unit_test.cc
Normal file
881
src/modules/audio_processing/main/test/unit_test/unit_test.cc
Normal file
@@ -0,0 +1,881 @@
|
||||
/*
|
||||
* 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 <cstdio>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "audio_processing.h"
|
||||
#include "audio_processing_unittest.pb.h"
|
||||
#include "event_wrapper.h"
|
||||
#include "module_common_types.h"
|
||||
#include "thread_wrapper.h"
|
||||
#include "trace.h"
|
||||
#include "signal_processing_library.h"
|
||||
|
||||
using webrtc::AudioProcessing;
|
||||
using webrtc::AudioFrame;
|
||||
using webrtc::GainControl;
|
||||
using webrtc::NoiseSuppression;
|
||||
using webrtc::EchoCancellation;
|
||||
using webrtc::EventWrapper;
|
||||
using webrtc::Trace;
|
||||
using webrtc::LevelEstimator;
|
||||
using webrtc::EchoCancellation;
|
||||
using webrtc::EchoControlMobile;
|
||||
using webrtc::VoiceDetection;
|
||||
|
||||
namespace {
|
||||
// When true, this will compare the output data with the results stored to
|
||||
// file. This is the typical case. When the file should be updated, it can
|
||||
// be set to false with the command-line switch --write_output_data.
|
||||
bool global_read_output_data = true;
|
||||
|
||||
class ApmEnvironment : public ::testing::Environment {
|
||||
public:
|
||||
virtual void SetUp() {
|
||||
Trace::CreateTrace();
|
||||
ASSERT_EQ(0, Trace::SetTraceFile("apm_trace.txt"));
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
Trace::ReturnTrace();
|
||||
}
|
||||
};
|
||||
|
||||
class ApmTest : public ::testing::Test {
|
||||
protected:
|
||||
ApmTest();
|
||||
virtual void SetUp();
|
||||
virtual void TearDown();
|
||||
|
||||
webrtc::AudioProcessing* apm_;
|
||||
webrtc::AudioFrame* frame_;
|
||||
webrtc::AudioFrame* revframe_;
|
||||
FILE* far_file_;
|
||||
FILE* near_file_;
|
||||
bool update_output_data_;
|
||||
};
|
||||
|
||||
ApmTest::ApmTest()
|
||||
: apm_(NULL),
|
||||
far_file_(NULL),
|
||||
near_file_(NULL),
|
||||
frame_(NULL),
|
||||
revframe_(NULL) {}
|
||||
|
||||
void ApmTest::SetUp() {
|
||||
apm_ = AudioProcessing::Create(0);
|
||||
ASSERT_TRUE(apm_ != NULL);
|
||||
|
||||
frame_ = new AudioFrame();
|
||||
revframe_ = new AudioFrame();
|
||||
|
||||
ASSERT_EQ(apm_->kNoError, apm_->set_sample_rate_hz(32000));
|
||||
ASSERT_EQ(apm_->kNoError, apm_->set_num_channels(2, 2));
|
||||
ASSERT_EQ(apm_->kNoError, apm_->set_num_reverse_channels(2));
|
||||
|
||||
frame_->_payloadDataLengthInSamples = 320;
|
||||
frame_->_audioChannel = 2;
|
||||
frame_->_frequencyInHz = 32000;
|
||||
revframe_->_payloadDataLengthInSamples = 320;
|
||||
revframe_->_audioChannel = 2;
|
||||
revframe_->_frequencyInHz = 32000;
|
||||
|
||||
far_file_ = fopen("aec_far.pcm", "rb");
|
||||
ASSERT_TRUE(far_file_ != NULL) << "Could not open input file aec_far.pcm\n";
|
||||
near_file_ = fopen("aec_near.pcm", "rb");
|
||||
ASSERT_TRUE(near_file_ != NULL) << "Could not open input file aec_near.pcm\n";
|
||||
}
|
||||
|
||||
void ApmTest::TearDown() {
|
||||
if (frame_) {
|
||||
delete frame_;
|
||||
}
|
||||
frame_ = NULL;
|
||||
|
||||
if (revframe_) {
|
||||
delete revframe_;
|
||||
}
|
||||
revframe_ = NULL;
|
||||
|
||||
if (far_file_) {
|
||||
ASSERT_EQ(0, fclose(far_file_));
|
||||
}
|
||||
far_file_ = NULL;
|
||||
|
||||
if (near_file_) {
|
||||
ASSERT_EQ(0, fclose(near_file_));
|
||||
}
|
||||
near_file_ = NULL;
|
||||
|
||||
if (apm_ != NULL) {
|
||||
AudioProcessing::Destroy(apm_);
|
||||
}
|
||||
apm_ = NULL;
|
||||
}
|
||||
|
||||
void MixStereoToMono(WebRtc_Word16* stereo,
|
||||
WebRtc_Word16* mono,
|
||||
int numSamples) {
|
||||
for (int i = 0; i < numSamples; i++) {
|
||||
int int32 = (static_cast<int>(stereo[i * 2]) +
|
||||
static_cast<int>(stereo[i * 2 + 1])) >> 1;
|
||||
mono[i] = static_cast<WebRtc_Word16>(int32);
|
||||
}
|
||||
}
|
||||
|
||||
void WriteMessageLiteToFile(const char* filename,
|
||||
const ::google::protobuf::MessageLite& message) {
|
||||
assert(filename != NULL);
|
||||
|
||||
FILE* file = fopen(filename, "wb");
|
||||
ASSERT_TRUE(file != NULL) << "Could not open " << filename;
|
||||
int size = message.ByteSize();
|
||||
ASSERT_GT(size, 0);
|
||||
unsigned char* array = new unsigned char[size];
|
||||
ASSERT_TRUE(message.SerializeToArray(array, size));
|
||||
|
||||
ASSERT_EQ(1, fwrite(&size, sizeof(int), 1, file));
|
||||
ASSERT_EQ(size, fwrite(array, sizeof(unsigned char), size, file));
|
||||
|
||||
delete [] array;
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
void ReadMessageLiteFromFile(const char* filename,
|
||||
::google::protobuf::MessageLite* message) {
|
||||
assert(filename != NULL);
|
||||
assert(message != NULL);
|
||||
|
||||
FILE* file = fopen(filename, "rb");
|
||||
ASSERT_TRUE(file != NULL) << "Could not open " << filename;
|
||||
int size = 0;
|
||||
ASSERT_EQ(1, fread(&size, sizeof(int), 1, file));
|
||||
ASSERT_GT(size, 0);
|
||||
unsigned char* array = new unsigned char[size];
|
||||
ASSERT_EQ(size, fread(array, sizeof(unsigned char), size, file));
|
||||
|
||||
ASSERT_TRUE(message->ParseFromArray(array, size));
|
||||
|
||||
delete [] array;
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
struct ThreadData {
|
||||
ThreadData(int thread_num_, AudioProcessing* ap_)
|
||||
: thread_num(thread_num_),
|
||||
error(false),
|
||||
ap(ap_) {}
|
||||
int thread_num;
|
||||
bool error;
|
||||
AudioProcessing* ap;
|
||||
};
|
||||
|
||||
// Don't use GTest here; non-thread-safe on Windows (as of 1.5.0).
|
||||
bool DeadlockProc(void* thread_object) {
|
||||
ThreadData* thread_data = static_cast<ThreadData*>(thread_object);
|
||||
AudioProcessing* ap = thread_data->ap;
|
||||
int err = ap->kNoError;
|
||||
|
||||
AudioFrame primary_frame;
|
||||
AudioFrame reverse_frame;
|
||||
primary_frame._payloadDataLengthInSamples = 320;
|
||||
primary_frame._audioChannel = 2;
|
||||
primary_frame._frequencyInHz = 32000;
|
||||
reverse_frame._payloadDataLengthInSamples = 320;
|
||||
reverse_frame._audioChannel = 2;
|
||||
reverse_frame._frequencyInHz = 32000;
|
||||
|
||||
ap->echo_cancellation()->Enable(true);
|
||||
ap->gain_control()->Enable(true);
|
||||
ap->high_pass_filter()->Enable(true);
|
||||
ap->level_estimator()->Enable(true);
|
||||
ap->noise_suppression()->Enable(true);
|
||||
ap->voice_detection()->Enable(true);
|
||||
|
||||
if (thread_data->thread_num % 2 == 0) {
|
||||
err = ap->AnalyzeReverseStream(&reverse_frame);
|
||||
if (err != ap->kNoError) {
|
||||
printf("Error in AnalyzeReverseStream(): %d\n", err);
|
||||
thread_data->error = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (thread_data->thread_num % 2 == 1) {
|
||||
ap->set_stream_delay_ms(0);
|
||||
ap->echo_cancellation()->set_stream_drift_samples(0);
|
||||
ap->gain_control()->set_stream_analog_level(0);
|
||||
err = ap->ProcessStream(&primary_frame);
|
||||
if (err == ap->kStreamParameterNotSetError) {
|
||||
printf("Expected kStreamParameterNotSetError in ProcessStream(): %d\n",
|
||||
err);
|
||||
} else if (err != ap->kNoError) {
|
||||
printf("Error in ProcessStream(): %d\n", err);
|
||||
thread_data->error = true;
|
||||
return false;
|
||||
}
|
||||
ap->gain_control()->stream_analog_level();
|
||||
}
|
||||
|
||||
EventWrapper* event = EventWrapper::Create();
|
||||
event->Wait(1);
|
||||
delete event;
|
||||
event = NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*TEST_F(ApmTest, Deadlock) {
|
||||
const int num_threads = 16;
|
||||
std::vector<ThreadWrapper*> threads(num_threads);
|
||||
std::vector<ThreadData*> thread_data(num_threads);
|
||||
|
||||
ASSERT_EQ(apm_->kNoError, apm_->set_sample_rate_hz(32000));
|
||||
ASSERT_EQ(apm_->kNoError, apm_->set_num_channels(2, 2));
|
||||
ASSERT_EQ(apm_->kNoError, apm_->set_num_reverse_channels(2));
|
||||
|
||||
for (int i = 0; i < num_threads; i++) {
|
||||
thread_data[i] = new ThreadData(i, apm_);
|
||||
threads[i] = ThreadWrapper::CreateThread(DeadlockProc,
|
||||
thread_data[i],
|
||||
kNormalPriority,
|
||||
0);
|
||||
ASSERT_TRUE(threads[i] != NULL);
|
||||
unsigned int thread_id = 0;
|
||||
threads[i]->Start(thread_id);
|
||||
}
|
||||
|
||||
EventWrapper* event = EventWrapper::Create();
|
||||
ASSERT_EQ(kEventTimeout, event->Wait(5000));
|
||||
delete event;
|
||||
event = NULL;
|
||||
|
||||
for (int i = 0; i < num_threads; i++) {
|
||||
// This will return false if the thread has deadlocked.
|
||||
ASSERT_TRUE(threads[i]->Stop());
|
||||
ASSERT_FALSE(thread_data[i]->error);
|
||||
delete threads[i];
|
||||
threads[i] = NULL;
|
||||
delete thread_data[i];
|
||||
thread_data[i] = NULL;
|
||||
}
|
||||
}*/
|
||||
|
||||
TEST_F(ApmTest, StreamParameters) {
|
||||
// No errors when the components are disabled.
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->ProcessStream(frame_));
|
||||
|
||||
// Missing agc level
|
||||
EXPECT_EQ(apm_->kNoError, apm_->Initialize());
|
||||
EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true));
|
||||
EXPECT_EQ(apm_->kStreamParameterNotSetError,
|
||||
apm_->ProcessStream(frame_));
|
||||
EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(100));
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->echo_cancellation()->set_stream_drift_samples(0));
|
||||
EXPECT_EQ(apm_->kStreamParameterNotSetError,
|
||||
apm_->ProcessStream(frame_));
|
||||
EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(false));
|
||||
|
||||
// Missing delay
|
||||
EXPECT_EQ(apm_->kNoError, apm_->Initialize());
|
||||
EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->Enable(true));
|
||||
EXPECT_EQ(apm_->kStreamParameterNotSetError,
|
||||
apm_->ProcessStream(frame_));
|
||||
EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true));
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->echo_cancellation()->set_stream_drift_samples(0));
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->gain_control()->set_stream_analog_level(127));
|
||||
EXPECT_EQ(apm_->kStreamParameterNotSetError,
|
||||
apm_->ProcessStream(frame_));
|
||||
EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(false));
|
||||
|
||||
// Missing drift
|
||||
EXPECT_EQ(apm_->kNoError, apm_->Initialize());
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->echo_cancellation()->enable_drift_compensation(true));
|
||||
EXPECT_EQ(apm_->kStreamParameterNotSetError,
|
||||
apm_->ProcessStream(frame_));
|
||||
EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true));
|
||||
EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(100));
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->gain_control()->set_stream_analog_level(127));
|
||||
EXPECT_EQ(apm_->kStreamParameterNotSetError,
|
||||
apm_->ProcessStream(frame_));
|
||||
|
||||
// No stream parameters
|
||||
EXPECT_EQ(apm_->kNoError, apm_->Initialize());
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->AnalyzeReverseStream(revframe_));
|
||||
EXPECT_EQ(apm_->kStreamParameterNotSetError,
|
||||
apm_->ProcessStream(frame_));
|
||||
|
||||
// All there
|
||||
EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true));
|
||||
EXPECT_EQ(apm_->kNoError, apm_->Initialize());
|
||||
EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(100));
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->echo_cancellation()->set_stream_drift_samples(0));
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->gain_control()->set_stream_analog_level(127));
|
||||
EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_));
|
||||
}
|
||||
|
||||
TEST_F(ApmTest, Channels) {
|
||||
// Testing number of invalid channels
|
||||
EXPECT_EQ(apm_->kBadParameterError, apm_->set_num_channels(0, 1));
|
||||
EXPECT_EQ(apm_->kBadParameterError, apm_->set_num_channels(1, 0));
|
||||
EXPECT_EQ(apm_->kBadParameterError, apm_->set_num_channels(3, 1));
|
||||
EXPECT_EQ(apm_->kBadParameterError, apm_->set_num_channels(1, 3));
|
||||
EXPECT_EQ(apm_->kBadParameterError, apm_->set_num_reverse_channels(0));
|
||||
EXPECT_EQ(apm_->kBadParameterError, apm_->set_num_reverse_channels(3));
|
||||
// Testing number of valid channels
|
||||
for (int i = 1; i < 3; i++) {
|
||||
for (int j = 1; j < 3; j++) {
|
||||
if (j > i) {
|
||||
EXPECT_EQ(apm_->kBadParameterError, apm_->set_num_channels(i, j));
|
||||
} else {
|
||||
EXPECT_EQ(apm_->kNoError, apm_->set_num_channels(i, j));
|
||||
EXPECT_EQ(j, apm_->num_output_channels());
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(i, apm_->num_input_channels());
|
||||
EXPECT_EQ(apm_->kNoError, apm_->set_num_reverse_channels(i));
|
||||
EXPECT_EQ(i, apm_->num_reverse_channels());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ApmTest, SampleRates) {
|
||||
// Testing invalid sample rates
|
||||
EXPECT_EQ(apm_->kBadParameterError, apm_->set_sample_rate_hz(10000));
|
||||
// Testing valid sample rates
|
||||
int fs[] = {8000, 16000, 32000};
|
||||
for (size_t i = 0; i < sizeof(fs) / sizeof(*fs); i++) {
|
||||
EXPECT_EQ(apm_->kNoError, apm_->set_sample_rate_hz(fs[i]));
|
||||
EXPECT_EQ(fs[i], apm_->sample_rate_hz());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ApmTest, Process) {
|
||||
GOOGLE_PROTOBUF_VERIFY_VERSION;
|
||||
audio_processing_unittest::OutputData output_data;
|
||||
|
||||
if (global_read_output_data) {
|
||||
ReadMessageLiteFromFile("output_data.pb", &output_data);
|
||||
|
||||
} else {
|
||||
// We don't have a file; add the required tests to the protobuf.
|
||||
int rev_ch[] = {1, 2};
|
||||
int ch[] = {1, 2};
|
||||
int fs[] = {8000, 16000, 32000};
|
||||
for (size_t i = 0; i < sizeof(rev_ch) / sizeof(*rev_ch); i++) {
|
||||
for (size_t j = 0; j < sizeof(ch) / sizeof(*ch); j++) {
|
||||
for (size_t k = 0; k < sizeof(fs) / sizeof(*fs); k++) {
|
||||
audio_processing_unittest::Test* test = output_data.add_test();
|
||||
test->set_numreversechannels(rev_ch[i]);
|
||||
test->set_numchannels(ch[j]);
|
||||
test->set_samplerate(fs[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->echo_cancellation()->enable_drift_compensation(true));
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->echo_cancellation()->enable_metrics(true));
|
||||
EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->Enable(true));
|
||||
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->gain_control()->set_mode(GainControl::kAdaptiveAnalog));
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->gain_control()->set_analog_level_limits(0, 255));
|
||||
EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true));
|
||||
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->high_pass_filter()->Enable(true));
|
||||
|
||||
//EXPECT_EQ(apm_->kNoError,
|
||||
// apm_->level_estimator()->Enable(true));
|
||||
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->noise_suppression()->Enable(true));
|
||||
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->voice_detection()->Enable(true));
|
||||
|
||||
for (int i = 0; i < output_data.test_size(); i++) {
|
||||
printf("Running test %d of %d...\n", i + 1, output_data.test_size());
|
||||
|
||||
audio_processing_unittest::Test* test = output_data.mutable_test(i);
|
||||
const int num_samples = test->samplerate() / 100;
|
||||
revframe_->_payloadDataLengthInSamples = num_samples;
|
||||
revframe_->_audioChannel = test->numreversechannels();
|
||||
revframe_->_frequencyInHz = test->samplerate();
|
||||
frame_->_payloadDataLengthInSamples = num_samples;
|
||||
frame_->_audioChannel = test->numchannels();
|
||||
frame_->_frequencyInHz = test->samplerate();
|
||||
|
||||
EXPECT_EQ(apm_->kNoError, apm_->Initialize());
|
||||
ASSERT_EQ(apm_->kNoError, apm_->set_sample_rate_hz(test->samplerate()));
|
||||
ASSERT_EQ(apm_->kNoError, apm_->set_num_channels(frame_->_audioChannel,
|
||||
frame_->_audioChannel));
|
||||
ASSERT_EQ(apm_->kNoError,
|
||||
apm_->set_num_reverse_channels(revframe_->_audioChannel));
|
||||
|
||||
|
||||
int has_echo_count = 0;
|
||||
int has_voice_count = 0;
|
||||
int is_saturated_count = 0;
|
||||
|
||||
while (1) {
|
||||
WebRtc_Word16 temp_data[640];
|
||||
int analog_level = 127;
|
||||
|
||||
// Read far-end frame
|
||||
size_t read_count = fread(temp_data,
|
||||
sizeof(WebRtc_Word16),
|
||||
num_samples * 2,
|
||||
far_file_);
|
||||
if (read_count != static_cast<size_t>(num_samples * 2)) {
|
||||
// Check that the file really ended.
|
||||
ASSERT_NE(0, feof(far_file_));
|
||||
break; // This is expected.
|
||||
}
|
||||
|
||||
if (revframe_->_audioChannel == 1) {
|
||||
MixStereoToMono(temp_data, revframe_->_payloadData,
|
||||
revframe_->_payloadDataLengthInSamples);
|
||||
} else {
|
||||
memcpy(revframe_->_payloadData,
|
||||
&temp_data[0],
|
||||
sizeof(WebRtc_Word16) * read_count);
|
||||
}
|
||||
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->AnalyzeReverseStream(revframe_));
|
||||
|
||||
EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(0));
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->echo_cancellation()->set_stream_drift_samples(0));
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->gain_control()->set_stream_analog_level(analog_level));
|
||||
|
||||
// Read near-end frame
|
||||
read_count = fread(temp_data,
|
||||
sizeof(WebRtc_Word16),
|
||||
num_samples * 2,
|
||||
near_file_);
|
||||
if (read_count != static_cast<size_t>(num_samples * 2)) {
|
||||
// Check that the file really ended.
|
||||
ASSERT_NE(0, feof(near_file_));
|
||||
break; // This is expected.
|
||||
}
|
||||
|
||||
if (frame_->_audioChannel == 1) {
|
||||
MixStereoToMono(temp_data, frame_->_payloadData, num_samples);
|
||||
} else {
|
||||
memcpy(frame_->_payloadData,
|
||||
&temp_data[0],
|
||||
sizeof(WebRtc_Word16) * read_count);
|
||||
}
|
||||
|
||||
EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_));
|
||||
|
||||
if (apm_->echo_cancellation()->stream_has_echo()) {
|
||||
has_echo_count++;
|
||||
}
|
||||
|
||||
analog_level = apm_->gain_control()->stream_analog_level();
|
||||
if (apm_->gain_control()->stream_is_saturated()) {
|
||||
is_saturated_count++;
|
||||
}
|
||||
if (apm_->voice_detection()->stream_has_voice()) {
|
||||
has_voice_count++;
|
||||
}
|
||||
}
|
||||
|
||||
//<-- Statistics -->
|
||||
//LevelEstimator::Metrics far_metrics;
|
||||
//LevelEstimator::Metrics near_metrics;
|
||||
//EchoCancellation::Metrics echo_metrics;
|
||||
//LevelEstimator::Metrics far_metrics_ref_;
|
||||
//LevelEstimator::Metrics near_metrics_ref_;
|
||||
//EchoCancellation::Metrics echo_metrics_ref_;
|
||||
//EXPECT_EQ(apm_->kNoError,
|
||||
// apm_->echo_cancellation()->GetMetrics(&echo_metrics));
|
||||
//EXPECT_EQ(apm_->kNoError,
|
||||
// apm_->level_estimator()->GetMetrics(&near_metrics,
|
||||
|
||||
// TODO(ajm): check echo metrics and output audio.
|
||||
if (global_read_output_data) {
|
||||
EXPECT_EQ(has_echo_count,
|
||||
test->hasechocount());
|
||||
EXPECT_EQ(has_voice_count,
|
||||
test->hasvoicecount());
|
||||
EXPECT_EQ(is_saturated_count,
|
||||
test->issaturatedcount());
|
||||
} else {
|
||||
test->set_hasechocount(has_echo_count);
|
||||
test->set_hasvoicecount(has_voice_count);
|
||||
test->set_issaturatedcount(is_saturated_count);
|
||||
}
|
||||
|
||||
rewind(far_file_);
|
||||
rewind(near_file_);
|
||||
}
|
||||
|
||||
if (!global_read_output_data) {
|
||||
WriteMessageLiteToFile("output_data.pb", output_data);
|
||||
}
|
||||
|
||||
google::protobuf::ShutdownProtobufLibrary();
|
||||
}
|
||||
|
||||
TEST_F(ApmTest, EchoCancellation) {
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->echo_cancellation()->enable_drift_compensation(true));
|
||||
EXPECT_TRUE(apm_->echo_cancellation()->is_drift_compensation_enabled());
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->echo_cancellation()->enable_drift_compensation(false));
|
||||
EXPECT_FALSE(apm_->echo_cancellation()->is_drift_compensation_enabled());
|
||||
|
||||
EXPECT_EQ(apm_->kBadParameterError,
|
||||
apm_->echo_cancellation()->set_device_sample_rate_hz(4000));
|
||||
EXPECT_EQ(apm_->kBadParameterError,
|
||||
apm_->echo_cancellation()->set_device_sample_rate_hz(100000));
|
||||
|
||||
int rate[] = {16000, 44100, 48000};
|
||||
for (size_t i = 0; i < sizeof(rate)/sizeof(*rate); i++) {
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->echo_cancellation()->set_device_sample_rate_hz(rate[i]));
|
||||
EXPECT_EQ(rate[i],
|
||||
apm_->echo_cancellation()->device_sample_rate_hz());
|
||||
}
|
||||
|
||||
EXPECT_EQ(apm_->kBadParameterError,
|
||||
apm_->echo_cancellation()->set_suppression_level(
|
||||
static_cast<EchoCancellation::SuppressionLevel>(-1)));
|
||||
|
||||
EXPECT_EQ(apm_->kBadParameterError,
|
||||
apm_->echo_cancellation()->set_suppression_level(
|
||||
static_cast<EchoCancellation::SuppressionLevel>(4)));
|
||||
|
||||
EchoCancellation::SuppressionLevel level[] = {
|
||||
EchoCancellation::kLowSuppression,
|
||||
EchoCancellation::kModerateSuppression,
|
||||
EchoCancellation::kHighSuppression,
|
||||
};
|
||||
for (size_t i = 0; i < sizeof(level)/sizeof(*level); i++) {
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->echo_cancellation()->set_suppression_level(level[i]));
|
||||
EXPECT_EQ(level[i],
|
||||
apm_->echo_cancellation()->suppression_level());
|
||||
}
|
||||
|
||||
EchoCancellation::Metrics metrics;
|
||||
EXPECT_EQ(apm_->kNotEnabledError,
|
||||
apm_->echo_cancellation()->GetMetrics(&metrics));
|
||||
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->echo_cancellation()->enable_metrics(true));
|
||||
EXPECT_TRUE(apm_->echo_cancellation()->are_metrics_enabled());
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->echo_cancellation()->enable_metrics(false));
|
||||
EXPECT_FALSE(apm_->echo_cancellation()->are_metrics_enabled());
|
||||
|
||||
EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->Enable(true));
|
||||
EXPECT_TRUE(apm_->echo_cancellation()->is_enabled());
|
||||
EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->Enable(false));
|
||||
EXPECT_FALSE(apm_->echo_cancellation()->is_enabled());
|
||||
}
|
||||
|
||||
TEST_F(ApmTest, EchoControlMobile) {
|
||||
// AECM won't use super-wideband.
|
||||
EXPECT_EQ(apm_->kNoError, apm_->set_sample_rate_hz(32000));
|
||||
EXPECT_EQ(apm_->kBadSampleRateError, apm_->echo_control_mobile()->Enable(true));
|
||||
EXPECT_EQ(apm_->kNoError, apm_->set_sample_rate_hz(16000));
|
||||
// Turn AECM on (and AEC off)
|
||||
EXPECT_EQ(apm_->kNoError, apm_->echo_control_mobile()->Enable(true));
|
||||
EXPECT_TRUE(apm_->echo_control_mobile()->is_enabled());
|
||||
|
||||
EXPECT_EQ(apm_->kBadParameterError,
|
||||
apm_->echo_control_mobile()->set_routing_mode(
|
||||
static_cast<EchoControlMobile::RoutingMode>(-1)));
|
||||
EXPECT_EQ(apm_->kBadParameterError,
|
||||
apm_->echo_control_mobile()->set_routing_mode(
|
||||
static_cast<EchoControlMobile::RoutingMode>(5)));
|
||||
|
||||
// Toggle routing modes
|
||||
EchoControlMobile::RoutingMode mode[] = {
|
||||
EchoControlMobile::kQuietEarpieceOrHeadset,
|
||||
EchoControlMobile::kEarpiece,
|
||||
EchoControlMobile::kLoudEarpiece,
|
||||
EchoControlMobile::kSpeakerphone,
|
||||
EchoControlMobile::kLoudSpeakerphone,
|
||||
};
|
||||
for (size_t i = 0; i < sizeof(mode)/sizeof(*mode); i++) {
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->echo_control_mobile()->set_routing_mode(mode[i]));
|
||||
EXPECT_EQ(mode[i],
|
||||
apm_->echo_control_mobile()->routing_mode());
|
||||
}
|
||||
// Turn comfort noise off/on
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->echo_control_mobile()->enable_comfort_noise(false));
|
||||
EXPECT_FALSE(apm_->echo_control_mobile()->is_comfort_noise_enabled());
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->echo_control_mobile()->enable_comfort_noise(true));
|
||||
EXPECT_TRUE(apm_->echo_control_mobile()->is_comfort_noise_enabled());
|
||||
// Turn AECM off
|
||||
EXPECT_EQ(apm_->kNoError, apm_->echo_control_mobile()->Enable(false));
|
||||
EXPECT_FALSE(apm_->echo_control_mobile()->is_enabled());
|
||||
}
|
||||
|
||||
TEST_F(ApmTest, GainControl) {
|
||||
// Testing gain modes
|
||||
EXPECT_EQ(apm_->kBadParameterError,
|
||||
apm_->gain_control()->set_mode(static_cast<GainControl::Mode>(-1)));
|
||||
|
||||
EXPECT_EQ(apm_->kBadParameterError,
|
||||
apm_->gain_control()->set_mode(static_cast<GainControl::Mode>(3)));
|
||||
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->gain_control()->set_mode(
|
||||
apm_->gain_control()->mode()));
|
||||
|
||||
GainControl::Mode mode[] = {
|
||||
GainControl::kAdaptiveAnalog,
|
||||
GainControl::kAdaptiveDigital,
|
||||
GainControl::kFixedDigital
|
||||
};
|
||||
for (size_t i = 0; i < sizeof(mode)/sizeof(*mode); i++) {
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->gain_control()->set_mode(mode[i]));
|
||||
EXPECT_EQ(mode[i], apm_->gain_control()->mode());
|
||||
}
|
||||
// Testing invalid target levels
|
||||
EXPECT_EQ(apm_->kBadParameterError,
|
||||
apm_->gain_control()->set_target_level_dbfs(-3));
|
||||
EXPECT_EQ(apm_->kBadParameterError,
|
||||
apm_->gain_control()->set_target_level_dbfs(-40));
|
||||
// Testing valid target levels
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->gain_control()->set_target_level_dbfs(
|
||||
apm_->gain_control()->target_level_dbfs()));
|
||||
|
||||
int level_dbfs[] = {0, 6, 31};
|
||||
for (size_t i = 0; i < sizeof(level_dbfs)/sizeof(*level_dbfs); i++) {
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->gain_control()->set_target_level_dbfs(level_dbfs[i]));
|
||||
EXPECT_EQ(level_dbfs[i], apm_->gain_control()->target_level_dbfs());
|
||||
}
|
||||
|
||||
// Testing invalid compression gains
|
||||
EXPECT_EQ(apm_->kBadParameterError,
|
||||
apm_->gain_control()->set_compression_gain_db(-1));
|
||||
EXPECT_EQ(apm_->kBadParameterError,
|
||||
apm_->gain_control()->set_compression_gain_db(100));
|
||||
|
||||
// Testing valid compression gains
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->gain_control()->set_compression_gain_db(
|
||||
apm_->gain_control()->compression_gain_db()));
|
||||
|
||||
int gain_db[] = {0, 10, 90};
|
||||
for (size_t i = 0; i < sizeof(gain_db)/sizeof(*gain_db); i++) {
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->gain_control()->set_compression_gain_db(gain_db[i]));
|
||||
EXPECT_EQ(gain_db[i], apm_->gain_control()->compression_gain_db());
|
||||
}
|
||||
|
||||
// Testing limiter off/on
|
||||
EXPECT_EQ(apm_->kNoError, apm_->gain_control()->enable_limiter(false));
|
||||
EXPECT_FALSE(apm_->gain_control()->is_limiter_enabled());
|
||||
EXPECT_EQ(apm_->kNoError, apm_->gain_control()->enable_limiter(true));
|
||||
EXPECT_TRUE(apm_->gain_control()->is_limiter_enabled());
|
||||
|
||||
// Testing invalid level limits
|
||||
EXPECT_EQ(apm_->kBadParameterError,
|
||||
apm_->gain_control()->set_analog_level_limits(-1, 512));
|
||||
EXPECT_EQ(apm_->kBadParameterError,
|
||||
apm_->gain_control()->set_analog_level_limits(100000, 512));
|
||||
EXPECT_EQ(apm_->kBadParameterError,
|
||||
apm_->gain_control()->set_analog_level_limits(512, -1));
|
||||
EXPECT_EQ(apm_->kBadParameterError,
|
||||
apm_->gain_control()->set_analog_level_limits(512, 100000));
|
||||
EXPECT_EQ(apm_->kBadParameterError,
|
||||
apm_->gain_control()->set_analog_level_limits(512, 255));
|
||||
|
||||
// Testing valid level limits
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->gain_control()->set_analog_level_limits(
|
||||
apm_->gain_control()->analog_level_minimum(),
|
||||
apm_->gain_control()->analog_level_maximum()));
|
||||
|
||||
int min_level[] = {0, 255, 1024};
|
||||
for (size_t i = 0; i < sizeof(min_level)/sizeof(*min_level); i++) {
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->gain_control()->set_analog_level_limits(min_level[i], 1024));
|
||||
EXPECT_EQ(min_level[i], apm_->gain_control()->analog_level_minimum());
|
||||
}
|
||||
|
||||
int max_level[] = {0, 1024, 65535};
|
||||
for (size_t i = 0; i < sizeof(min_level)/sizeof(*min_level); i++) {
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->gain_control()->set_analog_level_limits(0, max_level[i]));
|
||||
EXPECT_EQ(max_level[i], apm_->gain_control()->analog_level_maximum());
|
||||
}
|
||||
|
||||
// TODO(ajm): stream_is_saturated() and stream_analog_level()
|
||||
|
||||
// Turn AGC off
|
||||
EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(false));
|
||||
EXPECT_FALSE(apm_->gain_control()->is_enabled());
|
||||
}
|
||||
|
||||
TEST_F(ApmTest, NoiseSuppression) {
|
||||
// Tesing invalid suppression levels
|
||||
EXPECT_EQ(apm_->kBadParameterError,
|
||||
apm_->noise_suppression()->set_level(
|
||||
static_cast<NoiseSuppression::Level>(-1)));
|
||||
|
||||
EXPECT_EQ(apm_->kBadParameterError,
|
||||
apm_->noise_suppression()->set_level(
|
||||
static_cast<NoiseSuppression::Level>(5)));
|
||||
|
||||
// Tesing valid suppression levels
|
||||
NoiseSuppression::Level level[] = {
|
||||
NoiseSuppression::kLow,
|
||||
NoiseSuppression::kModerate,
|
||||
NoiseSuppression::kHigh,
|
||||
NoiseSuppression::kVeryHigh
|
||||
};
|
||||
for (size_t i = 0; i < sizeof(level)/sizeof(*level); i++) {
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->noise_suppression()->set_level(level[i]));
|
||||
EXPECT_EQ(level[i], apm_->noise_suppression()->level());
|
||||
}
|
||||
|
||||
// Turing NS on/off
|
||||
EXPECT_EQ(apm_->kNoError, apm_->noise_suppression()->Enable(true));
|
||||
EXPECT_TRUE(apm_->noise_suppression()->is_enabled());
|
||||
EXPECT_EQ(apm_->kNoError, apm_->noise_suppression()->Enable(false));
|
||||
EXPECT_FALSE(apm_->noise_suppression()->is_enabled());
|
||||
}
|
||||
|
||||
TEST_F(ApmTest, HighPassFilter) {
|
||||
// Turing HP filter on/off
|
||||
EXPECT_EQ(apm_->kNoError, apm_->high_pass_filter()->Enable(true));
|
||||
EXPECT_TRUE(apm_->high_pass_filter()->is_enabled());
|
||||
EXPECT_EQ(apm_->kNoError, apm_->high_pass_filter()->Enable(false));
|
||||
EXPECT_FALSE(apm_->high_pass_filter()->is_enabled());
|
||||
}
|
||||
|
||||
TEST_F(ApmTest, LevelEstimator) {
|
||||
// Turing Level estimator on/off
|
||||
EXPECT_EQ(apm_->kUnsupportedComponentError,
|
||||
apm_->level_estimator()->Enable(true));
|
||||
EXPECT_FALSE(apm_->level_estimator()->is_enabled());
|
||||
EXPECT_EQ(apm_->kUnsupportedComponentError,
|
||||
apm_->level_estimator()->Enable(false));
|
||||
EXPECT_FALSE(apm_->level_estimator()->is_enabled());
|
||||
}
|
||||
|
||||
TEST_F(ApmTest, VoiceDetection) {
|
||||
// Test external VAD
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->voice_detection()->set_stream_has_voice(true));
|
||||
EXPECT_TRUE(apm_->voice_detection()->stream_has_voice());
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->voice_detection()->set_stream_has_voice(false));
|
||||
EXPECT_FALSE(apm_->voice_detection()->stream_has_voice());
|
||||
|
||||
// Tesing invalid likelihoods
|
||||
EXPECT_EQ(apm_->kBadParameterError,
|
||||
apm_->voice_detection()->set_likelihood(
|
||||
static_cast<VoiceDetection::Likelihood>(-1)));
|
||||
|
||||
EXPECT_EQ(apm_->kBadParameterError,
|
||||
apm_->voice_detection()->set_likelihood(
|
||||
static_cast<VoiceDetection::Likelihood>(5)));
|
||||
|
||||
// Tesing valid likelihoods
|
||||
VoiceDetection::Likelihood likelihood[] = {
|
||||
VoiceDetection::kVeryLowLikelihood,
|
||||
VoiceDetection::kLowLikelihood,
|
||||
VoiceDetection::kModerateLikelihood,
|
||||
VoiceDetection::kHighLikelihood
|
||||
};
|
||||
for (size_t i = 0; i < sizeof(likelihood)/sizeof(*likelihood); i++) {
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->voice_detection()->set_likelihood(likelihood[i]));
|
||||
EXPECT_EQ(likelihood[i], apm_->voice_detection()->likelihood());
|
||||
}
|
||||
|
||||
/* TODO(bjornv): Enable once VAD supports other frame lengths than 10 ms
|
||||
// Tesing invalid frame sizes
|
||||
EXPECT_EQ(apm_->kBadParameterError,
|
||||
apm_->voice_detection()->set_frame_size_ms(12));
|
||||
|
||||
// Tesing valid frame sizes
|
||||
for (int i = 10; i <= 30; i += 10) {
|
||||
EXPECT_EQ(apm_->kNoError,
|
||||
apm_->voice_detection()->set_frame_size_ms(i));
|
||||
EXPECT_EQ(i, apm_->voice_detection()->frame_size_ms());
|
||||
}
|
||||
*/
|
||||
|
||||
// Turing VAD on/off
|
||||
EXPECT_EQ(apm_->kNoError, apm_->voice_detection()->Enable(true));
|
||||
EXPECT_TRUE(apm_->voice_detection()->is_enabled());
|
||||
EXPECT_EQ(apm_->kNoError, apm_->voice_detection()->Enable(false));
|
||||
EXPECT_FALSE(apm_->voice_detection()->is_enabled());
|
||||
|
||||
// TODO(bjornv): Add tests for streamed voice; stream_has_voice()
|
||||
}
|
||||
|
||||
// Below are some ideas for tests from VPM.
|
||||
|
||||
/*TEST_F(VideoProcessingModuleTest, GetVersionTest)
|
||||
{
|
||||
}
|
||||
|
||||
TEST_F(VideoProcessingModuleTest, HandleNullBuffer)
|
||||
{
|
||||
}
|
||||
|
||||
TEST_F(VideoProcessingModuleTest, HandleBadSize)
|
||||
{
|
||||
}
|
||||
|
||||
TEST_F(VideoProcessingModuleTest, IdenticalResultsAfterReset)
|
||||
{
|
||||
}
|
||||
*/
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
ApmEnvironment* env = new ApmEnvironment; // GTest takes ownership.
|
||||
::testing::AddGlobalTestEnvironment(env);
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "--write_output_data") == 0) {
|
||||
global_read_output_data = false;
|
||||
}
|
||||
}
|
||||
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -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 : VAD 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_
|
||||
@@ -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 "signal_processing_library.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_
|
||||
50
src/modules/audio_processing/ns/main/source/Android.mk
Normal file
50
src/modules/audio_processing/ns/main/source/Android.mk
Normal file
@@ -0,0 +1,50 @@
|
||||
# This file is generated by gyp; do not edit. This means you!
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE_CLASS := STATIC_LIBRARIES
|
||||
LOCAL_MODULE := libwebrtc_ns
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_GENERATED_SOURCES :=
|
||||
LOCAL_SRC_FILES := \
|
||||
noise_suppression_x.c \
|
||||
nsx_core.c
|
||||
|
||||
# floating point
|
||||
# noise_suppression.c ns_core.c
|
||||
|
||||
# Flags passed to both C and C++ files.
|
||||
MY_CFLAGS :=
|
||||
MY_CFLAGS_C :=
|
||||
MY_DEFS := '-DNO_TCMALLOC' \
|
||||
'-DNO_HEAPCHECKER' \
|
||||
'-DWEBRTC_TARGET_PC' \
|
||||
'-DWEBRTC_LINUX' \
|
||||
'-DWEBRTC_THREAD_RR' \
|
||||
'-DWEBRTC_ANDROID' \
|
||||
'-DANDROID'
|
||||
|
||||
LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS)
|
||||
|
||||
# Include paths placed before CFLAGS/CPPFLAGS
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../../.. \
|
||||
$(LOCAL_PATH)/../interface \
|
||||
$(LOCAL_PATH)/../../../utility \
|
||||
$(LOCAL_PATH)/../../../../../common_audio/signal_processing_library/main/interface
|
||||
|
||||
# Flags passed to only C++ (and not C) files.
|
||||
LOCAL_CPPFLAGS :=
|
||||
|
||||
LOCAL_LDFLAGS :=
|
||||
|
||||
LOCAL_STATIC_LIBRARIES :=
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := libcutils \
|
||||
libdl \
|
||||
libstlport
|
||||
LOCAL_ADDITIONAL_DEPENDENCIES :=
|
||||
|
||||
include external/stlport/libstlport.mk
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
53
src/modules/audio_processing/ns/main/source/defines.h
Normal file
53
src/modules/audio_processing/ns/main/source/defines.h
Normal 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_
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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 for 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);
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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 for 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);
|
||||
}
|
||||
|
||||
67
src/modules/audio_processing/ns/main/source/ns.gyp
Normal file
67
src/modules/audio_processing/ns/main/source/ns.gyp
Normal file
@@ -0,0 +1,67 @@
|
||||
# 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.
|
||||
|
||||
{
|
||||
'includes': [
|
||||
'../../../../../common_settings.gypi',
|
||||
],
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'ns',
|
||||
'type': '<(library)',
|
||||
'dependencies': [
|
||||
'../../../../../common_audio/signal_processing_library/main/source/spl.gyp:spl',
|
||||
'../../../utility/util.gyp: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': [
|
||||
'../../../../../common_audio/signal_processing_library/main/source/spl.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',
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
# Local Variables:
|
||||
# tab-width:2
|
||||
# indent-tabs-mode:nil
|
||||
# End:
|
||||
# vim: set expandtab tabstop=2 shiftwidth=2:
|
||||
1500
src/modules/audio_processing/ns/main/source/ns_core.c
Normal file
1500
src/modules/audio_processing/ns/main/source/ns_core.c
Normal file
File diff suppressed because it is too large
Load Diff
179
src/modules/audio_processing/ns/main/source/ns_core.h
Normal file
179
src/modules/audio_processing/ns/main/source/ns_core.h
Normal 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_
|
||||
2493
src/modules/audio_processing/ns/main/source/nsx_core.c
Normal file
2493
src/modules/audio_processing/ns/main/source/nsx_core.c
Normal file
File diff suppressed because it is too large
Load Diff
169
src/modules/audio_processing/ns/main/source/nsx_core.h
Normal file
169
src/modules/audio_processing/ns/main/source/nsx_core.h
Normal file
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* 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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_NSX_CORE_H_
|
||||
58
src/modules/audio_processing/ns/main/source/nsx_defines.h
Normal file
58
src/modules/audio_processing/ns/main/source/nsx_defines.h
Normal 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.
|
||||
*/
|
||||
|
||||
#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 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_
|
||||
573
src/modules/audio_processing/ns/main/source/windows_private.h
Normal file
573
src/modules/audio_processing/ns/main/source/windows_private.h
Normal file
@@ -0,0 +1,573 @@
|
||||
/*
|
||||
* 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_
|
||||
49
src/modules/audio_processing/utility/Android.mk
Normal file
49
src/modules/audio_processing/utility/Android.mk
Normal file
@@ -0,0 +1,49 @@
|
||||
# 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.
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_ARM_MODE := arm
|
||||
LOCAL_MODULE_CLASS := STATIC_LIBRARIES
|
||||
LOCAL_MODULE := libwebrtc_apm_utility
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_GENERATED_SOURCES :=
|
||||
LOCAL_SRC_FILES := fft4g.c \
|
||||
ring_buffer.c
|
||||
|
||||
# Flags passed to both C and C++ files.
|
||||
MY_CFLAGS :=
|
||||
MY_CFLAGS_C :=
|
||||
MY_DEFS := '-DNO_TCMALLOC' \
|
||||
'-DNO_HEAPCHECKER' \
|
||||
'-DWEBRTC_TARGET_PC' \
|
||||
'-DWEBRTC_LINUX' \
|
||||
'-DWEBRTC_THREAD_RR' \
|
||||
'-DWEBRTC_ANDROID' \
|
||||
'-DANDROID'
|
||||
LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS)
|
||||
|
||||
# Include paths placed before CFLAGS/CPPFLAGS
|
||||
LOCAL_C_INCLUDES := \
|
||||
$(LOCAL_PATH)
|
||||
|
||||
# Flags passed to only C++ (and not C) files.
|
||||
LOCAL_CPPFLAGS :=
|
||||
LOCAL_LDFLAGS :=
|
||||
|
||||
LOCAL_STATIC_LIBRARIES :=
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := libcutils \
|
||||
libdl \
|
||||
libstlport
|
||||
LOCAL_ADDITIONAL_DEPENDENCIES :=
|
||||
|
||||
include external/stlport/libstlport.mk
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
1356
src/modules/audio_processing/utility/fft4g.c
Normal file
1356
src/modules/audio_processing/utility/fft4g.c
Normal file
File diff suppressed because it is too large
Load Diff
18
src/modules/audio_processing/utility/fft4g.h
Normal file
18
src/modules/audio_processing/utility/fft4g.h
Normal 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
|
||||
|
||||
239
src/modules/audio_processing/utility/ring_buffer.c
Normal file
239
src/modules/audio_processing/utility/ring_buffer.c
Normal 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;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user