You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1220 lines
41 KiB
1220 lines
41 KiB
/* Copyright (C) 2003 Epic Games (written by Jean-Marc Valin) |
|
Copyright (C) 2004-2006 Epic Games |
|
|
|
File: preprocess.c |
|
Preprocessor with denoising based on the algorithm by Ephraim and Malah |
|
|
|
Redistribution and use in source and binary forms, with or without |
|
modification, are permitted provided that the following conditions are |
|
met: |
|
|
|
1. Redistributions of source code must retain the above copyright notice, |
|
this list of conditions and the following disclaimer. |
|
|
|
2. Redistributions in binary form must reproduce the above copyright |
|
notice, this list of conditions and the following disclaimer in the |
|
documentation and/or other materials provided with the distribution. |
|
|
|
3. The name of the author may not be used to endorse or promote products |
|
derived from this software without specific prior written permission. |
|
|
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
|
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
POSSIBILITY OF SUCH DAMAGE. |
|
*/ |
|
|
|
|
|
/* |
|
Recommended papers: |
|
|
|
Y. Ephraim and D. Malah, "Speech enhancement using minimum mean-square error |
|
short-time spectral amplitude estimator". IEEE Transactions on Acoustics, |
|
Speech and Signal Processing, vol. ASSP-32, no. 6, pp. 1109-1121, 1984. |
|
|
|
Y. Ephraim and D. Malah, "Speech enhancement using minimum mean-square error |
|
log-spectral amplitude estimator". IEEE Transactions on Acoustics, Speech and |
|
Signal Processing, vol. ASSP-33, no. 2, pp. 443-445, 1985. |
|
|
|
I. Cohen and B. Berdugo, "Speech enhancement for non-stationary noise environments". |
|
Signal Processing, vol. 81, no. 2, pp. 2403-2418, 2001. |
|
|
|
Stefan Gustafsson, Rainer Martin, Peter Jax, and Peter Vary. "A psychoacoustic |
|
approach to combined acoustic echo cancellation and noise reduction". IEEE |
|
Transactions on Speech and Audio Processing, 2002. |
|
|
|
J.-M. Valin, J. Rouat, and F. Michaud, "Microphone array post-filter for separation |
|
of simultaneous non-stationary sources". In Proceedings IEEE International |
|
Conference on Acoustics, Speech, and Signal Processing, 2004. |
|
*/ |
|
|
|
#ifdef HAVE_CONFIG_H |
|
#include "config.h" |
|
#endif |
|
|
|
#include <math.h> |
|
#include "speex/speex_preprocess.h" |
|
#include "speex/speex_echo.h" |
|
#include "arch.h" |
|
#include "fftwrap.h" |
|
#include "filterbank.h" |
|
#include "math_approx.h" |
|
#include "os_support.h" |
|
|
|
#ifndef M_PI |
|
#define M_PI 3.14159263 |
|
#endif |
|
|
|
#define LOUDNESS_EXP 5.f |
|
#define AMP_SCALE .001f |
|
#define AMP_SCALE_1 1000.f |
|
|
|
#define NB_BANDS 24 |
|
|
|
#define SPEECH_PROB_START_DEFAULT QCONST16(0.35f,15) |
|
#define SPEECH_PROB_CONTINUE_DEFAULT QCONST16(0.20f,15) |
|
#define NOISE_SUPPRESS_DEFAULT -15 |
|
#define ECHO_SUPPRESS_DEFAULT -40 |
|
#define ECHO_SUPPRESS_ACTIVE_DEFAULT -15 |
|
|
|
#ifndef NULL |
|
#define NULL 0 |
|
#endif |
|
|
|
#define SQR(x) ((x)*(x)) |
|
#define SQR16(x) (MULT16_16((x),(x))) |
|
#define SQR16_Q15(x) (MULT16_16_Q15((x),(x))) |
|
|
|
#ifdef FIXED_POINT |
|
static inline spx_word16_t DIV32_16_Q8(spx_word32_t a, spx_word32_t b) |
|
{ |
|
if (SHR32(a,7) >= b) |
|
{ |
|
return 32767; |
|
} else { |
|
if (b>=QCONST32(1,23)) |
|
{ |
|
a = SHR32(a,8); |
|
b = SHR32(b,8); |
|
} |
|
if (b>=QCONST32(1,19)) |
|
{ |
|
a = SHR32(a,4); |
|
b = SHR32(b,4); |
|
} |
|
if (b>=QCONST32(1,15)) |
|
{ |
|
a = SHR32(a,4); |
|
b = SHR32(b,4); |
|
} |
|
a = SHL32(a,8); |
|
return PDIV32_16(a,b); |
|
} |
|
|
|
} |
|
static inline spx_word16_t DIV32_16_Q15(spx_word32_t a, spx_word32_t b) |
|
{ |
|
if (SHR32(a,15) >= b) |
|
{ |
|
return 32767; |
|
} else { |
|
if (b>=QCONST32(1,23)) |
|
{ |
|
a = SHR32(a,8); |
|
b = SHR32(b,8); |
|
} |
|
if (b>=QCONST32(1,19)) |
|
{ |
|
a = SHR32(a,4); |
|
b = SHR32(b,4); |
|
} |
|
if (b>=QCONST32(1,15)) |
|
{ |
|
a = SHR32(a,4); |
|
b = SHR32(b,4); |
|
} |
|
a = SHL32(a,15)-a; |
|
return DIV32_16(a,b); |
|
} |
|
} |
|
#define SNR_SCALING 256.f |
|
#define SNR_SCALING_1 0.0039062f |
|
#define SNR_SHIFT 8 |
|
|
|
#define FRAC_SCALING 32767.f |
|
#define FRAC_SCALING_1 3.0518e-05 |
|
#define FRAC_SHIFT 1 |
|
|
|
#define EXPIN_SCALING 2048.f |
|
#define EXPIN_SCALING_1 0.00048828f |
|
#define EXPIN_SHIFT 11 |
|
#define EXPOUT_SCALING_1 1.5259e-05 |
|
|
|
#define NOISE_SHIFT 7 |
|
|
|
#else |
|
|
|
#define DIV32_16_Q8(a,b) ((a)/(b)) |
|
#define DIV32_16_Q15(a,b) ((a)/(b)) |
|
#define SNR_SCALING 1.f |
|
#define SNR_SCALING_1 1.f |
|
#define SNR_SHIFT 0 |
|
#define FRAC_SCALING 1.f |
|
#define FRAC_SCALING_1 1.f |
|
#define FRAC_SHIFT 0 |
|
#define NOISE_SHIFT 0 |
|
|
|
#define EXPIN_SCALING 1.f |
|
#define EXPIN_SCALING_1 1.f |
|
#define EXPOUT_SCALING_1 1.f |
|
|
|
#endif |
|
|
|
/** Speex pre-processor state. */ |
|
struct SpeexPreprocessState_ { |
|
/* Basic info */ |
|
int frame_size; /**< Number of samples processed each time */ |
|
int ps_size; /**< Number of points in the power spectrum */ |
|
int sampling_rate; /**< Sampling rate of the input/output */ |
|
int nbands; |
|
FilterBank *bank; |
|
|
|
/* Parameters */ |
|
int denoise_enabled; |
|
int vad_enabled; |
|
int dereverb_enabled; |
|
spx_word16_t reverb_decay; |
|
spx_word16_t reverb_level; |
|
spx_word16_t speech_prob_start; |
|
spx_word16_t speech_prob_continue; |
|
int noise_suppress; |
|
int echo_suppress; |
|
int echo_suppress_active; |
|
SpeexEchoState *echo_state; |
|
|
|
spx_word16_t speech_prob; /**< Probability last frame was speech */ |
|
|
|
/* DSP-related arrays */ |
|
spx_word16_t *frame; /**< Processing frame (2*ps_size) */ |
|
spx_word16_t *ft; /**< Processing frame in freq domain (2*ps_size) */ |
|
spx_word32_t *ps; /**< Current power spectrum */ |
|
spx_word16_t *gain2; /**< Adjusted gains */ |
|
spx_word16_t *gain_floor; /**< Minimum gain allowed */ |
|
spx_word16_t *window; /**< Analysis/Synthesis window */ |
|
spx_word32_t *noise; /**< Noise estimate */ |
|
spx_word32_t *reverb_estimate; /**< Estimate of reverb energy */ |
|
spx_word32_t *old_ps; /**< Power spectrum for last frame */ |
|
spx_word16_t *gain; /**< Ephraim Malah gain */ |
|
spx_word16_t *prior; /**< A-priori SNR */ |
|
spx_word16_t *post; /**< A-posteriori SNR */ |
|
|
|
spx_word32_t *S; /**< Smoothed power spectrum */ |
|
spx_word32_t *Smin; /**< See Cohen paper */ |
|
spx_word32_t *Stmp; /**< See Cohen paper */ |
|
int *update_prob; /**< Probability of speech presence for noise update */ |
|
|
|
spx_word16_t *zeta; /**< Smoothed a priori SNR */ |
|
spx_word32_t *echo_noise; |
|
spx_word32_t *residual_echo; |
|
|
|
/* Misc */ |
|
spx_word16_t *inbuf; /**< Input buffer (overlapped analysis) */ |
|
spx_word16_t *outbuf; /**< Output buffer (for overlap and add) */ |
|
|
|
/* AGC stuff, only for floating point for now */ |
|
#ifndef FIXED_POINT |
|
int agc_enabled; |
|
float agc_level; |
|
float loudness_accum; |
|
float *loudness_weight; /**< Perceptual loudness curve */ |
|
float loudness; /**< Loudness estimate */ |
|
float agc_gain; /**< Current AGC gain */ |
|
float max_gain; /**< Maximum gain allowed */ |
|
float max_increase_step; /**< Maximum increase in gain from one frame to another */ |
|
float max_decrease_step; /**< Maximum decrease in gain from one frame to another */ |
|
float prev_loudness; /**< Loudness of previous frame */ |
|
float init_max; /**< Current gain limit during initialisation */ |
|
#endif |
|
int nb_adapt; /**< Number of frames used for adaptation so far */ |
|
int was_speech; |
|
int min_count; /**< Number of frames processed so far */ |
|
void *fft_lookup; /**< Lookup table for the FFT */ |
|
#ifdef FIXED_POINT |
|
int frame_shift; |
|
#endif |
|
}; |
|
|
|
|
|
static void conj_window(spx_word16_t *w, int len) |
|
{ |
|
int i; |
|
for (i=0;i<len;i++) |
|
{ |
|
spx_word16_t tmp; |
|
#ifdef FIXED_POINT |
|
spx_word16_t x = DIV32_16(MULT16_16(32767,i),len); |
|
#else |
|
spx_word16_t x = DIV32_16(MULT16_16(QCONST16(4.f,13),i),len); |
|
#endif |
|
int inv=0; |
|
if (x<QCONST16(1.f,13)) |
|
{ |
|
} else if (x<QCONST16(2.f,13)) |
|
{ |
|
x=QCONST16(2.f,13)-x; |
|
inv=1; |
|
} else if (x<QCONST16(3.f,13)) |
|
{ |
|
x=x-QCONST16(2.f,13); |
|
inv=1; |
|
} else { |
|
x=QCONST16(2.f,13)-x+QCONST16(2.f,13); /* 4 - x */ |
|
} |
|
x = MULT16_16_Q14(QCONST16(1.271903f,14), x); |
|
tmp = SQR16_Q15(QCONST16(.5f,15)-MULT16_16_P15(QCONST16(.5f,15),spx_cos_norm(SHL32(EXTEND32(x),2)))); |
|
if (inv) |
|
tmp=SUB16(Q15_ONE,tmp); |
|
w[i]=spx_sqrt(SHL32(EXTEND32(tmp),15)); |
|
} |
|
} |
|
|
|
|
|
#ifdef FIXED_POINT |
|
/* This function approximates the gain function |
|
y = gamma(1.25)^2 * M(-.25;1;-x) / sqrt(x) |
|
which multiplied by xi/(1+xi) is the optimal gain |
|
in the loudness domain ( sqrt[amplitude] ) |
|
Input in Q11 format, output in Q15 |
|
*/ |
|
static inline spx_word32_t hypergeom_gain(spx_word32_t xx) |
|
{ |
|
int ind; |
|
spx_word16_t frac; |
|
/* Q13 table */ |
|
static const spx_word16_t table[21] = { |
|
6730, 8357, 9868, 11267, 12563, 13770, 14898, |
|
15959, 16961, 17911, 18816, 19682, 20512, 21311, |
|
22082, 22827, 23549, 24250, 24931, 25594, 26241}; |
|
ind = SHR32(xx,10); |
|
if (ind<0) |
|
return Q15_ONE; |
|
if (ind>19) |
|
return ADD32(EXTEND32(Q15_ONE),EXTEND32(DIV32_16(QCONST32(.1296,23), SHR32(xx,EXPIN_SHIFT-SNR_SHIFT)))); |
|
frac = SHL32(xx-SHL32(ind,10),5); |
|
return SHL32(DIV32_16(PSHR32(MULT16_16(Q15_ONE-frac,table[ind]) + MULT16_16(frac,table[ind+1]),7),(spx_sqrt(SHL32(xx,15)+6711))),7); |
|
} |
|
|
|
static inline spx_word16_t qcurve(spx_word16_t x) |
|
{ |
|
x = MAX16(x, 1); |
|
return DIV32_16(SHL32(EXTEND32(32767),9),ADD16(512,MULT16_16_Q15(QCONST16(.60f,15),DIV32_16(32767,x)))); |
|
} |
|
|
|
/* Compute the gain floor based on different floors for the background noise and residual echo */ |
|
static void compute_gain_floor(int noise_suppress, int effective_echo_suppress, spx_word32_t *noise, spx_word32_t *echo, spx_word16_t *gain_floor, int len) |
|
{ |
|
int i; |
|
|
|
if (noise_suppress > effective_echo_suppress) |
|
{ |
|
spx_word16_t noise_gain, gain_ratio; |
|
noise_gain = EXTRACT16(MIN32(Q15_ONE,SHR32(spx_exp(MULT16_16(QCONST16(0.11513,11),noise_suppress)),1))); |
|
gain_ratio = EXTRACT16(MIN32(Q15_ONE,SHR32(spx_exp(MULT16_16(QCONST16(.2302585f,11),effective_echo_suppress-noise_suppress)),1))); |
|
|
|
/* gain_floor = sqrt [ (noise*noise_floor + echo*echo_floor) / (noise+echo) ] */ |
|
for (i=0;i<len;i++) |
|
gain_floor[i] = MULT16_16_Q15(noise_gain, |
|
spx_sqrt(SHL32(EXTEND32(DIV32_16_Q15(PSHR32(noise[i],NOISE_SHIFT) + MULT16_32_Q15(gain_ratio,echo[i]), |
|
(1+PSHR32(noise[i],NOISE_SHIFT) + echo[i]) )),15))); |
|
} else { |
|
spx_word16_t echo_gain, gain_ratio; |
|
echo_gain = EXTRACT16(MIN32(Q15_ONE,SHR32(spx_exp(MULT16_16(QCONST16(0.11513,11),effective_echo_suppress)),1))); |
|
gain_ratio = EXTRACT16(MIN32(Q15_ONE,SHR32(spx_exp(MULT16_16(QCONST16(.2302585f,11),noise_suppress-effective_echo_suppress)),1))); |
|
|
|
/* gain_floor = sqrt [ (noise*noise_floor + echo*echo_floor) / (noise+echo) ] */ |
|
for (i=0;i<len;i++) |
|
gain_floor[i] = MULT16_16_Q15(echo_gain, |
|
spx_sqrt(SHL32(EXTEND32(DIV32_16_Q15(MULT16_32_Q15(gain_ratio,PSHR32(noise[i],NOISE_SHIFT)) + echo[i], |
|
(1+PSHR32(noise[i],NOISE_SHIFT) + echo[i]) )),15))); |
|
} |
|
} |
|
|
|
#else |
|
/* This function approximates the gain function |
|
y = gamma(1.25)^2 * M(-.25;1;-x) / sqrt(x) |
|
which multiplied by xi/(1+xi) is the optimal gain |
|
in the loudness domain ( sqrt[amplitude] ) |
|
*/ |
|
static inline spx_word32_t hypergeom_gain(spx_word32_t xx) |
|
{ |
|
int ind; |
|
float integer, frac; |
|
float x; |
|
static const float table[21] = { |
|
0.82157f, 1.02017f, 1.20461f, 1.37534f, 1.53363f, 1.68092f, 1.81865f, |
|
1.94811f, 2.07038f, 2.18638f, 2.29688f, 2.40255f, 2.50391f, 2.60144f, |
|
2.69551f, 2.78647f, 2.87458f, 2.96015f, 3.04333f, 3.12431f, 3.20326f}; |
|
x = EXPIN_SCALING_1*xx; |
|
integer = floor(2*x); |
|
ind = (int)integer; |
|
if (ind<0) |
|
return FRAC_SCALING; |
|
if (ind>19) |
|
return FRAC_SCALING*(1+.1296/x); |
|
frac = 2*x-integer; |
|
return FRAC_SCALING*((1-frac)*table[ind] + frac*table[ind+1])/sqrt(x+.0001f); |
|
} |
|
|
|
static inline spx_word16_t qcurve(spx_word16_t x) |
|
{ |
|
return 1.f/(1.f+.15f/(SNR_SCALING_1*x)); |
|
} |
|
|
|
static void compute_gain_floor(int noise_suppress, int effective_echo_suppress, spx_word32_t *noise, spx_word32_t *echo, spx_word16_t *gain_floor, int len) |
|
{ |
|
int i; |
|
float echo_floor; |
|
float noise_floor; |
|
|
|
noise_floor = exp(.2302585f*noise_suppress); |
|
echo_floor = exp(.2302585f*effective_echo_suppress); |
|
|
|
/* Compute the gain floor based on different floors for the background noise and residual echo */ |
|
for (i=0;i<len;i++) |
|
gain_floor[i] = FRAC_SCALING*sqrt(noise_floor*PSHR32(noise[i],NOISE_SHIFT) + echo_floor*echo[i])/sqrt(1+PSHR32(noise[i],NOISE_SHIFT) + echo[i]); |
|
} |
|
|
|
#endif |
|
EXPORT SpeexPreprocessState *speex_preprocess_state_init(int frame_size, int sampling_rate) |
|
{ |
|
int i; |
|
int N, N3, N4, M; |
|
|
|
SpeexPreprocessState *st = (SpeexPreprocessState *)speex_alloc(sizeof(SpeexPreprocessState)); |
|
st->frame_size = frame_size; |
|
|
|
/* Round ps_size down to the nearest power of two */ |
|
#if 0 |
|
i=1; |
|
st->ps_size = st->frame_size; |
|
while(1) |
|
{ |
|
if (st->ps_size & ~i) |
|
{ |
|
st->ps_size &= ~i; |
|
i<<=1; |
|
} else { |
|
break; |
|
} |
|
} |
|
|
|
|
|
if (st->ps_size < 3*st->frame_size/4) |
|
st->ps_size = st->ps_size * 3 / 2; |
|
#else |
|
st->ps_size = st->frame_size; |
|
#endif |
|
|
|
N = st->ps_size; |
|
N3 = 2*N - st->frame_size; |
|
N4 = st->frame_size - N3; |
|
|
|
st->sampling_rate = sampling_rate; |
|
st->denoise_enabled = 1; |
|
st->vad_enabled = 0; |
|
st->dereverb_enabled = 0; |
|
st->reverb_decay = 0; |
|
st->reverb_level = 0; |
|
st->noise_suppress = NOISE_SUPPRESS_DEFAULT; |
|
st->echo_suppress = ECHO_SUPPRESS_DEFAULT; |
|
st->echo_suppress_active = ECHO_SUPPRESS_ACTIVE_DEFAULT; |
|
|
|
st->speech_prob_start = SPEECH_PROB_START_DEFAULT; |
|
st->speech_prob_continue = SPEECH_PROB_CONTINUE_DEFAULT; |
|
|
|
st->echo_state = NULL; |
|
|
|
st->nbands = NB_BANDS; |
|
M = st->nbands; |
|
st->bank = filterbank_new(M, sampling_rate, N, 1); |
|
|
|
st->frame = (spx_word16_t*)speex_alloc(2*N*sizeof(spx_word16_t)); |
|
st->window = (spx_word16_t*)speex_alloc(2*N*sizeof(spx_word16_t)); |
|
st->ft = (spx_word16_t*)speex_alloc(2*N*sizeof(spx_word16_t)); |
|
|
|
st->ps = (spx_word32_t*)speex_alloc((N+M)*sizeof(spx_word32_t)); |
|
st->noise = (spx_word32_t*)speex_alloc((N+M)*sizeof(spx_word32_t)); |
|
st->echo_noise = (spx_word32_t*)speex_alloc((N+M)*sizeof(spx_word32_t)); |
|
st->residual_echo = (spx_word32_t*)speex_alloc((N+M)*sizeof(spx_word32_t)); |
|
st->reverb_estimate = (spx_word32_t*)speex_alloc((N+M)*sizeof(spx_word32_t)); |
|
st->old_ps = (spx_word32_t*)speex_alloc((N+M)*sizeof(spx_word32_t)); |
|
st->prior = (spx_word16_t*)speex_alloc((N+M)*sizeof(spx_word16_t)); |
|
st->post = (spx_word16_t*)speex_alloc((N+M)*sizeof(spx_word16_t)); |
|
st->gain = (spx_word16_t*)speex_alloc((N+M)*sizeof(spx_word16_t)); |
|
st->gain2 = (spx_word16_t*)speex_alloc((N+M)*sizeof(spx_word16_t)); |
|
st->gain_floor = (spx_word16_t*)speex_alloc((N+M)*sizeof(spx_word16_t)); |
|
st->zeta = (spx_word16_t*)speex_alloc((N+M)*sizeof(spx_word16_t)); |
|
|
|
st->S = (spx_word32_t*)speex_alloc(N*sizeof(spx_word32_t)); |
|
st->Smin = (spx_word32_t*)speex_alloc(N*sizeof(spx_word32_t)); |
|
st->Stmp = (spx_word32_t*)speex_alloc(N*sizeof(spx_word32_t)); |
|
st->update_prob = (int*)speex_alloc(N*sizeof(int)); |
|
|
|
st->inbuf = (spx_word16_t*)speex_alloc(N3*sizeof(spx_word16_t)); |
|
st->outbuf = (spx_word16_t*)speex_alloc(N3*sizeof(spx_word16_t)); |
|
|
|
conj_window(st->window, 2*N3); |
|
for (i=2*N3;i<2*st->ps_size;i++) |
|
st->window[i]=Q15_ONE; |
|
|
|
if (N4>0) |
|
{ |
|
for (i=N3-1;i>=0;i--) |
|
{ |
|
st->window[i+N3+N4]=st->window[i+N3]; |
|
st->window[i+N3]=1; |
|
} |
|
} |
|
for (i=0;i<N+M;i++) |
|
{ |
|
st->noise[i]=QCONST32(1.f,NOISE_SHIFT); |
|
st->reverb_estimate[i]=0; |
|
st->old_ps[i]=1; |
|
st->gain[i]=Q15_ONE; |
|
st->post[i]=SHL16(1, SNR_SHIFT); |
|
st->prior[i]=SHL16(1, SNR_SHIFT); |
|
} |
|
|
|
for (i=0;i<N;i++) |
|
st->update_prob[i] = 1; |
|
for (i=0;i<N3;i++) |
|
{ |
|
st->inbuf[i]=0; |
|
st->outbuf[i]=0; |
|
} |
|
#ifndef FIXED_POINT |
|
st->agc_enabled = 0; |
|
st->agc_level = 8000; |
|
st->loudness_weight = (float*)speex_alloc(N*sizeof(float)); |
|
for (i=0;i<N;i++) |
|
{ |
|
float ff=((float)i)*.5*sampling_rate/((float)N); |
|
/*st->loudness_weight[i] = .5f*(1.f/(1.f+ff/8000.f))+1.f*exp(-.5f*(ff-3800.f)*(ff-3800.f)/9e5f);*/ |
|
st->loudness_weight[i] = .35f-.35f*ff/16000.f+.73f*exp(-.5f*(ff-3800)*(ff-3800)/9e5f); |
|
if (st->loudness_weight[i]<.01f) |
|
st->loudness_weight[i]=.01f; |
|
st->loudness_weight[i] *= st->loudness_weight[i]; |
|
} |
|
/*st->loudness = pow(AMP_SCALE*st->agc_level,LOUDNESS_EXP);*/ |
|
st->loudness = 1e-15; |
|
st->agc_gain = 1; |
|
st->max_gain = 30; |
|
st->max_increase_step = exp(0.11513f * 12.*st->frame_size / st->sampling_rate); |
|
st->max_decrease_step = exp(-0.11513f * 40.*st->frame_size / st->sampling_rate); |
|
st->prev_loudness = 1; |
|
st->init_max = 1; |
|
#endif |
|
st->was_speech = 0; |
|
|
|
st->fft_lookup = spx_fft_init(2*N); |
|
|
|
st->nb_adapt=0; |
|
st->min_count=0; |
|
return st; |
|
} |
|
|
|
EXPORT void speex_preprocess_state_destroy(SpeexPreprocessState *st) |
|
{ |
|
speex_free(st->frame); |
|
speex_free(st->ft); |
|
speex_free(st->ps); |
|
speex_free(st->gain2); |
|
speex_free(st->gain_floor); |
|
speex_free(st->window); |
|
speex_free(st->noise); |
|
speex_free(st->reverb_estimate); |
|
speex_free(st->old_ps); |
|
speex_free(st->gain); |
|
speex_free(st->prior); |
|
speex_free(st->post); |
|
#ifndef FIXED_POINT |
|
speex_free(st->loudness_weight); |
|
#endif |
|
speex_free(st->echo_noise); |
|
speex_free(st->residual_echo); |
|
|
|
speex_free(st->S); |
|
speex_free(st->Smin); |
|
speex_free(st->Stmp); |
|
speex_free(st->update_prob); |
|
speex_free(st->zeta); |
|
|
|
speex_free(st->inbuf); |
|
speex_free(st->outbuf); |
|
|
|
spx_fft_destroy(st->fft_lookup); |
|
filterbank_destroy(st->bank); |
|
speex_free(st); |
|
} |
|
|
|
/* FIXME: The AGC doesn't work yet with fixed-point*/ |
|
#ifndef FIXED_POINT |
|
static void speex_compute_agc(SpeexPreprocessState *st, spx_word16_t Pframe, spx_word16_t *ft) |
|
{ |
|
int i; |
|
int N = st->ps_size; |
|
float target_gain; |
|
float loudness=1.f; |
|
float rate; |
|
|
|
for (i=2;i<N;i++) |
|
{ |
|
loudness += 2.f*N*st->ps[i]* st->loudness_weight[i]; |
|
} |
|
loudness=sqrt(loudness); |
|
/*if (loudness < 2*pow(st->loudness, 1.0/LOUDNESS_EXP) && |
|
loudness*2 > pow(st->loudness, 1.0/LOUDNESS_EXP))*/ |
|
if (Pframe>.3f) |
|
{ |
|
/*rate=2.0f*Pframe*Pframe/(1+st->nb_loudness_adapt);*/ |
|
rate = .03*Pframe*Pframe; |
|
st->loudness = (1-rate)*st->loudness + (rate)*pow(AMP_SCALE*loudness, LOUDNESS_EXP); |
|
st->loudness_accum = (1-rate)*st->loudness_accum + rate; |
|
if (st->init_max < st->max_gain && st->nb_adapt > 20) |
|
st->init_max *= 1.f + .1f*Pframe*Pframe; |
|
} |
|
/*printf ("%f %f %f %f\n", Pframe, loudness, pow(st->loudness, 1.0f/LOUDNESS_EXP), st->loudness2);*/ |
|
|
|
target_gain = AMP_SCALE*st->agc_level*pow(st->loudness/(1e-4+st->loudness_accum), -1.0f/LOUDNESS_EXP); |
|
|
|
if ((Pframe>.5 && st->nb_adapt > 20) || target_gain < st->agc_gain) |
|
{ |
|
if (target_gain > st->max_increase_step*st->agc_gain) |
|
target_gain = st->max_increase_step*st->agc_gain; |
|
if (target_gain < st->max_decrease_step*st->agc_gain && loudness < 10*st->prev_loudness) |
|
target_gain = st->max_decrease_step*st->agc_gain; |
|
if (target_gain > st->max_gain) |
|
target_gain = st->max_gain; |
|
if (target_gain > st->init_max) |
|
target_gain = st->init_max; |
|
|
|
st->agc_gain = target_gain; |
|
} |
|
/*fprintf (stderr, "%f %f %f\n", loudness, (float)AMP_SCALE_1*pow(st->loudness, 1.0f/LOUDNESS_EXP), st->agc_gain);*/ |
|
|
|
for (i=0;i<2*N;i++) |
|
ft[i] *= st->agc_gain; |
|
st->prev_loudness = loudness; |
|
} |
|
#endif |
|
|
|
static void preprocess_analysis(SpeexPreprocessState *st, spx_int16_t *x) |
|
{ |
|
int i; |
|
int N = st->ps_size; |
|
int N3 = 2*N - st->frame_size; |
|
int N4 = st->frame_size - N3; |
|
spx_word32_t *ps=st->ps; |
|
|
|
/* 'Build' input frame */ |
|
for (i=0;i<N3;i++) |
|
st->frame[i]=st->inbuf[i]; |
|
for (i=0;i<st->frame_size;i++) |
|
st->frame[N3+i]=x[i]; |
|
|
|
/* Update inbuf */ |
|
for (i=0;i<N3;i++) |
|
st->inbuf[i]=x[N4+i]; |
|
|
|
/* Windowing */ |
|
for (i=0;i<2*N;i++) |
|
st->frame[i] = MULT16_16_Q15(st->frame[i], st->window[i]); |
|
|
|
#ifdef FIXED_POINT |
|
{ |
|
spx_word16_t max_val=0; |
|
for (i=0;i<2*N;i++) |
|
max_val = MAX16(max_val, ABS16(st->frame[i])); |
|
st->frame_shift = 14-spx_ilog2(EXTEND32(max_val)); |
|
for (i=0;i<2*N;i++) |
|
st->frame[i] = SHL16(st->frame[i], st->frame_shift); |
|
} |
|
#endif |
|
|
|
/* Perform FFT */ |
|
spx_fft(st->fft_lookup, st->frame, st->ft); |
|
|
|
/* Power spectrum */ |
|
ps[0]=MULT16_16(st->ft[0],st->ft[0]); |
|
for (i=1;i<N;i++) |
|
ps[i]=MULT16_16(st->ft[2*i-1],st->ft[2*i-1]) + MULT16_16(st->ft[2*i],st->ft[2*i]); |
|
for (i=0;i<N;i++) |
|
st->ps[i] = PSHR32(st->ps[i], 2*st->frame_shift); |
|
|
|
filterbank_compute_bank32(st->bank, ps, ps+N); |
|
} |
|
|
|
static void update_noise_prob(SpeexPreprocessState *st) |
|
{ |
|
int i; |
|
int min_range; |
|
int N = st->ps_size; |
|
|
|
for (i=1;i<N-1;i++) |
|
st->S[i] = MULT16_32_Q15(QCONST16(.8f,15),st->S[i]) + MULT16_32_Q15(QCONST16(.05f,15),st->ps[i-1]) |
|
+ MULT16_32_Q15(QCONST16(.1f,15),st->ps[i]) + MULT16_32_Q15(QCONST16(.05f,15),st->ps[i+1]); |
|
st->S[0] = MULT16_32_Q15(QCONST16(.8f,15),st->S[0]) + MULT16_32_Q15(QCONST16(.2f,15),st->ps[0]); |
|
st->S[N-1] = MULT16_32_Q15(QCONST16(.8f,15),st->S[N-1]) + MULT16_32_Q15(QCONST16(.2f,15),st->ps[N-1]); |
|
|
|
if (st->nb_adapt==1) |
|
{ |
|
for (i=0;i<N;i++) |
|
st->Smin[i] = st->Stmp[i] = 0; |
|
} |
|
|
|
if (st->nb_adapt < 100) |
|
min_range = 15; |
|
else if (st->nb_adapt < 1000) |
|
min_range = 50; |
|
else if (st->nb_adapt < 10000) |
|
min_range = 150; |
|
else |
|
min_range = 300; |
|
if (st->min_count > min_range) |
|
{ |
|
st->min_count = 0; |
|
for (i=0;i<N;i++) |
|
{ |
|
st->Smin[i] = MIN32(st->Stmp[i], st->S[i]); |
|
st->Stmp[i] = st->S[i]; |
|
} |
|
} else { |
|
for (i=0;i<N;i++) |
|
{ |
|
st->Smin[i] = MIN32(st->Smin[i], st->S[i]); |
|
st->Stmp[i] = MIN32(st->Stmp[i], st->S[i]); |
|
} |
|
} |
|
for (i=0;i<N;i++) |
|
{ |
|
if (MULT16_32_Q15(QCONST16(.4f,15),st->S[i]) > st->Smin[i]) |
|
st->update_prob[i] = 1; |
|
else |
|
st->update_prob[i] = 0; |
|
/*fprintf (stderr, "%f ", st->S[i]/st->Smin[i]);*/ |
|
/*fprintf (stderr, "%f ", st->update_prob[i]);*/ |
|
} |
|
|
|
} |
|
|
|
#define NOISE_OVERCOMPENS 1. |
|
|
|
void speex_echo_get_residual(SpeexEchoState *st, spx_word32_t *Yout, int len); |
|
|
|
EXPORT int speex_preprocess(SpeexPreprocessState *st, spx_int16_t *x, spx_int32_t *echo) |
|
{ |
|
return speex_preprocess_run(st, x); |
|
} |
|
|
|
EXPORT int speex_preprocess_run(SpeexPreprocessState *st, spx_int16_t *x) |
|
{ |
|
int i; |
|
int M; |
|
int N = st->ps_size; |
|
int N3 = 2*N - st->frame_size; |
|
int N4 = st->frame_size - N3; |
|
spx_word32_t *ps=st->ps; |
|
spx_word32_t Zframe; |
|
spx_word16_t Pframe; |
|
spx_word16_t beta, beta_1; |
|
spx_word16_t effective_echo_suppress; |
|
|
|
st->nb_adapt++; |
|
if (st->nb_adapt>20000) |
|
st->nb_adapt = 20000; |
|
st->min_count++; |
|
|
|
beta = MAX16(QCONST16(.03,15),DIV32_16(Q15_ONE,st->nb_adapt)); |
|
beta_1 = Q15_ONE-beta; |
|
M = st->nbands; |
|
/* Deal with residual echo if provided */ |
|
if (st->echo_state) |
|
{ |
|
speex_echo_get_residual(st->echo_state, st->residual_echo, N); |
|
#ifndef FIXED_POINT |
|
/* If there are NaNs or ridiculous values, it'll show up in the DC and we just reset everything to zero */ |
|
if (!(st->residual_echo[0] >=0 && st->residual_echo[0]<N*1e9f)) |
|
{ |
|
for (i=0;i<N;i++) |
|
st->residual_echo[i] = 0; |
|
} |
|
#endif |
|
for (i=0;i<N;i++) |
|
st->echo_noise[i] = MAX32(MULT16_32_Q15(QCONST16(.6f,15),st->echo_noise[i]), st->residual_echo[i]); |
|
filterbank_compute_bank32(st->bank, st->echo_noise, st->echo_noise+N); |
|
} else { |
|
for (i=0;i<N+M;i++) |
|
st->echo_noise[i] = 0; |
|
} |
|
preprocess_analysis(st, x); |
|
|
|
update_noise_prob(st); |
|
|
|
/* Noise estimation always updated for the 10 first frames */ |
|
/*if (st->nb_adapt<10) |
|
{ |
|
for (i=1;i<N-1;i++) |
|
st->update_prob[i] = 0; |
|
} |
|
*/ |
|
|
|
/* Update the noise estimate for the frequencies where it can be */ |
|
for (i=0;i<N;i++) |
|
{ |
|
if (!st->update_prob[i] || st->ps[i] < PSHR32(st->noise[i], NOISE_SHIFT)) |
|
st->noise[i] = MAX32(EXTEND32(0),MULT16_32_Q15(beta_1,st->noise[i]) + MULT16_32_Q15(beta,SHL32(st->ps[i],NOISE_SHIFT))); |
|
} |
|
filterbank_compute_bank32(st->bank, st->noise, st->noise+N); |
|
|
|
/* Special case for first frame */ |
|
if (st->nb_adapt==1) |
|
for (i=0;i<N+M;i++) |
|
st->old_ps[i] = ps[i]; |
|
|
|
/* Compute a posteriori SNR */ |
|
for (i=0;i<N+M;i++) |
|
{ |
|
spx_word16_t gamma; |
|
|
|
/* Total noise estimate including residual echo and reverberation */ |
|
spx_word32_t tot_noise = ADD32(ADD32(ADD32(EXTEND32(1), PSHR32(st->noise[i],NOISE_SHIFT)) , st->echo_noise[i]) , st->reverb_estimate[i]); |
|
|
|
/* A posteriori SNR = ps/noise - 1*/ |
|
st->post[i] = SUB16(DIV32_16_Q8(ps[i],tot_noise), QCONST16(1.f,SNR_SHIFT)); |
|
st->post[i]=MIN16(st->post[i], QCONST16(100.f,SNR_SHIFT)); |
|
|
|
/* Computing update gamma = .1 + .9*(old/(old+noise))^2 */ |
|
gamma = QCONST16(.1f,15)+MULT16_16_Q15(QCONST16(.89f,15),SQR16_Q15(DIV32_16_Q15(st->old_ps[i],ADD32(st->old_ps[i],tot_noise)))); |
|
|
|
/* A priori SNR update = gamma*max(0,post) + (1-gamma)*old/noise */ |
|
st->prior[i] = EXTRACT16(PSHR32(ADD32(MULT16_16(gamma,MAX16(0,st->post[i])), MULT16_16(Q15_ONE-gamma,DIV32_16_Q8(st->old_ps[i],tot_noise))), 15)); |
|
st->prior[i]=MIN16(st->prior[i], QCONST16(100.f,SNR_SHIFT)); |
|
} |
|
|
|
/*print_vec(st->post, N+M, "");*/ |
|
|
|
/* Recursive average of the a priori SNR. A bit smoothed for the psd components */ |
|
st->zeta[0] = PSHR32(ADD32(MULT16_16(QCONST16(.7f,15),st->zeta[0]), MULT16_16(QCONST16(.3f,15),st->prior[0])),15); |
|
for (i=1;i<N-1;i++) |
|
st->zeta[i] = PSHR32(ADD32(ADD32(ADD32(MULT16_16(QCONST16(.7f,15),st->zeta[i]), MULT16_16(QCONST16(.15f,15),st->prior[i])), |
|
MULT16_16(QCONST16(.075f,15),st->prior[i-1])), MULT16_16(QCONST16(.075f,15),st->prior[i+1])),15); |
|
for (i=N-1;i<N+M;i++) |
|
st->zeta[i] = PSHR32(ADD32(MULT16_16(QCONST16(.7f,15),st->zeta[i]), MULT16_16(QCONST16(.3f,15),st->prior[i])),15); |
|
|
|
/* Speech probability of presence for the entire frame is based on the average filterbank a priori SNR */ |
|
Zframe = 0; |
|
for (i=N;i<N+M;i++) |
|
Zframe = ADD32(Zframe, EXTEND32(st->zeta[i])); |
|
Pframe = QCONST16(.1f,15)+MULT16_16_Q15(QCONST16(.899f,15),qcurve(DIV32_16(Zframe,st->nbands))); |
|
|
|
effective_echo_suppress = EXTRACT16(PSHR32(ADD32(MULT16_16(SUB16(Q15_ONE,Pframe), st->echo_suppress), MULT16_16(Pframe, st->echo_suppress_active)),15)); |
|
|
|
compute_gain_floor(st->noise_suppress, effective_echo_suppress, st->noise+N, st->echo_noise+N, st->gain_floor+N, M); |
|
|
|
/* Compute Ephraim & Malah gain speech probability of presence for each critical band (Bark scale) |
|
Technically this is actually wrong because the EM gaim assumes a slightly different probability |
|
distribution */ |
|
for (i=N;i<N+M;i++) |
|
{ |
|
/* See EM and Cohen papers*/ |
|
spx_word32_t theta; |
|
/* Gain from hypergeometric function */ |
|
spx_word32_t MM; |
|
/* Weiner filter gain */ |
|
spx_word16_t prior_ratio; |
|
/* a priority probability of speech presence based on Bark sub-band alone */ |
|
spx_word16_t P1; |
|
/* Speech absence a priori probability (considering sub-band and frame) */ |
|
spx_word16_t q; |
|
#ifdef FIXED_POINT |
|
spx_word16_t tmp; |
|
#endif |
|
|
|
prior_ratio = PDIV32_16(SHL32(EXTEND32(st->prior[i]), 15), ADD16(st->prior[i], SHL32(1,SNR_SHIFT))); |
|
theta = MULT16_32_P15(prior_ratio, QCONST32(1.f,EXPIN_SHIFT)+SHL32(EXTEND32(st->post[i]),EXPIN_SHIFT-SNR_SHIFT)); |
|
|
|
MM = hypergeom_gain(theta); |
|
/* Gain with bound */ |
|
st->gain[i] = EXTRACT16(MIN32(Q15_ONE, MULT16_32_Q15(prior_ratio, MM))); |
|
/* Save old Bark power spectrum */ |
|
st->old_ps[i] = MULT16_32_P15(QCONST16(.2f,15),st->old_ps[i]) + MULT16_32_P15(MULT16_16_P15(QCONST16(.8f,15),SQR16_Q15(st->gain[i])),ps[i]); |
|
|
|
P1 = QCONST16(.199f,15)+MULT16_16_Q15(QCONST16(.8f,15),qcurve (st->zeta[i])); |
|
q = Q15_ONE-MULT16_16_Q15(Pframe,P1); |
|
#ifdef FIXED_POINT |
|
theta = MIN32(theta, EXTEND32(32767)); |
|
/*Q8*/tmp = MULT16_16_Q15((SHL32(1,SNR_SHIFT)+st->prior[i]),EXTRACT16(MIN32(Q15ONE,SHR32(spx_exp(-EXTRACT16(theta)),1)))); |
|
tmp = MIN16(QCONST16(3.,SNR_SHIFT), tmp); /* Prevent overflows in the next line*/ |
|
/*Q8*/tmp = EXTRACT16(PSHR32(MULT16_16(PDIV32_16(SHL32(EXTEND32(q),8),(Q15_ONE-q)),tmp),8)); |
|
st->gain2[i]=DIV32_16(SHL32(EXTEND32(32767),SNR_SHIFT), ADD16(256,tmp)); |
|
#else |
|
st->gain2[i]=1/(1.f + (q/(1.f-q))*(1+st->prior[i])*exp(-theta)); |
|
#endif |
|
} |
|
/* Convert the EM gains and speech prob to linear frequency */ |
|
filterbank_compute_psd16(st->bank,st->gain2+N, st->gain2); |
|
filterbank_compute_psd16(st->bank,st->gain+N, st->gain); |
|
|
|
/* Use 1 for linear gain resolution (best) or 0 for Bark gain resolution (faster) */ |
|
if (1) |
|
{ |
|
filterbank_compute_psd16(st->bank,st->gain_floor+N, st->gain_floor); |
|
|
|
/* Compute gain according to the Ephraim-Malah algorithm -- linear frequency */ |
|
for (i=0;i<N;i++) |
|
{ |
|
spx_word32_t MM; |
|
spx_word32_t theta; |
|
spx_word16_t prior_ratio; |
|
spx_word16_t tmp; |
|
spx_word16_t p; |
|
spx_word16_t g; |
|
|
|
/* Wiener filter gain */ |
|
prior_ratio = PDIV32_16(SHL32(EXTEND32(st->prior[i]), 15), ADD16(st->prior[i], SHL32(1,SNR_SHIFT))); |
|
theta = MULT16_32_P15(prior_ratio, QCONST32(1.f,EXPIN_SHIFT)+SHL32(EXTEND32(st->post[i]),EXPIN_SHIFT-SNR_SHIFT)); |
|
|
|
/* Optimal estimator for loudness domain */ |
|
MM = hypergeom_gain(theta); |
|
/* EM gain with bound */ |
|
g = EXTRACT16(MIN32(Q15_ONE, MULT16_32_Q15(prior_ratio, MM))); |
|
/* Interpolated speech probability of presence */ |
|
p = st->gain2[i]; |
|
|
|
/* Constrain the gain to be close to the Bark scale gain */ |
|
if (MULT16_16_Q15(QCONST16(.333f,15),g) > st->gain[i]) |
|
g = MULT16_16(3,st->gain[i]); |
|
st->gain[i] = g; |
|
|
|
/* Save old power spectrum */ |
|
st->old_ps[i] = MULT16_32_P15(QCONST16(.2f,15),st->old_ps[i]) + MULT16_32_P15(MULT16_16_P15(QCONST16(.8f,15),SQR16_Q15(st->gain[i])),ps[i]); |
|
|
|
/* Apply gain floor */ |
|
if (st->gain[i] < st->gain_floor[i]) |
|
st->gain[i] = st->gain_floor[i]; |
|
|
|
/* Exponential decay model for reverberation (unused) */ |
|
/*st->reverb_estimate[i] = st->reverb_decay*st->reverb_estimate[i] + st->reverb_decay*st->reverb_level*st->gain[i]*st->gain[i]*st->ps[i];*/ |
|
|
|
/* Take into account speech probability of presence (loudness domain MMSE estimator) */ |
|
/* gain2 = [p*sqrt(gain)+(1-p)*sqrt(gain _floor) ]^2 */ |
|
tmp = MULT16_16_P15(p,spx_sqrt(SHL32(EXTEND32(st->gain[i]),15))) + MULT16_16_P15(SUB16(Q15_ONE,p),spx_sqrt(SHL32(EXTEND32(st->gain_floor[i]),15))); |
|
st->gain2[i]=SQR16_Q15(tmp); |
|
|
|
/* Use this if you want a log-domain MMSE estimator instead */ |
|
/*st->gain2[i] = pow(st->gain[i], p) * pow(st->gain_floor[i],1.f-p);*/ |
|
} |
|
} else { |
|
for (i=N;i<N+M;i++) |
|
{ |
|
spx_word16_t tmp; |
|
spx_word16_t p = st->gain2[i]; |
|
st->gain[i] = MAX16(st->gain[i], st->gain_floor[i]); |
|
tmp = MULT16_16_P15(p,spx_sqrt(SHL32(EXTEND32(st->gain[i]),15))) + MULT16_16_P15(SUB16(Q15_ONE,p),spx_sqrt(SHL32(EXTEND32(st->gain_floor[i]),15))); |
|
st->gain2[i]=SQR16_Q15(tmp); |
|
} |
|
filterbank_compute_psd16(st->bank,st->gain2+N, st->gain2); |
|
} |
|
|
|
/* If noise suppression is off, don't apply the gain (but then why call this in the first place!) */ |
|
if (!st->denoise_enabled) |
|
{ |
|
for (i=0;i<N+M;i++) |
|
st->gain2[i]=Q15_ONE; |
|
} |
|
|
|
/* Apply computed gain */ |
|
for (i=1;i<N;i++) |
|
{ |
|
st->ft[2*i-1] = MULT16_16_P15(st->gain2[i],st->ft[2*i-1]); |
|
st->ft[2*i] = MULT16_16_P15(st->gain2[i],st->ft[2*i]); |
|
} |
|
st->ft[0] = MULT16_16_P15(st->gain2[0],st->ft[0]); |
|
st->ft[2*N-1] = MULT16_16_P15(st->gain2[N-1],st->ft[2*N-1]); |
|
|
|
/*FIXME: This *will* not work for fixed-point */ |
|
#ifndef FIXED_POINT |
|
if (st->agc_enabled) |
|
speex_compute_agc(st, Pframe, st->ft); |
|
#endif |
|
|
|
/* Inverse FFT with 1/N scaling */ |
|
spx_ifft(st->fft_lookup, st->ft, st->frame); |
|
/* Scale back to original (lower) amplitude */ |
|
for (i=0;i<2*N;i++) |
|
st->frame[i] = PSHR16(st->frame[i], st->frame_shift); |
|
|
|
/*FIXME: This *will* not work for fixed-point */ |
|
#ifndef FIXED_POINT |
|
if (st->agc_enabled) |
|
{ |
|
float max_sample=0; |
|
for (i=0;i<2*N;i++) |
|
if (fabs(st->frame[i])>max_sample) |
|
max_sample = fabs(st->frame[i]); |
|
if (max_sample>28000.f) |
|
{ |
|
float damp = 28000.f/max_sample; |
|
for (i=0;i<2*N;i++) |
|
st->frame[i] *= damp; |
|
} |
|
} |
|
#endif |
|
|
|
/* Synthesis window (for WOLA) */ |
|
for (i=0;i<2*N;i++) |
|
st->frame[i] = MULT16_16_Q15(st->frame[i], st->window[i]); |
|
|
|
/* Perform overlap and add */ |
|
for (i=0;i<N3;i++) |
|
x[i] = st->outbuf[i] + st->frame[i]; |
|
for (i=0;i<N4;i++) |
|
x[N3+i] = st->frame[N3+i]; |
|
|
|
/* Update outbuf */ |
|
for (i=0;i<N3;i++) |
|
st->outbuf[i] = st->frame[st->frame_size+i]; |
|
|
|
/* FIXME: This VAD is a kludge */ |
|
st->speech_prob = Pframe; |
|
if (st->vad_enabled) |
|
{ |
|
if (st->speech_prob > st->speech_prob_start || (st->was_speech && st->speech_prob > st->speech_prob_continue)) |
|
{ |
|
st->was_speech=1; |
|
return 1; |
|
} else |
|
{ |
|
st->was_speech=0; |
|
return 0; |
|
} |
|
} else { |
|
return 1; |
|
} |
|
} |
|
|
|
EXPORT void speex_preprocess_estimate_update(SpeexPreprocessState *st, spx_int16_t *x) |
|
{ |
|
int i; |
|
int N = st->ps_size; |
|
int N3 = 2*N - st->frame_size; |
|
int M; |
|
spx_word32_t *ps=st->ps; |
|
|
|
M = st->nbands; |
|
st->min_count++; |
|
|
|
preprocess_analysis(st, x); |
|
|
|
update_noise_prob(st); |
|
|
|
for (i=1;i<N-1;i++) |
|
{ |
|
if (!st->update_prob[i] || st->ps[i] < PSHR32(st->noise[i],NOISE_SHIFT)) |
|
{ |
|
st->noise[i] = MULT16_32_Q15(QCONST16(.95f,15),st->noise[i]) + MULT16_32_Q15(QCONST16(.05f,15),SHL32(st->ps[i],NOISE_SHIFT)); |
|
} |
|
} |
|
|
|
for (i=0;i<N3;i++) |
|
st->outbuf[i] = MULT16_16_Q15(x[st->frame_size-N3+i],st->window[st->frame_size+i]); |
|
|
|
/* Save old power spectrum */ |
|
for (i=0;i<N+M;i++) |
|
st->old_ps[i] = ps[i]; |
|
|
|
for (i=0;i<N;i++) |
|
st->reverb_estimate[i] = MULT16_32_Q15(st->reverb_decay, st->reverb_estimate[i]); |
|
} |
|
|
|
|
|
EXPORT int speex_preprocess_ctl(SpeexPreprocessState *state, int request, void *ptr) |
|
{ |
|
int i; |
|
SpeexPreprocessState *st; |
|
st=(SpeexPreprocessState*)state; |
|
switch(request) |
|
{ |
|
case SPEEX_PREPROCESS_SET_DENOISE: |
|
st->denoise_enabled = (*(spx_int32_t*)ptr); |
|
break; |
|
case SPEEX_PREPROCESS_GET_DENOISE: |
|
(*(spx_int32_t*)ptr) = st->denoise_enabled; |
|
break; |
|
#ifndef FIXED_POINT |
|
case SPEEX_PREPROCESS_SET_AGC: |
|
st->agc_enabled = (*(spx_int32_t*)ptr); |
|
break; |
|
case SPEEX_PREPROCESS_GET_AGC: |
|
(*(spx_int32_t*)ptr) = st->agc_enabled; |
|
break; |
|
#ifndef DISABLE_FLOAT_API |
|
case SPEEX_PREPROCESS_SET_AGC_LEVEL: |
|
st->agc_level = (*(float*)ptr); |
|
if (st->agc_level<1) |
|
st->agc_level=1; |
|
if (st->agc_level>32768) |
|
st->agc_level=32768; |
|
break; |
|
case SPEEX_PREPROCESS_GET_AGC_LEVEL: |
|
(*(float*)ptr) = st->agc_level; |
|
break; |
|
#endif /* #ifndef DISABLE_FLOAT_API */ |
|
case SPEEX_PREPROCESS_SET_AGC_INCREMENT: |
|
st->max_increase_step = exp(0.11513f * (*(spx_int32_t*)ptr)*st->frame_size / st->sampling_rate); |
|
break; |
|
case SPEEX_PREPROCESS_GET_AGC_INCREMENT: |
|
(*(spx_int32_t*)ptr) = floor(.5+8.6858*log(st->max_increase_step)*st->sampling_rate/st->frame_size); |
|
break; |
|
case SPEEX_PREPROCESS_SET_AGC_DECREMENT: |
|
st->max_decrease_step = exp(0.11513f * (*(spx_int32_t*)ptr)*st->frame_size / st->sampling_rate); |
|
break; |
|
case SPEEX_PREPROCESS_GET_AGC_DECREMENT: |
|
(*(spx_int32_t*)ptr) = floor(.5+8.6858*log(st->max_decrease_step)*st->sampling_rate/st->frame_size); |
|
break; |
|
case SPEEX_PREPROCESS_SET_AGC_MAX_GAIN: |
|
st->max_gain = exp(0.11513f * (*(spx_int32_t*)ptr)); |
|
break; |
|
case SPEEX_PREPROCESS_GET_AGC_MAX_GAIN: |
|
(*(spx_int32_t*)ptr) = floor(.5+8.6858*log(st->max_gain)); |
|
break; |
|
#endif |
|
case SPEEX_PREPROCESS_SET_VAD: |
|
// Disabled by mlebeau, we don't want this in the launched Google Mobile App for iPhone. |
|
//speex_warning("The VAD has been replaced by a hack pending a complete rewrite"); |
|
st->vad_enabled = (*(spx_int32_t*)ptr); |
|
break; |
|
case SPEEX_PREPROCESS_GET_VAD: |
|
(*(spx_int32_t*)ptr) = st->vad_enabled; |
|
break; |
|
|
|
case SPEEX_PREPROCESS_SET_DEREVERB: |
|
st->dereverb_enabled = (*(spx_int32_t*)ptr); |
|
for (i=0;i<st->ps_size;i++) |
|
st->reverb_estimate[i]=0; |
|
break; |
|
case SPEEX_PREPROCESS_GET_DEREVERB: |
|
(*(spx_int32_t*)ptr) = st->dereverb_enabled; |
|
break; |
|
|
|
case SPEEX_PREPROCESS_SET_DEREVERB_LEVEL: |
|
/* FIXME: Re-enable when de-reverberation is actually enabled again */ |
|
/*st->reverb_level = (*(float*)ptr);*/ |
|
break; |
|
case SPEEX_PREPROCESS_GET_DEREVERB_LEVEL: |
|
/* FIXME: Re-enable when de-reverberation is actually enabled again */ |
|
/*(*(float*)ptr) = st->reverb_level;*/ |
|
break; |
|
|
|
case SPEEX_PREPROCESS_SET_DEREVERB_DECAY: |
|
/* FIXME: Re-enable when de-reverberation is actually enabled again */ |
|
/*st->reverb_decay = (*(float*)ptr);*/ |
|
break; |
|
case SPEEX_PREPROCESS_GET_DEREVERB_DECAY: |
|
/* FIXME: Re-enable when de-reverberation is actually enabled again */ |
|
/*(*(float*)ptr) = st->reverb_decay;*/ |
|
break; |
|
|
|
case SPEEX_PREPROCESS_SET_PROB_START: |
|
*(spx_int32_t*)ptr = MIN32(100,MAX32(0, *(spx_int32_t*)ptr)); |
|
st->speech_prob_start = DIV32_16(MULT16_16(Q15ONE,*(spx_int32_t*)ptr), 100); |
|
break; |
|
case SPEEX_PREPROCESS_GET_PROB_START: |
|
(*(spx_int32_t*)ptr) = MULT16_16_Q15(st->speech_prob_start, 100); |
|
break; |
|
|
|
case SPEEX_PREPROCESS_SET_PROB_CONTINUE: |
|
*(spx_int32_t*)ptr = MIN32(100,MAX32(0, *(spx_int32_t*)ptr)); |
|
st->speech_prob_continue = DIV32_16(MULT16_16(Q15ONE,*(spx_int32_t*)ptr), 100); |
|
break; |
|
case SPEEX_PREPROCESS_GET_PROB_CONTINUE: |
|
(*(spx_int32_t*)ptr) = MULT16_16_Q15(st->speech_prob_continue, 100); |
|
break; |
|
|
|
case SPEEX_PREPROCESS_SET_NOISE_SUPPRESS: |
|
st->noise_suppress = -ABS(*(spx_int32_t*)ptr); |
|
break; |
|
case SPEEX_PREPROCESS_GET_NOISE_SUPPRESS: |
|
(*(spx_int32_t*)ptr) = st->noise_suppress; |
|
break; |
|
case SPEEX_PREPROCESS_SET_ECHO_SUPPRESS: |
|
st->echo_suppress = -ABS(*(spx_int32_t*)ptr); |
|
break; |
|
case SPEEX_PREPROCESS_GET_ECHO_SUPPRESS: |
|
(*(spx_int32_t*)ptr) = st->echo_suppress; |
|
break; |
|
case SPEEX_PREPROCESS_SET_ECHO_SUPPRESS_ACTIVE: |
|
st->echo_suppress_active = -ABS(*(spx_int32_t*)ptr); |
|
break; |
|
case SPEEX_PREPROCESS_GET_ECHO_SUPPRESS_ACTIVE: |
|
(*(spx_int32_t*)ptr) = st->echo_suppress_active; |
|
break; |
|
case SPEEX_PREPROCESS_SET_ECHO_STATE: |
|
st->echo_state = (SpeexEchoState*)ptr; |
|
break; |
|
case SPEEX_PREPROCESS_GET_ECHO_STATE: |
|
(*(SpeexEchoState**)ptr) = (SpeexEchoState*)st->echo_state; |
|
break; |
|
#ifndef FIXED_POINT |
|
case SPEEX_PREPROCESS_GET_AGC_LOUDNESS: |
|
(*(spx_int32_t*)ptr) = pow(st->loudness, 1.0/LOUDNESS_EXP); |
|
break; |
|
case SPEEX_PREPROCESS_GET_AGC_GAIN: |
|
(*(spx_int32_t*)ptr) = floor(.5+8.6858*log(st->agc_gain)); |
|
break; |
|
#endif |
|
case SPEEX_PREPROCESS_GET_PSD_SIZE: |
|
case SPEEX_PREPROCESS_GET_NOISE_PSD_SIZE: |
|
(*(spx_int32_t*)ptr) = st->ps_size; |
|
break; |
|
case SPEEX_PREPROCESS_GET_PSD: |
|
for(i=0;i<st->ps_size;i++) |
|
((spx_int32_t *)ptr)[i] = (spx_int32_t) st->ps[i]; |
|
break; |
|
case SPEEX_PREPROCESS_GET_NOISE_PSD: |
|
for(i=0;i<st->ps_size;i++) |
|
((spx_int32_t *)ptr)[i] = (spx_int32_t) PSHR32(st->noise[i], NOISE_SHIFT); |
|
break; |
|
case SPEEX_PREPROCESS_GET_PROB: |
|
(*(spx_int32_t*)ptr) = MULT16_16_Q15(st->speech_prob, 100); |
|
break; |
|
#ifndef FIXED_POINT |
|
case SPEEX_PREPROCESS_SET_AGC_TARGET: |
|
st->agc_level = (*(spx_int32_t*)ptr); |
|
if (st->agc_level<1) |
|
st->agc_level=1; |
|
if (st->agc_level>32768) |
|
st->agc_level=32768; |
|
break; |
|
case SPEEX_PREPROCESS_GET_AGC_TARGET: |
|
(*(spx_int32_t*)ptr) = st->agc_level; |
|
break; |
|
#endif |
|
default: |
|
speex_warning_int("Unknown speex_preprocess_ctl request: ", request); |
|
return -1; |
|
} |
|
return 0; |
|
} |
|
|
|
#ifdef FIXED_DEBUG |
|
long long spx_mips=0; |
|
#endif |
|
|
|
|