153a5a1b3Sopenharmony_ci/* Copyright (C) 2003 Epic Games (written by Jean-Marc Valin)
253a5a1b3Sopenharmony_ci   Copyright (C) 2004-2006 Epic Games
353a5a1b3Sopenharmony_ci
453a5a1b3Sopenharmony_ci   File: preprocess.c
553a5a1b3Sopenharmony_ci   Preprocessor with denoising based on the algorithm by Ephraim and Malah
653a5a1b3Sopenharmony_ci
753a5a1b3Sopenharmony_ci   Redistribution and use in source and binary forms, with or without
853a5a1b3Sopenharmony_ci   modification, are permitted provided that the following conditions are
953a5a1b3Sopenharmony_ci   met:
1053a5a1b3Sopenharmony_ci
1153a5a1b3Sopenharmony_ci   1. Redistributions of source code must retain the above copyright notice,
1253a5a1b3Sopenharmony_ci   this list of conditions and the following disclaimer.
1353a5a1b3Sopenharmony_ci
1453a5a1b3Sopenharmony_ci   2. Redistributions in binary form must reproduce the above copyright
1553a5a1b3Sopenharmony_ci   notice, this list of conditions and the following disclaimer in the
1653a5a1b3Sopenharmony_ci   documentation and/or other materials provided with the distribution.
1753a5a1b3Sopenharmony_ci
1853a5a1b3Sopenharmony_ci   3. The name of the author may not be used to endorse or promote products
1953a5a1b3Sopenharmony_ci   derived from this software without specific prior written permission.
2053a5a1b3Sopenharmony_ci
2153a5a1b3Sopenharmony_ci   THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2253a5a1b3Sopenharmony_ci   IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2353a5a1b3Sopenharmony_ci   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2453a5a1b3Sopenharmony_ci   DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
2553a5a1b3Sopenharmony_ci   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2653a5a1b3Sopenharmony_ci   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
2753a5a1b3Sopenharmony_ci   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2853a5a1b3Sopenharmony_ci   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
2953a5a1b3Sopenharmony_ci   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
3053a5a1b3Sopenharmony_ci   ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3153a5a1b3Sopenharmony_ci   POSSIBILITY OF SUCH DAMAGE.
3253a5a1b3Sopenharmony_ci*/
3353a5a1b3Sopenharmony_ci
3453a5a1b3Sopenharmony_ci
3553a5a1b3Sopenharmony_ci/*
3653a5a1b3Sopenharmony_ci   Recommended papers:
3753a5a1b3Sopenharmony_ci
3853a5a1b3Sopenharmony_ci   Y. Ephraim and D. Malah, "Speech enhancement using minimum mean-square error
3953a5a1b3Sopenharmony_ci   short-time spectral amplitude estimator". IEEE Transactions on Acoustics,
4053a5a1b3Sopenharmony_ci   Speech and Signal Processing, vol. ASSP-32, no. 6, pp. 1109-1121, 1984.
4153a5a1b3Sopenharmony_ci
4253a5a1b3Sopenharmony_ci   Y. Ephraim and D. Malah, "Speech enhancement using minimum mean-square error
4353a5a1b3Sopenharmony_ci   log-spectral amplitude estimator". IEEE Transactions on Acoustics, Speech and
4453a5a1b3Sopenharmony_ci   Signal Processing, vol. ASSP-33, no. 2, pp. 443-445, 1985.
4553a5a1b3Sopenharmony_ci
4653a5a1b3Sopenharmony_ci   I. Cohen and B. Berdugo, "Speech enhancement for non-stationary noise environments".
4753a5a1b3Sopenharmony_ci   Signal Processing, vol. 81, no. 2, pp. 2403-2418, 2001.
4853a5a1b3Sopenharmony_ci
4953a5a1b3Sopenharmony_ci   Stefan Gustafsson, Rainer Martin, Peter Jax, and Peter Vary. "A psychoacoustic
5053a5a1b3Sopenharmony_ci   approach to combined acoustic echo cancellation and noise reduction". IEEE
5153a5a1b3Sopenharmony_ci   Transactions on Speech and Audio Processing, 2002.
5253a5a1b3Sopenharmony_ci
5353a5a1b3Sopenharmony_ci   J.-M. Valin, J. Rouat, and F. Michaud, "Microphone array post-filter for separation
5453a5a1b3Sopenharmony_ci   of simultaneous non-stationary sources". In Proceedings IEEE International
5553a5a1b3Sopenharmony_ci   Conference on Acoustics, Speech, and Signal Processing, 2004.
5653a5a1b3Sopenharmony_ci*/
5753a5a1b3Sopenharmony_ci
5853a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H
5953a5a1b3Sopenharmony_ci#include "config.h"
6053a5a1b3Sopenharmony_ci#endif
6153a5a1b3Sopenharmony_ci
6253a5a1b3Sopenharmony_ci#include <math.h>
6353a5a1b3Sopenharmony_ci#include "speex/speex_preprocess.h"
6453a5a1b3Sopenharmony_ci#include "speex/speex_echo.h"
6553a5a1b3Sopenharmony_ci#include "arch.h"
6653a5a1b3Sopenharmony_ci#include "fftwrap.h"
6753a5a1b3Sopenharmony_ci#include "filterbank.h"
6853a5a1b3Sopenharmony_ci#include "math_approx.h"
6953a5a1b3Sopenharmony_ci#include "os_support.h"
7053a5a1b3Sopenharmony_ci
7153a5a1b3Sopenharmony_ci#define LOUDNESS_EXP 5.f
7253a5a1b3Sopenharmony_ci#define AMP_SCALE .001f
7353a5a1b3Sopenharmony_ci#define AMP_SCALE_1 1000.f
7453a5a1b3Sopenharmony_ci
7553a5a1b3Sopenharmony_ci#define NB_BANDS 24
7653a5a1b3Sopenharmony_ci
7753a5a1b3Sopenharmony_ci#define SPEECH_PROB_START_DEFAULT       QCONST16(0.35f,15)
7853a5a1b3Sopenharmony_ci#define SPEECH_PROB_CONTINUE_DEFAULT    QCONST16(0.20f,15)
7953a5a1b3Sopenharmony_ci#define NOISE_SUPPRESS_DEFAULT       -15
8053a5a1b3Sopenharmony_ci#define ECHO_SUPPRESS_DEFAULT        -40
8153a5a1b3Sopenharmony_ci#define ECHO_SUPPRESS_ACTIVE_DEFAULT -15
8253a5a1b3Sopenharmony_ci
8353a5a1b3Sopenharmony_ci#ifndef NULL
8453a5a1b3Sopenharmony_ci#define NULL 0
8553a5a1b3Sopenharmony_ci#endif
8653a5a1b3Sopenharmony_ci
8753a5a1b3Sopenharmony_ci#define SQR(x) ((x)*(x))
8853a5a1b3Sopenharmony_ci#define SQR16(x) (MULT16_16((x),(x)))
8953a5a1b3Sopenharmony_ci#define SQR16_Q15(x) (MULT16_16_Q15((x),(x)))
9053a5a1b3Sopenharmony_ci
9153a5a1b3Sopenharmony_ci#ifdef FIXED_POINT
9253a5a1b3Sopenharmony_cistatic inline spx_word16_t DIV32_16_Q8(spx_word32_t a, spx_word32_t b)
9353a5a1b3Sopenharmony_ci{
9453a5a1b3Sopenharmony_ci   if (SHR32(a,7) >= b)
9553a5a1b3Sopenharmony_ci   {
9653a5a1b3Sopenharmony_ci      return 32767;
9753a5a1b3Sopenharmony_ci   } else {
9853a5a1b3Sopenharmony_ci      if (b>=QCONST32(1,23))
9953a5a1b3Sopenharmony_ci      {
10053a5a1b3Sopenharmony_ci         a = SHR32(a,8);
10153a5a1b3Sopenharmony_ci         b = SHR32(b,8);
10253a5a1b3Sopenharmony_ci      }
10353a5a1b3Sopenharmony_ci      if (b>=QCONST32(1,19))
10453a5a1b3Sopenharmony_ci      {
10553a5a1b3Sopenharmony_ci         a = SHR32(a,4);
10653a5a1b3Sopenharmony_ci         b = SHR32(b,4);
10753a5a1b3Sopenharmony_ci      }
10853a5a1b3Sopenharmony_ci      if (b>=QCONST32(1,15))
10953a5a1b3Sopenharmony_ci      {
11053a5a1b3Sopenharmony_ci         a = SHR32(a,4);
11153a5a1b3Sopenharmony_ci         b = SHR32(b,4);
11253a5a1b3Sopenharmony_ci      }
11353a5a1b3Sopenharmony_ci      a = SHL32(a,8);
11453a5a1b3Sopenharmony_ci      return PDIV32_16(a,b);
11553a5a1b3Sopenharmony_ci   }
11653a5a1b3Sopenharmony_ci
11753a5a1b3Sopenharmony_ci}
11853a5a1b3Sopenharmony_cistatic inline spx_word16_t DIV32_16_Q15(spx_word32_t a, spx_word32_t b)
11953a5a1b3Sopenharmony_ci{
12053a5a1b3Sopenharmony_ci   if (SHR32(a,15) >= b)
12153a5a1b3Sopenharmony_ci   {
12253a5a1b3Sopenharmony_ci      return 32767;
12353a5a1b3Sopenharmony_ci   } else {
12453a5a1b3Sopenharmony_ci      if (b>=QCONST32(1,23))
12553a5a1b3Sopenharmony_ci      {
12653a5a1b3Sopenharmony_ci         a = SHR32(a,8);
12753a5a1b3Sopenharmony_ci         b = SHR32(b,8);
12853a5a1b3Sopenharmony_ci      }
12953a5a1b3Sopenharmony_ci      if (b>=QCONST32(1,19))
13053a5a1b3Sopenharmony_ci      {
13153a5a1b3Sopenharmony_ci         a = SHR32(a,4);
13253a5a1b3Sopenharmony_ci         b = SHR32(b,4);
13353a5a1b3Sopenharmony_ci      }
13453a5a1b3Sopenharmony_ci      if (b>=QCONST32(1,15))
13553a5a1b3Sopenharmony_ci      {
13653a5a1b3Sopenharmony_ci         a = SHR32(a,4);
13753a5a1b3Sopenharmony_ci         b = SHR32(b,4);
13853a5a1b3Sopenharmony_ci      }
13953a5a1b3Sopenharmony_ci      a = SHL32(a,15)-a;
14053a5a1b3Sopenharmony_ci      return DIV32_16(a,b);
14153a5a1b3Sopenharmony_ci   }
14253a5a1b3Sopenharmony_ci}
14353a5a1b3Sopenharmony_ci#define SNR_SCALING 256.f
14453a5a1b3Sopenharmony_ci#define SNR_SCALING_1 0.0039062f
14553a5a1b3Sopenharmony_ci#define SNR_SHIFT 8
14653a5a1b3Sopenharmony_ci
14753a5a1b3Sopenharmony_ci#define FRAC_SCALING 32767.f
14853a5a1b3Sopenharmony_ci#define FRAC_SCALING_1 3.0518e-05
14953a5a1b3Sopenharmony_ci#define FRAC_SHIFT 1
15053a5a1b3Sopenharmony_ci
15153a5a1b3Sopenharmony_ci#define EXPIN_SCALING 2048.f
15253a5a1b3Sopenharmony_ci#define EXPIN_SCALING_1 0.00048828f
15353a5a1b3Sopenharmony_ci#define EXPIN_SHIFT 11
15453a5a1b3Sopenharmony_ci#define EXPOUT_SCALING_1 1.5259e-05
15553a5a1b3Sopenharmony_ci
15653a5a1b3Sopenharmony_ci#define NOISE_SHIFT 7
15753a5a1b3Sopenharmony_ci
15853a5a1b3Sopenharmony_ci#else
15953a5a1b3Sopenharmony_ci
16053a5a1b3Sopenharmony_ci#define DIV32_16_Q8(a,b) ((a)/(b))
16153a5a1b3Sopenharmony_ci#define DIV32_16_Q15(a,b) ((a)/(b))
16253a5a1b3Sopenharmony_ci#define SNR_SCALING 1.f
16353a5a1b3Sopenharmony_ci#define SNR_SCALING_1 1.f
16453a5a1b3Sopenharmony_ci#define SNR_SHIFT 0
16553a5a1b3Sopenharmony_ci#define FRAC_SCALING 1.f
16653a5a1b3Sopenharmony_ci#define FRAC_SCALING_1 1.f
16753a5a1b3Sopenharmony_ci#define FRAC_SHIFT 0
16853a5a1b3Sopenharmony_ci#define NOISE_SHIFT 0
16953a5a1b3Sopenharmony_ci
17053a5a1b3Sopenharmony_ci#define EXPIN_SCALING 1.f
17153a5a1b3Sopenharmony_ci#define EXPIN_SCALING_1 1.f
17253a5a1b3Sopenharmony_ci#define EXPOUT_SCALING_1 1.f
17353a5a1b3Sopenharmony_ci
17453a5a1b3Sopenharmony_ci#endif
17553a5a1b3Sopenharmony_ci
17653a5a1b3Sopenharmony_ci/** Speex pre-processor state. */
17753a5a1b3Sopenharmony_cistruct SpeexPreprocessState_ {
17853a5a1b3Sopenharmony_ci   /* Basic info */
17953a5a1b3Sopenharmony_ci   int    frame_size;        /**< Number of samples processed each time */
18053a5a1b3Sopenharmony_ci   int    ps_size;           /**< Number of points in the power spectrum */
18153a5a1b3Sopenharmony_ci   int    sampling_rate;     /**< Sampling rate of the input/output */
18253a5a1b3Sopenharmony_ci   int    nbands;
18353a5a1b3Sopenharmony_ci   FilterBank *bank;
18453a5a1b3Sopenharmony_ci
18553a5a1b3Sopenharmony_ci   /* Parameters */
18653a5a1b3Sopenharmony_ci   int    denoise_enabled;
18753a5a1b3Sopenharmony_ci   int    vad_enabled;
18853a5a1b3Sopenharmony_ci   int    dereverb_enabled;
18953a5a1b3Sopenharmony_ci   spx_word16_t  reverb_decay;
19053a5a1b3Sopenharmony_ci   spx_word16_t  reverb_level;
19153a5a1b3Sopenharmony_ci   spx_word16_t speech_prob_start;
19253a5a1b3Sopenharmony_ci   spx_word16_t speech_prob_continue;
19353a5a1b3Sopenharmony_ci   int    noise_suppress;
19453a5a1b3Sopenharmony_ci   int    echo_suppress;
19553a5a1b3Sopenharmony_ci   int    echo_suppress_active;
19653a5a1b3Sopenharmony_ci   SpeexEchoState *echo_state;
19753a5a1b3Sopenharmony_ci
19853a5a1b3Sopenharmony_ci   spx_word16_t	speech_prob;  /**< Probability last frame was speech */
19953a5a1b3Sopenharmony_ci
20053a5a1b3Sopenharmony_ci   /* DSP-related arrays */
20153a5a1b3Sopenharmony_ci   spx_word16_t *frame;      /**< Processing frame (2*ps_size) */
20253a5a1b3Sopenharmony_ci   spx_word16_t *ft;         /**< Processing frame in freq domain (2*ps_size) */
20353a5a1b3Sopenharmony_ci   spx_word32_t *ps;         /**< Current power spectrum */
20453a5a1b3Sopenharmony_ci   spx_word16_t *gain2;      /**< Adjusted gains */
20553a5a1b3Sopenharmony_ci   spx_word16_t *gain_floor; /**< Minimum gain allowed */
20653a5a1b3Sopenharmony_ci   spx_word16_t *window;     /**< Analysis/Synthesis window */
20753a5a1b3Sopenharmony_ci   spx_word32_t *noise;      /**< Noise estimate */
20853a5a1b3Sopenharmony_ci   spx_word32_t *reverb_estimate; /**< Estimate of reverb energy */
20953a5a1b3Sopenharmony_ci   spx_word32_t *old_ps;     /**< Power spectrum for last frame */
21053a5a1b3Sopenharmony_ci   spx_word16_t *gain;       /**< Ephraim Malah gain */
21153a5a1b3Sopenharmony_ci   spx_word16_t *prior;      /**< A-priori SNR */
21253a5a1b3Sopenharmony_ci   spx_word16_t *post;       /**< A-posteriori SNR */
21353a5a1b3Sopenharmony_ci
21453a5a1b3Sopenharmony_ci   spx_word32_t *S;          /**< Smoothed power spectrum */
21553a5a1b3Sopenharmony_ci   spx_word32_t *Smin;       /**< See Cohen paper */
21653a5a1b3Sopenharmony_ci   spx_word32_t *Stmp;       /**< See Cohen paper */
21753a5a1b3Sopenharmony_ci   int *update_prob;         /**< Probability of speech presence for noise update */
21853a5a1b3Sopenharmony_ci
21953a5a1b3Sopenharmony_ci   spx_word16_t *zeta;       /**< Smoothed a priori SNR */
22053a5a1b3Sopenharmony_ci   spx_word32_t *echo_noise;
22153a5a1b3Sopenharmony_ci   spx_word32_t *residual_echo;
22253a5a1b3Sopenharmony_ci
22353a5a1b3Sopenharmony_ci   /* Misc */
22453a5a1b3Sopenharmony_ci   spx_word16_t *inbuf;      /**< Input buffer (overlapped analysis) */
22553a5a1b3Sopenharmony_ci   spx_word16_t *outbuf;     /**< Output buffer (for overlap and add) */
22653a5a1b3Sopenharmony_ci
22753a5a1b3Sopenharmony_ci   /* AGC stuff, only for floating point for now */
22853a5a1b3Sopenharmony_ci#ifndef FIXED_POINT
22953a5a1b3Sopenharmony_ci   int    agc_enabled;
23053a5a1b3Sopenharmony_ci   float  agc_level;
23153a5a1b3Sopenharmony_ci   float  loudness_accum;
23253a5a1b3Sopenharmony_ci   float *loudness_weight;   /**< Perceptual loudness curve */
23353a5a1b3Sopenharmony_ci   float  loudness;          /**< Loudness estimate */
23453a5a1b3Sopenharmony_ci   float  agc_gain;          /**< Current AGC gain */
23553a5a1b3Sopenharmony_ci   float  max_gain;          /**< Maximum gain allowed */
23653a5a1b3Sopenharmony_ci   float  max_increase_step; /**< Maximum increase in gain from one frame to another */
23753a5a1b3Sopenharmony_ci   float  max_decrease_step; /**< Maximum decrease in gain from one frame to another */
23853a5a1b3Sopenharmony_ci   float  prev_loudness;     /**< Loudness of previous frame */
23953a5a1b3Sopenharmony_ci   float  init_max;          /**< Current gain limit during initialisation */
24053a5a1b3Sopenharmony_ci#endif
24153a5a1b3Sopenharmony_ci   int    nb_adapt;          /**< Number of frames used for adaptation so far */
24253a5a1b3Sopenharmony_ci   int    was_speech;
24353a5a1b3Sopenharmony_ci   int    min_count;         /**< Number of frames processed so far */
24453a5a1b3Sopenharmony_ci   void  *fft_lookup;        /**< Lookup table for the FFT */
24553a5a1b3Sopenharmony_ci#ifdef FIXED_POINT
24653a5a1b3Sopenharmony_ci   int    frame_shift;
24753a5a1b3Sopenharmony_ci#endif
24853a5a1b3Sopenharmony_ci};
24953a5a1b3Sopenharmony_ci
25053a5a1b3Sopenharmony_ci
25153a5a1b3Sopenharmony_cistatic void conj_window(spx_word16_t *w, int len)
25253a5a1b3Sopenharmony_ci{
25353a5a1b3Sopenharmony_ci   int i;
25453a5a1b3Sopenharmony_ci   for (i=0;i<len;i++)
25553a5a1b3Sopenharmony_ci   {
25653a5a1b3Sopenharmony_ci      spx_word16_t tmp;
25753a5a1b3Sopenharmony_ci#ifdef FIXED_POINT
25853a5a1b3Sopenharmony_ci      spx_word16_t x = DIV32_16(MULT16_16(32767,i),len);
25953a5a1b3Sopenharmony_ci#else
26053a5a1b3Sopenharmony_ci      spx_word16_t x = DIV32_16(MULT16_16(QCONST16(4.f,13),i),len);
26153a5a1b3Sopenharmony_ci#endif
26253a5a1b3Sopenharmony_ci      int inv=0;
26353a5a1b3Sopenharmony_ci      if (x<QCONST16(1.f,13))
26453a5a1b3Sopenharmony_ci      {
26553a5a1b3Sopenharmony_ci      } else if (x<QCONST16(2.f,13))
26653a5a1b3Sopenharmony_ci      {
26753a5a1b3Sopenharmony_ci         x=QCONST16(2.f,13)-x;
26853a5a1b3Sopenharmony_ci         inv=1;
26953a5a1b3Sopenharmony_ci      } else if (x<QCONST16(3.f,13))
27053a5a1b3Sopenharmony_ci      {
27153a5a1b3Sopenharmony_ci         x=x-QCONST16(2.f,13);
27253a5a1b3Sopenharmony_ci         inv=1;
27353a5a1b3Sopenharmony_ci      } else {
27453a5a1b3Sopenharmony_ci         x=QCONST16(2.f,13)-x+QCONST16(2.f,13); /* 4 - x */
27553a5a1b3Sopenharmony_ci      }
27653a5a1b3Sopenharmony_ci      x = MULT16_16_Q14(QCONST16(1.271903f,14), x);
27753a5a1b3Sopenharmony_ci      tmp = SQR16_Q15(QCONST16(.5f,15)-MULT16_16_P15(QCONST16(.5f,15),spx_cos_norm(SHL32(EXTEND32(x),2))));
27853a5a1b3Sopenharmony_ci      if (inv)
27953a5a1b3Sopenharmony_ci         tmp=SUB16(Q15_ONE,tmp);
28053a5a1b3Sopenharmony_ci      w[i]=spx_sqrt(SHL32(EXTEND32(tmp),15));
28153a5a1b3Sopenharmony_ci   }
28253a5a1b3Sopenharmony_ci}
28353a5a1b3Sopenharmony_ci
28453a5a1b3Sopenharmony_ci
28553a5a1b3Sopenharmony_ci#ifdef FIXED_POINT
28653a5a1b3Sopenharmony_ci/* This function approximates the gain function
28753a5a1b3Sopenharmony_ci   y = gamma(1.25)^2 * M(-.25;1;-x) / sqrt(x)
28853a5a1b3Sopenharmony_ci   which multiplied by xi/(1+xi) is the optimal gain
28953a5a1b3Sopenharmony_ci   in the loudness domain ( sqrt[amplitude] )
29053a5a1b3Sopenharmony_ci   Input in Q11 format, output in Q15
29153a5a1b3Sopenharmony_ci*/
29253a5a1b3Sopenharmony_cistatic inline spx_word32_t hypergeom_gain(spx_word32_t xx)
29353a5a1b3Sopenharmony_ci{
29453a5a1b3Sopenharmony_ci   int ind;
29553a5a1b3Sopenharmony_ci   spx_word16_t frac;
29653a5a1b3Sopenharmony_ci   /* Q13 table */
29753a5a1b3Sopenharmony_ci   static const spx_word16_t table[21] = {
29853a5a1b3Sopenharmony_ci       6730,  8357,  9868, 11267, 12563, 13770, 14898,
29953a5a1b3Sopenharmony_ci      15959, 16961, 17911, 18816, 19682, 20512, 21311,
30053a5a1b3Sopenharmony_ci      22082, 22827, 23549, 24250, 24931, 25594, 26241};
30153a5a1b3Sopenharmony_ci      ind = SHR32(xx,10);
30253a5a1b3Sopenharmony_ci      if (ind<0)
30353a5a1b3Sopenharmony_ci         return Q15_ONE;
30453a5a1b3Sopenharmony_ci      if (ind>19)
30553a5a1b3Sopenharmony_ci         return ADD32(EXTEND32(Q15_ONE),EXTEND32(DIV32_16(QCONST32(.1296,23), SHR32(xx,EXPIN_SHIFT-SNR_SHIFT))));
30653a5a1b3Sopenharmony_ci      frac = SHL32(xx-SHL32(ind,10),5);
30753a5a1b3Sopenharmony_ci      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);
30853a5a1b3Sopenharmony_ci}
30953a5a1b3Sopenharmony_ci
31053a5a1b3Sopenharmony_cistatic inline spx_word16_t qcurve(spx_word16_t x)
31153a5a1b3Sopenharmony_ci{
31253a5a1b3Sopenharmony_ci   x = MAX16(x, 1);
31353a5a1b3Sopenharmony_ci   return DIV32_16(SHL32(EXTEND32(32767),9),ADD16(512,MULT16_16_Q15(QCONST16(.60f,15),DIV32_16(32767,x))));
31453a5a1b3Sopenharmony_ci}
31553a5a1b3Sopenharmony_ci
31653a5a1b3Sopenharmony_ci/* Compute the gain floor based on different floors for the background noise and residual echo */
31753a5a1b3Sopenharmony_cistatic 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)
31853a5a1b3Sopenharmony_ci{
31953a5a1b3Sopenharmony_ci   int i;
32053a5a1b3Sopenharmony_ci
32153a5a1b3Sopenharmony_ci   if (noise_suppress > effective_echo_suppress)
32253a5a1b3Sopenharmony_ci   {
32353a5a1b3Sopenharmony_ci      spx_word16_t noise_gain, gain_ratio;
32453a5a1b3Sopenharmony_ci      noise_gain = EXTRACT16(MIN32(Q15_ONE,SHR32(spx_exp(MULT16_16(QCONST16(0.11513,11),noise_suppress)),1)));
32553a5a1b3Sopenharmony_ci      gain_ratio = EXTRACT16(MIN32(Q15_ONE,SHR32(spx_exp(MULT16_16(QCONST16(.2302585f,11),effective_echo_suppress-noise_suppress)),1)));
32653a5a1b3Sopenharmony_ci
32753a5a1b3Sopenharmony_ci      /* gain_floor = sqrt [ (noise*noise_floor + echo*echo_floor) / (noise+echo) ] */
32853a5a1b3Sopenharmony_ci      for (i=0;i<len;i++)
32953a5a1b3Sopenharmony_ci         gain_floor[i] = MULT16_16_Q15(noise_gain,
33053a5a1b3Sopenharmony_ci                                       spx_sqrt(SHL32(EXTEND32(DIV32_16_Q15(PSHR32(noise[i],NOISE_SHIFT) + MULT16_32_Q15(gain_ratio,echo[i]),
33153a5a1b3Sopenharmony_ci                                             (1+PSHR32(noise[i],NOISE_SHIFT) + echo[i]) )),15)));
33253a5a1b3Sopenharmony_ci   } else {
33353a5a1b3Sopenharmony_ci      spx_word16_t echo_gain, gain_ratio;
33453a5a1b3Sopenharmony_ci      echo_gain = EXTRACT16(MIN32(Q15_ONE,SHR32(spx_exp(MULT16_16(QCONST16(0.11513,11),effective_echo_suppress)),1)));
33553a5a1b3Sopenharmony_ci      gain_ratio = EXTRACT16(MIN32(Q15_ONE,SHR32(spx_exp(MULT16_16(QCONST16(.2302585f,11),noise_suppress-effective_echo_suppress)),1)));
33653a5a1b3Sopenharmony_ci
33753a5a1b3Sopenharmony_ci      /* gain_floor = sqrt [ (noise*noise_floor + echo*echo_floor) / (noise+echo) ] */
33853a5a1b3Sopenharmony_ci      for (i=0;i<len;i++)
33953a5a1b3Sopenharmony_ci         gain_floor[i] = MULT16_16_Q15(echo_gain,
34053a5a1b3Sopenharmony_ci                                       spx_sqrt(SHL32(EXTEND32(DIV32_16_Q15(MULT16_32_Q15(gain_ratio,PSHR32(noise[i],NOISE_SHIFT)) + echo[i],
34153a5a1b3Sopenharmony_ci                                             (1+PSHR32(noise[i],NOISE_SHIFT) + echo[i]) )),15)));
34253a5a1b3Sopenharmony_ci   }
34353a5a1b3Sopenharmony_ci}
34453a5a1b3Sopenharmony_ci
34553a5a1b3Sopenharmony_ci#else
34653a5a1b3Sopenharmony_ci/* This function approximates the gain function
34753a5a1b3Sopenharmony_ci   y = gamma(1.25)^2 * M(-.25;1;-x) / sqrt(x)
34853a5a1b3Sopenharmony_ci   which multiplied by xi/(1+xi) is the optimal gain
34953a5a1b3Sopenharmony_ci   in the loudness domain ( sqrt[amplitude] )
35053a5a1b3Sopenharmony_ci*/
35153a5a1b3Sopenharmony_cistatic inline spx_word32_t hypergeom_gain(spx_word32_t xx)
35253a5a1b3Sopenharmony_ci{
35353a5a1b3Sopenharmony_ci   int ind;
35453a5a1b3Sopenharmony_ci   float integer, frac;
35553a5a1b3Sopenharmony_ci   float x;
35653a5a1b3Sopenharmony_ci   static const float table[21] = {
35753a5a1b3Sopenharmony_ci      0.82157f, 1.02017f, 1.20461f, 1.37534f, 1.53363f, 1.68092f, 1.81865f,
35853a5a1b3Sopenharmony_ci      1.94811f, 2.07038f, 2.18638f, 2.29688f, 2.40255f, 2.50391f, 2.60144f,
35953a5a1b3Sopenharmony_ci      2.69551f, 2.78647f, 2.87458f, 2.96015f, 3.04333f, 3.12431f, 3.20326f};
36053a5a1b3Sopenharmony_ci      x = EXPIN_SCALING_1*xx;
36153a5a1b3Sopenharmony_ci      integer = floor(2*x);
36253a5a1b3Sopenharmony_ci      ind = (int)integer;
36353a5a1b3Sopenharmony_ci      if (ind<0)
36453a5a1b3Sopenharmony_ci         return FRAC_SCALING;
36553a5a1b3Sopenharmony_ci      if (ind>19)
36653a5a1b3Sopenharmony_ci         return FRAC_SCALING*(1+.1296/x);
36753a5a1b3Sopenharmony_ci      frac = 2*x-integer;
36853a5a1b3Sopenharmony_ci      return FRAC_SCALING*((1-frac)*table[ind] + frac*table[ind+1])/sqrt(x+.0001f);
36953a5a1b3Sopenharmony_ci}
37053a5a1b3Sopenharmony_ci
37153a5a1b3Sopenharmony_cistatic inline spx_word16_t qcurve(spx_word16_t x)
37253a5a1b3Sopenharmony_ci{
37353a5a1b3Sopenharmony_ci   return 1.f/(1.f+.15f/(SNR_SCALING_1*x));
37453a5a1b3Sopenharmony_ci}
37553a5a1b3Sopenharmony_ci
37653a5a1b3Sopenharmony_cistatic 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)
37753a5a1b3Sopenharmony_ci{
37853a5a1b3Sopenharmony_ci   int i;
37953a5a1b3Sopenharmony_ci   float echo_floor;
38053a5a1b3Sopenharmony_ci   float noise_floor;
38153a5a1b3Sopenharmony_ci
38253a5a1b3Sopenharmony_ci   noise_floor = exp(.2302585f*noise_suppress);
38353a5a1b3Sopenharmony_ci   echo_floor = exp(.2302585f*effective_echo_suppress);
38453a5a1b3Sopenharmony_ci
38553a5a1b3Sopenharmony_ci   /* Compute the gain floor based on different floors for the background noise and residual echo */
38653a5a1b3Sopenharmony_ci   for (i=0;i<len;i++)
38753a5a1b3Sopenharmony_ci      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]);
38853a5a1b3Sopenharmony_ci}
38953a5a1b3Sopenharmony_ci
39053a5a1b3Sopenharmony_ci#endif
39153a5a1b3Sopenharmony_ciEXPORT SpeexPreprocessState *speex_preprocess_state_init(int frame_size, int sampling_rate)
39253a5a1b3Sopenharmony_ci{
39353a5a1b3Sopenharmony_ci   int i;
39453a5a1b3Sopenharmony_ci   int N, N3, N4, M;
39553a5a1b3Sopenharmony_ci
39653a5a1b3Sopenharmony_ci   SpeexPreprocessState *st = (SpeexPreprocessState *)speex_alloc(sizeof(SpeexPreprocessState));
39753a5a1b3Sopenharmony_ci   st->frame_size = frame_size;
39853a5a1b3Sopenharmony_ci
39953a5a1b3Sopenharmony_ci   /* Round ps_size down to the nearest power of two */
40053a5a1b3Sopenharmony_ci#if 0
40153a5a1b3Sopenharmony_ci   i=1;
40253a5a1b3Sopenharmony_ci   st->ps_size = st->frame_size;
40353a5a1b3Sopenharmony_ci   while(1)
40453a5a1b3Sopenharmony_ci   {
40553a5a1b3Sopenharmony_ci      if (st->ps_size & ~i)
40653a5a1b3Sopenharmony_ci      {
40753a5a1b3Sopenharmony_ci         st->ps_size &= ~i;
40853a5a1b3Sopenharmony_ci         i<<=1;
40953a5a1b3Sopenharmony_ci      } else {
41053a5a1b3Sopenharmony_ci         break;
41153a5a1b3Sopenharmony_ci      }
41253a5a1b3Sopenharmony_ci   }
41353a5a1b3Sopenharmony_ci
41453a5a1b3Sopenharmony_ci
41553a5a1b3Sopenharmony_ci   if (st->ps_size < 3*st->frame_size/4)
41653a5a1b3Sopenharmony_ci      st->ps_size = st->ps_size * 3 / 2;
41753a5a1b3Sopenharmony_ci#else
41853a5a1b3Sopenharmony_ci   st->ps_size = st->frame_size;
41953a5a1b3Sopenharmony_ci#endif
42053a5a1b3Sopenharmony_ci
42153a5a1b3Sopenharmony_ci   N = st->ps_size;
42253a5a1b3Sopenharmony_ci   N3 = 2*N - st->frame_size;
42353a5a1b3Sopenharmony_ci   N4 = st->frame_size - N3;
42453a5a1b3Sopenharmony_ci
42553a5a1b3Sopenharmony_ci   st->sampling_rate = sampling_rate;
42653a5a1b3Sopenharmony_ci   st->denoise_enabled = 1;
42753a5a1b3Sopenharmony_ci   st->vad_enabled = 0;
42853a5a1b3Sopenharmony_ci   st->dereverb_enabled = 0;
42953a5a1b3Sopenharmony_ci   st->reverb_decay = 0;
43053a5a1b3Sopenharmony_ci   st->reverb_level = 0;
43153a5a1b3Sopenharmony_ci   st->noise_suppress = NOISE_SUPPRESS_DEFAULT;
43253a5a1b3Sopenharmony_ci   st->echo_suppress = ECHO_SUPPRESS_DEFAULT;
43353a5a1b3Sopenharmony_ci   st->echo_suppress_active = ECHO_SUPPRESS_ACTIVE_DEFAULT;
43453a5a1b3Sopenharmony_ci
43553a5a1b3Sopenharmony_ci   st->speech_prob_start = SPEECH_PROB_START_DEFAULT;
43653a5a1b3Sopenharmony_ci   st->speech_prob_continue = SPEECH_PROB_CONTINUE_DEFAULT;
43753a5a1b3Sopenharmony_ci
43853a5a1b3Sopenharmony_ci   st->echo_state = NULL;
43953a5a1b3Sopenharmony_ci
44053a5a1b3Sopenharmony_ci   st->nbands = NB_BANDS;
44153a5a1b3Sopenharmony_ci   M = st->nbands;
44253a5a1b3Sopenharmony_ci   st->bank = filterbank_new(M, sampling_rate, N, 1);
44353a5a1b3Sopenharmony_ci
44453a5a1b3Sopenharmony_ci   st->frame = (spx_word16_t*)speex_alloc(2*N*sizeof(spx_word16_t));
44553a5a1b3Sopenharmony_ci   st->window = (spx_word16_t*)speex_alloc(2*N*sizeof(spx_word16_t));
44653a5a1b3Sopenharmony_ci   st->ft = (spx_word16_t*)speex_alloc(2*N*sizeof(spx_word16_t));
44753a5a1b3Sopenharmony_ci
44853a5a1b3Sopenharmony_ci   st->ps = (spx_word32_t*)speex_alloc((N+M)*sizeof(spx_word32_t));
44953a5a1b3Sopenharmony_ci   st->noise = (spx_word32_t*)speex_alloc((N+M)*sizeof(spx_word32_t));
45053a5a1b3Sopenharmony_ci   st->echo_noise = (spx_word32_t*)speex_alloc((N+M)*sizeof(spx_word32_t));
45153a5a1b3Sopenharmony_ci   st->residual_echo = (spx_word32_t*)speex_alloc((N+M)*sizeof(spx_word32_t));
45253a5a1b3Sopenharmony_ci   st->reverb_estimate = (spx_word32_t*)speex_alloc((N+M)*sizeof(spx_word32_t));
45353a5a1b3Sopenharmony_ci   st->old_ps = (spx_word32_t*)speex_alloc((N+M)*sizeof(spx_word32_t));
45453a5a1b3Sopenharmony_ci   st->prior = (spx_word16_t*)speex_alloc((N+M)*sizeof(spx_word16_t));
45553a5a1b3Sopenharmony_ci   st->post = (spx_word16_t*)speex_alloc((N+M)*sizeof(spx_word16_t));
45653a5a1b3Sopenharmony_ci   st->gain = (spx_word16_t*)speex_alloc((N+M)*sizeof(spx_word16_t));
45753a5a1b3Sopenharmony_ci   st->gain2 = (spx_word16_t*)speex_alloc((N+M)*sizeof(spx_word16_t));
45853a5a1b3Sopenharmony_ci   st->gain_floor = (spx_word16_t*)speex_alloc((N+M)*sizeof(spx_word16_t));
45953a5a1b3Sopenharmony_ci   st->zeta = (spx_word16_t*)speex_alloc((N+M)*sizeof(spx_word16_t));
46053a5a1b3Sopenharmony_ci
46153a5a1b3Sopenharmony_ci   st->S = (spx_word32_t*)speex_alloc(N*sizeof(spx_word32_t));
46253a5a1b3Sopenharmony_ci   st->Smin = (spx_word32_t*)speex_alloc(N*sizeof(spx_word32_t));
46353a5a1b3Sopenharmony_ci   st->Stmp = (spx_word32_t*)speex_alloc(N*sizeof(spx_word32_t));
46453a5a1b3Sopenharmony_ci   st->update_prob = (int*)speex_alloc(N*sizeof(int));
46553a5a1b3Sopenharmony_ci
46653a5a1b3Sopenharmony_ci   st->inbuf = (spx_word16_t*)speex_alloc(N3*sizeof(spx_word16_t));
46753a5a1b3Sopenharmony_ci   st->outbuf = (spx_word16_t*)speex_alloc(N3*sizeof(spx_word16_t));
46853a5a1b3Sopenharmony_ci
46953a5a1b3Sopenharmony_ci   conj_window(st->window, 2*N3);
47053a5a1b3Sopenharmony_ci   for (i=2*N3;i<2*st->ps_size;i++)
47153a5a1b3Sopenharmony_ci      st->window[i]=Q15_ONE;
47253a5a1b3Sopenharmony_ci
47353a5a1b3Sopenharmony_ci   if (N4>0)
47453a5a1b3Sopenharmony_ci   {
47553a5a1b3Sopenharmony_ci      for (i=N3-1;i>=0;i--)
47653a5a1b3Sopenharmony_ci      {
47753a5a1b3Sopenharmony_ci         st->window[i+N3+N4]=st->window[i+N3];
47853a5a1b3Sopenharmony_ci         st->window[i+N3]=1;
47953a5a1b3Sopenharmony_ci      }
48053a5a1b3Sopenharmony_ci   }
48153a5a1b3Sopenharmony_ci   for (i=0;i<N+M;i++)
48253a5a1b3Sopenharmony_ci   {
48353a5a1b3Sopenharmony_ci      st->noise[i]=QCONST32(1.f,NOISE_SHIFT);
48453a5a1b3Sopenharmony_ci      st->reverb_estimate[i]=0;
48553a5a1b3Sopenharmony_ci      st->old_ps[i]=1;
48653a5a1b3Sopenharmony_ci      st->gain[i]=Q15_ONE;
48753a5a1b3Sopenharmony_ci      st->post[i]=SHL16(1, SNR_SHIFT);
48853a5a1b3Sopenharmony_ci      st->prior[i]=SHL16(1, SNR_SHIFT);
48953a5a1b3Sopenharmony_ci   }
49053a5a1b3Sopenharmony_ci
49153a5a1b3Sopenharmony_ci   for (i=0;i<N;i++)
49253a5a1b3Sopenharmony_ci      st->update_prob[i] = 1;
49353a5a1b3Sopenharmony_ci   for (i=0;i<N3;i++)
49453a5a1b3Sopenharmony_ci   {
49553a5a1b3Sopenharmony_ci      st->inbuf[i]=0;
49653a5a1b3Sopenharmony_ci      st->outbuf[i]=0;
49753a5a1b3Sopenharmony_ci   }
49853a5a1b3Sopenharmony_ci#ifndef FIXED_POINT
49953a5a1b3Sopenharmony_ci   st->agc_enabled = 0;
50053a5a1b3Sopenharmony_ci   st->agc_level = 8000;
50153a5a1b3Sopenharmony_ci   st->loudness_weight = (float*)speex_alloc(N*sizeof(float));
50253a5a1b3Sopenharmony_ci   for (i=0;i<N;i++)
50353a5a1b3Sopenharmony_ci   {
50453a5a1b3Sopenharmony_ci      float ff=((float)i)*.5*sampling_rate/((float)N);
50553a5a1b3Sopenharmony_ci      /*st->loudness_weight[i] = .5f*(1.f/(1.f+ff/8000.f))+1.f*exp(-.5f*(ff-3800.f)*(ff-3800.f)/9e5f);*/
50653a5a1b3Sopenharmony_ci      st->loudness_weight[i] = .35f-.35f*ff/16000.f+.73f*exp(-.5f*(ff-3800)*(ff-3800)/9e5f);
50753a5a1b3Sopenharmony_ci      if (st->loudness_weight[i]<.01f)
50853a5a1b3Sopenharmony_ci         st->loudness_weight[i]=.01f;
50953a5a1b3Sopenharmony_ci      st->loudness_weight[i] *= st->loudness_weight[i];
51053a5a1b3Sopenharmony_ci   }
51153a5a1b3Sopenharmony_ci   /*st->loudness = pow(AMP_SCALE*st->agc_level,LOUDNESS_EXP);*/
51253a5a1b3Sopenharmony_ci   st->loudness = 1e-15;
51353a5a1b3Sopenharmony_ci   st->agc_gain = 1;
51453a5a1b3Sopenharmony_ci   st->max_gain = 30;
51553a5a1b3Sopenharmony_ci   st->max_increase_step = exp(0.11513f * 12.*st->frame_size / st->sampling_rate);
51653a5a1b3Sopenharmony_ci   st->max_decrease_step = exp(-0.11513f * 40.*st->frame_size / st->sampling_rate);
51753a5a1b3Sopenharmony_ci   st->prev_loudness = 1;
51853a5a1b3Sopenharmony_ci   st->init_max = 1;
51953a5a1b3Sopenharmony_ci#endif
52053a5a1b3Sopenharmony_ci   st->was_speech = 0;
52153a5a1b3Sopenharmony_ci
52253a5a1b3Sopenharmony_ci   st->fft_lookup = spx_fft_init(2*N);
52353a5a1b3Sopenharmony_ci
52453a5a1b3Sopenharmony_ci   st->nb_adapt=0;
52553a5a1b3Sopenharmony_ci   st->min_count=0;
52653a5a1b3Sopenharmony_ci   return st;
52753a5a1b3Sopenharmony_ci}
52853a5a1b3Sopenharmony_ci
52953a5a1b3Sopenharmony_ciEXPORT void speex_preprocess_state_destroy(SpeexPreprocessState *st)
53053a5a1b3Sopenharmony_ci{
53153a5a1b3Sopenharmony_ci   speex_free(st->frame);
53253a5a1b3Sopenharmony_ci   speex_free(st->ft);
53353a5a1b3Sopenharmony_ci   speex_free(st->ps);
53453a5a1b3Sopenharmony_ci   speex_free(st->gain2);
53553a5a1b3Sopenharmony_ci   speex_free(st->gain_floor);
53653a5a1b3Sopenharmony_ci   speex_free(st->window);
53753a5a1b3Sopenharmony_ci   speex_free(st->noise);
53853a5a1b3Sopenharmony_ci   speex_free(st->reverb_estimate);
53953a5a1b3Sopenharmony_ci   speex_free(st->old_ps);
54053a5a1b3Sopenharmony_ci   speex_free(st->gain);
54153a5a1b3Sopenharmony_ci   speex_free(st->prior);
54253a5a1b3Sopenharmony_ci   speex_free(st->post);
54353a5a1b3Sopenharmony_ci#ifndef FIXED_POINT
54453a5a1b3Sopenharmony_ci   speex_free(st->loudness_weight);
54553a5a1b3Sopenharmony_ci#endif
54653a5a1b3Sopenharmony_ci   speex_free(st->echo_noise);
54753a5a1b3Sopenharmony_ci   speex_free(st->residual_echo);
54853a5a1b3Sopenharmony_ci
54953a5a1b3Sopenharmony_ci   speex_free(st->S);
55053a5a1b3Sopenharmony_ci   speex_free(st->Smin);
55153a5a1b3Sopenharmony_ci   speex_free(st->Stmp);
55253a5a1b3Sopenharmony_ci   speex_free(st->update_prob);
55353a5a1b3Sopenharmony_ci   speex_free(st->zeta);
55453a5a1b3Sopenharmony_ci
55553a5a1b3Sopenharmony_ci   speex_free(st->inbuf);
55653a5a1b3Sopenharmony_ci   speex_free(st->outbuf);
55753a5a1b3Sopenharmony_ci
55853a5a1b3Sopenharmony_ci   spx_fft_destroy(st->fft_lookup);
55953a5a1b3Sopenharmony_ci   filterbank_destroy(st->bank);
56053a5a1b3Sopenharmony_ci   speex_free(st);
56153a5a1b3Sopenharmony_ci}
56253a5a1b3Sopenharmony_ci
56353a5a1b3Sopenharmony_ci/* FIXME: The AGC doesn't work yet with fixed-point*/
56453a5a1b3Sopenharmony_ci#ifndef FIXED_POINT
56553a5a1b3Sopenharmony_cistatic void speex_compute_agc(SpeexPreprocessState *st, spx_word16_t Pframe, spx_word16_t *ft)
56653a5a1b3Sopenharmony_ci{
56753a5a1b3Sopenharmony_ci   int i;
56853a5a1b3Sopenharmony_ci   int N = st->ps_size;
56953a5a1b3Sopenharmony_ci   float target_gain;
57053a5a1b3Sopenharmony_ci   float loudness=1.f;
57153a5a1b3Sopenharmony_ci   float rate;
57253a5a1b3Sopenharmony_ci
57353a5a1b3Sopenharmony_ci   for (i=2;i<N;i++)
57453a5a1b3Sopenharmony_ci   {
57553a5a1b3Sopenharmony_ci      loudness += 2.f*N*st->ps[i]* st->loudness_weight[i];
57653a5a1b3Sopenharmony_ci   }
57753a5a1b3Sopenharmony_ci   loudness=sqrt(loudness);
57853a5a1b3Sopenharmony_ci      /*if (loudness < 2*pow(st->loudness, 1.0/LOUDNESS_EXP) &&
57953a5a1b3Sopenharmony_ci   loudness*2 > pow(st->loudness, 1.0/LOUDNESS_EXP))*/
58053a5a1b3Sopenharmony_ci   if (Pframe>.3f)
58153a5a1b3Sopenharmony_ci   {
58253a5a1b3Sopenharmony_ci      /*rate=2.0f*Pframe*Pframe/(1+st->nb_loudness_adapt);*/
58353a5a1b3Sopenharmony_ci      rate = .03*Pframe*Pframe;
58453a5a1b3Sopenharmony_ci      st->loudness = (1-rate)*st->loudness + (rate)*pow(AMP_SCALE*loudness, LOUDNESS_EXP);
58553a5a1b3Sopenharmony_ci      st->loudness_accum = (1-rate)*st->loudness_accum + rate;
58653a5a1b3Sopenharmony_ci      if (st->init_max < st->max_gain && st->nb_adapt > 20)
58753a5a1b3Sopenharmony_ci         st->init_max *= 1.f + .1f*Pframe*Pframe;
58853a5a1b3Sopenharmony_ci   }
58953a5a1b3Sopenharmony_ci   /*printf ("%f %f %f %f\n", Pframe, loudness, pow(st->loudness, 1.0f/LOUDNESS_EXP), st->loudness2);*/
59053a5a1b3Sopenharmony_ci
59153a5a1b3Sopenharmony_ci   target_gain = AMP_SCALE*st->agc_level*pow(st->loudness/(1e-4+st->loudness_accum), -1.0f/LOUDNESS_EXP);
59253a5a1b3Sopenharmony_ci
59353a5a1b3Sopenharmony_ci   if ((Pframe>.5  && st->nb_adapt > 20) || target_gain < st->agc_gain)
59453a5a1b3Sopenharmony_ci   {
59553a5a1b3Sopenharmony_ci      if (target_gain > st->max_increase_step*st->agc_gain)
59653a5a1b3Sopenharmony_ci         target_gain = st->max_increase_step*st->agc_gain;
59753a5a1b3Sopenharmony_ci      if (target_gain < st->max_decrease_step*st->agc_gain && loudness < 10*st->prev_loudness)
59853a5a1b3Sopenharmony_ci         target_gain = st->max_decrease_step*st->agc_gain;
59953a5a1b3Sopenharmony_ci      if (target_gain > st->max_gain)
60053a5a1b3Sopenharmony_ci         target_gain = st->max_gain;
60153a5a1b3Sopenharmony_ci      if (target_gain > st->init_max)
60253a5a1b3Sopenharmony_ci         target_gain = st->init_max;
60353a5a1b3Sopenharmony_ci
60453a5a1b3Sopenharmony_ci      st->agc_gain = target_gain;
60553a5a1b3Sopenharmony_ci   }
60653a5a1b3Sopenharmony_ci   /*fprintf (stderr, "%f %f %f\n", loudness, (float)AMP_SCALE_1*pow(st->loudness, 1.0f/LOUDNESS_EXP), st->agc_gain);*/
60753a5a1b3Sopenharmony_ci
60853a5a1b3Sopenharmony_ci   for (i=0;i<2*N;i++)
60953a5a1b3Sopenharmony_ci      ft[i] *= st->agc_gain;
61053a5a1b3Sopenharmony_ci   st->prev_loudness = loudness;
61153a5a1b3Sopenharmony_ci}
61253a5a1b3Sopenharmony_ci#endif
61353a5a1b3Sopenharmony_ci
61453a5a1b3Sopenharmony_cistatic void preprocess_analysis(SpeexPreprocessState *st, spx_int16_t *x)
61553a5a1b3Sopenharmony_ci{
61653a5a1b3Sopenharmony_ci   int i;
61753a5a1b3Sopenharmony_ci   int N = st->ps_size;
61853a5a1b3Sopenharmony_ci   int N3 = 2*N - st->frame_size;
61953a5a1b3Sopenharmony_ci   int N4 = st->frame_size - N3;
62053a5a1b3Sopenharmony_ci   spx_word32_t *ps=st->ps;
62153a5a1b3Sopenharmony_ci
62253a5a1b3Sopenharmony_ci   /* 'Build' input frame */
62353a5a1b3Sopenharmony_ci   for (i=0;i<N3;i++)
62453a5a1b3Sopenharmony_ci      st->frame[i]=st->inbuf[i];
62553a5a1b3Sopenharmony_ci   for (i=0;i<st->frame_size;i++)
62653a5a1b3Sopenharmony_ci      st->frame[N3+i]=x[i];
62753a5a1b3Sopenharmony_ci
62853a5a1b3Sopenharmony_ci   /* Update inbuf */
62953a5a1b3Sopenharmony_ci   for (i=0;i<N3;i++)
63053a5a1b3Sopenharmony_ci      st->inbuf[i]=x[N4+i];
63153a5a1b3Sopenharmony_ci
63253a5a1b3Sopenharmony_ci   /* Windowing */
63353a5a1b3Sopenharmony_ci   for (i=0;i<2*N;i++)
63453a5a1b3Sopenharmony_ci      st->frame[i] = MULT16_16_Q15(st->frame[i], st->window[i]);
63553a5a1b3Sopenharmony_ci
63653a5a1b3Sopenharmony_ci#ifdef FIXED_POINT
63753a5a1b3Sopenharmony_ci   {
63853a5a1b3Sopenharmony_ci      spx_word16_t max_val=0;
63953a5a1b3Sopenharmony_ci      for (i=0;i<2*N;i++)
64053a5a1b3Sopenharmony_ci         max_val = MAX16(max_val, ABS16(st->frame[i]));
64153a5a1b3Sopenharmony_ci      st->frame_shift = 14-spx_ilog2(EXTEND32(max_val));
64253a5a1b3Sopenharmony_ci      for (i=0;i<2*N;i++)
64353a5a1b3Sopenharmony_ci         st->frame[i] = SHL16(st->frame[i], st->frame_shift);
64453a5a1b3Sopenharmony_ci   }
64553a5a1b3Sopenharmony_ci#endif
64653a5a1b3Sopenharmony_ci
64753a5a1b3Sopenharmony_ci   /* Perform FFT */
64853a5a1b3Sopenharmony_ci   spx_fft(st->fft_lookup, st->frame, st->ft);
64953a5a1b3Sopenharmony_ci
65053a5a1b3Sopenharmony_ci   /* Power spectrum */
65153a5a1b3Sopenharmony_ci   ps[0]=MULT16_16(st->ft[0],st->ft[0]);
65253a5a1b3Sopenharmony_ci   for (i=1;i<N;i++)
65353a5a1b3Sopenharmony_ci      ps[i]=MULT16_16(st->ft[2*i-1],st->ft[2*i-1]) + MULT16_16(st->ft[2*i],st->ft[2*i]);
65453a5a1b3Sopenharmony_ci   for (i=0;i<N;i++)
65553a5a1b3Sopenharmony_ci      st->ps[i] = PSHR32(st->ps[i], 2*st->frame_shift);
65653a5a1b3Sopenharmony_ci
65753a5a1b3Sopenharmony_ci   filterbank_compute_bank32(st->bank, ps, ps+N);
65853a5a1b3Sopenharmony_ci}
65953a5a1b3Sopenharmony_ci
66053a5a1b3Sopenharmony_cistatic void update_noise_prob(SpeexPreprocessState *st)
66153a5a1b3Sopenharmony_ci{
66253a5a1b3Sopenharmony_ci   int i;
66353a5a1b3Sopenharmony_ci   int min_range;
66453a5a1b3Sopenharmony_ci   int N = st->ps_size;
66553a5a1b3Sopenharmony_ci
66653a5a1b3Sopenharmony_ci   for (i=1;i<N-1;i++)
66753a5a1b3Sopenharmony_ci      st->S[i] =  MULT16_32_Q15(QCONST16(.8f,15),st->S[i]) + MULT16_32_Q15(QCONST16(.05f,15),st->ps[i-1])
66853a5a1b3Sopenharmony_ci                      + MULT16_32_Q15(QCONST16(.1f,15),st->ps[i]) + MULT16_32_Q15(QCONST16(.05f,15),st->ps[i+1]);
66953a5a1b3Sopenharmony_ci   st->S[0] =  MULT16_32_Q15(QCONST16(.8f,15),st->S[0]) + MULT16_32_Q15(QCONST16(.2f,15),st->ps[0]);
67053a5a1b3Sopenharmony_ci   st->S[N-1] =  MULT16_32_Q15(QCONST16(.8f,15),st->S[N-1]) + MULT16_32_Q15(QCONST16(.2f,15),st->ps[N-1]);
67153a5a1b3Sopenharmony_ci
67253a5a1b3Sopenharmony_ci   if (st->nb_adapt==1)
67353a5a1b3Sopenharmony_ci   {
67453a5a1b3Sopenharmony_ci      for (i=0;i<N;i++)
67553a5a1b3Sopenharmony_ci         st->Smin[i] = st->Stmp[i] = 0;
67653a5a1b3Sopenharmony_ci   }
67753a5a1b3Sopenharmony_ci
67853a5a1b3Sopenharmony_ci   if (st->nb_adapt < 100)
67953a5a1b3Sopenharmony_ci      min_range = 15;
68053a5a1b3Sopenharmony_ci   else if (st->nb_adapt < 1000)
68153a5a1b3Sopenharmony_ci      min_range = 50;
68253a5a1b3Sopenharmony_ci   else if (st->nb_adapt < 10000)
68353a5a1b3Sopenharmony_ci      min_range = 150;
68453a5a1b3Sopenharmony_ci   else
68553a5a1b3Sopenharmony_ci      min_range = 300;
68653a5a1b3Sopenharmony_ci   if (st->min_count > min_range)
68753a5a1b3Sopenharmony_ci   {
68853a5a1b3Sopenharmony_ci      st->min_count = 0;
68953a5a1b3Sopenharmony_ci      for (i=0;i<N;i++)
69053a5a1b3Sopenharmony_ci      {
69153a5a1b3Sopenharmony_ci         st->Smin[i] = MIN32(st->Stmp[i], st->S[i]);
69253a5a1b3Sopenharmony_ci         st->Stmp[i] = st->S[i];
69353a5a1b3Sopenharmony_ci      }
69453a5a1b3Sopenharmony_ci   } else {
69553a5a1b3Sopenharmony_ci      for (i=0;i<N;i++)
69653a5a1b3Sopenharmony_ci      {
69753a5a1b3Sopenharmony_ci         st->Smin[i] = MIN32(st->Smin[i], st->S[i]);
69853a5a1b3Sopenharmony_ci         st->Stmp[i] = MIN32(st->Stmp[i], st->S[i]);
69953a5a1b3Sopenharmony_ci      }
70053a5a1b3Sopenharmony_ci   }
70153a5a1b3Sopenharmony_ci   for (i=0;i<N;i++)
70253a5a1b3Sopenharmony_ci   {
70353a5a1b3Sopenharmony_ci      if (MULT16_32_Q15(QCONST16(.4f,15),st->S[i]) > st->Smin[i])
70453a5a1b3Sopenharmony_ci         st->update_prob[i] = 1;
70553a5a1b3Sopenharmony_ci      else
70653a5a1b3Sopenharmony_ci         st->update_prob[i] = 0;
70753a5a1b3Sopenharmony_ci      /*fprintf (stderr, "%f ", st->S[i]/st->Smin[i]);*/
70853a5a1b3Sopenharmony_ci      /*fprintf (stderr, "%f ", st->update_prob[i]);*/
70953a5a1b3Sopenharmony_ci   }
71053a5a1b3Sopenharmony_ci
71153a5a1b3Sopenharmony_ci}
71253a5a1b3Sopenharmony_ci
71353a5a1b3Sopenharmony_ci#define NOISE_OVERCOMPENS 1.
71453a5a1b3Sopenharmony_ci
71553a5a1b3Sopenharmony_civoid speex_echo_get_residual(SpeexEchoState *st, spx_word32_t *Yout, int len);
71653a5a1b3Sopenharmony_ci
71753a5a1b3Sopenharmony_ciEXPORT int speex_preprocess(SpeexPreprocessState *st, spx_int16_t *x, spx_int32_t *echo)
71853a5a1b3Sopenharmony_ci{
71953a5a1b3Sopenharmony_ci   return speex_preprocess_run(st, x);
72053a5a1b3Sopenharmony_ci}
72153a5a1b3Sopenharmony_ci
72253a5a1b3Sopenharmony_ciEXPORT int speex_preprocess_run(SpeexPreprocessState *st, spx_int16_t *x)
72353a5a1b3Sopenharmony_ci{
72453a5a1b3Sopenharmony_ci   int i;
72553a5a1b3Sopenharmony_ci   int M;
72653a5a1b3Sopenharmony_ci   int N = st->ps_size;
72753a5a1b3Sopenharmony_ci   int N3 = 2*N - st->frame_size;
72853a5a1b3Sopenharmony_ci   int N4 = st->frame_size - N3;
72953a5a1b3Sopenharmony_ci   spx_word32_t *ps=st->ps;
73053a5a1b3Sopenharmony_ci   spx_word32_t Zframe;
73153a5a1b3Sopenharmony_ci   spx_word16_t Pframe;
73253a5a1b3Sopenharmony_ci   spx_word16_t beta, beta_1;
73353a5a1b3Sopenharmony_ci   spx_word16_t effective_echo_suppress;
73453a5a1b3Sopenharmony_ci
73553a5a1b3Sopenharmony_ci   st->nb_adapt++;
73653a5a1b3Sopenharmony_ci   if (st->nb_adapt>20000)
73753a5a1b3Sopenharmony_ci      st->nb_adapt = 20000;
73853a5a1b3Sopenharmony_ci   st->min_count++;
73953a5a1b3Sopenharmony_ci
74053a5a1b3Sopenharmony_ci   beta = MAX16(QCONST16(.03,15),DIV32_16(Q15_ONE,st->nb_adapt));
74153a5a1b3Sopenharmony_ci   beta_1 = Q15_ONE-beta;
74253a5a1b3Sopenharmony_ci   M = st->nbands;
74353a5a1b3Sopenharmony_ci   /* Deal with residual echo if provided */
74453a5a1b3Sopenharmony_ci   if (st->echo_state)
74553a5a1b3Sopenharmony_ci   {
74653a5a1b3Sopenharmony_ci      speex_echo_get_residual(st->echo_state, st->residual_echo, N);
74753a5a1b3Sopenharmony_ci#ifndef FIXED_POINT
74853a5a1b3Sopenharmony_ci      /* If there are NaNs or ridiculous values, it'll show up in the DC and we just reset everything to zero */
74953a5a1b3Sopenharmony_ci      if (!(st->residual_echo[0] >=0 && st->residual_echo[0]<N*1e9f))
75053a5a1b3Sopenharmony_ci      {
75153a5a1b3Sopenharmony_ci         for (i=0;i<N;i++)
75253a5a1b3Sopenharmony_ci            st->residual_echo[i] = 0;
75353a5a1b3Sopenharmony_ci      }
75453a5a1b3Sopenharmony_ci#endif
75553a5a1b3Sopenharmony_ci      for (i=0;i<N;i++)
75653a5a1b3Sopenharmony_ci         st->echo_noise[i] = MAX32(MULT16_32_Q15(QCONST16(.6f,15),st->echo_noise[i]), st->residual_echo[i]);
75753a5a1b3Sopenharmony_ci      filterbank_compute_bank32(st->bank, st->echo_noise, st->echo_noise+N);
75853a5a1b3Sopenharmony_ci   } else {
75953a5a1b3Sopenharmony_ci      for (i=0;i<N+M;i++)
76053a5a1b3Sopenharmony_ci         st->echo_noise[i] = 0;
76153a5a1b3Sopenharmony_ci   }
76253a5a1b3Sopenharmony_ci   preprocess_analysis(st, x);
76353a5a1b3Sopenharmony_ci
76453a5a1b3Sopenharmony_ci   update_noise_prob(st);
76553a5a1b3Sopenharmony_ci
76653a5a1b3Sopenharmony_ci   /* Noise estimation always updated for the 10 first frames */
76753a5a1b3Sopenharmony_ci   /*if (st->nb_adapt<10)
76853a5a1b3Sopenharmony_ci   {
76953a5a1b3Sopenharmony_ci      for (i=1;i<N-1;i++)
77053a5a1b3Sopenharmony_ci         st->update_prob[i] = 0;
77153a5a1b3Sopenharmony_ci   }
77253a5a1b3Sopenharmony_ci   */
77353a5a1b3Sopenharmony_ci
77453a5a1b3Sopenharmony_ci   /* Update the noise estimate for the frequencies where it can be */
77553a5a1b3Sopenharmony_ci   for (i=0;i<N;i++)
77653a5a1b3Sopenharmony_ci   {
77753a5a1b3Sopenharmony_ci      if (!st->update_prob[i] || st->ps[i] < PSHR32(st->noise[i], NOISE_SHIFT))
77853a5a1b3Sopenharmony_ci         st->noise[i] = MAX32(EXTEND32(0),MULT16_32_Q15(beta_1,st->noise[i]) + MULT16_32_Q15(beta,SHL32(st->ps[i],NOISE_SHIFT)));
77953a5a1b3Sopenharmony_ci   }
78053a5a1b3Sopenharmony_ci   filterbank_compute_bank32(st->bank, st->noise, st->noise+N);
78153a5a1b3Sopenharmony_ci
78253a5a1b3Sopenharmony_ci   /* Special case for first frame */
78353a5a1b3Sopenharmony_ci   if (st->nb_adapt==1)
78453a5a1b3Sopenharmony_ci      for (i=0;i<N+M;i++)
78553a5a1b3Sopenharmony_ci         st->old_ps[i] = ps[i];
78653a5a1b3Sopenharmony_ci
78753a5a1b3Sopenharmony_ci   /* Compute a posteriori SNR */
78853a5a1b3Sopenharmony_ci   for (i=0;i<N+M;i++)
78953a5a1b3Sopenharmony_ci   {
79053a5a1b3Sopenharmony_ci      spx_word16_t gamma;
79153a5a1b3Sopenharmony_ci
79253a5a1b3Sopenharmony_ci      /* Total noise estimate including residual echo and reverberation */
79353a5a1b3Sopenharmony_ci      spx_word32_t tot_noise = ADD32(ADD32(ADD32(EXTEND32(1), PSHR32(st->noise[i],NOISE_SHIFT)) , st->echo_noise[i]) , st->reverb_estimate[i]);
79453a5a1b3Sopenharmony_ci
79553a5a1b3Sopenharmony_ci      /* A posteriori SNR = ps/noise - 1*/
79653a5a1b3Sopenharmony_ci      st->post[i] = SUB16(DIV32_16_Q8(ps[i],tot_noise), QCONST16(1.f,SNR_SHIFT));
79753a5a1b3Sopenharmony_ci      st->post[i]=MIN16(st->post[i], QCONST16(100.f,SNR_SHIFT));
79853a5a1b3Sopenharmony_ci
79953a5a1b3Sopenharmony_ci      /* Computing update gamma = .1 + .9*(old/(old+noise))^2 */
80053a5a1b3Sopenharmony_ci      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))));
80153a5a1b3Sopenharmony_ci
80253a5a1b3Sopenharmony_ci      /* A priori SNR update = gamma*max(0,post) + (1-gamma)*old/noise */
80353a5a1b3Sopenharmony_ci      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));
80453a5a1b3Sopenharmony_ci      st->prior[i]=MIN16(st->prior[i], QCONST16(100.f,SNR_SHIFT));
80553a5a1b3Sopenharmony_ci   }
80653a5a1b3Sopenharmony_ci
80753a5a1b3Sopenharmony_ci   /*print_vec(st->post, N+M, "");*/
80853a5a1b3Sopenharmony_ci
80953a5a1b3Sopenharmony_ci   /* Recursive average of the a priori SNR. A bit smoothed for the psd components */
81053a5a1b3Sopenharmony_ci   st->zeta[0] = PSHR32(ADD32(MULT16_16(QCONST16(.7f,15),st->zeta[0]), MULT16_16(QCONST16(.3f,15),st->prior[0])),15);
81153a5a1b3Sopenharmony_ci   for (i=1;i<N-1;i++)
81253a5a1b3Sopenharmony_ci      st->zeta[i] = PSHR32(ADD32(ADD32(ADD32(MULT16_16(QCONST16(.7f,15),st->zeta[i]), MULT16_16(QCONST16(.15f,15),st->prior[i])),
81353a5a1b3Sopenharmony_ci                           MULT16_16(QCONST16(.075f,15),st->prior[i-1])), MULT16_16(QCONST16(.075f,15),st->prior[i+1])),15);
81453a5a1b3Sopenharmony_ci   for (i=N-1;i<N+M;i++)
81553a5a1b3Sopenharmony_ci      st->zeta[i] = PSHR32(ADD32(MULT16_16(QCONST16(.7f,15),st->zeta[i]), MULT16_16(QCONST16(.3f,15),st->prior[i])),15);
81653a5a1b3Sopenharmony_ci
81753a5a1b3Sopenharmony_ci   /* Speech probability of presence for the entire frame is based on the average filterbank a priori SNR */
81853a5a1b3Sopenharmony_ci   Zframe = 0;
81953a5a1b3Sopenharmony_ci   for (i=N;i<N+M;i++)
82053a5a1b3Sopenharmony_ci      Zframe = ADD32(Zframe, EXTEND32(st->zeta[i]));
82153a5a1b3Sopenharmony_ci   Pframe = QCONST16(.1f,15)+MULT16_16_Q15(QCONST16(.899f,15),qcurve(DIV32_16(Zframe,st->nbands)));
82253a5a1b3Sopenharmony_ci
82353a5a1b3Sopenharmony_ci   effective_echo_suppress = EXTRACT16(PSHR32(ADD32(MULT16_16(SUB16(Q15_ONE,Pframe), st->echo_suppress), MULT16_16(Pframe, st->echo_suppress_active)),15));
82453a5a1b3Sopenharmony_ci
82553a5a1b3Sopenharmony_ci   compute_gain_floor(st->noise_suppress, effective_echo_suppress, st->noise+N, st->echo_noise+N, st->gain_floor+N, M);
82653a5a1b3Sopenharmony_ci
82753a5a1b3Sopenharmony_ci   /* Compute Ephraim & Malah gain speech probability of presence for each critical band (Bark scale)
82853a5a1b3Sopenharmony_ci      Technically this is actually wrong because the EM gaim assumes a slightly different probability
82953a5a1b3Sopenharmony_ci      distribution */
83053a5a1b3Sopenharmony_ci   for (i=N;i<N+M;i++)
83153a5a1b3Sopenharmony_ci   {
83253a5a1b3Sopenharmony_ci      /* See EM and Cohen papers*/
83353a5a1b3Sopenharmony_ci      spx_word32_t theta;
83453a5a1b3Sopenharmony_ci      /* Gain from hypergeometric function */
83553a5a1b3Sopenharmony_ci      spx_word32_t MM;
83653a5a1b3Sopenharmony_ci      /* Weiner filter gain */
83753a5a1b3Sopenharmony_ci      spx_word16_t prior_ratio;
83853a5a1b3Sopenharmony_ci      /* a priority probability of speech presence based on Bark sub-band alone */
83953a5a1b3Sopenharmony_ci      spx_word16_t P1;
84053a5a1b3Sopenharmony_ci      /* Speech absence a priori probability (considering sub-band and frame) */
84153a5a1b3Sopenharmony_ci      spx_word16_t q;
84253a5a1b3Sopenharmony_ci#ifdef FIXED_POINT
84353a5a1b3Sopenharmony_ci      spx_word16_t tmp;
84453a5a1b3Sopenharmony_ci#endif
84553a5a1b3Sopenharmony_ci
84653a5a1b3Sopenharmony_ci      prior_ratio = PDIV32_16(SHL32(EXTEND32(st->prior[i]), 15), ADD16(st->prior[i], SHL32(1,SNR_SHIFT)));
84753a5a1b3Sopenharmony_ci      theta = MULT16_32_P15(prior_ratio, QCONST32(1.f,EXPIN_SHIFT)+SHL32(EXTEND32(st->post[i]),EXPIN_SHIFT-SNR_SHIFT));
84853a5a1b3Sopenharmony_ci
84953a5a1b3Sopenharmony_ci      MM = hypergeom_gain(theta);
85053a5a1b3Sopenharmony_ci      /* Gain with bound */
85153a5a1b3Sopenharmony_ci      st->gain[i] = EXTRACT16(MIN32(Q15_ONE, MULT16_32_Q15(prior_ratio, MM)));
85253a5a1b3Sopenharmony_ci      /* Save old Bark power spectrum */
85353a5a1b3Sopenharmony_ci      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]);
85453a5a1b3Sopenharmony_ci
85553a5a1b3Sopenharmony_ci      P1 = QCONST16(.199f,15)+MULT16_16_Q15(QCONST16(.8f,15),qcurve (st->zeta[i]));
85653a5a1b3Sopenharmony_ci      q = Q15_ONE-MULT16_16_Q15(Pframe,P1);
85753a5a1b3Sopenharmony_ci#ifdef FIXED_POINT
85853a5a1b3Sopenharmony_ci      theta = MIN32(theta, EXTEND32(32767));
85953a5a1b3Sopenharmony_ci/*Q8*/tmp = MULT16_16_Q15((SHL32(1,SNR_SHIFT)+st->prior[i]),EXTRACT16(MIN32(Q15ONE,SHR32(spx_exp(-EXTRACT16(theta)),1))));
86053a5a1b3Sopenharmony_ci      tmp = MIN16(QCONST16(3.,SNR_SHIFT), tmp); /* Prevent overflows in the next line*/
86153a5a1b3Sopenharmony_ci/*Q8*/tmp = EXTRACT16(PSHR32(MULT16_16(PDIV32_16(SHL32(EXTEND32(q),8),(Q15_ONE-q)),tmp),8));
86253a5a1b3Sopenharmony_ci      st->gain2[i]=DIV32_16(SHL32(EXTEND32(32767),SNR_SHIFT), ADD16(256,tmp));
86353a5a1b3Sopenharmony_ci#else
86453a5a1b3Sopenharmony_ci      st->gain2[i]=1/(1.f + (q/(1.f-q))*(1+st->prior[i])*exp(-theta));
86553a5a1b3Sopenharmony_ci#endif
86653a5a1b3Sopenharmony_ci   }
86753a5a1b3Sopenharmony_ci   /* Convert the EM gains and speech prob to linear frequency */
86853a5a1b3Sopenharmony_ci   filterbank_compute_psd16(st->bank,st->gain2+N, st->gain2);
86953a5a1b3Sopenharmony_ci   filterbank_compute_psd16(st->bank,st->gain+N, st->gain);
87053a5a1b3Sopenharmony_ci
87153a5a1b3Sopenharmony_ci   /* Use 1 for linear gain resolution (best) or 0 for Bark gain resolution (faster) */
87253a5a1b3Sopenharmony_ci   if (1)
87353a5a1b3Sopenharmony_ci   {
87453a5a1b3Sopenharmony_ci      filterbank_compute_psd16(st->bank,st->gain_floor+N, st->gain_floor);
87553a5a1b3Sopenharmony_ci
87653a5a1b3Sopenharmony_ci      /* Compute gain according to the Ephraim-Malah algorithm -- linear frequency */
87753a5a1b3Sopenharmony_ci      for (i=0;i<N;i++)
87853a5a1b3Sopenharmony_ci      {
87953a5a1b3Sopenharmony_ci         spx_word32_t MM;
88053a5a1b3Sopenharmony_ci         spx_word32_t theta;
88153a5a1b3Sopenharmony_ci         spx_word16_t prior_ratio;
88253a5a1b3Sopenharmony_ci         spx_word16_t tmp;
88353a5a1b3Sopenharmony_ci         spx_word16_t p;
88453a5a1b3Sopenharmony_ci         spx_word16_t g;
88553a5a1b3Sopenharmony_ci
88653a5a1b3Sopenharmony_ci         /* Wiener filter gain */
88753a5a1b3Sopenharmony_ci         prior_ratio = PDIV32_16(SHL32(EXTEND32(st->prior[i]), 15), ADD16(st->prior[i], SHL32(1,SNR_SHIFT)));
88853a5a1b3Sopenharmony_ci         theta = MULT16_32_P15(prior_ratio, QCONST32(1.f,EXPIN_SHIFT)+SHL32(EXTEND32(st->post[i]),EXPIN_SHIFT-SNR_SHIFT));
88953a5a1b3Sopenharmony_ci
89053a5a1b3Sopenharmony_ci         /* Optimal estimator for loudness domain */
89153a5a1b3Sopenharmony_ci         MM = hypergeom_gain(theta);
89253a5a1b3Sopenharmony_ci         /* EM gain with bound */
89353a5a1b3Sopenharmony_ci         g = EXTRACT16(MIN32(Q15_ONE, MULT16_32_Q15(prior_ratio, MM)));
89453a5a1b3Sopenharmony_ci         /* Interpolated speech probability of presence */
89553a5a1b3Sopenharmony_ci         p = st->gain2[i];
89653a5a1b3Sopenharmony_ci
89753a5a1b3Sopenharmony_ci         /* Constrain the gain to be close to the Bark scale gain */
89853a5a1b3Sopenharmony_ci         if (MULT16_16_Q15(QCONST16(.333f,15),g) > st->gain[i])
89953a5a1b3Sopenharmony_ci            g = MULT16_16(3,st->gain[i]);
90053a5a1b3Sopenharmony_ci         st->gain[i] = g;
90153a5a1b3Sopenharmony_ci
90253a5a1b3Sopenharmony_ci         /* Save old power spectrum */
90353a5a1b3Sopenharmony_ci         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]);
90453a5a1b3Sopenharmony_ci
90553a5a1b3Sopenharmony_ci         /* Apply gain floor */
90653a5a1b3Sopenharmony_ci         if (st->gain[i] < st->gain_floor[i])
90753a5a1b3Sopenharmony_ci            st->gain[i] = st->gain_floor[i];
90853a5a1b3Sopenharmony_ci
90953a5a1b3Sopenharmony_ci         /* Exponential decay model for reverberation (unused) */
91053a5a1b3Sopenharmony_ci         /*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];*/
91153a5a1b3Sopenharmony_ci
91253a5a1b3Sopenharmony_ci         /* Take into account speech probability of presence (loudness domain MMSE estimator) */
91353a5a1b3Sopenharmony_ci         /* gain2 = [p*sqrt(gain)+(1-p)*sqrt(gain _floor) ]^2 */
91453a5a1b3Sopenharmony_ci         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)));
91553a5a1b3Sopenharmony_ci         st->gain2[i]=SQR16_Q15(tmp);
91653a5a1b3Sopenharmony_ci
91753a5a1b3Sopenharmony_ci         /* Use this if you want a log-domain MMSE estimator instead */
91853a5a1b3Sopenharmony_ci         /*st->gain2[i] = pow(st->gain[i], p) * pow(st->gain_floor[i],1.f-p);*/
91953a5a1b3Sopenharmony_ci      }
92053a5a1b3Sopenharmony_ci   } else {
92153a5a1b3Sopenharmony_ci      for (i=N;i<N+M;i++)
92253a5a1b3Sopenharmony_ci      {
92353a5a1b3Sopenharmony_ci         spx_word16_t tmp;
92453a5a1b3Sopenharmony_ci         spx_word16_t p = st->gain2[i];
92553a5a1b3Sopenharmony_ci         st->gain[i] = MAX16(st->gain[i], st->gain_floor[i]);
92653a5a1b3Sopenharmony_ci         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)));
92753a5a1b3Sopenharmony_ci         st->gain2[i]=SQR16_Q15(tmp);
92853a5a1b3Sopenharmony_ci      }
92953a5a1b3Sopenharmony_ci      filterbank_compute_psd16(st->bank,st->gain2+N, st->gain2);
93053a5a1b3Sopenharmony_ci   }
93153a5a1b3Sopenharmony_ci
93253a5a1b3Sopenharmony_ci   /* If noise suppression is off, don't apply the gain (but then why call this in the first place!) */
93353a5a1b3Sopenharmony_ci   if (!st->denoise_enabled)
93453a5a1b3Sopenharmony_ci   {
93553a5a1b3Sopenharmony_ci      for (i=0;i<N+M;i++)
93653a5a1b3Sopenharmony_ci         st->gain2[i]=Q15_ONE;
93753a5a1b3Sopenharmony_ci   }
93853a5a1b3Sopenharmony_ci
93953a5a1b3Sopenharmony_ci   /* Apply computed gain */
94053a5a1b3Sopenharmony_ci   for (i=1;i<N;i++)
94153a5a1b3Sopenharmony_ci   {
94253a5a1b3Sopenharmony_ci      st->ft[2*i-1] = MULT16_16_P15(st->gain2[i],st->ft[2*i-1]);
94353a5a1b3Sopenharmony_ci      st->ft[2*i] = MULT16_16_P15(st->gain2[i],st->ft[2*i]);
94453a5a1b3Sopenharmony_ci   }
94553a5a1b3Sopenharmony_ci   st->ft[0] = MULT16_16_P15(st->gain2[0],st->ft[0]);
94653a5a1b3Sopenharmony_ci   st->ft[2*N-1] = MULT16_16_P15(st->gain2[N-1],st->ft[2*N-1]);
94753a5a1b3Sopenharmony_ci
94853a5a1b3Sopenharmony_ci   /*FIXME: This *will* not work for fixed-point */
94953a5a1b3Sopenharmony_ci#ifndef FIXED_POINT
95053a5a1b3Sopenharmony_ci   if (st->agc_enabled)
95153a5a1b3Sopenharmony_ci      speex_compute_agc(st, Pframe, st->ft);
95253a5a1b3Sopenharmony_ci#endif
95353a5a1b3Sopenharmony_ci
95453a5a1b3Sopenharmony_ci   /* Inverse FFT with 1/N scaling */
95553a5a1b3Sopenharmony_ci   spx_ifft(st->fft_lookup, st->ft, st->frame);
95653a5a1b3Sopenharmony_ci   /* Scale back to original (lower) amplitude */
95753a5a1b3Sopenharmony_ci   for (i=0;i<2*N;i++)
95853a5a1b3Sopenharmony_ci      st->frame[i] = PSHR16(st->frame[i], st->frame_shift);
95953a5a1b3Sopenharmony_ci
96053a5a1b3Sopenharmony_ci   /*FIXME: This *will* not work for fixed-point */
96153a5a1b3Sopenharmony_ci#ifndef FIXED_POINT
96253a5a1b3Sopenharmony_ci   if (st->agc_enabled)
96353a5a1b3Sopenharmony_ci   {
96453a5a1b3Sopenharmony_ci      float max_sample=0;
96553a5a1b3Sopenharmony_ci      for (i=0;i<2*N;i++)
96653a5a1b3Sopenharmony_ci         if (fabs(st->frame[i])>max_sample)
96753a5a1b3Sopenharmony_ci            max_sample = fabs(st->frame[i]);
96853a5a1b3Sopenharmony_ci      if (max_sample>28000.f)
96953a5a1b3Sopenharmony_ci      {
97053a5a1b3Sopenharmony_ci         float damp = 28000.f/max_sample;
97153a5a1b3Sopenharmony_ci         for (i=0;i<2*N;i++)
97253a5a1b3Sopenharmony_ci            st->frame[i] *= damp;
97353a5a1b3Sopenharmony_ci      }
97453a5a1b3Sopenharmony_ci   }
97553a5a1b3Sopenharmony_ci#endif
97653a5a1b3Sopenharmony_ci
97753a5a1b3Sopenharmony_ci   /* Synthesis window (for WOLA) */
97853a5a1b3Sopenharmony_ci   for (i=0;i<2*N;i++)
97953a5a1b3Sopenharmony_ci      st->frame[i] = MULT16_16_Q15(st->frame[i], st->window[i]);
98053a5a1b3Sopenharmony_ci
98153a5a1b3Sopenharmony_ci   /* Perform overlap and add */
98253a5a1b3Sopenharmony_ci   for (i=0;i<N3;i++)
98353a5a1b3Sopenharmony_ci      x[i] = WORD2INT(ADD32(EXTEND32(st->outbuf[i]), EXTEND32(st->frame[i])));
98453a5a1b3Sopenharmony_ci   for (i=0;i<N4;i++)
98553a5a1b3Sopenharmony_ci      x[N3+i] = st->frame[N3+i];
98653a5a1b3Sopenharmony_ci
98753a5a1b3Sopenharmony_ci   /* Update outbuf */
98853a5a1b3Sopenharmony_ci   for (i=0;i<N3;i++)
98953a5a1b3Sopenharmony_ci      st->outbuf[i] = st->frame[st->frame_size+i];
99053a5a1b3Sopenharmony_ci
99153a5a1b3Sopenharmony_ci   /* FIXME: This VAD is a kludge */
99253a5a1b3Sopenharmony_ci   st->speech_prob = Pframe;
99353a5a1b3Sopenharmony_ci   if (st->vad_enabled)
99453a5a1b3Sopenharmony_ci   {
99553a5a1b3Sopenharmony_ci      if (st->speech_prob > st->speech_prob_start || (st->was_speech && st->speech_prob > st->speech_prob_continue))
99653a5a1b3Sopenharmony_ci      {
99753a5a1b3Sopenharmony_ci         st->was_speech=1;
99853a5a1b3Sopenharmony_ci         return 1;
99953a5a1b3Sopenharmony_ci      } else
100053a5a1b3Sopenharmony_ci      {
100153a5a1b3Sopenharmony_ci         st->was_speech=0;
100253a5a1b3Sopenharmony_ci         return 0;
100353a5a1b3Sopenharmony_ci      }
100453a5a1b3Sopenharmony_ci   } else {
100553a5a1b3Sopenharmony_ci      return 1;
100653a5a1b3Sopenharmony_ci   }
100753a5a1b3Sopenharmony_ci}
100853a5a1b3Sopenharmony_ci
100953a5a1b3Sopenharmony_ciEXPORT void speex_preprocess_estimate_update(SpeexPreprocessState *st, spx_int16_t *x)
101053a5a1b3Sopenharmony_ci{
101153a5a1b3Sopenharmony_ci   int i;
101253a5a1b3Sopenharmony_ci   int N = st->ps_size;
101353a5a1b3Sopenharmony_ci   int N3 = 2*N - st->frame_size;
101453a5a1b3Sopenharmony_ci   int M;
101553a5a1b3Sopenharmony_ci   spx_word32_t *ps=st->ps;
101653a5a1b3Sopenharmony_ci
101753a5a1b3Sopenharmony_ci   M = st->nbands;
101853a5a1b3Sopenharmony_ci   st->min_count++;
101953a5a1b3Sopenharmony_ci
102053a5a1b3Sopenharmony_ci   preprocess_analysis(st, x);
102153a5a1b3Sopenharmony_ci
102253a5a1b3Sopenharmony_ci   update_noise_prob(st);
102353a5a1b3Sopenharmony_ci
102453a5a1b3Sopenharmony_ci   for (i=1;i<N-1;i++)
102553a5a1b3Sopenharmony_ci   {
102653a5a1b3Sopenharmony_ci      if (!st->update_prob[i] || st->ps[i] < PSHR32(st->noise[i],NOISE_SHIFT))
102753a5a1b3Sopenharmony_ci      {
102853a5a1b3Sopenharmony_ci         st->noise[i] = MULT16_32_Q15(QCONST16(.95f,15),st->noise[i]) + MULT16_32_Q15(QCONST16(.05f,15),SHL32(st->ps[i],NOISE_SHIFT));
102953a5a1b3Sopenharmony_ci      }
103053a5a1b3Sopenharmony_ci   }
103153a5a1b3Sopenharmony_ci
103253a5a1b3Sopenharmony_ci   for (i=0;i<N3;i++)
103353a5a1b3Sopenharmony_ci      st->outbuf[i] = MULT16_16_Q15(x[st->frame_size-N3+i],st->window[st->frame_size+i]);
103453a5a1b3Sopenharmony_ci
103553a5a1b3Sopenharmony_ci   /* Save old power spectrum */
103653a5a1b3Sopenharmony_ci   for (i=0;i<N+M;i++)
103753a5a1b3Sopenharmony_ci      st->old_ps[i] = ps[i];
103853a5a1b3Sopenharmony_ci
103953a5a1b3Sopenharmony_ci   for (i=0;i<N;i++)
104053a5a1b3Sopenharmony_ci      st->reverb_estimate[i] = MULT16_32_Q15(st->reverb_decay, st->reverb_estimate[i]);
104153a5a1b3Sopenharmony_ci}
104253a5a1b3Sopenharmony_ci
104353a5a1b3Sopenharmony_ci
104453a5a1b3Sopenharmony_ciEXPORT int speex_preprocess_ctl(SpeexPreprocessState *state, int request, void *ptr)
104553a5a1b3Sopenharmony_ci{
104653a5a1b3Sopenharmony_ci   int i;
104753a5a1b3Sopenharmony_ci   SpeexPreprocessState *st;
104853a5a1b3Sopenharmony_ci   st=(SpeexPreprocessState*)state;
104953a5a1b3Sopenharmony_ci   switch(request)
105053a5a1b3Sopenharmony_ci   {
105153a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_SET_DENOISE:
105253a5a1b3Sopenharmony_ci      st->denoise_enabled = (*(spx_int32_t*)ptr);
105353a5a1b3Sopenharmony_ci      break;
105453a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_GET_DENOISE:
105553a5a1b3Sopenharmony_ci      (*(spx_int32_t*)ptr) = st->denoise_enabled;
105653a5a1b3Sopenharmony_ci      break;
105753a5a1b3Sopenharmony_ci#ifndef FIXED_POINT
105853a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_SET_AGC:
105953a5a1b3Sopenharmony_ci      st->agc_enabled = (*(spx_int32_t*)ptr);
106053a5a1b3Sopenharmony_ci      break;
106153a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_GET_AGC:
106253a5a1b3Sopenharmony_ci      (*(spx_int32_t*)ptr) = st->agc_enabled;
106353a5a1b3Sopenharmony_ci      break;
106453a5a1b3Sopenharmony_ci#ifndef DISABLE_FLOAT_API
106553a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_SET_AGC_LEVEL:
106653a5a1b3Sopenharmony_ci      st->agc_level = (*(float*)ptr);
106753a5a1b3Sopenharmony_ci      if (st->agc_level<1)
106853a5a1b3Sopenharmony_ci         st->agc_level=1;
106953a5a1b3Sopenharmony_ci      if (st->agc_level>32768)
107053a5a1b3Sopenharmony_ci         st->agc_level=32768;
107153a5a1b3Sopenharmony_ci      break;
107253a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_GET_AGC_LEVEL:
107353a5a1b3Sopenharmony_ci      (*(float*)ptr) = st->agc_level;
107453a5a1b3Sopenharmony_ci      break;
107553a5a1b3Sopenharmony_ci#endif /* #ifndef DISABLE_FLOAT_API */
107653a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_SET_AGC_INCREMENT:
107753a5a1b3Sopenharmony_ci      st->max_increase_step = exp(0.11513f * (*(spx_int32_t*)ptr)*st->frame_size / st->sampling_rate);
107853a5a1b3Sopenharmony_ci      break;
107953a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_GET_AGC_INCREMENT:
108053a5a1b3Sopenharmony_ci      (*(spx_int32_t*)ptr) = floor(.5+8.6858*log(st->max_increase_step)*st->sampling_rate/st->frame_size);
108153a5a1b3Sopenharmony_ci      break;
108253a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_SET_AGC_DECREMENT:
108353a5a1b3Sopenharmony_ci      st->max_decrease_step = exp(0.11513f * (*(spx_int32_t*)ptr)*st->frame_size / st->sampling_rate);
108453a5a1b3Sopenharmony_ci      break;
108553a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_GET_AGC_DECREMENT:
108653a5a1b3Sopenharmony_ci      (*(spx_int32_t*)ptr) = floor(.5+8.6858*log(st->max_decrease_step)*st->sampling_rate/st->frame_size);
108753a5a1b3Sopenharmony_ci      break;
108853a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_SET_AGC_MAX_GAIN:
108953a5a1b3Sopenharmony_ci      st->max_gain = exp(0.11513f * (*(spx_int32_t*)ptr));
109053a5a1b3Sopenharmony_ci      break;
109153a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_GET_AGC_MAX_GAIN:
109253a5a1b3Sopenharmony_ci      (*(spx_int32_t*)ptr) = floor(.5+8.6858*log(st->max_gain));
109353a5a1b3Sopenharmony_ci      break;
109453a5a1b3Sopenharmony_ci#endif
109553a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_SET_VAD:
109653a5a1b3Sopenharmony_ci      speex_warning("The VAD has been replaced by a hack pending a complete rewrite");
109753a5a1b3Sopenharmony_ci      st->vad_enabled = (*(spx_int32_t*)ptr);
109853a5a1b3Sopenharmony_ci      break;
109953a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_GET_VAD:
110053a5a1b3Sopenharmony_ci      (*(spx_int32_t*)ptr) = st->vad_enabled;
110153a5a1b3Sopenharmony_ci      break;
110253a5a1b3Sopenharmony_ci
110353a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_SET_DEREVERB:
110453a5a1b3Sopenharmony_ci      st->dereverb_enabled = (*(spx_int32_t*)ptr);
110553a5a1b3Sopenharmony_ci      for (i=0;i<st->ps_size;i++)
110653a5a1b3Sopenharmony_ci         st->reverb_estimate[i]=0;
110753a5a1b3Sopenharmony_ci      break;
110853a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_GET_DEREVERB:
110953a5a1b3Sopenharmony_ci      (*(spx_int32_t*)ptr) = st->dereverb_enabled;
111053a5a1b3Sopenharmony_ci      break;
111153a5a1b3Sopenharmony_ci
111253a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_SET_DEREVERB_LEVEL:
111353a5a1b3Sopenharmony_ci      /* FIXME: Re-enable when de-reverberation is actually enabled again */
111453a5a1b3Sopenharmony_ci      /*st->reverb_level = (*(float*)ptr);*/
111553a5a1b3Sopenharmony_ci      break;
111653a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_GET_DEREVERB_LEVEL:
111753a5a1b3Sopenharmony_ci      /* FIXME: Re-enable when de-reverberation is actually enabled again */
111853a5a1b3Sopenharmony_ci      /*(*(float*)ptr) = st->reverb_level;*/
111953a5a1b3Sopenharmony_ci      break;
112053a5a1b3Sopenharmony_ci
112153a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_SET_DEREVERB_DECAY:
112253a5a1b3Sopenharmony_ci      /* FIXME: Re-enable when de-reverberation is actually enabled again */
112353a5a1b3Sopenharmony_ci      /*st->reverb_decay = (*(float*)ptr);*/
112453a5a1b3Sopenharmony_ci      break;
112553a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_GET_DEREVERB_DECAY:
112653a5a1b3Sopenharmony_ci      /* FIXME: Re-enable when de-reverberation is actually enabled again */
112753a5a1b3Sopenharmony_ci      /*(*(float*)ptr) = st->reverb_decay;*/
112853a5a1b3Sopenharmony_ci      break;
112953a5a1b3Sopenharmony_ci
113053a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_SET_PROB_START:
113153a5a1b3Sopenharmony_ci      *(spx_int32_t*)ptr = MIN32(100,MAX32(0, *(spx_int32_t*)ptr));
113253a5a1b3Sopenharmony_ci      st->speech_prob_start = DIV32_16(MULT16_16(Q15ONE,*(spx_int32_t*)ptr), 100);
113353a5a1b3Sopenharmony_ci      break;
113453a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_GET_PROB_START:
113553a5a1b3Sopenharmony_ci      (*(spx_int32_t*)ptr) = MULT16_16_Q15(st->speech_prob_start, 100);
113653a5a1b3Sopenharmony_ci      break;
113753a5a1b3Sopenharmony_ci
113853a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_SET_PROB_CONTINUE:
113953a5a1b3Sopenharmony_ci      *(spx_int32_t*)ptr = MIN32(100,MAX32(0, *(spx_int32_t*)ptr));
114053a5a1b3Sopenharmony_ci      st->speech_prob_continue = DIV32_16(MULT16_16(Q15ONE,*(spx_int32_t*)ptr), 100);
114153a5a1b3Sopenharmony_ci      break;
114253a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_GET_PROB_CONTINUE:
114353a5a1b3Sopenharmony_ci      (*(spx_int32_t*)ptr) = MULT16_16_Q15(st->speech_prob_continue, 100);
114453a5a1b3Sopenharmony_ci      break;
114553a5a1b3Sopenharmony_ci
114653a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_SET_NOISE_SUPPRESS:
114753a5a1b3Sopenharmony_ci      st->noise_suppress = -ABS(*(spx_int32_t*)ptr);
114853a5a1b3Sopenharmony_ci      break;
114953a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_GET_NOISE_SUPPRESS:
115053a5a1b3Sopenharmony_ci      (*(spx_int32_t*)ptr) = st->noise_suppress;
115153a5a1b3Sopenharmony_ci      break;
115253a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_SET_ECHO_SUPPRESS:
115353a5a1b3Sopenharmony_ci      st->echo_suppress = -ABS(*(spx_int32_t*)ptr);
115453a5a1b3Sopenharmony_ci      break;
115553a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_GET_ECHO_SUPPRESS:
115653a5a1b3Sopenharmony_ci      (*(spx_int32_t*)ptr) = st->echo_suppress;
115753a5a1b3Sopenharmony_ci      break;
115853a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_SET_ECHO_SUPPRESS_ACTIVE:
115953a5a1b3Sopenharmony_ci      st->echo_suppress_active = -ABS(*(spx_int32_t*)ptr);
116053a5a1b3Sopenharmony_ci      break;
116153a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_GET_ECHO_SUPPRESS_ACTIVE:
116253a5a1b3Sopenharmony_ci      (*(spx_int32_t*)ptr) = st->echo_suppress_active;
116353a5a1b3Sopenharmony_ci      break;
116453a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_SET_ECHO_STATE:
116553a5a1b3Sopenharmony_ci      st->echo_state = (SpeexEchoState*)ptr;
116653a5a1b3Sopenharmony_ci      break;
116753a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_GET_ECHO_STATE:
116853a5a1b3Sopenharmony_ci      (*(SpeexEchoState**)ptr) = (SpeexEchoState*)st->echo_state;
116953a5a1b3Sopenharmony_ci      break;
117053a5a1b3Sopenharmony_ci#ifndef FIXED_POINT
117153a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_GET_AGC_LOUDNESS:
117253a5a1b3Sopenharmony_ci      (*(spx_int32_t*)ptr) = pow(st->loudness, 1.0/LOUDNESS_EXP);
117353a5a1b3Sopenharmony_ci      break;
117453a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_GET_AGC_GAIN:
117553a5a1b3Sopenharmony_ci      (*(spx_int32_t*)ptr) = floor(.5+8.6858*log(st->agc_gain));
117653a5a1b3Sopenharmony_ci      break;
117753a5a1b3Sopenharmony_ci#endif
117853a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_GET_PSD_SIZE:
117953a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_GET_NOISE_PSD_SIZE:
118053a5a1b3Sopenharmony_ci      (*(spx_int32_t*)ptr) = st->ps_size;
118153a5a1b3Sopenharmony_ci      break;
118253a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_GET_PSD:
118353a5a1b3Sopenharmony_ci      for(i=0;i<st->ps_size;i++)
118453a5a1b3Sopenharmony_ci      	((spx_int32_t *)ptr)[i] = (spx_int32_t) st->ps[i];
118553a5a1b3Sopenharmony_ci      break;
118653a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_GET_NOISE_PSD:
118753a5a1b3Sopenharmony_ci      for(i=0;i<st->ps_size;i++)
118853a5a1b3Sopenharmony_ci      	((spx_int32_t *)ptr)[i] = (spx_int32_t) PSHR32(st->noise[i], NOISE_SHIFT);
118953a5a1b3Sopenharmony_ci      break;
119053a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_GET_PROB:
119153a5a1b3Sopenharmony_ci      (*(spx_int32_t*)ptr) = MULT16_16_Q15(st->speech_prob, 100);
119253a5a1b3Sopenharmony_ci      break;
119353a5a1b3Sopenharmony_ci#ifndef FIXED_POINT
119453a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_SET_AGC_TARGET:
119553a5a1b3Sopenharmony_ci      st->agc_level = (*(spx_int32_t*)ptr);
119653a5a1b3Sopenharmony_ci      if (st->agc_level<1)
119753a5a1b3Sopenharmony_ci         st->agc_level=1;
119853a5a1b3Sopenharmony_ci      if (st->agc_level>32768)
119953a5a1b3Sopenharmony_ci         st->agc_level=32768;
120053a5a1b3Sopenharmony_ci      break;
120153a5a1b3Sopenharmony_ci   case SPEEX_PREPROCESS_GET_AGC_TARGET:
120253a5a1b3Sopenharmony_ci      (*(spx_int32_t*)ptr) = st->agc_level;
120353a5a1b3Sopenharmony_ci      break;
120453a5a1b3Sopenharmony_ci#endif
120553a5a1b3Sopenharmony_ci   default:
120653a5a1b3Sopenharmony_ci      speex_warning_int("Unknown speex_preprocess_ctl request: ", request);
120753a5a1b3Sopenharmony_ci      return -1;
120853a5a1b3Sopenharmony_ci   }
120953a5a1b3Sopenharmony_ci   return 0;
121053a5a1b3Sopenharmony_ci}
121153a5a1b3Sopenharmony_ci
121253a5a1b3Sopenharmony_ci#ifdef FIXED_DEBUG
121353a5a1b3Sopenharmony_cilong long spx_mips=0;
121453a5a1b3Sopenharmony_ci#endif
121553a5a1b3Sopenharmony_ci
1216