Reland "Refactoring of the noise suppressor and adding true multichannel support"

This is a reland of 87a7b82520b83a6cf42da27cdc46142c2eb6248c

Original change's description:
> Refactoring of the noise suppressor and adding true multichannel support
> 
> This CL adds proper multichannel support to the noise suppressor.
> To accomplish that in a safe way, a full refactoring of the noise
> suppressor code has been done.
> 
> Due to floating point precision, the changes made are not entirely
> bitexact. They are, however, very close to being bitexact.
> 
> As a safety measure, the former noise suppressor code is preserved
> and a kill-switch is added to allow revering to the legacy noise
> suppressor in case issues arise.
> 
> Bug: webrtc:10895, b/143344262
> Change-Id: I0b071011b23265ac12e6d4b3956499d122286657
> Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/158407
> Commit-Queue: Per Åhgren <peah@webrtc.org>
> Reviewed-by: Gustaf Ullberg <gustaf@webrtc.org>
> Cr-Commit-Position: refs/heads/master@{#29646}

Bug: webrtc:10895, b/143344262
Change-Id: I236f1e67bb0baa4e30908a4cf7a8a7bb55fbced3
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/158747
Reviewed-by: Gustaf Ullberg <gustaf@webrtc.org>
Commit-Queue: Per Åhgren <peah@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#29663}
diff --git a/modules/audio_processing/legacy_ns/defines.h b/modules/audio_processing/legacy_ns/defines.h
new file mode 100644
index 0000000..02e0318
--- /dev/null
+++ b/modules/audio_processing/legacy_ns/defines.h
@@ -0,0 +1,52 @@
+/*
+ *  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_AUDIO_PROCESSING_LEGACY_NS_DEFINES_H_
+#define MODULES_AUDIO_PROCESSING_LEGACY_NS_DEFINES_H_
+
+#define BLOCKL_MAX 160        // max processing block length: 160
+#define ANAL_BLOCKL_MAX 256   // max analysis block length: 256
+#define HALF_ANAL_BLOCKL 129  // half max analysis block length + 1
+#define NUM_HIGH_BANDS_MAX 2  // max number of high bands: 2
+
+#define QUANTILE 0.25f
+
+#define SIMULT 3
+#define END_STARTUP_LONG 200
+#define END_STARTUP_SHORT 50
+#define FACTOR 40.f
+#define WIDTH 0.01f
+
+// Length of fft work arrays.
+#define IP_LENGTH \
+  (ANAL_BLOCKL_MAX >> 1)  // must be at least ceil(2 + sqrt(ANAL_BLOCKL_MAX/2))
+#define W_LENGTH (ANAL_BLOCKL_MAX >> 1)
+
+// PARAMETERS FOR NEW METHOD
+#define DD_PR_SNR 0.98f        // DD update of prior SNR
+#define LRT_TAVG 0.5f          // tavg parameter for LRT (previously 0.90)
+#define SPECT_FL_TAVG 0.30f    // tavg parameter for spectral flatness measure
+#define SPECT_DIFF_TAVG 0.30f  // tavg parameter for spectral difference measure
+#define PRIOR_UPDATE 0.1f      // update parameter of prior model
+#define NOISE_UPDATE 0.9f      // update parameter for noise
+#define SPEECH_UPDATE 0.99f    // update parameter when likely speech
+#define WIDTH_PR_MAP 4.0f      // width parameter in sigmoid map for prior model
+#define LRT_FEATURE_THR 0.5f   // default threshold for LRT feature
+#define SF_FEATURE_THR 0.5f  // default threshold for Spectral Flatness feature
+#define SD_FEATURE_THR \
+  0.5f  // default threshold for Spectral Difference feature
+#define PROB_RANGE \
+  0.2f                     // probability threshold for noise state in
+                           // speech/noise likelihood
+#define HIST_PAR_EST 1000  // histogram size for estimation of parameters
+#define GAMMA_PAUSE 0.05f  // update for conservative noise estimate
+//
+#define B_LIM 0.5f  // threshold in final energy gain factor calculation
+#endif              // MODULES_AUDIO_PROCESSING_LEGACY_NS_DEFINES_H_
diff --git a/modules/audio_processing/legacy_ns/noise_suppression.c b/modules/audio_processing/legacy_ns/noise_suppression.c
new file mode 100644
index 0000000..8b95640
--- /dev/null
+++ b/modules/audio_processing/legacy_ns/noise_suppression.c
@@ -0,0 +1,71 @@
+/*
+ *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/audio_processing/legacy_ns/noise_suppression.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "common_audio/signal_processing/include/signal_processing_library.h"
+#include "modules/audio_processing/legacy_ns/defines.h"
+#include "modules/audio_processing/legacy_ns/ns_core.h"
+
+NsHandle* WebRtcNs_Create() {
+  NoiseSuppressionC* self = malloc(sizeof(NoiseSuppressionC));
+  self->initFlag = 0;
+  return (NsHandle*)self;
+}
+
+void WebRtcNs_Free(NsHandle* NS_inst) {
+  free(NS_inst);
+}
+
+int WebRtcNs_Init(NsHandle* NS_inst, uint32_t fs) {
+  return WebRtcNs_InitCore((NoiseSuppressionC*)NS_inst, fs);
+}
+
+int WebRtcNs_set_policy(NsHandle* NS_inst, int mode) {
+  return WebRtcNs_set_policy_core((NoiseSuppressionC*)NS_inst, mode);
+}
+
+void WebRtcNs_Analyze(NsHandle* NS_inst, const float* spframe) {
+  WebRtcNs_AnalyzeCore((NoiseSuppressionC*)NS_inst, spframe);
+}
+
+void WebRtcNs_Process(NsHandle* NS_inst,
+                      const float* const* spframe,
+                      size_t num_bands,
+                      float* const* outframe) {
+  WebRtcNs_ProcessCore((NoiseSuppressionC*)NS_inst, spframe, num_bands,
+                       outframe);
+}
+
+float WebRtcNs_prior_speech_probability(NsHandle* handle) {
+  NoiseSuppressionC* self = (NoiseSuppressionC*)handle;
+  if (handle == NULL) {
+    return -1;
+  }
+  if (self->initFlag == 0) {
+    return -1;
+  }
+  return self->priorSpeechProb;
+}
+
+const float* WebRtcNs_noise_estimate(const NsHandle* handle) {
+  const NoiseSuppressionC* self = (const NoiseSuppressionC*)handle;
+  if (handle == NULL || self->initFlag == 0) {
+    return NULL;
+  }
+  return self->noise;
+}
+
+size_t WebRtcNs_num_freq() {
+  return HALF_ANAL_BLOCKL;
+}
diff --git a/modules/audio_processing/legacy_ns/noise_suppression.h b/modules/audio_processing/legacy_ns/noise_suppression.h
new file mode 100644
index 0000000..01f04cc
--- /dev/null
+++ b/modules/audio_processing/legacy_ns/noise_suppression.h
@@ -0,0 +1,134 @@
+/*
+ *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_AUDIO_PROCESSING_LEGACY_NS_NOISE_SUPPRESSION_H_
+#define MODULES_AUDIO_PROCESSING_LEGACY_NS_NOISE_SUPPRESSION_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+typedef struct NsHandleT NsHandle;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * This function creates an instance of the floating point Noise Suppression.
+ */
+NsHandle* WebRtcNs_Create(void);
+
+/*
+ * This function frees the dynamic memory of a specified noise suppression
+ * instance.
+ *
+ * Input:
+ *      - NS_inst       : Pointer to NS instance that should be freed
+ */
+void WebRtcNs_Free(NsHandle* NS_inst);
+
+/*
+ * This function initializes a NS instance and has to be called before any other
+ * processing is made.
+ *
+ * Input:
+ *      - NS_inst       : Instance that should be initialized
+ *      - fs            : sampling frequency
+ *
+ * Output:
+ *      - NS_inst       : Initialized instance
+ *
+ * Return value         :  0 - Ok
+ *                        -1 - Error
+ */
+int WebRtcNs_Init(NsHandle* NS_inst, uint32_t fs);
+
+/*
+ * This changes the aggressiveness of the noise suppression method.
+ *
+ * Input:
+ *      - NS_inst       : Noise suppression instance.
+ *      - mode          : 0: Mild, 1: Medium , 2: Aggressive
+ *
+ * Output:
+ *      - NS_inst       : Updated instance.
+ *
+ * Return value         :  0 - Ok
+ *                        -1 - Error
+ */
+int WebRtcNs_set_policy(NsHandle* NS_inst, int mode);
+
+/*
+ * This functions estimates the background noise for the inserted speech frame.
+ * The input and output signals should always be 10ms (80 or 160 samples).
+ *
+ * Input
+ *      - NS_inst       : Noise suppression instance.
+ *      - spframe       : Pointer to speech frame buffer for L band
+ *
+ * Output:
+ *      - NS_inst       : Updated NS instance
+ */
+void WebRtcNs_Analyze(NsHandle* NS_inst, const float* spframe);
+
+/*
+ * This functions does Noise Suppression for the inserted speech frame. The
+ * input and output signals should always be 10ms (80 or 160 samples).
+ *
+ * Input
+ *      - NS_inst       : Noise suppression instance.
+ *      - spframe       : Pointer to speech frame buffer for each band
+ *      - num_bands     : Number of bands
+ *
+ * Output:
+ *      - NS_inst       : Updated NS instance
+ *      - outframe      : Pointer to output frame for each band
+ */
+void WebRtcNs_Process(NsHandle* NS_inst,
+                      const float* const* spframe,
+                      size_t num_bands,
+                      float* const* outframe);
+
+/* Returns the internally used prior speech probability of the current frame.
+ * There is a frequency bin based one as well, with which this should not be
+ * confused.
+ *
+ * Input
+ *      - handle        : Noise suppression instance.
+ *
+ * Return value         : Prior speech probability in interval [0.0, 1.0].
+ *                        -1 - NULL pointer or uninitialized instance.
+ */
+float WebRtcNs_prior_speech_probability(NsHandle* handle);
+
+/* Returns a pointer to the noise estimate per frequency bin. The number of
+ * frequency bins can be provided using WebRtcNs_num_freq().
+ *
+ * Input
+ *      - handle        : Noise suppression instance.
+ *
+ * Return value         : Pointer to the noise estimate per frequency bin.
+ *                        Returns NULL if the input is a NULL pointer or an
+ *                        uninitialized instance.
+ */
+const float* WebRtcNs_noise_estimate(const NsHandle* handle);
+
+/* Returns the number of frequency bins, which is the length of the noise
+ * estimate for example.
+ *
+ * Return value         : Number of frequency bins.
+ */
+size_t WebRtcNs_num_freq(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // MODULES_AUDIO_PROCESSING_LEGACY_NS_NOISE_SUPPRESSION_H_
diff --git a/modules/audio_processing/legacy_ns/noise_suppression_x.c b/modules/audio_processing/legacy_ns/noise_suppression_x.c
new file mode 100644
index 0000000..faa866e
--- /dev/null
+++ b/modules/audio_processing/legacy_ns/noise_suppression_x.c
@@ -0,0 +1,60 @@
+/*
+ *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/audio_processing/legacy_ns/noise_suppression_x.h"
+
+#include <stdlib.h>
+
+#include "common_audio/signal_processing/include/real_fft.h"
+#include "modules/audio_processing/legacy_ns/nsx_core.h"
+#include "modules/audio_processing/legacy_ns/nsx_defines.h"
+
+NsxHandle* WebRtcNsx_Create() {
+  NoiseSuppressionFixedC* self = malloc(sizeof(NoiseSuppressionFixedC));
+  self->real_fft = NULL;
+  self->initFlag = 0;
+  return (NsxHandle*)self;
+}
+
+void WebRtcNsx_Free(NsxHandle* nsxInst) {
+  WebRtcSpl_FreeRealFFT(((NoiseSuppressionFixedC*)nsxInst)->real_fft);
+  free(nsxInst);
+}
+
+int WebRtcNsx_Init(NsxHandle* nsxInst, uint32_t fs) {
+  return WebRtcNsx_InitCore((NoiseSuppressionFixedC*)nsxInst, fs);
+}
+
+int WebRtcNsx_set_policy(NsxHandle* nsxInst, int mode) {
+  return WebRtcNsx_set_policy_core((NoiseSuppressionFixedC*)nsxInst, mode);
+}
+
+void WebRtcNsx_Process(NsxHandle* nsxInst,
+                      const int16_t* const* speechFrame,
+                      int num_bands,
+                      int16_t* const* outFrame) {
+  WebRtcNsx_ProcessCore((NoiseSuppressionFixedC*)nsxInst, speechFrame,
+                        num_bands, outFrame);
+}
+
+const uint32_t* WebRtcNsx_noise_estimate(const NsxHandle* nsxInst,
+                                         int* q_noise) {
+  *q_noise = 11;
+  const NoiseSuppressionFixedC* self = (const NoiseSuppressionFixedC*)nsxInst;
+  if (nsxInst == NULL || self->initFlag == 0) {
+    return NULL;
+  }
+  *q_noise += self->prevQNoise;
+  return self->prevNoiseU32;
+}
+
+size_t WebRtcNsx_num_freq() {
+  return HALF_ANAL_BLOCKL;
+}
diff --git a/modules/audio_processing/legacy_ns/noise_suppression_x.h b/modules/audio_processing/legacy_ns/noise_suppression_x.h
new file mode 100644
index 0000000..572db41
--- /dev/null
+++ b/modules/audio_processing/legacy_ns/noise_suppression_x.h
@@ -0,0 +1,112 @@
+/*
+ *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_AUDIO_PROCESSING_LEGACY_NS_NOISE_SUPPRESSION_X_H_
+#define MODULES_AUDIO_PROCESSING_LEGACY_NS_NOISE_SUPPRESSION_X_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+typedef struct NsxHandleT NsxHandle;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * This function creates an instance of the fixed point Noise Suppression.
+ */
+NsxHandle* WebRtcNsx_Create(void);
+
+/*
+ * This function frees the dynamic memory of a specified Noise Suppression
+ * instance.
+ *
+ * Input:
+ *      - nsxInst       : Pointer to NS instance that should be freed
+ */
+void WebRtcNsx_Free(NsxHandle* nsxInst);
+
+/*
+ * This function initializes a NS instance
+ *
+ * Input:
+ *      - nsxInst       : Instance that should be initialized
+ *      - fs            : sampling frequency
+ *
+ * Output:
+ *      - nsxInst       : Initialized instance
+ *
+ * Return value         :  0 - Ok
+ *                        -1 - Error
+ */
+int WebRtcNsx_Init(NsxHandle* nsxInst, uint32_t fs);
+
+/*
+ * This changes the aggressiveness of the noise suppression method.
+ *
+ * Input:
+ *      - nsxInst       : Instance that should be initialized
+ *      - mode          : 0: Mild, 1: Medium , 2: Aggressive
+ *
+ * Output:
+ *      - nsxInst       : Initialized instance
+ *
+ * Return value         :  0 - Ok
+ *                        -1 - Error
+ */
+int WebRtcNsx_set_policy(NsxHandle* nsxInst, int mode);
+
+/*
+ * This functions does noise suppression for the inserted speech frame. The
+ * input and output signals should always be 10ms (80 or 160 samples).
+ *
+ * Input
+ *      - nsxInst       : NSx instance. Needs to be initiated before call.
+ *      - speechFrame   : Pointer to speech frame buffer for each band
+ *      - num_bands     : Number of bands
+ *
+ * Output:
+ *      - nsxInst       : Updated NSx instance
+ *      - outFrame      : Pointer to output frame for each band
+ */
+void WebRtcNsx_Process(NsxHandle* nsxInst,
+                       const int16_t* const* speechFrame,
+                       int num_bands,
+                       int16_t* const* outFrame);
+
+/* Returns a pointer to the noise estimate per frequency bin. The number of
+ * frequency bins can be provided using WebRtcNsx_num_freq().
+ *
+ * Input
+ *      - nsxInst       : NSx instance. Needs to be initiated before call.
+ *      - q_noise       : Q value of the noise estimate, which is the number of
+ *                        bits that it needs to be right-shifted to be
+ *                        normalized.
+ *
+ * Return value         : Pointer to the noise estimate per frequency bin.
+ *                        Returns NULL if the input is a NULL pointer or an
+ *                        uninitialized instance.
+ */
+const uint32_t* WebRtcNsx_noise_estimate(const NsxHandle* nsxInst,
+                                         int* q_noise);
+
+/* Returns the number of frequency bins, which is the length of the noise
+ * estimate for example.
+ *
+ * Return value         : Number of frequency bins.
+ */
+size_t WebRtcNsx_num_freq(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // MODULES_AUDIO_PROCESSING_LEGACY_NS_NOISE_SUPPRESSION_X_H_
diff --git a/modules/audio_processing/legacy_ns/ns_core.c b/modules/audio_processing/legacy_ns/ns_core.c
new file mode 100644
index 0000000..b5dc829
--- /dev/null
+++ b/modules/audio_processing/legacy_ns/ns_core.c
@@ -0,0 +1,1420 @@
+/*
+ *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "rtc_base/checks.h"
+#include "common_audio/signal_processing/include/signal_processing_library.h"
+#include "common_audio/third_party/fft4g/fft4g.h"
+#include "modules/audio_processing/legacy_ns/noise_suppression.h"
+#include "modules/audio_processing/legacy_ns/ns_core.h"
+#include "modules/audio_processing/legacy_ns/windows_private.h"
+
+// Set Feature Extraction Parameters.
+static void set_feature_extraction_parameters(NoiseSuppressionC* self) {
+  // Bin size of histogram.
+  self->featureExtractionParams.binSizeLrt = 0.1f;
+  self->featureExtractionParams.binSizeSpecFlat = 0.05f;
+  self->featureExtractionParams.binSizeSpecDiff = 0.1f;
+
+  // Range of histogram over which LRT threshold is computed.
+  self->featureExtractionParams.rangeAvgHistLrt = 1.f;
+
+  // Scale parameters: multiply dominant peaks of the histograms by scale factor
+  // to obtain thresholds for prior model.
+  // For LRT and spectral difference.
+  self->featureExtractionParams.factor1ModelPars = 1.2f;
+  // For spectral_flatness: used when noise is flatter than speech.
+  self->featureExtractionParams.factor2ModelPars = 0.9f;
+
+  // Peak limit for spectral flatness (varies between 0 and 1).
+  self->featureExtractionParams.thresPosSpecFlat = 0.6f;
+
+  // Limit on spacing of two highest peaks in histogram: spacing determined by
+  // bin size.
+  self->featureExtractionParams.limitPeakSpacingSpecFlat =
+      2 * self->featureExtractionParams.binSizeSpecFlat;
+  self->featureExtractionParams.limitPeakSpacingSpecDiff =
+      2 * self->featureExtractionParams.binSizeSpecDiff;
+
+  // Limit on relevance of second peak.
+  self->featureExtractionParams.limitPeakWeightsSpecFlat = 0.5f;
+  self->featureExtractionParams.limitPeakWeightsSpecDiff = 0.5f;
+
+  // Fluctuation limit of LRT feature.
+  self->featureExtractionParams.thresFluctLrt = 0.05f;
+
+  // Limit on the max and min values for the feature thresholds.
+  self->featureExtractionParams.maxLrt = 1.f;
+  self->featureExtractionParams.minLrt = 0.2f;
+
+  self->featureExtractionParams.maxSpecFlat = 0.95f;
+  self->featureExtractionParams.minSpecFlat = 0.1f;
+
+  self->featureExtractionParams.maxSpecDiff = 1.f;
+  self->featureExtractionParams.minSpecDiff = 0.16f;
+
+  // Criteria of weight of histogram peak to accept/reject feature.
+  self->featureExtractionParams.thresWeightSpecFlat =
+      (int)(0.3 * (self->modelUpdatePars[1]));  // For spectral flatness.
+  self->featureExtractionParams.thresWeightSpecDiff =
+      (int)(0.3 * (self->modelUpdatePars[1]));  // For spectral difference.
+}
+
+// Initialize state.
+int WebRtcNs_InitCore(NoiseSuppressionC* self, uint32_t fs) {
+  int i;
+  // Check for valid pointer.
+  if (self == NULL) {
+    return -1;
+  }
+
+  // Initialization of struct.
+  if (fs == 8000 || fs == 16000 || fs == 32000 || fs == 48000) {
+    self->fs = fs;
+  } else {
+    return -1;
+  }
+  self->windShift = 0;
+  // We only support 10ms frames.
+  if (fs == 8000) {
+    self->blockLen = 80;
+    self->anaLen = 128;
+    self->window = kBlocks80w128;
+  } else {
+    self->blockLen = 160;
+    self->anaLen = 256;
+    self->window = kBlocks160w256;
+  }
+  self->magnLen = self->anaLen / 2 + 1;  // Number of frequency bins.
+
+  // Initialize FFT work arrays.
+  self->ip[0] = 0;  // Setting this triggers initialization.
+  memset(self->dataBuf, 0, sizeof(float) * ANAL_BLOCKL_MAX);
+  WebRtc_rdft(self->anaLen, 1, self->dataBuf, self->ip, self->wfft);
+
+  memset(self->analyzeBuf, 0, sizeof(float) * ANAL_BLOCKL_MAX);
+  memset(self->dataBuf, 0, sizeof(float) * ANAL_BLOCKL_MAX);
+  memset(self->syntBuf, 0, sizeof(float) * ANAL_BLOCKL_MAX);
+
+  // For HB processing.
+  memset(self->dataBufHB,
+         0,
+         sizeof(float) * NUM_HIGH_BANDS_MAX * ANAL_BLOCKL_MAX);
+
+  // For quantile noise estimation.
+  memset(self->quantile, 0, sizeof(float) * HALF_ANAL_BLOCKL);
+  for (i = 0; i < SIMULT * HALF_ANAL_BLOCKL; i++) {
+    self->lquantile[i] = 8.f;
+    self->density[i] = 0.3f;
+  }
+
+  for (i = 0; i < SIMULT; i++) {
+    self->counter[i] =
+        (int)floor((float)(END_STARTUP_LONG * (i + 1)) / (float)SIMULT);
+  }
+
+  self->updates = 0;
+
+  // Wiener filter initialization.
+  for (i = 0; i < HALF_ANAL_BLOCKL; i++) {
+    self->smooth[i] = 1.f;
+  }
+
+  // Set the aggressiveness: default.
+  self->aggrMode = 0;
+
+  // Initialize variables for new method.
+  self->priorSpeechProb = 0.5f;  // Prior prob for speech/noise.
+  // Previous analyze mag spectrum.
+  memset(self->magnPrevAnalyze, 0, sizeof(float) * HALF_ANAL_BLOCKL);
+  // Previous process mag spectrum.
+  memset(self->magnPrevProcess, 0, sizeof(float) * HALF_ANAL_BLOCKL);
+  // Current noise-spectrum.
+  memset(self->noise, 0, sizeof(float) * HALF_ANAL_BLOCKL);
+  // Previous noise-spectrum.
+  memset(self->noisePrev, 0, sizeof(float) * HALF_ANAL_BLOCKL);
+  // Conservative noise spectrum estimate.
+  memset(self->magnAvgPause, 0, sizeof(float) * HALF_ANAL_BLOCKL);
+  // For estimation of HB in second pass.
+  memset(self->speechProb, 0, sizeof(float) * HALF_ANAL_BLOCKL);
+  // Initial average magnitude spectrum.
+  memset(self->initMagnEst, 0, sizeof(float) * HALF_ANAL_BLOCKL);
+  for (i = 0; i < HALF_ANAL_BLOCKL; i++) {
+    // Smooth LR (same as threshold).
+    self->logLrtTimeAvg[i] = LRT_FEATURE_THR;
+  }
+
+  // Feature quantities.
+  // Spectral flatness (start on threshold).
+  self->featureData[0] = SF_FEATURE_THR;
+  self->featureData[1] = 0.f;  // Spectral entropy: not used in this version.
+  self->featureData[2] = 0.f;  // Spectral variance: not used in this version.
+  // Average LRT factor (start on threshold).
+  self->featureData[3] = LRT_FEATURE_THR;
+  // Spectral template diff (start on threshold).
+  self->featureData[4] = SF_FEATURE_THR;
+  self->featureData[5] = 0.f;  // Normalization for spectral difference.
+  // Window time-average of input magnitude spectrum.
+  self->featureData[6] = 0.f;
+
+  memset(self->parametricNoise, 0, sizeof(float) * HALF_ANAL_BLOCKL);
+
+  // Histogram quantities: used to estimate/update thresholds for features.
+  memset(self->histLrt, 0, sizeof(int) * HIST_PAR_EST);
+  memset(self->histSpecFlat, 0, sizeof(int) * HIST_PAR_EST);
+  memset(self->histSpecDiff, 0, sizeof(int) * HIST_PAR_EST);
+
+
+  self->blockInd = -1;  // Frame counter.
+  // Default threshold for LRT feature.
+  self->priorModelPars[0] = LRT_FEATURE_THR;
+  // Threshold for spectral flatness: determined on-line.
+  self->priorModelPars[1] = 0.5f;
+  // sgn_map par for spectral measure: 1 for flatness measure.
+  self->priorModelPars[2] = 1.f;
+  // Threshold for template-difference feature: determined on-line.
+  self->priorModelPars[3] = 0.5f;
+  // Default weighting parameter for LRT feature.
+  self->priorModelPars[4] = 1.f;
+  // Default weighting parameter for spectral flatness feature.
+  self->priorModelPars[5] = 0.f;
+  // Default weighting parameter for spectral difference feature.
+  self->priorModelPars[6] = 0.f;
+
+  // Update flag for parameters:
+  // 0 no update, 1 = update once, 2 = update every window.
+  self->modelUpdatePars[0] = 2;
+  self->modelUpdatePars[1] = 500;  // Window for update.
+  // Counter for update of conservative noise spectrum.
+  self->modelUpdatePars[2] = 0;
+  // Counter if the feature thresholds are updated during the sequence.
+  self->modelUpdatePars[3] = self->modelUpdatePars[1];
+
+  self->signalEnergy = 0.0;
+  self->sumMagn = 0.0;
+  self->whiteNoiseLevel = 0.0;
+  self->pinkNoiseNumerator = 0.0;
+  self->pinkNoiseExp = 0.0;
+
+  set_feature_extraction_parameters(self);
+
+  // Default mode.
+  WebRtcNs_set_policy_core(self, 0);
+
+  self->initFlag = 1;
+  return 0;
+}
+
+// Estimate noise.
+static void NoiseEstimation(NoiseSuppressionC* self,
+                            float* magn,
+                            float* noise) {
+  size_t i, s, offset;
+  float lmagn[HALF_ANAL_BLOCKL], delta;
+
+  if (self->updates < END_STARTUP_LONG) {
+    self->updates++;
+  }
+
+  for (i = 0; i < self->magnLen; i++) {
+    lmagn[i] = (float)log(magn[i]);
+  }
+
+  // Loop over simultaneous estimates.
+  for (s = 0; s < SIMULT; s++) {
+    offset = s * self->magnLen;
+
+    // newquantest(...)
+    for (i = 0; i < self->magnLen; i++) {
+      // Compute delta.
+      if (self->density[offset + i] > 1.0) {
+        delta = FACTOR * 1.f / self->density[offset + i];
+      } else {
+        delta = FACTOR;
+      }
+
+      // Update log quantile estimate.
+      if (lmagn[i] > self->lquantile[offset + i]) {
+        self->lquantile[offset + i] +=
+            QUANTILE * delta / (float)(self->counter[s] + 1);
+      } else {
+        self->lquantile[offset + i] -=
+            (1.f - QUANTILE) * delta / (float)(self->counter[s] + 1);
+      }
+
+      // Update density estimate.
+      if (fabs(lmagn[i] - self->lquantile[offset + i]) < WIDTH) {
+        self->density[offset + i] =
+            ((float)self->counter[s] * self->density[offset + i] +
+             1.f / (2.f * WIDTH)) /
+            (float)(self->counter[s] + 1);
+      }
+    }  // End loop over magnitude spectrum.
+
+    if (self->counter[s] >= END_STARTUP_LONG) {
+      self->counter[s] = 0;
+      if (self->updates >= END_STARTUP_LONG) {
+        for (i = 0; i < self->magnLen; i++) {
+          self->quantile[i] = (float)exp(self->lquantile[offset + i]);
+        }
+      }
+    }
+
+    self->counter[s]++;
+  }  // End loop over simultaneous estimates.
+
+  // Sequentially update the noise during startup.
+  if (self->updates < END_STARTUP_LONG) {
+    // Use the last "s" to get noise during startup that differ from zero.
+    for (i = 0; i < self->magnLen; i++) {
+      self->quantile[i] = (float)exp(self->lquantile[offset + i]);
+    }
+  }
+
+  for (i = 0; i < self->magnLen; i++) {
+    noise[i] = self->quantile[i];
+  }
+}
+
+// Extract thresholds for feature parameters.
+// Histograms are computed over some window size (given by
+// self->modelUpdatePars[1]).
+// Thresholds and weights are extracted every window.
+// |flag| = 0 updates histogram only, |flag| = 1 computes the threshold/weights.
+// Threshold and weights are returned in: self->priorModelPars.
+static void FeatureParameterExtraction(NoiseSuppressionC* self, int flag) {
+  int i, useFeatureSpecFlat, useFeatureSpecDiff, numHistLrt;
+  int maxPeak1, maxPeak2;
+  int weightPeak1SpecFlat, weightPeak2SpecFlat, weightPeak1SpecDiff,
+      weightPeak2SpecDiff;
+
+  float binMid, featureSum;
+  float posPeak1SpecFlat, posPeak2SpecFlat, posPeak1SpecDiff, posPeak2SpecDiff;
+  float fluctLrt, avgHistLrt, avgSquareHistLrt, avgHistLrtCompl;
+
+  // 3 features: LRT, flatness, difference.
+  // lrt_feature = self->featureData[3];
+  // flat_feature = self->featureData[0];
+  // diff_feature = self->featureData[4];
+
+  // Update histograms.
+  if (flag == 0) {
+    // LRT
+    if ((self->featureData[3] <
+         HIST_PAR_EST * self->featureExtractionParams.binSizeLrt) &&
+        (self->featureData[3] >= 0.0)) {
+      i = (int)(self->featureData[3] /
+                self->featureExtractionParams.binSizeLrt);
+      self->histLrt[i]++;
+    }
+    // Spectral flatness.
+    if ((self->featureData[0] <
+         HIST_PAR_EST * self->featureExtractionParams.binSizeSpecFlat) &&
+        (self->featureData[0] >= 0.0)) {
+      i = (int)(self->featureData[0] /
+                self->featureExtractionParams.binSizeSpecFlat);
+      self->histSpecFlat[i]++;
+    }
+    // Spectral difference.
+    if ((self->featureData[4] <
+         HIST_PAR_EST * self->featureExtractionParams.binSizeSpecDiff) &&
+        (self->featureData[4] >= 0.0)) {
+      i = (int)(self->featureData[4] /
+                self->featureExtractionParams.binSizeSpecDiff);
+      self->histSpecDiff[i]++;
+    }
+  }
+
+  // Extract parameters for speech/noise probability.
+  if (flag == 1) {
+    // LRT feature: compute the average over
+    // self->featureExtractionParams.rangeAvgHistLrt.
+    avgHistLrt = 0.0;
+    avgHistLrtCompl = 0.0;
+    avgSquareHistLrt = 0.0;
+    numHistLrt = 0;
+    for (i = 0; i < HIST_PAR_EST; i++) {
+      binMid = ((float)i + 0.5f) * self->featureExtractionParams.binSizeLrt;
+      if (binMid <= self->featureExtractionParams.rangeAvgHistLrt) {
+        avgHistLrt += self->histLrt[i] * binMid;
+        numHistLrt += self->histLrt[i];
+      }
+      avgSquareHistLrt += self->histLrt[i] * binMid * binMid;
+      avgHistLrtCompl += self->histLrt[i] * binMid;
+    }
+    if (numHistLrt > 0) {
+      avgHistLrt = avgHistLrt / ((float)numHistLrt);
+    }
+    avgHistLrtCompl = avgHistLrtCompl / ((float)self->modelUpdatePars[1]);
+    avgSquareHistLrt = avgSquareHistLrt / ((float)self->modelUpdatePars[1]);
+    fluctLrt = avgSquareHistLrt - avgHistLrt * avgHistLrtCompl;
+    // Get threshold for LRT feature.
+    if (fluctLrt < self->featureExtractionParams.thresFluctLrt) {
+      // Very low fluctuation, so likely noise.
+      self->priorModelPars[0] = self->featureExtractionParams.maxLrt;
+    } else {
+      self->priorModelPars[0] =
+          self->featureExtractionParams.factor1ModelPars * avgHistLrt;
+      // Check if value is within min/max range.
+      if (self->priorModelPars[0] < self->featureExtractionParams.minLrt) {
+        self->priorModelPars[0] = self->featureExtractionParams.minLrt;
+      }
+      if (self->priorModelPars[0] > self->featureExtractionParams.maxLrt) {
+        self->priorModelPars[0] = self->featureExtractionParams.maxLrt;
+      }
+    }
+    // Done with LRT feature.
+
+    // For spectral flatness and spectral difference: compute the main peaks of
+    // histogram.
+    maxPeak1 = 0;
+    maxPeak2 = 0;
+    posPeak1SpecFlat = 0.0;
+    posPeak2SpecFlat = 0.0;
+    weightPeak1SpecFlat = 0;
+    weightPeak2SpecFlat = 0;
+
+    // Peaks for flatness.
+    for (i = 0; i < HIST_PAR_EST; i++) {
+      binMid =
+          (i + 0.5f) * self->featureExtractionParams.binSizeSpecFlat;
+      if (self->histSpecFlat[i] > maxPeak1) {
+        // Found new "first" peak.
+        maxPeak2 = maxPeak1;
+        weightPeak2SpecFlat = weightPeak1SpecFlat;
+        posPeak2SpecFlat = posPeak1SpecFlat;
+
+        maxPeak1 = self->histSpecFlat[i];
+        weightPeak1SpecFlat = self->histSpecFlat[i];
+        posPeak1SpecFlat = binMid;
+      } else if (self->histSpecFlat[i] > maxPeak2) {
+        // Found new "second" peak.
+        maxPeak2 = self->histSpecFlat[i];
+        weightPeak2SpecFlat = self->histSpecFlat[i];
+        posPeak2SpecFlat = binMid;
+      }
+    }
+
+    // Compute two peaks for spectral difference.
+    maxPeak1 = 0;
+    maxPeak2 = 0;
+    posPeak1SpecDiff = 0.0;
+    posPeak2SpecDiff = 0.0;
+    weightPeak1SpecDiff = 0;
+    weightPeak2SpecDiff = 0;
+    // Peaks for spectral difference.
+    for (i = 0; i < HIST_PAR_EST; i++) {
+      binMid =
+          ((float)i + 0.5f) * self->featureExtractionParams.binSizeSpecDiff;
+      if (self->histSpecDiff[i] > maxPeak1) {
+        // Found new "first" peak.
+        maxPeak2 = maxPeak1;
+        weightPeak2SpecDiff = weightPeak1SpecDiff;
+        posPeak2SpecDiff = posPeak1SpecDiff;
+
+        maxPeak1 = self->histSpecDiff[i];
+        weightPeak1SpecDiff = self->histSpecDiff[i];
+        posPeak1SpecDiff = binMid;
+      } else if (self->histSpecDiff[i] > maxPeak2) {
+        // Found new "second" peak.
+        maxPeak2 = self->histSpecDiff[i];
+        weightPeak2SpecDiff = self->histSpecDiff[i];
+        posPeak2SpecDiff = binMid;
+      }
+    }
+
+    // For spectrum flatness feature.
+    useFeatureSpecFlat = 1;
+    // Merge the two peaks if they are close.
+    if ((fabs(posPeak2SpecFlat - posPeak1SpecFlat) <
+         self->featureExtractionParams.limitPeakSpacingSpecFlat) &&
+        (weightPeak2SpecFlat >
+         self->featureExtractionParams.limitPeakWeightsSpecFlat *
+             weightPeak1SpecFlat)) {
+      weightPeak1SpecFlat += weightPeak2SpecFlat;
+      posPeak1SpecFlat = 0.5f * (posPeak1SpecFlat + posPeak2SpecFlat);
+    }
+    // Reject if weight of peaks is not large enough, or peak value too small.
+    if (weightPeak1SpecFlat <
+            self->featureExtractionParams.thresWeightSpecFlat ||
+        posPeak1SpecFlat < self->featureExtractionParams.thresPosSpecFlat) {
+      useFeatureSpecFlat = 0;
+    }
+    // If selected, get the threshold.
+    if (useFeatureSpecFlat == 1) {
+      // Compute the threshold.
+      self->priorModelPars[1] =
+          self->featureExtractionParams.factor2ModelPars * posPeak1SpecFlat;
+      // Check if value is within min/max range.
+      if (self->priorModelPars[1] < self->featureExtractionParams.minSpecFlat) {
+        self->priorModelPars[1] = self->featureExtractionParams.minSpecFlat;
+      }
+      if (self->priorModelPars[1] > self->featureExtractionParams.maxSpecFlat) {
+        self->priorModelPars[1] = self->featureExtractionParams.maxSpecFlat;
+      }
+    }
+    // Done with flatness feature.
+
+    // For template feature.
+    useFeatureSpecDiff = 1;
+    // Merge the two peaks if they are close.
+    if ((fabs(posPeak2SpecDiff - posPeak1SpecDiff) <
+         self->featureExtractionParams.limitPeakSpacingSpecDiff) &&
+        (weightPeak2SpecDiff >
+         self->featureExtractionParams.limitPeakWeightsSpecDiff *
+             weightPeak1SpecDiff)) {
+      weightPeak1SpecDiff += weightPeak2SpecDiff;
+      posPeak1SpecDiff = 0.5f * (posPeak1SpecDiff + posPeak2SpecDiff);
+    }
+    // Get the threshold value.
+    self->priorModelPars[3] =
+        self->featureExtractionParams.factor1ModelPars * posPeak1SpecDiff;
+    // Reject if weight of peaks is not large enough.
+    if (weightPeak1SpecDiff <
+        self->featureExtractionParams.thresWeightSpecDiff) {
+      useFeatureSpecDiff = 0;
+    }
+    // Check if value is within min/max range.
+    if (self->priorModelPars[3] < self->featureExtractionParams.minSpecDiff) {
+      self->priorModelPars[3] = self->featureExtractionParams.minSpecDiff;
+    }
+    if (self->priorModelPars[3] > self->featureExtractionParams.maxSpecDiff) {
+      self->priorModelPars[3] = self->featureExtractionParams.maxSpecDiff;
+    }
+    // Done with spectral difference feature.
+
+    // Don't use template feature if fluctuation of LRT feature is very low:
+    // most likely just noise state.
+    if (fluctLrt < self->featureExtractionParams.thresFluctLrt) {
+      useFeatureSpecDiff = 0;
+    }
+
+    // Select the weights between the features.
+    // self->priorModelPars[4] is weight for LRT: always selected.
+    // self->priorModelPars[5] is weight for spectral flatness.
+    // self->priorModelPars[6] is weight for spectral difference.
+    featureSum = (float)(1 + useFeatureSpecFlat + useFeatureSpecDiff);
+    self->priorModelPars[4] = 1.f / featureSum;
+    self->priorModelPars[5] = ((float)useFeatureSpecFlat) / featureSum;
+    self->priorModelPars[6] = ((float)useFeatureSpecDiff) / featureSum;
+
+    // Set hists to zero for next update.
+    if (self->modelUpdatePars[0] >= 1) {
+      for (i = 0; i < HIST_PAR_EST; i++) {
+        self->histLrt[i] = 0;
+        self->histSpecFlat[i] = 0;
+        self->histSpecDiff[i] = 0;
+      }
+    }
+  }  // End of flag == 1.
+}
+
+// Compute spectral flatness on input spectrum.
+// |magnIn| is the magnitude spectrum.
+// Spectral flatness is returned in self->featureData[0].
+static void ComputeSpectralFlatness(NoiseSuppressionC* self,
+                                    const float* magnIn) {
+  size_t i;
+  size_t shiftLP = 1;  // Option to remove first bin(s) from spectral measures.
+  float avgSpectralFlatnessNum, avgSpectralFlatnessDen, spectralTmp;
+
+  // Compute spectral measures.
+  // For flatness.
+  avgSpectralFlatnessNum = 0.0;
+  avgSpectralFlatnessDen = self->sumMagn;
+  for (i = 0; i < shiftLP; i++) {
+    avgSpectralFlatnessDen -= magnIn[i];
+  }
+  // Compute log of ratio of the geometric to arithmetic mean: check for log(0)
+  // case.
+  for (i = shiftLP; i < self->magnLen; i++) {
+    if (magnIn[i] > 0.0) {
+      avgSpectralFlatnessNum += (float)log(magnIn[i]);
+    } else {
+      self->featureData[0] -= SPECT_FL_TAVG * self->featureData[0];
+      return;
+    }
+  }
+  // Normalize.
+  avgSpectralFlatnessDen = avgSpectralFlatnessDen / self->magnLen;
+  avgSpectralFlatnessNum = avgSpectralFlatnessNum / self->magnLen;
+
+  // Ratio and inverse log: check for case of log(0).
+  spectralTmp = (float)exp(avgSpectralFlatnessNum) / avgSpectralFlatnessDen;
+
+  // Time-avg update of spectral flatness feature.
+  self->featureData[0] += SPECT_FL_TAVG * (spectralTmp - self->featureData[0]);
+  // Done with flatness feature.
+}
+
+// Compute prior and post SNR based on quantile noise estimation.
+// Compute DD estimate of prior SNR.
+// Inputs:
+//   * |magn| is the signal magnitude spectrum estimate.
+//   * |noise| is the magnitude noise spectrum estimate.
+// Outputs:
+//   * |snrLocPrior| is the computed prior SNR.
+//   * |snrLocPost| is the computed post SNR.
+static void ComputeSnr(const NoiseSuppressionC* self,
+                       const float* magn,
+                       const float* noise,
+                       float* snrLocPrior,
+                       float* snrLocPost) {
+  size_t i;
+
+  for (i = 0; i < self->magnLen; i++) {
+    // Previous post SNR.
+    // Previous estimate: based on previous frame with gain filter.
+    float previousEstimateStsa = self->magnPrevAnalyze[i] /
+        (self->noisePrev[i] + 0.0001f) * self->smooth[i];
+    // Post SNR.
+    snrLocPost[i] = 0.f;
+    if (magn[i] > noise[i]) {
+      snrLocPost[i] = magn[i] / (noise[i] + 0.0001f) - 1.f;
+    }
+    // DD estimate is sum of two terms: current estimate and previous estimate.
+    // Directed decision update of snrPrior.
+    snrLocPrior[i] =
+        DD_PR_SNR * previousEstimateStsa + (1.f - DD_PR_SNR) * snrLocPost[i];
+  }  // End of loop over frequencies.
+}
+
+// Compute the difference measure between input spectrum and a template/learned
+// noise spectrum.
+// |magnIn| is the input spectrum.
+// The reference/template spectrum is self->magnAvgPause[i].
+// Returns (normalized) spectral difference in self->featureData[4].
+static void ComputeSpectralDifference(NoiseSuppressionC* self,
+                                      const float* magnIn) {
+  // avgDiffNormMagn = var(magnIn) - cov(magnIn, magnAvgPause)^2 /
+  // var(magnAvgPause)
+  size_t i;
+  float avgPause, avgMagn, covMagnPause, varPause, varMagn, avgDiffNormMagn;
+
+  avgPause = 0.0;
+  avgMagn = self->sumMagn;
+  // Compute average quantities.
+  for (i = 0; i < self->magnLen; i++) {
+    // Conservative smooth noise spectrum from pause frames.
+    avgPause += self->magnAvgPause[i];
+  }
+  avgPause /= self->magnLen;
+  avgMagn /= self->magnLen;
+
+  covMagnPause = 0.0;
+  varPause = 0.0;
+  varMagn = 0.0;
+  // Compute variance and covariance quantities.
+  for (i = 0; i < self->magnLen; i++) {
+    covMagnPause += (magnIn[i] - avgMagn) * (self->magnAvgPause[i] - avgPause);
+    varPause +=
+        (self->magnAvgPause[i] - avgPause) * (self->magnAvgPause[i] - avgPause);
+    varMagn += (magnIn[i] - avgMagn) * (magnIn[i] - avgMagn);
+  }
+  covMagnPause /= self->magnLen;
+  varPause /= self->magnLen;
+  varMagn /= self->magnLen;
+  // Update of average magnitude spectrum.
+  self->featureData[6] += self->signalEnergy;
+
+  avgDiffNormMagn =
+      varMagn - (covMagnPause * covMagnPause) / (varPause + 0.0001f);
+  // Normalize and compute time-avg update of difference feature.
+  avgDiffNormMagn = (float)(avgDiffNormMagn / (self->featureData[5] + 0.0001f));
+  self->featureData[4] +=
+      SPECT_DIFF_TAVG * (avgDiffNormMagn - self->featureData[4]);
+}
+
+// Compute speech/noise probability.
+// Speech/noise probability is returned in |probSpeechFinal|.
+// |magn| is the input magnitude spectrum.
+// |noise| is the noise spectrum.
+// |snrLocPrior| is the prior SNR for each frequency.
+// |snrLocPost| is the post SNR for each frequency.
+static void SpeechNoiseProb(NoiseSuppressionC* self,
+                            float* probSpeechFinal,
+                            const float* snrLocPrior,
+                            const float* snrLocPost) {
+  size_t i;
+  int sgnMap;
+  float invLrt, gainPrior, indPrior;
+  float logLrtTimeAvgKsum, besselTmp;
+  float indicator0, indicator1, indicator2;
+  float tmpFloat1, tmpFloat2;
+  float weightIndPrior0, weightIndPrior1, weightIndPrior2;
+  float threshPrior0, threshPrior1, threshPrior2;
+  float widthPrior, widthPrior0, widthPrior1, widthPrior2;
+
+  widthPrior0 = WIDTH_PR_MAP;
+  // Width for pause region: lower range, so increase width in tanh map.
+  widthPrior1 = 2.f * WIDTH_PR_MAP;
+  widthPrior2 = 2.f * WIDTH_PR_MAP;  // For spectral-difference measure.
+
+  // Threshold parameters for features.
+  threshPrior0 = self->priorModelPars[0];
+  threshPrior1 = self->priorModelPars[1];
+  threshPrior2 = self->priorModelPars[3];
+
+  // Sign for flatness feature.
+  sgnMap = (int)(self->priorModelPars[2]);
+
+  // Weight parameters for features.
+  weightIndPrior0 = self->priorModelPars[4];
+  weightIndPrior1 = self->priorModelPars[5];
+  weightIndPrior2 = self->priorModelPars[6];
+
+  // Compute feature based on average LR factor.
+  // This is the average over all frequencies of the smooth log LRT.
+  logLrtTimeAvgKsum = 0.0;
+  for (i = 0; i < self->magnLen; i++) {
+    tmpFloat1 = 1.f + 2.f * snrLocPrior[i];
+    tmpFloat2 = 2.f * snrLocPrior[i] / (tmpFloat1 + 0.0001f);
+    besselTmp = (snrLocPost[i] + 1.f) * tmpFloat2;
+    self->logLrtTimeAvg[i] +=
+        LRT_TAVG * (besselTmp - (float)log(tmpFloat1) - self->logLrtTimeAvg[i]);
+    logLrtTimeAvgKsum += self->logLrtTimeAvg[i];
+  }
+  logLrtTimeAvgKsum = (float)logLrtTimeAvgKsum / (self->magnLen);
+  self->featureData[3] = logLrtTimeAvgKsum;
+  // Done with computation of LR factor.
+
+  // Compute the indicator functions.
+  // Average LRT feature.
+  widthPrior = widthPrior0;
+  // Use larger width in tanh map for pause regions.
+  if (logLrtTimeAvgKsum < threshPrior0) {
+    widthPrior = widthPrior1;
+  }
+  // Compute indicator function: sigmoid map.
+  indicator0 =
+      0.5f *
+      ((float)tanh(widthPrior * (logLrtTimeAvgKsum - threshPrior0)) + 1.f);
+
+  // Spectral flatness feature.
+  tmpFloat1 = self->featureData[0];
+  widthPrior = widthPrior0;
+  // Use larger width in tanh map for pause regions.
+  if (sgnMap == 1 && (tmpFloat1 > threshPrior1)) {
+    widthPrior = widthPrior1;
+  }
+  if (sgnMap == -1 && (tmpFloat1 < threshPrior1)) {
+    widthPrior = widthPrior1;
+  }
+  // Compute indicator function: sigmoid map.
+  indicator1 =
+      0.5f *
+      ((float)tanh((float)sgnMap * widthPrior * (threshPrior1 - tmpFloat1)) +
+       1.f);
+
+  // For template spectrum-difference.
+  tmpFloat1 = self->featureData[4];
+  widthPrior = widthPrior0;
+  // Use larger width in tanh map for pause regions.
+  if (tmpFloat1 < threshPrior2) {
+    widthPrior = widthPrior2;
+  }
+  // Compute indicator function: sigmoid map.
+  indicator2 =
+      0.5f * ((float)tanh(widthPrior * (tmpFloat1 - threshPrior2)) + 1.f);
+
+  // Combine the indicator function with the feature weights.
+  indPrior = weightIndPrior0 * indicator0 + weightIndPrior1 * indicator1 +
+             weightIndPrior2 * indicator2;
+  // Done with computing indicator function.
+
+  // Compute the prior probability.
+  self->priorSpeechProb += PRIOR_UPDATE * (indPrior - self->priorSpeechProb);
+  // Make sure probabilities are within range: keep floor to 0.01.
+  if (self->priorSpeechProb > 1.f) {
+    self->priorSpeechProb = 1.f;
+  }
+  if (self->priorSpeechProb < 0.01f) {
+    self->priorSpeechProb = 0.01f;
+  }
+
+  // Final speech probability: combine prior model with LR factor:.
+  gainPrior = (1.f - self->priorSpeechProb) / (self->priorSpeechProb + 0.0001f);
+  for (i = 0; i < self->magnLen; i++) {
+    invLrt = (float)exp(-self->logLrtTimeAvg[i]);
+    invLrt = (float)gainPrior * invLrt;
+    probSpeechFinal[i] = 1.f / (1.f + invLrt);
+  }
+}
+
+// Update the noise features.
+// Inputs:
+//   * |magn| is the signal magnitude spectrum estimate.
+//   * |updateParsFlag| is an update flag for parameters.
+static void FeatureUpdate(NoiseSuppressionC* self,
+                          const float* magn,
+                          int updateParsFlag) {
+  // Compute spectral flatness on input spectrum.
+  ComputeSpectralFlatness(self, magn);
+  // Compute difference of input spectrum with learned/estimated noise spectrum.
+  ComputeSpectralDifference(self, magn);
+  // Compute histograms for parameter decisions (thresholds and weights for
+  // features).
+  // Parameters are extracted once every window time.
+  // (=self->modelUpdatePars[1])
+  if (updateParsFlag >= 1) {
+    // Counter update.
+    self->modelUpdatePars[3]--;
+    // Update histogram.
+    if (self->modelUpdatePars[3] > 0) {
+      FeatureParameterExtraction(self, 0);
+    }
+    // Compute model parameters.
+    if (self->modelUpdatePars[3] == 0) {
+      FeatureParameterExtraction(self, 1);
+      self->modelUpdatePars[3] = self->modelUpdatePars[1];
+      // If wish to update only once, set flag to zero.
+      if (updateParsFlag == 1) {
+        self->modelUpdatePars[0] = 0;
+      } else {
+        // Update every window:
+        // Get normalization for spectral difference for next window estimate.
+        self->featureData[6] =
+            self->featureData[6] / ((float)self->modelUpdatePars[1]);
+        self->featureData[5] =
+            0.5f * (self->featureData[6] + self->featureData[5]);
+        self->featureData[6] = 0.f;
+      }
+    }
+  }
+}
+
+// Update the noise estimate.
+// Inputs:
+//   * |magn| is the signal magnitude spectrum estimate.
+//   * |snrLocPrior| is the prior SNR.
+//   * |snrLocPost| is the post SNR.
+// Output:
+//   * |noise| is the updated noise magnitude spectrum estimate.
+static void UpdateNoiseEstimate(NoiseSuppressionC* self,
+                                const float* magn,
+                                const float* snrLocPrior,
+                                const float* snrLocPost,
+                                float* noise) {
+  size_t i;
+  float probSpeech, probNonSpeech;
+  // Time-avg parameter for noise update.
+  float gammaNoiseTmp = NOISE_UPDATE;
+  float gammaNoiseOld;
+  float noiseUpdateTmp;
+
+  for (i = 0; i < self->magnLen; i++) {
+    probSpeech = self->speechProb[i];
+    probNonSpeech = 1.f - probSpeech;
+    // Temporary noise update:
+    // Use it for speech frames if update value is less than previous.
+    noiseUpdateTmp = gammaNoiseTmp * self->noisePrev[i] +
+                     (1.f - gammaNoiseTmp) * (probNonSpeech * magn[i] +
+                                              probSpeech * self->noisePrev[i]);
+    // Time-constant based on speech/noise state.
+    gammaNoiseOld = gammaNoiseTmp;
+    gammaNoiseTmp = NOISE_UPDATE;
+    // Increase gamma (i.e., less noise update) for frame likely to be speech.
+    if (probSpeech > PROB_RANGE) {
+      gammaNoiseTmp = SPEECH_UPDATE;
+    }
+    // Conservative noise update.
+    if (probSpeech < PROB_RANGE) {
+      self->magnAvgPause[i] += GAMMA_PAUSE * (magn[i] - self->magnAvgPause[i]);
+    }
+    // Noise update.
+    if (gammaNoiseTmp == gammaNoiseOld) {
+      noise[i] = noiseUpdateTmp;
+    } else {
+      noise[i] = gammaNoiseTmp * self->noisePrev[i] +
+                 (1.f - gammaNoiseTmp) * (probNonSpeech * magn[i] +
+                                          probSpeech * self->noisePrev[i]);
+      // Allow for noise update downwards:
+      // If noise update decreases the noise, it is safe, so allow it to
+      // happen.
+      if (noiseUpdateTmp < noise[i]) {
+        noise[i] = noiseUpdateTmp;
+      }
+    }
+  }  // End of freq loop.
+}
+
+// Updates |buffer| with a new |frame|.
+// Inputs:
+//   * |frame| is a new speech frame or NULL for setting to zero.
+//   * |frame_length| is the length of the new frame.
+//   * |buffer_length| is the length of the buffer.
+// Output:
+//   * |buffer| is the updated buffer.
+static void UpdateBuffer(const float* frame,
+                         size_t frame_length,
+                         size_t buffer_length,
+                         float* buffer) {
+  RTC_DCHECK_LT(buffer_length, 2 * frame_length);
+
+  memcpy(buffer,
+         buffer + frame_length,
+         sizeof(*buffer) * (buffer_length - frame_length));
+  if (frame) {
+    memcpy(buffer + buffer_length - frame_length,
+           frame,
+           sizeof(*buffer) * frame_length);
+  } else {
+    memset(buffer + buffer_length - frame_length,
+           0,
+           sizeof(*buffer) * frame_length);
+  }
+}
+
+// Transforms the signal from time to frequency domain.
+// Inputs:
+//   * |time_data| is the signal in the time domain.
+//   * |time_data_length| is the length of the analysis buffer.
+//   * |magnitude_length| is the length of the spectrum magnitude, which equals
+//     the length of both |real| and |imag| (time_data_length / 2 + 1).
+// Outputs:
+//   * |time_data| is the signal in the frequency domain.
+//   * |real| is the real part of the frequency domain.
+//   * |imag| is the imaginary part of the frequency domain.
+//   * |magn| is the calculated signal magnitude in the frequency domain.
+static void FFT(NoiseSuppressionC* self,
+                float* time_data,
+                size_t time_data_length,
+                size_t magnitude_length,
+                float* real,
+                float* imag,
+                float* magn) {
+  size_t i;
+
+  RTC_DCHECK_EQ(magnitude_length, time_data_length / 2 + 1);
+
+  WebRtc_rdft(time_data_length, 1, time_data, self->ip, self->wfft);
+
+  imag[0] = 0;
+  real[0] = time_data[0];
+  magn[0] = fabsf(real[0]) + 1.f;
+  imag[magnitude_length - 1] = 0;
+  real[magnitude_length - 1] = time_data[1];
+  magn[magnitude_length - 1] = fabsf(real[magnitude_length - 1]) + 1.f;
+  for (i = 1; i < magnitude_length - 1; ++i) {
+    real[i] = time_data[2 * i];
+    imag[i] = time_data[2 * i + 1];
+    // Magnitude spectrum.
+    magn[i] = sqrtf(real[i] * real[i] + imag[i] * imag[i]) + 1.f;
+  }
+}
+
+// Transforms the signal from frequency to time domain.
+// Inputs:
+//   * |real| is the real part of the frequency domain.
+//   * |imag| is the imaginary part of the frequency domain.
+//   * |magnitude_length| is the length of the spectrum magnitude, which equals
+//     the length of both |real| and |imag|.
+//   * |time_data_length| is the length of the analysis buffer
+//     (2 * (magnitude_length - 1)).
+// Output:
+//   * |time_data| is the signal in the time domain.
+static void IFFT(NoiseSuppressionC* self,
+                 const float* real,
+                 const float* imag,
+                 size_t magnitude_length,
+                 size_t time_data_length,
+                 float* time_data) {
+  size_t i;
+
+  RTC_DCHECK_EQ(time_data_length, 2 * (magnitude_length - 1));
+
+  time_data[0] = real[0];
+  time_data[1] = real[magnitude_length - 1];
+  for (i = 1; i < magnitude_length - 1; ++i) {
+    time_data[2 * i] = real[i];
+    time_data[2 * i + 1] = imag[i];
+  }
+  WebRtc_rdft(time_data_length, -1, time_data, self->ip, self->wfft);
+
+  for (i = 0; i < time_data_length; ++i) {
+    time_data[i] *= 2.f / time_data_length;  // FFT scaling.
+  }
+}
+
+// Calculates the energy of a buffer.
+// Inputs:
+//   * |buffer| is the buffer over which the energy is calculated.
+//   * |length| is the length of the buffer.
+// Returns the calculated energy.
+static float Energy(const float* buffer, size_t length) {
+  size_t i;
+  float energy = 0.f;
+
+  for (i = 0; i < length; ++i) {
+    energy += buffer[i] * buffer[i];
+  }
+
+  return energy;
+}
+
+// Windows a buffer.
+// Inputs:
+//   * |window| is the window by which to multiply.
+//   * |data| is the data without windowing.
+//   * |length| is the length of the window and data.
+// Output:
+//   * |data_windowed| is the windowed data.
+static void Windowing(const float* window,
+                      const float* data,
+                      size_t length,
+                      float* data_windowed) {
+  size_t i;
+
+  for (i = 0; i < length; ++i) {
+    data_windowed[i] = window[i] * data[i];
+  }
+}
+
+// Estimate prior SNR decision-directed and compute DD based Wiener Filter.
+// Input:
+//   * |magn| is the signal magnitude spectrum estimate.
+// Output:
+//   * |theFilter| is the frequency response of the computed Wiener filter.
+static void ComputeDdBasedWienerFilter(const NoiseSuppressionC* self,
+                                       const float* magn,
+                                       float* theFilter) {
+  size_t i;
+  float snrPrior, previousEstimateStsa, currentEstimateStsa;
+
+  for (i = 0; i < self->magnLen; i++) {
+    // Previous estimate: based on previous frame with gain filter.
+    previousEstimateStsa = self->magnPrevProcess[i] /
+                           (self->noisePrev[i] + 0.0001f) * self->smooth[i];
+    // Post and prior SNR.
+    currentEstimateStsa = 0.f;
+    if (magn[i] > self->noise[i]) {
+      currentEstimateStsa = magn[i] / (self->noise[i] + 0.0001f) - 1.f;
+    }
+    // DD estimate is sum of two terms: current estimate and previous estimate.
+    // Directed decision update of |snrPrior|.
+    snrPrior = DD_PR_SNR * previousEstimateStsa +
+               (1.f - DD_PR_SNR) * currentEstimateStsa;
+    // Gain filter.
+    theFilter[i] = snrPrior / (self->overdrive + snrPrior);
+  }  // End of loop over frequencies.
+}
+
+// Changes the aggressiveness of the noise suppression method.
+// |mode| = 0 is mild (6dB), |mode| = 1 is medium (10dB) and |mode| = 2 is
+// aggressive (15dB).
+// Returns 0 on success and -1 otherwise.
+int WebRtcNs_set_policy_core(NoiseSuppressionC* self, int mode) {
+  // Allow for modes: 0, 1, 2, 3.
+  if (mode < 0 || mode > 3) {
+    return (-1);
+  }
+
+  self->aggrMode = mode;
+  if (mode == 0) {
+    self->overdrive = 1.f;
+    self->denoiseBound = 0.5f;
+    self->gainmap = 0;
+  } else if (mode == 1) {
+    // self->overdrive = 1.25f;
+    self->overdrive = 1.f;
+    self->denoiseBound = 0.25f;
+    self->gainmap = 1;
+  } else if (mode == 2) {
+    // self->overdrive = 1.25f;
+    self->overdrive = 1.1f;
+    self->denoiseBound = 0.125f;
+    self->gainmap = 1;
+  } else if (mode == 3) {
+    // self->overdrive = 1.3f;
+    self->overdrive = 1.25f;
+    self->denoiseBound = 0.09f;
+    self->gainmap = 1;
+  }
+  return 0;
+}
+
+void WebRtcNs_AnalyzeCore(NoiseSuppressionC* self, const float* speechFrame) {
+  size_t i;
+  const size_t kStartBand = 5;  // Skip first frequency bins during estimation.
+  int updateParsFlag;
+  float energy;
+  float signalEnergy = 0.f;
+  float sumMagn = 0.f;
+  float tmpFloat1, tmpFloat2, tmpFloat3;
+  float winData[ANAL_BLOCKL_MAX];
+  float magn[HALF_ANAL_BLOCKL], noise[HALF_ANAL_BLOCKL];
+  float snrLocPost[HALF_ANAL_BLOCKL], snrLocPrior[HALF_ANAL_BLOCKL];
+  float real[ANAL_BLOCKL_MAX], imag[HALF_ANAL_BLOCKL];
+  // Variables during startup.
+  float sum_log_i = 0.0;
+  float sum_log_i_square = 0.0;
+  float sum_log_magn = 0.0;
+  float sum_log_i_log_magn = 0.0;
+  float parametric_exp = 0.0;
+  float parametric_num = 0.0;
+
+  // Check that initiation has been done.
+  RTC_DCHECK_EQ(1, self->initFlag);
+  updateParsFlag = self->modelUpdatePars[0];
+
+  // Update analysis buffer for L band.
+  UpdateBuffer(speechFrame, self->blockLen, self->anaLen, self->analyzeBuf);
+
+  Windowing(self->window, self->analyzeBuf, self->anaLen, winData);
+  energy = Energy(winData, self->anaLen);
+  if (energy == 0.0) {
+    // We want to avoid updating statistics in this case:
+    // Updating feature statistics when we have zeros only will cause
+    // thresholds to move towards zero signal situations. This in turn has the
+    // effect that once the signal is "turned on" (non-zero values) everything
+    // will be treated as speech and there is no noise suppression effect.
+    // Depending on the duration of the inactive signal it takes a
+    // considerable amount of time for the system to learn what is noise and
+    // what is speech.
+    self->signalEnergy = 0;
+    return;
+  }
+
+  self->blockInd++;  // Update the block index only when we process a block.
+
+  FFT(self, winData, self->anaLen, self->magnLen, real, imag, magn);
+
+  for (i = 0; i < self->magnLen; i++) {
+    signalEnergy += real[i] * real[i] + imag[i] * imag[i];
+    sumMagn += magn[i];
+    if (self->blockInd < END_STARTUP_SHORT) {
+      if (i >= kStartBand) {
+        tmpFloat2 = logf((float)i);
+        sum_log_i += tmpFloat2;
+        sum_log_i_square += tmpFloat2 * tmpFloat2;
+        tmpFloat1 = logf(magn[i]);
+        sum_log_magn += tmpFloat1;
+        sum_log_i_log_magn += tmpFloat2 * tmpFloat1;
+      }
+    }
+  }
+  signalEnergy /= self->magnLen;
+  self->signalEnergy = signalEnergy;
+  self->sumMagn = sumMagn;
+
+  // Quantile noise estimate.
+  NoiseEstimation(self, magn, noise);
+  // Compute simplified noise model during startup.
+  if (self->blockInd < END_STARTUP_SHORT) {
+    // Estimate White noise.
+    self->whiteNoiseLevel += sumMagn / self->magnLen * self->overdrive;
+    // Estimate Pink noise parameters.
+    tmpFloat1 = sum_log_i_square * (self->magnLen - kStartBand);
+    tmpFloat1 -= (sum_log_i * sum_log_i);
+    tmpFloat2 =
+        (sum_log_i_square * sum_log_magn - sum_log_i * sum_log_i_log_magn);
+    tmpFloat3 = tmpFloat2 / tmpFloat1;
+    // Constrain the estimated spectrum to be positive.
+    if (tmpFloat3 < 0.f) {
+      tmpFloat3 = 0.f;
+    }
+    self->pinkNoiseNumerator += tmpFloat3;
+    tmpFloat2 = (sum_log_i * sum_log_magn);
+    tmpFloat2 -= (self->magnLen - kStartBand) * sum_log_i_log_magn;
+    tmpFloat3 = tmpFloat2 / tmpFloat1;
+    // Constrain the pink noise power to be in the interval [0, 1].
+    if (tmpFloat3 < 0.f) {
+      tmpFloat3 = 0.f;
+    }
+    if (tmpFloat3 > 1.f) {
+      tmpFloat3 = 1.f;
+    }
+    self->pinkNoiseExp += tmpFloat3;
+
+    // Calculate frequency independent parts of parametric noise estimate.
+    if (self->pinkNoiseExp > 0.f) {
+      // Use pink noise estimate.
+      parametric_num =
+          expf(self->pinkNoiseNumerator / (float)(self->blockInd + 1));
+      parametric_num *= (float)(self->blockInd + 1);
+      parametric_exp = self->pinkNoiseExp / (float)(self->blockInd + 1);
+    }
+    for (i = 0; i < self->magnLen; i++) {
+      // Estimate the background noise using the white and pink noise
+      // parameters.
+      if (self->pinkNoiseExp == 0.f) {
+        // Use white noise estimate.
+        self->parametricNoise[i] = self->whiteNoiseLevel;
+      } else {
+        // Use pink noise estimate.
+        float use_band = (float)(i < kStartBand ? kStartBand : i);
+        self->parametricNoise[i] =
+            parametric_num / powf(use_band, parametric_exp);
+      }
+      // Weight quantile noise with modeled noise.
+      noise[i] *= (self->blockInd);
+      tmpFloat2 =
+          self->parametricNoise[i] * (END_STARTUP_SHORT - self->blockInd);
+      noise[i] += (tmpFloat2 / (float)(self->blockInd + 1));
+      noise[i] /= END_STARTUP_SHORT;
+    }
+  }
+  // Compute average signal during END_STARTUP_LONG time:
+  // used to normalize spectral difference measure.
+  if (self->blockInd < END_STARTUP_LONG) {
+    self->featureData[5] *= self->blockInd;
+    self->featureData[5] += signalEnergy;
+    self->featureData[5] /= (self->blockInd + 1);
+  }
+
+  // Post and prior SNR needed for SpeechNoiseProb.
+  ComputeSnr(self, magn, noise, snrLocPrior, snrLocPost);
+
+  FeatureUpdate(self, magn, updateParsFlag);
+  SpeechNoiseProb(self, self->speechProb, snrLocPrior, snrLocPost);
+  UpdateNoiseEstimate(self, magn, snrLocPrior, snrLocPost, noise);
+
+  // Keep track of noise spectrum for next frame.
+  memcpy(self->noise, noise, sizeof(*noise) * self->magnLen);
+  memcpy(self->magnPrevAnalyze, magn, sizeof(*magn) * self->magnLen);
+}
+
+void WebRtcNs_ProcessCore(NoiseSuppressionC* self,
+                          const float* const* speechFrame,
+                          size_t num_bands,
+                          float* const* outFrame) {
+  // Main routine for noise reduction.
+  int flagHB = 0;
+  size_t i, j;
+
+  float energy1, energy2, gain, factor, factor1, factor2;
+  float fout[BLOCKL_MAX];
+  float winData[ANAL_BLOCKL_MAX];
+  float magn[HALF_ANAL_BLOCKL];
+  float theFilter[HALF_ANAL_BLOCKL], theFilterTmp[HALF_ANAL_BLOCKL];
+  float real[ANAL_BLOCKL_MAX], imag[HALF_ANAL_BLOCKL];
+
+  // SWB variables.
+  int deltaBweHB = 1;
+  int deltaGainHB = 1;
+  float decayBweHB = 1.0;
+  float gainMapParHB = 1.0;
+  float gainTimeDomainHB = 1.0;
+  float avgProbSpeechHB, avgProbSpeechHBTmp, avgFilterGainHB, gainModHB;
+  float sumMagnAnalyze, sumMagnProcess;
+
+  // Check that initiation has been done.
+  RTC_DCHECK_EQ(1, self->initFlag);
+  RTC_DCHECK_LE(num_bands - 1, NUM_HIGH_BANDS_MAX);
+
+  const float* const* speechFrameHB = NULL;
+  float* const* outFrameHB = NULL;
+  size_t num_high_bands = 0;
+  if (num_bands > 1) {
+    speechFrameHB = &speechFrame[1];
+    outFrameHB = &outFrame[1];
+    num_high_bands = num_bands - 1;
+    flagHB = 1;
+    // Range for averaging low band quantities for H band gain.
+    deltaBweHB = (int)self->magnLen / 4;
+    deltaGainHB = deltaBweHB;
+  }
+
+  // Update analysis buffer for L band.
+  UpdateBuffer(speechFrame[0], self->blockLen, self->anaLen, self->dataBuf);
+
+  if (flagHB == 1) {
+    // Update analysis buffer for H bands.
+    for (i = 0; i < num_high_bands; ++i) {
+      UpdateBuffer(speechFrameHB[i],
+                   self->blockLen,
+                   self->anaLen,
+                   self->dataBufHB[i]);
+    }
+  }
+
+  Windowing(self->window, self->dataBuf, self->anaLen, winData);
+  energy1 = Energy(winData, self->anaLen);
+  if (energy1 == 0.0 || self->signalEnergy == 0) {
+    // Synthesize the special case of zero input.
+    // Read out fully processed segment.
+    for (i = self->windShift; i < self->blockLen + self->windShift; i++) {
+      fout[i - self->windShift] = self->syntBuf[i];
+    }
+    // Update synthesis buffer.
+    UpdateBuffer(NULL, self->blockLen, self->anaLen, self->syntBuf);
+
+    for (i = 0; i < self->blockLen; ++i)
+      outFrame[0][i] =
+          WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX, fout[i], WEBRTC_SPL_WORD16_MIN);
+
+    // For time-domain gain of HB.
+    if (flagHB == 1) {
+      for (i = 0; i < num_high_bands; ++i) {
+        for (j = 0; j < self->blockLen; ++j) {
+          outFrameHB[i][j] = WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX,
+                                            self->dataBufHB[i][j],
+                                            WEBRTC_SPL_WORD16_MIN);
+        }
+      }
+    }
+
+    return;
+  }
+
+  FFT(self, winData, self->anaLen, self->magnLen, real, imag, magn);
+
+  if (self->blockInd < END_STARTUP_SHORT) {
+    for (i = 0; i < self->magnLen; i++) {
+      self->initMagnEst[i] += magn[i];
+    }
+  }
+
+  ComputeDdBasedWienerFilter(self, magn, theFilter);
+
+  for (i = 0; i < self->magnLen; i++) {
+    // Flooring bottom.
+    if (theFilter[i] < self->denoiseBound) {
+      theFilter[i] = self->denoiseBound;
+    }
+    // Flooring top.
+    if (theFilter[i] > 1.f) {
+      theFilter[i] = 1.f;
+    }
+    if (self->blockInd < END_STARTUP_SHORT) {
+      theFilterTmp[i] =
+          (self->initMagnEst[i] - self->overdrive * self->parametricNoise[i]);
+      theFilterTmp[i] /= (self->initMagnEst[i] + 0.0001f);
+      // Flooring bottom.
+      if (theFilterTmp[i] < self->denoiseBound) {
+        theFilterTmp[i] = self->denoiseBound;
+      }
+      // Flooring top.
+      if (theFilterTmp[i] > 1.f) {
+        theFilterTmp[i] = 1.f;
+      }
+      // Weight the two suppression filters.
+      theFilter[i] *= (self->blockInd);
+      theFilterTmp[i] *= (END_STARTUP_SHORT - self->blockInd);
+      theFilter[i] += theFilterTmp[i];
+      theFilter[i] /= (END_STARTUP_SHORT);
+    }
+
+    self->smooth[i] = theFilter[i];
+    real[i] *= self->smooth[i];
+    imag[i] *= self->smooth[i];
+  }
+  // Keep track of |magn| spectrum for next frame.
+  memcpy(self->magnPrevProcess, magn, sizeof(*magn) * self->magnLen);
+  memcpy(self->noisePrev, self->noise, sizeof(self->noise[0]) * self->magnLen);
+  // Back to time domain.
+  IFFT(self, real, imag, self->magnLen, self->anaLen, winData);
+
+  // Scale factor: only do it after END_STARTUP_LONG time.
+  factor = 1.f;
+  if (self->gainmap == 1 && self->blockInd > END_STARTUP_LONG) {
+    factor1 = 1.f;
+    factor2 = 1.f;
+
+    energy2 = Energy(winData, self->anaLen);
+    gain = (float)sqrt(energy2 / (energy1 + 1.f));
+
+    // Scaling for new version.
+    if (gain > B_LIM) {
+      factor1 = 1.f + 1.3f * (gain - B_LIM);
+      if (gain * factor1 > 1.f) {
+        factor1 = 1.f / gain;
+      }
+    }
+    if (gain < B_LIM) {
+      // Don't reduce scale too much for pause regions:
+      // attenuation here should be controlled by flooring.
+      if (gain <= self->denoiseBound) {
+        gain = self->denoiseBound;
+      }
+      factor2 = 1.f - 0.3f * (B_LIM - gain);
+    }
+    // Combine both scales with speech/noise prob:
+    // note prior (priorSpeechProb) is not frequency dependent.
+    factor = self->priorSpeechProb * factor1 +
+             (1.f - self->priorSpeechProb) * factor2;
+  }  // Out of self->gainmap == 1.
+
+  Windowing(self->window, winData, self->anaLen, winData);
+
+  // Synthesis.
+  for (i = 0; i < self->anaLen; i++) {
+    self->syntBuf[i] += factor * winData[i];
+  }
+  // Read out fully processed segment.
+  for (i = self->windShift; i < self->blockLen + self->windShift; i++) {
+    fout[i - self->windShift] = self->syntBuf[i];
+  }
+  // Update synthesis buffer.
+  UpdateBuffer(NULL, self->blockLen, self->anaLen, self->syntBuf);
+
+  for (i = 0; i < self->blockLen; ++i)
+    outFrame[0][i] =
+        WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX, fout[i], WEBRTC_SPL_WORD16_MIN);
+
+  // For time-domain gain of HB.
+  if (flagHB == 1) {
+    // Average speech prob from low band.
+    // Average over second half (i.e., 4->8kHz) of frequencies spectrum.
+    avgProbSpeechHB = 0.0;
+    for (i = self->magnLen - deltaBweHB - 1; i < self->magnLen - 1; i++) {
+      avgProbSpeechHB += self->speechProb[i];
+    }
+    avgProbSpeechHB = avgProbSpeechHB / ((float)deltaBweHB);
+    // If the speech was suppressed by a component between Analyze and
+    // Process, for example the AEC, then it should not be considered speech
+    // for high band suppression purposes.
+    sumMagnAnalyze = 0;
+    sumMagnProcess = 0;
+    for (i = 0; i < self->magnLen; ++i) {
+      sumMagnAnalyze += self->magnPrevAnalyze[i];
+      sumMagnProcess += self->magnPrevProcess[i];
+    }
+    RTC_DCHECK_GT(sumMagnAnalyze, 0);
+    avgProbSpeechHB *= sumMagnProcess / sumMagnAnalyze;
+    // Average filter gain from low band.
+    // Average over second half (i.e., 4->8kHz) of frequencies spectrum.
+    avgFilterGainHB = 0.0;
+    for (i = self->magnLen - deltaGainHB - 1; i < self->magnLen - 1; i++) {
+      avgFilterGainHB += self->smooth[i];
+    }
+    avgFilterGainHB = avgFilterGainHB / ((float)(deltaGainHB));
+    avgProbSpeechHBTmp = 2.f * avgProbSpeechHB - 1.f;
+    // Gain based on speech probability.
+    gainModHB = 0.5f * (1.f + (float)tanh(gainMapParHB * avgProbSpeechHBTmp));
+    // Combine gain with low band gain.
+    gainTimeDomainHB = 0.5f * gainModHB + 0.5f * avgFilterGainHB;
+    if (avgProbSpeechHB >= 0.5f) {
+      gainTimeDomainHB = 0.25f * gainModHB + 0.75f * avgFilterGainHB;
+    }
+    gainTimeDomainHB = gainTimeDomainHB * decayBweHB;
+    // Make sure gain is within flooring range.
+    // Flooring bottom.
+    if (gainTimeDomainHB < self->denoiseBound) {
+      gainTimeDomainHB = self->denoiseBound;
+    }
+    // Flooring top.
+    if (gainTimeDomainHB > 1.f) {
+      gainTimeDomainHB = 1.f;
+    }
+    // Apply gain.
+    for (i = 0; i < num_high_bands; ++i) {
+      for (j = 0; j < self->blockLen; j++) {
+        outFrameHB[i][j] =
+            WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX,
+                           gainTimeDomainHB * self->dataBufHB[i][j],
+                           WEBRTC_SPL_WORD16_MIN);
+      }
+    }
+  }  // End of H band gain computation.
+}
diff --git a/modules/audio_processing/legacy_ns/ns_core.h b/modules/audio_processing/legacy_ns/ns_core.h
new file mode 100644
index 0000000..7a3b0a4
--- /dev/null
+++ b/modules/audio_processing/legacy_ns/ns_core.h
@@ -0,0 +1,188 @@
+/*
+ *  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_AUDIO_PROCESSING_LEGACY_NS_NS_CORE_H_
+#define MODULES_AUDIO_PROCESSING_LEGACY_NS_NS_CORE_H_
+
+#include "modules/audio_processing/legacy_ns/defines.h"
+
+typedef struct NSParaExtract_ {
+  // Bin size of histogram.
+  float binSizeLrt;
+  float binSizeSpecFlat;
+  float binSizeSpecDiff;
+  // Range of histogram over which LRT threshold is computed.
+  float rangeAvgHistLrt;
+  // Scale parameters: multiply dominant peaks of the histograms by scale factor
+  // to obtain thresholds for prior model.
+  float factor1ModelPars;  // For LRT and spectral difference.
+  float factor2ModelPars;  // For spectral_flatness: used when noise is flatter
+                           // than speech.
+  // Peak limit for spectral flatness (varies between 0 and 1).
+  float thresPosSpecFlat;
+  // Limit on spacing of two highest peaks in histogram: spacing determined by
+  // bin size.
+  float limitPeakSpacingSpecFlat;
+  float limitPeakSpacingSpecDiff;
+  // Limit on relevance of second peak.
+  float limitPeakWeightsSpecFlat;
+  float limitPeakWeightsSpecDiff;
+  // Limit on fluctuation of LRT feature.
+  float thresFluctLrt;
+  // Limit on the max and min values for the feature thresholds.
+  float maxLrt;
+  float minLrt;
+  float maxSpecFlat;
+  float minSpecFlat;
+  float maxSpecDiff;
+  float minSpecDiff;
+  // Criteria of weight of histogram peak to accept/reject feature.
+  int thresWeightSpecFlat;
+  int thresWeightSpecDiff;
+} NSParaExtract;
+
+typedef struct NoiseSuppressionC_ {
+  uint32_t fs;
+  size_t blockLen;
+  size_t windShift;
+  size_t anaLen;
+  size_t magnLen;
+  int aggrMode;
+  const float* window;
+  float analyzeBuf[ANAL_BLOCKL_MAX];
+  float dataBuf[ANAL_BLOCKL_MAX];
+  float syntBuf[ANAL_BLOCKL_MAX];
+
+  int initFlag;
+  // Parameters for quantile noise estimation.
+  float density[SIMULT * HALF_ANAL_BLOCKL];
+  float lquantile[SIMULT * HALF_ANAL_BLOCKL];
+  float quantile[HALF_ANAL_BLOCKL];
+  int counter[SIMULT];
+  int updates;
+  // Parameters for Wiener filter.
+  float smooth[HALF_ANAL_BLOCKL];
+  float overdrive;
+  float denoiseBound;
+  int gainmap;
+  // FFT work arrays.
+  size_t ip[IP_LENGTH];
+  float wfft[W_LENGTH];
+
+  // Parameters for new method: some not needed, will reduce/cleanup later.
+  int32_t blockInd;        // Frame index counter.
+  int modelUpdatePars[4];  // Parameters for updating or estimating.
+  // Thresholds/weights for prior model.
+  float priorModelPars[7];            // Parameters for prior model.
+  float noise[HALF_ANAL_BLOCKL];      // Noise spectrum from current frame.
+  float noisePrev[HALF_ANAL_BLOCKL];  // Noise spectrum from previous frame.
+  // Magnitude spectrum of previous analyze frame.
+  float magnPrevAnalyze[HALF_ANAL_BLOCKL];
+  // Magnitude spectrum of previous process frame.
+  float magnPrevProcess[HALF_ANAL_BLOCKL];
+  float logLrtTimeAvg[HALF_ANAL_BLOCKL];  // Log LRT factor with time-smoothing.
+  float priorSpeechProb;                  // Prior speech/noise probability.
+  float featureData[7];
+  // Conservative noise spectrum estimate.
+  float magnAvgPause[HALF_ANAL_BLOCKL];
+  float signalEnergy;  // Energy of |magn|.
+  float sumMagn;
+  float whiteNoiseLevel;                // Initial noise estimate.
+  float initMagnEst[HALF_ANAL_BLOCKL];  // Initial magnitude spectrum estimate.
+  float pinkNoiseNumerator;             // Pink noise parameter: numerator.
+  float pinkNoiseExp;  // Pink noise parameter: power of frequencies.
+  float parametricNoise[HALF_ANAL_BLOCKL];
+  // Parameters for feature extraction.
+  NSParaExtract featureExtractionParams;
+  // Histograms for parameter estimation.
+  int histLrt[HIST_PAR_EST];
+  int histSpecFlat[HIST_PAR_EST];
+  int histSpecDiff[HIST_PAR_EST];
+  // Quantities for high band estimate.
+  float speechProb[HALF_ANAL_BLOCKL];  // Final speech/noise prob: prior + LRT.
+  // Buffering data for HB.
+  float dataBufHB[NUM_HIGH_BANDS_MAX][ANAL_BLOCKL_MAX];
+} NoiseSuppressionC;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/****************************************************************************
+ * WebRtcNs_InitCore(...)
+ *
+ * This function initializes a noise suppression instance
+ *
+ * Input:
+ *      - self          : Instance that should be initialized
+ *      - fs            : Sampling frequency
+ *
+ * Output:
+ *      - self          : Initialized instance
+ *
+ * Return value         :  0 - Ok
+ *                        -1 - Error
+ */
+int WebRtcNs_InitCore(NoiseSuppressionC* self, uint32_t fs);
+
+/****************************************************************************
+ * WebRtcNs_set_policy_core(...)
+ *
+ * This changes the aggressiveness of the noise suppression method.
+ *
+ * Input:
+ *      - self          : Instance that should be initialized
+ *      - mode          : 0: Mild (6dB), 1: Medium (10dB), 2: Aggressive (15dB)
+ *
+ * Output:
+ *      - self          : Initialized instance
+ *
+ * Return value         :  0 - Ok
+ *                        -1 - Error
+ */
+int WebRtcNs_set_policy_core(NoiseSuppressionC* self, int mode);
+
+/****************************************************************************
+ * WebRtcNs_AnalyzeCore
+ *
+ * Estimate the background noise.
+ *
+ * Input:
+ *      - self          : Instance that should be initialized
+ *      - speechFrame   : Input speech frame for lower band
+ *
+ * Output:
+ *      - self          : Updated instance
+ */
+void WebRtcNs_AnalyzeCore(NoiseSuppressionC* self, const float* speechFrame);
+
+/****************************************************************************
+ * WebRtcNs_ProcessCore
+ *
+ * Do noise suppression.
+ *
+ * Input:
+ *      - self          : Instance that should be initialized
+ *      - inFrame       : Input speech frame for each band
+ *      - num_bands     : Number of bands
+ *
+ * Output:
+ *      - self          : Updated instance
+ *      - outFrame      : Output speech frame for each band
+ */
+void WebRtcNs_ProcessCore(NoiseSuppressionC* self,
+                          const float* const* inFrame,
+                          size_t num_bands,
+                          float* const* outFrame);
+
+#ifdef __cplusplus
+}
+#endif
+#endif  // MODULES_AUDIO_PROCESSING_LEGACY_NS_NS_CORE_H_
diff --git a/modules/audio_processing/legacy_ns/nsx_core.c b/modules/audio_processing/legacy_ns/nsx_core.c
new file mode 100644
index 0000000..aa1e73b
--- /dev/null
+++ b/modules/audio_processing/legacy_ns/nsx_core.c
@@ -0,0 +1,2154 @@
+/*
+ *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/audio_processing/legacy_ns/noise_suppression_x.h"
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common_audio/signal_processing/include/real_fft.h"
+#include "modules/audio_processing/legacy_ns/nsx_core.h"
+#include "rtc_base/checks.h"
+#include "system_wrappers/include/cpu_features_wrapper.h"
+
+#if defined(WEBRTC_HAS_NEON)
+/* Tables are defined in ARM assembly files. */
+extern const int16_t WebRtcNsx_kLogTable[9];
+extern const int16_t WebRtcNsx_kCounterDiv[201];
+extern const int16_t WebRtcNsx_kLogTableFrac[256];
+#else
+static const int16_t WebRtcNsx_kLogTable[9] = {0,   177,  355,  532, 710,
+                                               887, 1065, 1242, 1420};
+
+static const int16_t WebRtcNsx_kCounterDiv[201] = {
+    32767, 16384, 10923, 8192, 6554, 5461, 4681, 4096, 3641, 3277, 2979, 2731,
+    2521,  2341,  2185,  2048, 1928, 1820, 1725, 1638, 1560, 1489, 1425, 1365,
+    1311,  1260,  1214,  1170, 1130, 1092, 1057, 1024, 993,  964,  936,  910,
+    886,   862,   840,   819,  799,  780,  762,  745,  728,  712,  697,  683,
+    669,   655,   643,   630,  618,  607,  596,  585,  575,  565,  555,  546,
+    537,   529,   520,   512,  504,  496,  489,  482,  475,  468,  462,  455,
+    449,   443,   437,   431,  426,  420,  415,  410,  405,  400,  395,  390,
+    386,   381,   377,   372,  368,  364,  360,  356,  352,  349,  345,  341,
+    338,   334,   331,   328,  324,  321,  318,  315,  312,  309,  306,  303,
+    301,   298,   295,   293,  290,  287,  285,  282,  280,  278,  275,  273,
+    271,   269,   266,   264,  262,  260,  258,  256,  254,  252,  250,  248,
+    246,   245,   243,   241,  239,  237,  236,  234,  232,  231,  229,  228,
+    226,   224,   223,   221,  220,  218,  217,  216,  214,  213,  211,  210,
+    209,   207,   206,   205,  204,  202,  201,  200,  199,  197,  196,  195,
+    194,   193,   192,   191,  189,  188,  187,  186,  185,  184,  183,  182,
+    181,   180,   179,   178,  177,  176,  175,  174,  173,  172,  172,  171,
+    170,   169,   168,   167,  166,  165,  165,  164,  163};
+
+static const int16_t WebRtcNsx_kLogTableFrac[256] = {
+    0,   1,   3,   4,   6,   7,   9,   10,  11,  13,  14,  16,  17,  18,  20,
+    21,  22,  24,  25,  26,  28,  29,  30,  32,  33,  34,  36,  37,  38,  40,
+    41,  42,  44,  45,  46,  47,  49,  50,  51,  52,  54,  55,  56,  57,  59,
+    60,  61,  62,  63,  65,  66,  67,  68,  69,  71,  72,  73,  74,  75,  77,
+    78,  79,  80,  81,  82,  84,  85,  86,  87,  88,  89,  90,  92,  93,  94,
+    95,  96,  97,  98,  99,  100, 102, 103, 104, 105, 106, 107, 108, 109, 110,
+    111, 112, 113, 114, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126,
+    127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
+    142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 155,
+    156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 169,
+    170, 171, 172, 173, 174, 175, 176, 177, 178, 178, 179, 180, 181, 182, 183,
+    184, 185, 185, 186, 187, 188, 189, 190, 191, 192, 192, 193, 194, 195, 196,
+    197, 198, 198, 199, 200, 201, 202, 203, 203, 204, 205, 206, 207, 208, 208,
+    209, 210, 211, 212, 212, 213, 214, 215, 216, 216, 217, 218, 219, 220, 220,
+    221, 222, 223, 224, 224, 225, 226, 227, 228, 228, 229, 230, 231, 231, 232,
+    233, 234, 234, 235, 236, 237, 238, 238, 239, 240, 241, 241, 242, 243, 244,
+    244, 245, 246, 247, 247, 248, 249, 249, 250, 251, 252, 252, 253, 254, 255,
+    255};
+#endif  // WEBRTC_HAS_NEON
+
+// Skip first frequency bins during estimation. (0 <= value < 64)
+static const size_t kStartBand = 5;
+
+// hybrib Hanning & flat window
+static const int16_t kBlocks80w128x[128] = {
+    0,     536,   1072,  1606,  2139,  2669,  3196,  3720,  4240,  4756,  5266,
+    5771,  6270,  6762,  7246,  7723,  8192,  8652,  9102,  9543,  9974,  10394,
+    10803, 11200, 11585, 11958, 12318, 12665, 12998, 13318, 13623, 13913, 14189,
+    14449, 14694, 14924, 15137, 15334, 15515, 15679, 15826, 15956, 16069, 16165,
+    16244, 16305, 16349, 16375, 16384, 16384, 16384, 16384, 16384, 16384, 16384,
+    16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384,
+    16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384,
+    16384, 16384, 16384, 16384, 16375, 16349, 16305, 16244, 16165, 16069, 15956,
+    15826, 15679, 15515, 15334, 15137, 14924, 14694, 14449, 14189, 13913, 13623,
+    13318, 12998, 12665, 12318, 11958, 11585, 11200, 10803, 10394, 9974,  9543,
+    9102,  8652,  8192,  7723,  7246,  6762,  6270,  5771,  5266,  4756,  4240,
+    3720,  3196,  2669,  2139,  1606,  1072,  536};
+
+// hybrib Hanning & flat window
+static const int16_t kBlocks160w256x[256] = {
+    0,     268,   536,   804,   1072,  1339,  1606,  1872,  2139,  2404,  2669,
+    2933,  3196,  3459,  3720,  3981,  4240,  4499,  4756,  5012,  5266,  5520,
+    5771,  6021,  6270,  6517,  6762,  7005,  7246,  7486,  7723,  7959,  8192,
+    8423,  8652,  8878,  9102,  9324,  9543,  9760,  9974,  10185, 10394, 10600,
+    10803, 11003, 11200, 11394, 11585, 11773, 11958, 12140, 12318, 12493, 12665,
+    12833, 12998, 13160, 13318, 13472, 13623, 13770, 13913, 14053, 14189, 14321,
+    14449, 14574, 14694, 14811, 14924, 15032, 15137, 15237, 15334, 15426, 15515,
+    15599, 15679, 15754, 15826, 15893, 15956, 16015, 16069, 16119, 16165, 16207,
+    16244, 16277, 16305, 16329, 16349, 16364, 16375, 16382, 16384, 16384, 16384,
+    16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384,
+    16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384,
+    16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384,
+    16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384,
+    16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384,
+    16384, 16384, 16384, 16384, 16384, 16384, 16384, 16382, 16375, 16364, 16349,
+    16329, 16305, 16277, 16244, 16207, 16165, 16119, 16069, 16015, 15956, 15893,
+    15826, 15754, 15679, 15599, 15515, 15426, 15334, 15237, 15137, 15032, 14924,
+    14811, 14694, 14574, 14449, 14321, 14189, 14053, 13913, 13770, 13623, 13472,
+    13318, 13160, 12998, 12833, 12665, 12493, 12318, 12140, 11958, 11773, 11585,
+    11394, 11200, 11003, 10803, 10600, 10394, 10185, 9974,  9760,  9543,  9324,
+    9102,  8878,  8652,  8423,  8192,  7959,  7723,  7486,  7246,  7005,  6762,
+    6517,  6270,  6021,  5771,  5520,  5266,  5012,  4756,  4499,  4240,  3981,
+    3720,  3459,  3196,  2933,  2669,  2404,  2139,  1872,  1606,  1339,  1072,
+    804,   536,   268};
+
+// Gain factor1 table: Input value in Q8 and output value in Q13
+// original floating point code
+//  if (gain > blim) {
+//    factor1 = 1.0 + 1.3 * (gain - blim);
+//    if (gain * factor1 > 1.0) {
+//      factor1 = 1.0 / gain;
+//    }
+//  } else {
+//    factor1 = 1.0;
+//  }
+static const int16_t kFactor1Table[257] = {
+    8192,  8192,  8192,  8192,  8192,  8192,  8192,  8192,  8192,  8192,  8192,
+    8192,  8192,  8192,  8192,  8192,  8192,  8192,  8192,  8192,  8192,  8192,
+    8192,  8192,  8192,  8192,  8192,  8192,  8192,  8192,  8192,  8192,  8192,
+    8192,  8192,  8192,  8192,  8192,  8192,  8192,  8192,  8192,  8192,  8192,
+    8192,  8192,  8192,  8192,  8192,  8192,  8192,  8192,  8192,  8192,  8192,
+    8192,  8192,  8192,  8192,  8192,  8192,  8192,  8192,  8192,  8192,  8233,
+    8274,  8315,  8355,  8396,  8436,  8475,  8515,  8554,  8592,  8631,  8669,
+    8707,  8745,  8783,  8820,  8857,  8894,  8931,  8967,  9003,  9039,  9075,
+    9111,  9146,  9181,  9216,  9251,  9286,  9320,  9354,  9388,  9422,  9456,
+    9489,  9523,  9556,  9589,  9622,  9655,  9687,  9719,  9752,  9784,  9816,
+    9848,  9879,  9911,  9942,  9973,  10004, 10035, 10066, 10097, 10128, 10158,
+    10188, 10218, 10249, 10279, 10308, 10338, 10368, 10397, 10426, 10456, 10485,
+    10514, 10543, 10572, 10600, 10629, 10657, 10686, 10714, 10742, 10770, 10798,
+    10826, 10854, 10882, 10847, 10810, 10774, 10737, 10701, 10666, 10631, 10596,
+    10562, 10527, 10494, 10460, 10427, 10394, 10362, 10329, 10297, 10266, 10235,
+    10203, 10173, 10142, 10112, 10082, 10052, 10023, 9994,  9965,  9936,  9908,
+    9879,  9851,  9824,  9796,  9769,  9742,  9715,  9689,  9662,  9636,  9610,
+    9584,  9559,  9534,  9508,  9484,  9459,  9434,  9410,  9386,  9362,  9338,
+    9314,  9291,  9268,  9245,  9222,  9199,  9176,  9154,  9132,  9110,  9088,
+    9066,  9044,  9023,  9002,  8980,  8959,  8939,  8918,  8897,  8877,  8857,
+    8836,  8816,  8796,  8777,  8757,  8738,  8718,  8699,  8680,  8661,  8642,
+    8623,  8605,  8586,  8568,  8550,  8532,  8514,  8496,  8478,  8460,  8443,
+    8425,  8408,  8391,  8373,  8356,  8339,  8323,  8306,  8289,  8273,  8256,
+    8240,  8224,  8208,  8192};
+
+// For Factor2 tables
+// original floating point code
+// if (gain > blim) {
+//   factor2 = 1.0;
+// } else {
+//   factor2 = 1.0 - 0.3 * (blim - gain);
+//   if (gain <= inst->denoiseBound) {
+//     factor2 = 1.0 - 0.3 * (blim - inst->denoiseBound);
+//   }
+// }
+//
+// Gain factor table: Input value in Q8 and output value in Q13
+static const int16_t kFactor2Aggressiveness1[257] = {
+    7577, 7577, 7577, 7577, 7577, 7577, 7577, 7577, 7577, 7577, 7577, 7577,
+    7577, 7577, 7577, 7577, 7577, 7596, 7614, 7632, 7650, 7667, 7683, 7699,
+    7715, 7731, 7746, 7761, 7775, 7790, 7804, 7818, 7832, 7845, 7858, 7871,
+    7884, 7897, 7910, 7922, 7934, 7946, 7958, 7970, 7982, 7993, 8004, 8016,
+    8027, 8038, 8049, 8060, 8070, 8081, 8091, 8102, 8112, 8122, 8132, 8143,
+    8152, 8162, 8172, 8182, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192};
+
+// Gain factor table: Input value in Q8 and output value in Q13
+static const int16_t kFactor2Aggressiveness2[257] = {
+    7270, 7270, 7270, 7270, 7270, 7306, 7339, 7369, 7397, 7424, 7448, 7472,
+    7495, 7517, 7537, 7558, 7577, 7596, 7614, 7632, 7650, 7667, 7683, 7699,
+    7715, 7731, 7746, 7761, 7775, 7790, 7804, 7818, 7832, 7845, 7858, 7871,
+    7884, 7897, 7910, 7922, 7934, 7946, 7958, 7970, 7982, 7993, 8004, 8016,
+    8027, 8038, 8049, 8060, 8070, 8081, 8091, 8102, 8112, 8122, 8132, 8143,
+    8152, 8162, 8172, 8182, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192};
+
+// Gain factor table: Input value in Q8 and output value in Q13
+static const int16_t kFactor2Aggressiveness3[257] = {
+    7184, 7184, 7184, 7229, 7270, 7306, 7339, 7369, 7397, 7424, 7448, 7472,
+    7495, 7517, 7537, 7558, 7577, 7596, 7614, 7632, 7650, 7667, 7683, 7699,
+    7715, 7731, 7746, 7761, 7775, 7790, 7804, 7818, 7832, 7845, 7858, 7871,
+    7884, 7897, 7910, 7922, 7934, 7946, 7958, 7970, 7982, 7993, 8004, 8016,
+    8027, 8038, 8049, 8060, 8070, 8081, 8091, 8102, 8112, 8122, 8132, 8143,
+    8152, 8162, 8172, 8182, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
+    8192, 8192, 8192, 8192, 8192};
+
+// sum of log2(i) from table index to inst->anaLen2 in Q5
+// Note that the first table value is invalid, since log2(0) = -infinity
+static const int16_t kSumLogIndex[66] = {
+    0,     22917, 22917, 22885, 22834, 22770, 22696, 22613, 22524, 22428,
+    22326, 22220, 22109, 21994, 21876, 21754, 21629, 21501, 21370, 21237,
+    21101, 20963, 20822, 20679, 20535, 20388, 20239, 20089, 19937, 19783,
+    19628, 19470, 19312, 19152, 18991, 18828, 18664, 18498, 18331, 18164,
+    17994, 17824, 17653, 17480, 17306, 17132, 16956, 16779, 16602, 16423,
+    16243, 16063, 15881, 15699, 15515, 15331, 15146, 14960, 14774, 14586,
+    14398, 14209, 14019, 13829, 13637, 13445};
+
+// sum of log2(i)^2 from table index to inst->anaLen2 in Q2
+// Note that the first table value is invalid, since log2(0) = -infinity
+static const int16_t kSumSquareLogIndex[66] = {
+    0,     16959, 16959, 16955, 16945, 16929, 16908, 16881, 16850, 16814,
+    16773, 16729, 16681, 16630, 16575, 16517, 16456, 16392, 16325, 16256,
+    16184, 16109, 16032, 15952, 15870, 15786, 15700, 15612, 15521, 15429,
+    15334, 15238, 15140, 15040, 14938, 14834, 14729, 14622, 14514, 14404,
+    14292, 14179, 14064, 13947, 13830, 13710, 13590, 13468, 13344, 13220,
+    13094, 12966, 12837, 12707, 12576, 12444, 12310, 12175, 12039, 11902,
+    11763, 11624, 11483, 11341, 11198, 11054};
+
+// log2(table index) in Q12
+// Note that the first table value is invalid, since log2(0) = -infinity
+static const int16_t kLogIndex[129] = {
+    0,     0,     4096,  6492,  8192,  9511,  10588, 11499, 12288, 12984, 13607,
+    14170, 14684, 15157, 15595, 16003, 16384, 16742, 17080, 17400, 17703, 17991,
+    18266, 18529, 18780, 19021, 19253, 19476, 19691, 19898, 20099, 20292, 20480,
+    20662, 20838, 21010, 21176, 21338, 21496, 21649, 21799, 21945, 22087, 22226,
+    22362, 22495, 22625, 22752, 22876, 22998, 23117, 23234, 23349, 23462, 23572,
+    23680, 23787, 23892, 23994, 24095, 24195, 24292, 24388, 24483, 24576, 24668,
+    24758, 24847, 24934, 25021, 25106, 25189, 25272, 25354, 25434, 25513, 25592,
+    25669, 25745, 25820, 25895, 25968, 26041, 26112, 26183, 26253, 26322, 26390,
+    26458, 26525, 26591, 26656, 26721, 26784, 26848, 26910, 26972, 27033, 27094,
+    27154, 27213, 27272, 27330, 27388, 27445, 27502, 27558, 27613, 27668, 27722,
+    27776, 27830, 27883, 27935, 27988, 28039, 28090, 28141, 28191, 28241, 28291,
+    28340, 28388, 28437, 28484, 28532, 28579, 28626, 28672};
+
+// determinant of estimation matrix in Q0 corresponding to the log2 tables above
+// Note that the first table value is invalid, since log2(0) = -infinity
+static const int16_t kDeterminantEstMatrix[66] = {
+    0,     29814, 25574, 22640, 20351, 18469, 16873, 15491, 14277, 13199, 12233,
+    11362, 10571, 9851,  9192,  8587,  8030,  7515,  7038,  6596,  6186,  5804,
+    5448,  5115,  4805,  4514,  4242,  3988,  3749,  3524,  3314,  3116,  2930,
+    2755,  2590,  2435,  2289,  2152,  2022,  1900,  1785,  1677,  1575,  1478,
+    1388,  1302,  1221,  1145,  1073,  1005,  942,   881,   825,   771,   721,
+    674,   629,   587,   547,   510,   475,   442,   411,   382,   355,   330};
+
+// Update the noise estimation information.
+static void UpdateNoiseEstimate(NoiseSuppressionFixedC* inst, int offset) {
+  int32_t tmp32no1 = 0;
+  int32_t tmp32no2 = 0;
+  int16_t tmp16 = 0;
+  const int16_t kExp2Const = 11819;  // Q13
+
+  size_t i = 0;
+
+  tmp16 =
+      WebRtcSpl_MaxValueW16(inst->noiseEstLogQuantile + offset, inst->magnLen);
+  // Guarantee a Q-domain as high as possible and still fit in int16
+  inst->qNoise =
+      14 - (int)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(kExp2Const, tmp16, 21);
+  for (i = 0; i < inst->magnLen; i++) {
+    // inst->quantile[i]=exp(inst->lquantile[offset+i]);
+    // in Q21
+    tmp32no2 = kExp2Const * inst->noiseEstLogQuantile[offset + i];
+    tmp32no1 = (0x00200000 | (tmp32no2 & 0x001FFFFF));  // 2^21 + frac
+    tmp16 = (int16_t)(tmp32no2 >> 21);
+    tmp16 -= 21;                     // shift 21 to get result in Q0
+    tmp16 += (int16_t)inst->qNoise;  // shift to get result in Q(qNoise)
+    if (tmp16 < 0) {
+      tmp32no1 >>= -tmp16;
+    } else {
+      tmp32no1 <<= tmp16;
+    }
+    inst->noiseEstQuantile[i] = WebRtcSpl_SatW32ToW16(tmp32no1);
+  }
+}
+
+// Noise Estimation
+static void NoiseEstimationC(NoiseSuppressionFixedC* inst,
+                             uint16_t* magn,
+                             uint32_t* noise,
+                             int16_t* q_noise) {
+  int16_t lmagn[HALF_ANAL_BLOCKL], counter, countDiv;
+  int16_t countProd, delta, zeros, frac;
+  int16_t log2, tabind, logval, tmp16, tmp16no1, tmp16no2;
+  const int16_t log2_const = 22713;  // Q15
+  const int16_t width_factor = 21845;
+
+  size_t i, s, offset;
+
+  tabind = inst->stages - inst->normData;
+  RTC_DCHECK_LT(tabind, 9);
+  RTC_DCHECK_GT(tabind, -9);
+  if (tabind < 0) {
+    logval = -WebRtcNsx_kLogTable[-tabind];
+  } else {
+    logval = WebRtcNsx_kLogTable[tabind];
+  }
+
+  // lmagn(i)=log(magn(i))=log(2)*log2(magn(i))
+  // magn is in Q(-stages), and the real lmagn values are:
+  // real_lmagn(i)=log(magn(i)*2^stages)=log(magn(i))+log(2^stages)
+  // lmagn in Q8
+  for (i = 0; i < inst->magnLen; i++) {
+    if (magn[i]) {
+      zeros = WebRtcSpl_NormU32((uint32_t)magn[i]);
+      frac = (int16_t)((((uint32_t)magn[i] << zeros) & 0x7FFFFFFF) >> 23);
+      // log2(magn(i))
+      RTC_DCHECK_LT(frac, 256);
+      log2 = (int16_t)(((31 - zeros) << 8) + WebRtcNsx_kLogTableFrac[frac]);
+      // log2(magn(i))*log(2)
+      lmagn[i] = (int16_t)((log2 * log2_const) >> 15);
+      // + log(2^stages)
+      lmagn[i] += logval;
+    } else {
+      lmagn[i] = logval;  // 0;
+    }
+  }
+
+  // loop over simultaneous estimates
+  for (s = 0; s < SIMULT; s++) {
+    offset = s * inst->magnLen;
+
+    // Get counter values from state
+    counter = inst->noiseEstCounter[s];
+    RTC_DCHECK_LT(counter, 201);
+    countDiv = WebRtcNsx_kCounterDiv[counter];
+    countProd = (int16_t)(counter * countDiv);
+
+    // quant_est(...)
+    for (i = 0; i < inst->magnLen; i++) {
+      // compute delta
+      if (inst->noiseEstDensity[offset + i] > 512) {
+        // Get the value for delta by shifting intead of dividing.
+        int factor = WebRtcSpl_NormW16(inst->noiseEstDensity[offset + i]);
+        delta = (int16_t)(FACTOR_Q16 >> (14 - factor));
+      } else {
+        delta = FACTOR_Q7;
+        if (inst->blockIndex < END_STARTUP_LONG) {
+          // Smaller step size during startup. This prevents from using
+          // unrealistic values causing overflow.
+          delta = FACTOR_Q7_STARTUP;
+        }
+      }
+
+      // update log quantile estimate
+      tmp16 = (int16_t)((delta * countDiv) >> 14);
+      if (lmagn[i] > inst->noiseEstLogQuantile[offset + i]) {
+        // +=QUANTILE*delta/(inst->counter[s]+1) QUANTILE=0.25, =1 in Q2
+        // CounterDiv=1/(inst->counter[s]+1) in Q15
+        tmp16 += 2;
+        inst->noiseEstLogQuantile[offset + i] += tmp16 / 4;
+      } else {
+        tmp16 += 1;
+        // *(1-QUANTILE), in Q2 QUANTILE=0.25, 1-0.25=0.75=3 in Q2
+        // TODO(bjornv): investigate why we need to truncate twice.
+        tmp16no2 = (int16_t)((tmp16 / 2) * 3 / 2);
+        inst->noiseEstLogQuantile[offset + i] -= tmp16no2;
+        if (inst->noiseEstLogQuantile[offset + i] < logval) {
+          // This is the smallest fixed point representation we can
+          // have, hence we limit the output.
+          inst->noiseEstLogQuantile[offset + i] = logval;
+        }
+      }
+
+      // update density estimate
+      if (WEBRTC_SPL_ABS_W16(lmagn[i] - inst->noiseEstLogQuantile[offset + i]) <
+          WIDTH_Q8) {
+        tmp16no1 = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(
+            inst->noiseEstDensity[offset + i], countProd, 15);
+        tmp16no2 = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(width_factor,
+                                                                 countDiv, 15);
+        inst->noiseEstDensity[offset + i] = tmp16no1 + tmp16no2;
+      }
+    }  // end loop over magnitude spectrum
+
+    if (counter >= END_STARTUP_LONG) {
+      inst->noiseEstCounter[s] = 0;
+      if (inst->blockIndex >= END_STARTUP_LONG) {
+        UpdateNoiseEstimate(inst, offset);
+      }
+    }
+    inst->noiseEstCounter[s]++;
+
+  }  // end loop over simultaneous estimates
+
+  // Sequentially update the noise during startup
+  if (inst->blockIndex < END_STARTUP_LONG) {
+    UpdateNoiseEstimate(inst, offset);
+  }
+
+  for (i = 0; i < inst->magnLen; i++) {
+    noise[i] = (uint32_t)(inst->noiseEstQuantile[i]);  // Q(qNoise)
+  }
+  (*q_noise) = (int16_t)inst->qNoise;
+}
+
+// Filter the data in the frequency domain, and create spectrum.
+static void PrepareSpectrumC(NoiseSuppressionFixedC* inst, int16_t* freq_buf) {
+  size_t i = 0, j = 0;
+
+  for (i = 0; i < inst->magnLen; i++) {
+    inst->real[i] =
+        (int16_t)((inst->real[i] * (int16_t)(inst->noiseSupFilter[i])) >>
+                  14);  // Q(normData-stages)
+    inst->imag[i] =
+        (int16_t)((inst->imag[i] * (int16_t)(inst->noiseSupFilter[i])) >>
+                  14);  // Q(normData-stages)
+  }
+
+  freq_buf[0] = inst->real[0];
+  freq_buf[1] = -inst->imag[0];
+  for (i = 1, j = 2; i < inst->anaLen2; i += 1, j += 2) {
+    freq_buf[j] = inst->real[i];
+    freq_buf[j + 1] = -inst->imag[i];
+  }
+  freq_buf[inst->anaLen] = inst->real[inst->anaLen2];
+  freq_buf[inst->anaLen + 1] = -inst->imag[inst->anaLen2];
+}
+
+// Denormalize the real-valued signal |in|, the output from inverse FFT.
+static void DenormalizeC(NoiseSuppressionFixedC* inst,
+                         int16_t* in,
+                         int factor) {
+  size_t i = 0;
+  int32_t tmp32 = 0;
+  for (i = 0; i < inst->anaLen; i += 1) {
+    tmp32 = WEBRTC_SPL_SHIFT_W32((int32_t)in[i], factor - inst->normData);
+    inst->real[i] = WebRtcSpl_SatW32ToW16(tmp32);  // Q0
+  }
+}
+
+// For the noise supression process, synthesis, read out fully processed
+// segment, and update synthesis buffer.
+static void SynthesisUpdateC(NoiseSuppressionFixedC* inst,
+                             int16_t* out_frame,
+                             int16_t gain_factor) {
+  size_t i = 0;
+  int16_t tmp16a = 0;
+  int16_t tmp16b = 0;
+  int32_t tmp32 = 0;
+
+  // synthesis
+  for (i = 0; i < inst->anaLen; i++) {
+    tmp16a = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(
+        inst->window[i], inst->real[i], 14);  // Q0, window in Q14
+    tmp32 =
+        WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(tmp16a, gain_factor, 13);  // Q0
+    // Down shift with rounding
+    tmp16b = WebRtcSpl_SatW32ToW16(tmp32);  // Q0
+    inst->synthesisBuffer[i] = WebRtcSpl_AddSatW16(inst->synthesisBuffer[i],
+                                                   tmp16b);  // Q0
+  }
+
+  // read out fully processed segment
+  for (i = 0; i < inst->blockLen10ms; i++) {
+    out_frame[i] = inst->synthesisBuffer[i];  // Q0
+  }
+
+  // update synthesis buffer
+  memcpy(inst->synthesisBuffer, inst->synthesisBuffer + inst->blockLen10ms,
+         (inst->anaLen - inst->blockLen10ms) * sizeof(*inst->synthesisBuffer));
+  WebRtcSpl_ZerosArrayW16(
+      inst->synthesisBuffer + inst->anaLen - inst->blockLen10ms,
+      inst->blockLen10ms);
+}
+
+// Update analysis buffer for lower band, and window data before FFT.
+static void AnalysisUpdateC(NoiseSuppressionFixedC* inst,
+                            int16_t* out,
+                            int16_t* new_speech) {
+  size_t i = 0;
+
+  // For lower band update analysis buffer.
+  memcpy(inst->analysisBuffer, inst->analysisBuffer + inst->blockLen10ms,
+         (inst->anaLen - inst->blockLen10ms) * sizeof(*inst->analysisBuffer));
+  memcpy(inst->analysisBuffer + inst->anaLen - inst->blockLen10ms, new_speech,
+         inst->blockLen10ms * sizeof(*inst->analysisBuffer));
+
+  // Window data before FFT.
+  for (i = 0; i < inst->anaLen; i++) {
+    out[i] = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(
+        inst->window[i], inst->analysisBuffer[i], 14);  // Q0
+  }
+}
+
+// Normalize the real-valued signal |in|, the input to forward FFT.
+static void NormalizeRealBufferC(NoiseSuppressionFixedC* inst,
+                                 const int16_t* in,
+                                 int16_t* out) {
+  size_t i = 0;
+  RTC_DCHECK_GE(inst->normData, 0);
+  for (i = 0; i < inst->anaLen; ++i) {
+    out[i] = in[i] << inst->normData;  // Q(normData)
+  }
+}
+
+// Declare function pointers.
+NoiseEstimation WebRtcNsx_NoiseEstimation;
+PrepareSpectrum WebRtcNsx_PrepareSpectrum;
+SynthesisUpdate WebRtcNsx_SynthesisUpdate;
+AnalysisUpdate WebRtcNsx_AnalysisUpdate;
+Denormalize WebRtcNsx_Denormalize;
+NormalizeRealBuffer WebRtcNsx_NormalizeRealBuffer;
+
+#if defined(WEBRTC_HAS_NEON)
+// Initialize function pointers for ARM Neon platform.
+static void WebRtcNsx_InitNeon(void) {
+  WebRtcNsx_NoiseEstimation = WebRtcNsx_NoiseEstimationNeon;
+  WebRtcNsx_PrepareSpectrum = WebRtcNsx_PrepareSpectrumNeon;
+  WebRtcNsx_SynthesisUpdate = WebRtcNsx_SynthesisUpdateNeon;
+  WebRtcNsx_AnalysisUpdate = WebRtcNsx_AnalysisUpdateNeon;
+}
+#endif
+
+#if defined(MIPS32_LE)
+// Initialize function pointers for MIPS platform.
+static void WebRtcNsx_InitMips(void) {
+  WebRtcNsx_PrepareSpectrum = WebRtcNsx_PrepareSpectrum_mips;
+  WebRtcNsx_SynthesisUpdate = WebRtcNsx_SynthesisUpdate_mips;
+  WebRtcNsx_AnalysisUpdate = WebRtcNsx_AnalysisUpdate_mips;
+  WebRtcNsx_NormalizeRealBuffer = WebRtcNsx_NormalizeRealBuffer_mips;
+#if defined(MIPS_DSP_R1_LE)
+  WebRtcNsx_Denormalize = WebRtcNsx_Denormalize_mips;
+#endif
+}
+#endif
+
+void WebRtcNsx_CalcParametricNoiseEstimate(NoiseSuppressionFixedC* inst,
+                                           int16_t pink_noise_exp_avg,
+                                           int32_t pink_noise_num_avg,
+                                           int freq_index,
+                                           uint32_t* noise_estimate,
+                                           uint32_t* noise_estimate_avg) {
+  int32_t tmp32no1 = 0;
+  int32_t tmp32no2 = 0;
+
+  int16_t int_part = 0;
+  int16_t frac_part = 0;
+
+  // Use pink noise estimate
+  // noise_estimate = 2^(pinkNoiseNumerator + pinkNoiseExp * log2(j))
+  RTC_DCHECK_GE(freq_index, 0);
+  RTC_DCHECK_LT(freq_index, 129);
+  tmp32no2 = (pink_noise_exp_avg * kLogIndex[freq_index]) >> 15;  // Q11
+  tmp32no1 = pink_noise_num_avg - tmp32no2;                       // Q11
+
+  // Calculate output: 2^tmp32no1
+  // Output in Q(minNorm-stages)
+  tmp32no1 += (inst->minNorm - inst->stages) << 11;
+  if (tmp32no1 > 0) {
+    int_part = (int16_t)(tmp32no1 >> 11);
+    frac_part = (int16_t)(tmp32no1 & 0x000007ff);  // Q11
+    // Piecewise linear approximation of 'b' in
+    // 2^(int_part+frac_part) = 2^int_part * (1 + b)
+    // 'b' is given in Q11 and below stored in frac_part.
+    if (frac_part >> 10) {
+      // Upper fractional part
+      tmp32no2 = (2048 - frac_part) * 1244;  // Q21
+      tmp32no2 = 2048 - (tmp32no2 >> 10);
+    } else {
+      // Lower fractional part
+      tmp32no2 = (frac_part * 804) >> 10;
+    }
+    // Shift fractional part to Q(minNorm-stages)
+    tmp32no2 = WEBRTC_SPL_SHIFT_W32(tmp32no2, int_part - 11);
+    *noise_estimate_avg = (1 << int_part) + (uint32_t)tmp32no2;
+    // Scale up to initMagnEst, which is not block averaged
+    *noise_estimate = (*noise_estimate_avg) * (uint32_t)(inst->blockIndex + 1);
+  }
+}
+
+// Initialize state
+int32_t WebRtcNsx_InitCore(NoiseSuppressionFixedC* inst, uint32_t fs) {
+  int i;
+
+  // check for valid pointer
+  if (inst == NULL) {
+    return -1;
+  }
+  //
+
+  // Initialization of struct
+  if (fs == 8000 || fs == 16000 || fs == 32000 || fs == 48000) {
+    inst->fs = fs;
+  } else {
+    return -1;
+  }
+
+  if (fs == 8000) {
+    inst->blockLen10ms = 80;
+    inst->anaLen = 128;
+    inst->stages = 7;
+    inst->window = kBlocks80w128x;
+    inst->thresholdLogLrt = 131072;  // default threshold for LRT feature
+    inst->maxLrt = 0x0040000;
+    inst->minLrt = 52429;
+  } else {
+    inst->blockLen10ms = 160;
+    inst->anaLen = 256;
+    inst->stages = 8;
+    inst->window = kBlocks160w256x;
+    inst->thresholdLogLrt = 212644;  // default threshold for LRT feature
+    inst->maxLrt = 0x0080000;
+    inst->minLrt = 104858;
+  }
+  inst->anaLen2 = inst->anaLen / 2;
+  inst->magnLen = inst->anaLen2 + 1;
+
+  if (inst->real_fft != NULL) {
+    WebRtcSpl_FreeRealFFT(inst->real_fft);
+  }
+  inst->real_fft = WebRtcSpl_CreateRealFFT(inst->stages);
+  if (inst->real_fft == NULL) {
+    return -1;
+  }
+
+  WebRtcSpl_ZerosArrayW16(inst->analysisBuffer, ANAL_BLOCKL_MAX);
+  WebRtcSpl_ZerosArrayW16(inst->synthesisBuffer, ANAL_BLOCKL_MAX);
+
+  // for HB processing
+  WebRtcSpl_ZerosArrayW16(inst->dataBufHBFX[0],
+                          NUM_HIGH_BANDS_MAX * ANAL_BLOCKL_MAX);
+  // for quantile noise estimation
+  WebRtcSpl_ZerosArrayW16(inst->noiseEstQuantile, HALF_ANAL_BLOCKL);
+  for (i = 0; i < SIMULT * HALF_ANAL_BLOCKL; i++) {
+    inst->noiseEstLogQuantile[i] = 2048;  // Q8
+    inst->noiseEstDensity[i] = 153;       // Q9
+  }
+  for (i = 0; i < SIMULT; i++) {
+    inst->noiseEstCounter[i] = (int16_t)(END_STARTUP_LONG * (i + 1)) / SIMULT;
+  }
+
+  // Initialize suppression filter with ones
+  WebRtcSpl_MemSetW16((int16_t*)inst->noiseSupFilter, 16384, HALF_ANAL_BLOCKL);
+
+  // Set the aggressiveness: default
+  inst->aggrMode = 0;
+
+  // initialize variables for new method
+  inst->priorNonSpeechProb =
+      8192;  // Q14(0.5) prior probability for speech/noise
+  for (i = 0; i < HALF_ANAL_BLOCKL; i++) {
+    inst->prevMagnU16[i] = 0;
+    inst->prevNoiseU32[i] = 0;      // previous noise-spectrum
+    inst->logLrtTimeAvgW32[i] = 0;  // smooth LR ratio
+    inst->avgMagnPause[i] = 0;      // conservative noise spectrum estimate
+    inst->initMagnEst[i] = 0;       // initial average magnitude spectrum
+  }
+
+  // feature quantities
+  inst->thresholdSpecDiff =
+      50;  // threshold for difference feature: determined on-line
+  inst->thresholdSpecFlat = 20480;  // threshold for flatness: determined
+                                    // on-line
+  inst->featureLogLrt =
+      inst->thresholdLogLrt;  // average LRT factor (= threshold)
+  inst->featureSpecFlat =
+      inst->thresholdSpecFlat;  // spectral flatness (= threshold)
+  inst->featureSpecDiff =
+      inst->thresholdSpecDiff;  // spectral difference (= threshold)
+  inst->weightLogLrt = 6;       // default weighting par for LRT feature
+  inst->weightSpecFlat =
+      0;  // default weighting par for spectral flatness feature
+  inst->weightSpecDiff =
+      0;  // default weighting par for spectral difference feature
+
+  inst->curAvgMagnEnergy = 0;  // window time-average of input magnitude
+                               // spectrum
+  inst->timeAvgMagnEnergy = 0;     // normalization for spectral difference
+  inst->timeAvgMagnEnergyTmp = 0;  // normalization for spectral difference
+
+  // histogram quantities: used to estimate/update thresholds for features
+  WebRtcSpl_ZerosArrayW16(inst->histLrt, HIST_PAR_EST);
+  WebRtcSpl_ZerosArrayW16(inst->histSpecDiff, HIST_PAR_EST);
+  WebRtcSpl_ZerosArrayW16(inst->histSpecFlat, HIST_PAR_EST);
+
+  inst->blockIndex = -1;  // frame counter
+
+  // inst->modelUpdate    = 500;   //window for update
+  inst->modelUpdate = (1 << STAT_UPDATES);  // window for update
+  inst->cntThresUpdate = 0;  // counter feature thresholds updates
+
+  inst->sumMagn = 0;
+  inst->magnEnergy = 0;
+  inst->prevQMagn = 0;
+  inst->qNoise = 0;
+  inst->prevQNoise = 0;
+
+  inst->energyIn = 0;
+  inst->scaleEnergyIn = 0;
+
+  inst->whiteNoiseLevel = 0;
+  inst->pinkNoiseNumerator = 0;
+  inst->pinkNoiseExp = 0;
+  inst->minNorm = 15;  // Start with full scale
+  inst->zeroInputSignal = 0;
+
+  // default mode
+  WebRtcNsx_set_policy_core(inst, 0);
+
+#ifdef NS_FILEDEBUG
+  inst->infile = fopen("indebug.pcm", "wb");
+  inst->outfile = fopen("outdebug.pcm", "wb");
+  inst->file1 = fopen("file1.pcm", "wb");
+  inst->file2 = fopen("file2.pcm", "wb");
+  inst->file3 = fopen("file3.pcm", "wb");
+  inst->file4 = fopen("file4.pcm", "wb");
+  inst->file5 = fopen("file5.pcm", "wb");
+#endif
+
+  // Initialize function pointers.
+  WebRtcNsx_NoiseEstimation = NoiseEstimationC;
+  WebRtcNsx_PrepareSpectrum = PrepareSpectrumC;
+  WebRtcNsx_SynthesisUpdate = SynthesisUpdateC;
+  WebRtcNsx_AnalysisUpdate = AnalysisUpdateC;
+  WebRtcNsx_Denormalize = DenormalizeC;
+  WebRtcNsx_NormalizeRealBuffer = NormalizeRealBufferC;
+
+#if defined(WEBRTC_HAS_NEON)
+  WebRtcNsx_InitNeon();
+#endif
+
+#if defined(MIPS32_LE)
+  WebRtcNsx_InitMips();
+#endif
+
+  inst->initFlag = 1;
+
+  return 0;
+}
+
+int WebRtcNsx_set_policy_core(NoiseSuppressionFixedC* inst, int mode) {
+  // allow for modes:0,1,2,3
+  if (mode < 0 || mode > 3) {
+    return -1;
+  }
+
+  inst->aggrMode = mode;
+  if (mode == 0) {
+    inst->overdrive = 256;      // Q8(1.0)
+    inst->denoiseBound = 8192;  // Q14(0.5)
+    inst->gainMap = 0;          // No gain compensation
+  } else if (mode == 1) {
+    inst->overdrive = 256;      // Q8(1.0)
+    inst->denoiseBound = 4096;  // Q14(0.25)
+    inst->factor2Table = kFactor2Aggressiveness1;
+    inst->gainMap = 1;
+  } else if (mode == 2) {
+    inst->overdrive = 282;      // ~= Q8(1.1)
+    inst->denoiseBound = 2048;  // Q14(0.125)
+    inst->factor2Table = kFactor2Aggressiveness2;
+    inst->gainMap = 1;
+  } else if (mode == 3) {
+    inst->overdrive = 320;      // Q8(1.25)
+    inst->denoiseBound = 1475;  // ~= Q14(0.09)
+    inst->factor2Table = kFactor2Aggressiveness3;
+    inst->gainMap = 1;
+  }
+  return 0;
+}
+
+// Extract thresholds for feature parameters
+// histograms are computed over some window_size (given by window_pars)
+// thresholds and weights are extracted every window
+// flag 0 means update histogram only, flag 1 means compute the
+// thresholds/weights threshold and weights are returned in:
+// inst->priorModelPars
+void WebRtcNsx_FeatureParameterExtraction(NoiseSuppressionFixedC* inst,
+                                          int flag) {
+  uint32_t tmpU32;
+  uint32_t histIndex;
+  uint32_t posPeak1SpecFlatFX, posPeak2SpecFlatFX;
+  uint32_t posPeak1SpecDiffFX, posPeak2SpecDiffFX;
+
+  int32_t tmp32;
+  int32_t fluctLrtFX, thresFluctLrtFX;
+  int32_t avgHistLrtFX, avgSquareHistLrtFX, avgHistLrtComplFX;
+
+  int16_t j;
+  int16_t numHistLrt;
+
+  int i;
+  int useFeatureSpecFlat, useFeatureSpecDiff, featureSum;
+  int maxPeak1, maxPeak2;
+  int weightPeak1SpecFlat, weightPeak2SpecFlat;
+  int weightPeak1SpecDiff, weightPeak2SpecDiff;
+
+  // update histograms
+  if (!flag) {
+    // LRT
+    // Type casting to UWord32 is safe since negative values will not be wrapped
+    // to larger values than HIST_PAR_EST
+    histIndex = (uint32_t)(inst->featureLogLrt);
+    if (histIndex < HIST_PAR_EST) {
+      inst->histLrt[histIndex]++;
+    }
+    // Spectral flatness
+    // (inst->featureSpecFlat*20)>>10 = (inst->featureSpecFlat*5)>>8
+    histIndex = (inst->featureSpecFlat * 5) >> 8;
+    if (histIndex < HIST_PAR_EST) {
+      inst->histSpecFlat[histIndex]++;
+    }
+    // Spectral difference
+    histIndex = HIST_PAR_EST;
+    if (inst->timeAvgMagnEnergy > 0) {
+      // Guard against division by zero
+      // If timeAvgMagnEnergy == 0 we have no normalizing statistics and
+      // therefore can't update the histogram
+      histIndex = ((inst->featureSpecDiff * 5) >> inst->stages) /
+                  inst->timeAvgMagnEnergy;
+    }
+    if (histIndex < HIST_PAR_EST) {
+      inst->histSpecDiff[histIndex]++;
+    }
+  }
+
+  // extract parameters for speech/noise probability
+  if (flag) {
+    useFeatureSpecDiff = 1;
+    // for LRT feature:
+    // compute the average over inst->featureExtractionParams.rangeAvgHistLrt
+    avgHistLrtFX = 0;
+    avgSquareHistLrtFX = 0;
+    numHistLrt = 0;
+    for (i = 0; i < BIN_SIZE_LRT; i++) {
+      j = (2 * i + 1);
+      tmp32 = inst->histLrt[i] * j;
+      avgHistLrtFX += tmp32;
+      numHistLrt += inst->histLrt[i];
+      avgSquareHistLrtFX += tmp32 * j;
+    }
+    avgHistLrtComplFX = avgHistLrtFX;
+    for (; i < HIST_PAR_EST; i++) {
+      j = (2 * i + 1);
+      tmp32 = inst->histLrt[i] * j;
+      avgHistLrtComplFX += tmp32;
+      avgSquareHistLrtFX += tmp32 * j;
+    }
+    fluctLrtFX =
+        avgSquareHistLrtFX * numHistLrt - avgHistLrtFX * avgHistLrtComplFX;
+    thresFluctLrtFX = THRES_FLUCT_LRT * numHistLrt;
+    // get threshold for LRT feature:
+    tmpU32 = (FACTOR_1_LRT_DIFF * (uint32_t)avgHistLrtFX);
+    if ((fluctLrtFX < thresFluctLrtFX) || (numHistLrt == 0) ||
+        (tmpU32 > (uint32_t)(100 * numHistLrt))) {
+      // very low fluctuation, so likely noise
+      inst->thresholdLogLrt = inst->maxLrt;
+    } else {
+      tmp32 = (int32_t)((tmpU32 << (9 + inst->stages)) / numHistLrt / 25);
+      // check if value is within min/max range
+      inst->thresholdLogLrt = WEBRTC_SPL_SAT(inst->maxLrt, tmp32, inst->minLrt);
+    }
+    if (fluctLrtFX < thresFluctLrtFX) {
+      // Do not use difference feature if fluctuation of LRT feature is very
+      // low: most likely just noise state
+      useFeatureSpecDiff = 0;
+    }
+
+    // for spectral flatness and spectral difference: compute the main peaks of
+    // histogram
+    maxPeak1 = 0;
+    maxPeak2 = 0;
+    posPeak1SpecFlatFX = 0;
+    posPeak2SpecFlatFX = 0;
+    weightPeak1SpecFlat = 0;
+    weightPeak2SpecFlat = 0;
+
+    // peaks for flatness
+    for (i = 0; i < HIST_PAR_EST; i++) {
+      if (inst->histSpecFlat[i] > maxPeak1) {
+        // Found new "first" peak
+        maxPeak2 = maxPeak1;
+        weightPeak2SpecFlat = weightPeak1SpecFlat;
+        posPeak2SpecFlatFX = posPeak1SpecFlatFX;
+
+        maxPeak1 = inst->histSpecFlat[i];
+        weightPeak1SpecFlat = inst->histSpecFlat[i];
+        posPeak1SpecFlatFX = (uint32_t)(2 * i + 1);
+      } else if (inst->histSpecFlat[i] > maxPeak2) {
+        // Found new "second" peak
+        maxPeak2 = inst->histSpecFlat[i];
+        weightPeak2SpecFlat = inst->histSpecFlat[i];
+        posPeak2SpecFlatFX = (uint32_t)(2 * i + 1);
+      }
+    }
+
+    // for spectral flatness feature
+    useFeatureSpecFlat = 1;
+    // merge the two peaks if they are close
+    if ((posPeak1SpecFlatFX - posPeak2SpecFlatFX < LIM_PEAK_SPACE_FLAT_DIFF) &&
+        (weightPeak2SpecFlat * LIM_PEAK_WEIGHT_FLAT_DIFF >
+         weightPeak1SpecFlat)) {
+      weightPeak1SpecFlat += weightPeak2SpecFlat;
+      posPeak1SpecFlatFX = (posPeak1SpecFlatFX + posPeak2SpecFlatFX) >> 1;
+    }
+    // reject if weight of peaks is not large enough, or peak value too small
+    if (weightPeak1SpecFlat < THRES_WEIGHT_FLAT_DIFF ||
+        posPeak1SpecFlatFX < THRES_PEAK_FLAT) {
+      useFeatureSpecFlat = 0;
+    } else {  // if selected, get the threshold
+      // compute the threshold and check if value is within min/max range
+      inst->thresholdSpecFlat =
+          WEBRTC_SPL_SAT(MAX_FLAT_Q10, FACTOR_2_FLAT_Q10 * posPeak1SpecFlatFX,
+                         MIN_FLAT_Q10);  // Q10
+    }
+    // done with flatness feature
+
+    if (useFeatureSpecDiff) {
+      // compute two peaks for spectral difference
+      maxPeak1 = 0;
+      maxPeak2 = 0;
+      posPeak1SpecDiffFX = 0;
+      posPeak2SpecDiffFX = 0;
+      weightPeak1SpecDiff = 0;
+      weightPeak2SpecDiff = 0;
+      // peaks for spectral difference
+      for (i = 0; i < HIST_PAR_EST; i++) {
+        if (inst->histSpecDiff[i] > maxPeak1) {
+          // Found new "first" peak
+          maxPeak2 = maxPeak1;
+          weightPeak2SpecDiff = weightPeak1SpecDiff;
+          posPeak2SpecDiffFX = posPeak1SpecDiffFX;
+
+          maxPeak1 = inst->histSpecDiff[i];
+          weightPeak1SpecDiff = inst->histSpecDiff[i];
+          posPeak1SpecDiffFX = (uint32_t)(2 * i + 1);
+        } else if (inst->histSpecDiff[i] > maxPeak2) {
+          // Found new "second" peak
+          maxPeak2 = inst->histSpecDiff[i];
+          weightPeak2SpecDiff = inst->histSpecDiff[i];
+          posPeak2SpecDiffFX = (uint32_t)(2 * i + 1);
+        }
+      }
+
+      // merge the two peaks if they are close
+      if ((posPeak1SpecDiffFX - posPeak2SpecDiffFX <
+           LIM_PEAK_SPACE_FLAT_DIFF) &&
+          (weightPeak2SpecDiff * LIM_PEAK_WEIGHT_FLAT_DIFF >
+           weightPeak1SpecDiff)) {
+        weightPeak1SpecDiff += weightPeak2SpecDiff;
+        posPeak1SpecDiffFX = (posPeak1SpecDiffFX + posPeak2SpecDiffFX) >> 1;
+      }
+      // get the threshold value and check if value is within min/max range
+      inst->thresholdSpecDiff =
+          WEBRTC_SPL_SAT(MAX_DIFF, FACTOR_1_LRT_DIFF * posPeak1SpecDiffFX,
+                         MIN_DIFF);  // 5x bigger
+      // reject if weight of peaks is not large enough
+      if (weightPeak1SpecDiff < THRES_WEIGHT_FLAT_DIFF) {
+        useFeatureSpecDiff = 0;
+      }
+      // done with spectral difference feature
+    }
+
+    // select the weights between the features
+    // inst->priorModelPars[4] is weight for LRT: always selected
+    featureSum = 6 / (1 + useFeatureSpecFlat + useFeatureSpecDiff);
+    inst->weightLogLrt = featureSum;
+    inst->weightSpecFlat = useFeatureSpecFlat * featureSum;
+    inst->weightSpecDiff = useFeatureSpecDiff * featureSum;
+
+    // set histograms to zero for next update
+    WebRtcSpl_ZerosArrayW16(inst->histLrt, HIST_PAR_EST);
+    WebRtcSpl_ZerosArrayW16(inst->histSpecDiff, HIST_PAR_EST);
+    WebRtcSpl_ZerosArrayW16(inst->histSpecFlat, HIST_PAR_EST);
+  }  // end of flag == 1
+}
+
+// Compute spectral flatness on input spectrum
+// magn is the magnitude spectrum
+// spectral flatness is returned in inst->featureSpecFlat
+void WebRtcNsx_ComputeSpectralFlatness(NoiseSuppressionFixedC* inst,
+                                       uint16_t* magn) {
+  uint32_t tmpU32;
+  uint32_t avgSpectralFlatnessNum, avgSpectralFlatnessDen;
+
+  int32_t tmp32;
+  int32_t currentSpectralFlatness, logCurSpectralFlatness;
+
+  int16_t zeros, frac, intPart;
+
+  size_t i;
+
+  // for flatness
+  avgSpectralFlatnessNum = 0;
+  avgSpectralFlatnessDen =
+      inst->sumMagn - (uint32_t)magn[0];  // Q(normData-stages)
+
+  // compute log of ratio of the geometric to arithmetic mean: check for log(0)
+  // case flatness = exp( sum(log(magn[i]))/N - log(sum(magn[i])/N) )
+  //          = exp( sum(log(magn[i]))/N ) * N / sum(magn[i])
+  //          = 2^( sum(log2(magn[i]))/N - (log2(sum(magn[i])) - log2(N)) )
+  //          [This is used]
+  for (i = 1; i < inst->magnLen; i++) {
+    // First bin is excluded from spectrum measures. Number of bins is now a
+    // power of 2
+    if (magn[i]) {
+      zeros = WebRtcSpl_NormU32((uint32_t)magn[i]);
+      frac = (int16_t)(
+          ((uint32_t)((uint32_t)(magn[i]) << zeros) & 0x7FFFFFFF) >> 23);
+      // log2(magn(i))
+      RTC_DCHECK_LT(frac, 256);
+      tmpU32 = (uint32_t)(((31 - zeros) << 8) +
+                          WebRtcNsx_kLogTableFrac[frac]);  // Q8
+      avgSpectralFlatnessNum += tmpU32;                    // Q8
+    } else {
+      // if at least one frequency component is zero, treat separately
+      tmpU32 = WEBRTC_SPL_UMUL_32_16(inst->featureSpecFlat,
+                                     SPECT_FLAT_TAVG_Q14);  // Q24
+      inst->featureSpecFlat -= tmpU32 >> 14;                // Q10
+      return;
+    }
+  }
+  // ratio and inverse log: check for case of log(0)
+  zeros = WebRtcSpl_NormU32(avgSpectralFlatnessDen);
+  frac = (int16_t)(((avgSpectralFlatnessDen << zeros) & 0x7FFFFFFF) >> 23);
+  // log2(avgSpectralFlatnessDen)
+  RTC_DCHECK_LT(frac, 256);
+  tmp32 = (int32_t)(((31 - zeros) << 8) + WebRtcNsx_kLogTableFrac[frac]);  // Q8
+  logCurSpectralFlatness = (int32_t)avgSpectralFlatnessNum;
+  logCurSpectralFlatness +=
+      ((int32_t)(inst->stages - 1) << (inst->stages + 7));  // Q(8+stages-1)
+  logCurSpectralFlatness -= (tmp32 << (inst->stages - 1));
+  logCurSpectralFlatness <<= (10 - inst->stages);  // Q17
+  tmp32 = (int32_t)(0x00020000 | (WEBRTC_SPL_ABS_W32(logCurSpectralFlatness) &
+                                  0x0001FFFF));  // Q17
+  intPart = 7 - (logCurSpectralFlatness >> 17);  // Add 7 for output in Q10.
+  if (intPart > 0) {
+    currentSpectralFlatness = tmp32 >> intPart;
+  } else {
+    currentSpectralFlatness = tmp32 << -intPart;
+  }
+
+  // time average update of spectral flatness feature
+  tmp32 = currentSpectralFlatness - (int32_t)inst->featureSpecFlat;  // Q10
+  tmp32 *= SPECT_FLAT_TAVG_Q14;                                      // Q24
+  inst->featureSpecFlat += tmp32 >> 14;                              // Q10
+  // done with flatness feature
+}
+
+// Compute the difference measure between input spectrum and a template/learned
+// noise spectrum magn_tmp is the input spectrum the reference/template spectrum
+// is  inst->magn_avg_pause[i] returns (normalized) spectral difference in
+// inst->featureSpecDiff
+void WebRtcNsx_ComputeSpectralDifference(NoiseSuppressionFixedC* inst,
+                                         uint16_t* magnIn) {
+  // This is to be calculated:
+  // avgDiffNormMagn = var(magnIn) - cov(magnIn, magnAvgPause)^2 /
+  // var(magnAvgPause)
+
+  uint32_t tmpU32no1, tmpU32no2;
+  uint32_t varMagnUFX, varPauseUFX, avgDiffNormMagnUFX;
+
+  int32_t tmp32no1, tmp32no2;
+  int32_t avgPauseFX, avgMagnFX, covMagnPauseFX;
+  int32_t maxPause, minPause;
+
+  int16_t tmp16no1;
+
+  size_t i;
+  int norm32, nShifts;
+
+  avgPauseFX = 0;
+  maxPause = 0;
+  minPause = inst->avgMagnPause[0];  // Q(prevQMagn)
+  // compute average quantities
+  for (i = 0; i < inst->magnLen; i++) {
+    // Compute mean of magn_pause
+    avgPauseFX += inst->avgMagnPause[i];  // in Q(prevQMagn)
+    maxPause = WEBRTC_SPL_MAX(maxPause, inst->avgMagnPause[i]);
+    minPause = WEBRTC_SPL_MIN(minPause, inst->avgMagnPause[i]);
+  }
+  // normalize by replacing div of "inst->magnLen" with "inst->stages-1" shifts
+  avgPauseFX >>= inst->stages - 1;
+  avgMagnFX = inst->sumMagn >> (inst->stages - 1);
+  // Largest possible deviation in magnPause for (co)var calculations
+  tmp32no1 = WEBRTC_SPL_MAX(maxPause - avgPauseFX, avgPauseFX - minPause);
+  // Get number of shifts to make sure we don't get wrap around in varPause
+  nShifts = WEBRTC_SPL_MAX(0, 10 + inst->stages - WebRtcSpl_NormW32(tmp32no1));
+
+  varMagnUFX = 0;
+  varPauseUFX = 0;
+  covMagnPauseFX = 0;
+  for (i = 0; i < inst->magnLen; i++) {
+    // Compute var and cov of magn and magn_pause
+    tmp16no1 = (int16_t)((int32_t)magnIn[i] - avgMagnFX);
+    tmp32no2 = inst->avgMagnPause[i] - avgPauseFX;
+    varMagnUFX += (uint32_t)(tmp16no1 * tmp16no1);  // Q(2*qMagn)
+    tmp32no1 = tmp32no2 * tmp16no1;                 // Q(prevQMagn+qMagn)
+    covMagnPauseFX += tmp32no1;                     // Q(prevQMagn+qMagn)
+    tmp32no1 = tmp32no2 >> nShifts;                 // Q(prevQMagn-minPause).
+    varPauseUFX += tmp32no1 * tmp32no1;             // Q(2*(prevQMagn-minPause))
+  }
+  // update of average magnitude spectrum: Q(-2*stages) and averaging replaced
+  // by shifts
+  inst->curAvgMagnEnergy +=
+      inst->magnEnergy >> (2 * inst->normData + inst->stages - 1);
+
+  avgDiffNormMagnUFX = varMagnUFX;  // Q(2*qMagn)
+  if ((varPauseUFX) && (covMagnPauseFX)) {
+    tmpU32no1 =
+        (uint32_t)WEBRTC_SPL_ABS_W32(covMagnPauseFX);  // Q(prevQMagn+qMagn)
+    norm32 = WebRtcSpl_NormU32(tmpU32no1) - 16;
+    if (norm32 > 0) {
+      tmpU32no1 <<= norm32;  // Q(prevQMagn+qMagn+norm32)
+    } else {
+      tmpU32no1 >>= -norm32;  // Q(prevQMagn+qMagn+norm32)
+    }
+    tmpU32no2 =
+        WEBRTC_SPL_UMUL(tmpU32no1, tmpU32no1);  // Q(2*(prevQMagn+qMagn-norm32))
+
+    nShifts += norm32;
+    nShifts <<= 1;
+    if (nShifts < 0) {
+      varPauseUFX >>= (-nShifts);  // Q(2*(qMagn+norm32+minPause))
+      nShifts = 0;
+    }
+    if (varPauseUFX > 0) {
+      // Q(2*(qMagn+norm32-16+minPause))
+      tmpU32no1 = tmpU32no2 / varPauseUFX;
+      tmpU32no1 >>= nShifts;
+
+      // Q(2*qMagn)
+      avgDiffNormMagnUFX -= WEBRTC_SPL_MIN(avgDiffNormMagnUFX, tmpU32no1);
+    } else {
+      avgDiffNormMagnUFX = 0;
+    }
+  }
+  // normalize and compute time average update of difference feature
+  tmpU32no1 = avgDiffNormMagnUFX >> (2 * inst->normData);
+  if (inst->featureSpecDiff > tmpU32no1) {
+    tmpU32no2 = WEBRTC_SPL_UMUL_32_16(inst->featureSpecDiff - tmpU32no1,
+                                      SPECT_DIFF_TAVG_Q8);  // Q(8-2*stages)
+    inst->featureSpecDiff -= tmpU32no2 >> 8;                // Q(-2*stages)
+  } else {
+    tmpU32no2 = WEBRTC_SPL_UMUL_32_16(tmpU32no1 - inst->featureSpecDiff,
+                                      SPECT_DIFF_TAVG_Q8);  // Q(8-2*stages)
+    inst->featureSpecDiff += tmpU32no2 >> 8;                // Q(-2*stages)
+  }
+}
+
+// Transform input (speechFrame) to frequency domain magnitude (magnU16)
+void WebRtcNsx_DataAnalysis(NoiseSuppressionFixedC* inst,
+                            short* speechFrame,
+                            uint16_t* magnU16) {
+  uint32_t tmpU32no1;
+
+  int32_t tmp_1_w32 = 0;
+  int32_t tmp_2_w32 = 0;
+  int32_t sum_log_magn = 0;
+  int32_t sum_log_i_log_magn = 0;
+
+  uint16_t sum_log_magn_u16 = 0;
+  uint16_t tmp_u16 = 0;
+
+  int16_t sum_log_i = 0;
+  int16_t sum_log_i_square = 0;
+  int16_t frac = 0;
+  int16_t log2 = 0;
+  int16_t matrix_determinant = 0;
+  int16_t maxWinData;
+
+  size_t i, j;
+  int zeros;
+  int net_norm = 0;
+  int right_shifts_in_magnU16 = 0;
+  int right_shifts_in_initMagnEst = 0;
+
+  int16_t winData_buff[ANAL_BLOCKL_MAX * 2 + 16];
+  int16_t realImag_buff[ANAL_BLOCKL_MAX * 2 + 16];
+
+  // Align the structures to 32-byte boundary for the FFT function.
+  int16_t* winData = (int16_t*)(((uintptr_t)winData_buff + 31) & ~31);
+  int16_t* realImag = (int16_t*)(((uintptr_t)realImag_buff + 31) & ~31);
+
+  // Update analysis buffer for lower band, and window data before FFT.
+  WebRtcNsx_AnalysisUpdate(inst, winData, speechFrame);
+
+  // Get input energy
+  inst->energyIn =
+      WebRtcSpl_Energy(winData, inst->anaLen, &inst->scaleEnergyIn);
+
+  // Reset zero input flag
+  inst->zeroInputSignal = 0;
+  // Acquire norm for winData
+  maxWinData = WebRtcSpl_MaxAbsValueW16(winData, inst->anaLen);
+  inst->normData = WebRtcSpl_NormW16(maxWinData);
+  if (maxWinData == 0) {
+    // Treat zero input separately.
+    inst->zeroInputSignal = 1;
+    return;
+  }
+
+  // Determine the net normalization in the frequency domain
+  net_norm = inst->stages - inst->normData;
+  // Track lowest normalization factor and use it to prevent wrap around in
+  // shifting
+  right_shifts_in_magnU16 = inst->normData - inst->minNorm;
+  right_shifts_in_initMagnEst = WEBRTC_SPL_MAX(-right_shifts_in_magnU16, 0);
+  inst->minNorm -= right_shifts_in_initMagnEst;
+  right_shifts_in_magnU16 = WEBRTC_SPL_MAX(right_shifts_in_magnU16, 0);
+
+  // create realImag as winData interleaved with zeros (= imag. part), normalize
+  // it
+  WebRtcNsx_NormalizeRealBuffer(inst, winData, realImag);
+
+  // FFT output will be in winData[].
+  WebRtcSpl_RealForwardFFT(inst->real_fft, realImag, winData);
+
+  inst->imag[0] = 0;  // Q(normData-stages)
+  inst->imag[inst->anaLen2] = 0;
+  inst->real[0] = winData[0];  // Q(normData-stages)
+  inst->real[inst->anaLen2] = winData[inst->anaLen];
+  // Q(2*(normData-stages))
+  inst->magnEnergy = (uint32_t)(inst->real[0] * inst->real[0]);
+  inst->magnEnergy +=
+      (uint32_t)(inst->real[inst->anaLen2] * inst->real[inst->anaLen2]);
+  magnU16[0] =
+      (uint16_t)WEBRTC_SPL_ABS_W16(inst->real[0]);  // Q(normData-stages)
+  magnU16[inst->anaLen2] =
+      (uint16_t)WEBRTC_SPL_ABS_W16(inst->real[inst->anaLen2]);
+  inst->sumMagn = (uint32_t)magnU16[0];  // Q(normData-stages)
+  inst->sumMagn += (uint32_t)magnU16[inst->anaLen2];
+
+  if (inst->blockIndex >= END_STARTUP_SHORT) {
+    for (i = 1, j = 2; i < inst->anaLen2; i += 1, j += 2) {
+      inst->real[i] = winData[j];
+      inst->imag[i] = -winData[j + 1];
+      // magnitude spectrum
+      // energy in Q(2*(normData-stages))
+      tmpU32no1 = (uint32_t)(winData[j] * winData[j]);
+      tmpU32no1 += (uint32_t)(winData[j + 1] * winData[j + 1]);
+      inst->magnEnergy += tmpU32no1;  // Q(2*(normData-stages))
+
+      magnU16[i] =
+          (uint16_t)WebRtcSpl_SqrtFloor(tmpU32no1);  // Q(normData-stages)
+      inst->sumMagn += (uint32_t)magnU16[i];         // Q(normData-stages)
+    }
+  } else {
+    //
+    // Gather information during startup for noise parameter estimation
+    //
+
+    // Switch initMagnEst to Q(minNorm-stages)
+    inst->initMagnEst[0] >>= right_shifts_in_initMagnEst;
+    inst->initMagnEst[inst->anaLen2] >>= right_shifts_in_initMagnEst;
+
+    // Update initMagnEst with magnU16 in Q(minNorm-stages).
+    inst->initMagnEst[0] += magnU16[0] >> right_shifts_in_magnU16;
+    inst->initMagnEst[inst->anaLen2] +=
+        magnU16[inst->anaLen2] >> right_shifts_in_magnU16;
+
+    log2 = 0;
+    if (magnU16[inst->anaLen2]) {
+      // Calculate log2(magnU16[inst->anaLen2])
+      zeros = WebRtcSpl_NormU32((uint32_t)magnU16[inst->anaLen2]);
+      frac = (int16_t)(
+          (((uint32_t)magnU16[inst->anaLen2] << zeros) & 0x7FFFFFFF) >>
+          23);  // Q8
+      // log2(magnU16(i)) in Q8
+      RTC_DCHECK_LT(frac, 256);
+      log2 = (int16_t)(((31 - zeros) << 8) + WebRtcNsx_kLogTableFrac[frac]);
+    }
+
+    sum_log_magn = (int32_t)log2;  // Q8
+    // sum_log_i_log_magn in Q17
+    sum_log_i_log_magn = (kLogIndex[inst->anaLen2] * log2) >> 3;
+
+    for (i = 1, j = 2; i < inst->anaLen2; i += 1, j += 2) {
+      inst->real[i] = winData[j];
+      inst->imag[i] = -winData[j + 1];
+      // magnitude spectrum
+      // energy in Q(2*(normData-stages))
+      tmpU32no1 = (uint32_t)(winData[j] * winData[j]);
+      tmpU32no1 += (uint32_t)(winData[j + 1] * winData[j + 1]);
+      inst->magnEnergy += tmpU32no1;  // Q(2*(normData-stages))
+
+      magnU16[i] =
+          (uint16_t)WebRtcSpl_SqrtFloor(tmpU32no1);  // Q(normData-stages)
+      inst->sumMagn += (uint32_t)magnU16[i];         // Q(normData-stages)
+
+      // Switch initMagnEst to Q(minNorm-stages)
+      inst->initMagnEst[i] >>= right_shifts_in_initMagnEst;
+
+      // Update initMagnEst with magnU16 in Q(minNorm-stages).
+      inst->initMagnEst[i] += magnU16[i] >> right_shifts_in_magnU16;
+
+      if (i >= kStartBand) {
+        // For pink noise estimation. Collect data neglecting lower frequency
+        // band
+        log2 = 0;
+        if (magnU16[i]) {
+          zeros = WebRtcSpl_NormU32((uint32_t)magnU16[i]);
+          frac =
+              (int16_t)((((uint32_t)magnU16[i] << zeros) & 0x7FFFFFFF) >> 23);
+          // log2(magnU16(i)) in Q8
+          RTC_DCHECK_LT(frac, 256);
+          log2 = (int16_t)(((31 - zeros) << 8) + WebRtcNsx_kLogTableFrac[frac]);
+        }
+        sum_log_magn += (int32_t)log2;  // Q8
+        // sum_log_i_log_magn in Q17
+        sum_log_i_log_magn += (kLogIndex[i] * log2) >> 3;
+      }
+    }
+
+    //
+    // compute simplified noise model during startup
+    //
+
+    // Estimate White noise
+
+    // Switch whiteNoiseLevel to Q(minNorm-stages)
+    inst->whiteNoiseLevel >>= right_shifts_in_initMagnEst;
+
+    // Update the average magnitude spectrum, used as noise estimate.
+    tmpU32no1 = WEBRTC_SPL_UMUL_32_16(inst->sumMagn, inst->overdrive);
+    tmpU32no1 >>= inst->stages + 8;
+
+    // Replacing division above with 'stages' shifts
+    // Shift to same Q-domain as whiteNoiseLevel
+    tmpU32no1 >>= right_shifts_in_magnU16;
+    // This operation is safe from wrap around as long as END_STARTUP_SHORT <
+    // 128
+    RTC_DCHECK_LT(END_STARTUP_SHORT, 128);
+    inst->whiteNoiseLevel += tmpU32no1;  // Q(minNorm-stages)
+
+    // Estimate Pink noise parameters
+    // Denominator used in both parameter estimates.
+    // The value is only dependent on the size of the frequency band
+    // (kStartBand) and to reduce computational complexity stored in a table
+    // (kDeterminantEstMatrix[])
+    RTC_DCHECK_LT(kStartBand, 66);
+    matrix_determinant = kDeterminantEstMatrix[kStartBand];  // Q0
+    sum_log_i = kSumLogIndex[kStartBand];                    // Q5
+    sum_log_i_square = kSumSquareLogIndex[kStartBand];       // Q2
+    if (inst->fs == 8000) {
+      // Adjust values to shorter blocks in narrow band.
+      tmp_1_w32 = (int32_t)matrix_determinant;
+      tmp_1_w32 += (kSumLogIndex[65] * sum_log_i) >> 9;
+      tmp_1_w32 -= (kSumLogIndex[65] * kSumLogIndex[65]) >> 10;
+      tmp_1_w32 -= (int32_t)sum_log_i_square << 4;
+      tmp_1_w32 -= ((inst->magnLen - kStartBand) * kSumSquareLogIndex[65]) >> 2;
+      matrix_determinant = (int16_t)tmp_1_w32;
+      sum_log_i -= kSumLogIndex[65];               // Q5
+      sum_log_i_square -= kSumSquareLogIndex[65];  // Q2
+    }
+
+    // Necessary number of shifts to fit sum_log_magn in a word16
+    zeros = 16 - WebRtcSpl_NormW32(sum_log_magn);
+    if (zeros < 0) {
+      zeros = 0;
+    }
+    tmp_1_w32 = sum_log_magn << 1;                      // Q9
+    sum_log_magn_u16 = (uint16_t)(tmp_1_w32 >> zeros);  // Q(9-zeros).
+
+    // Calculate and update pinkNoiseNumerator. Result in Q11.
+    tmp_2_w32 = WEBRTC_SPL_MUL_16_U16(sum_log_i_square,
+                                      sum_log_magn_u16);  // Q(11-zeros)
+    tmpU32no1 = sum_log_i_log_magn >> 12;                 // Q5
+
+    // Shift the largest value of sum_log_i and tmp32no3 before multiplication
+    tmp_u16 = ((uint16_t)sum_log_i << 1);  // Q6
+    if ((uint32_t)sum_log_i > tmpU32no1) {
+      tmp_u16 >>= zeros;
+    } else {
+      tmpU32no1 >>= zeros;
+    }
+    tmp_2_w32 -=
+        (int32_t)WEBRTC_SPL_UMUL_32_16(tmpU32no1, tmp_u16);  // Q(11-zeros)
+    matrix_determinant >>= zeros;                            // Q(-zeros)
+    tmp_2_w32 = WebRtcSpl_DivW32W16(tmp_2_w32, matrix_determinant);  // Q11
+    tmp_2_w32 += (int32_t)net_norm << 11;                            // Q11
+    if (tmp_2_w32 < 0) {
+      tmp_2_w32 = 0;
+    }
+    inst->pinkNoiseNumerator += tmp_2_w32;  // Q11
+
+    // Calculate and update pinkNoiseExp. Result in Q14.
+    tmp_2_w32 =
+        WEBRTC_SPL_MUL_16_U16(sum_log_i, sum_log_magn_u16);  // Q(14-zeros)
+    tmp_1_w32 = sum_log_i_log_magn >> (3 + zeros);
+    tmp_1_w32 *= inst->magnLen - kStartBand;
+    tmp_2_w32 -= tmp_1_w32;  // Q(14-zeros)
+    if (tmp_2_w32 > 0) {
+      // If the exponential parameter is negative force it to zero, which means
+      // a flat spectrum.
+      tmp_1_w32 = WebRtcSpl_DivW32W16(tmp_2_w32, matrix_determinant);  // Q14
+      inst->pinkNoiseExp += WEBRTC_SPL_SAT(16384, tmp_1_w32, 0);       // Q14
+    }
+  }
+}
+
+void WebRtcNsx_DataSynthesis(NoiseSuppressionFixedC* inst, short* outFrame) {
+  int32_t energyOut;
+
+  int16_t realImag_buff[ANAL_BLOCKL_MAX * 2 + 16];
+  int16_t rfft_out_buff[ANAL_BLOCKL_MAX * 2 + 16];
+
+  // Align the structures to 32-byte boundary for the FFT function.
+  int16_t* realImag = (int16_t*)(((uintptr_t)realImag_buff + 31) & ~31);
+  int16_t* rfft_out = (int16_t*)(((uintptr_t)rfft_out_buff + 31) & ~31);
+
+  int16_t tmp16no1, tmp16no2;
+  int16_t energyRatio;
+  int16_t gainFactor, gainFactor1, gainFactor2;
+
+  size_t i;
+  int outCIFFT;
+  int scaleEnergyOut = 0;
+
+  if (inst->zeroInputSignal) {
+    // synthesize the special case of zero input
+    // read out fully processed segment
+    for (i = 0; i < inst->blockLen10ms; i++) {
+      outFrame[i] = inst->synthesisBuffer[i];  // Q0
+    }
+    // update synthesis buffer
+    memcpy(
+        inst->synthesisBuffer, inst->synthesisBuffer + inst->blockLen10ms,
+        (inst->anaLen - inst->blockLen10ms) * sizeof(*inst->synthesisBuffer));
+    WebRtcSpl_ZerosArrayW16(
+        inst->synthesisBuffer + inst->anaLen - inst->blockLen10ms,
+        inst->blockLen10ms);
+    return;
+  }
+
+  // Filter the data in the frequency domain, and create spectrum.
+  WebRtcNsx_PrepareSpectrum(inst, realImag);
+
+  // Inverse FFT output will be in rfft_out[].
+  outCIFFT = WebRtcSpl_RealInverseFFT(inst->real_fft, realImag, rfft_out);
+
+  WebRtcNsx_Denormalize(inst, rfft_out, outCIFFT);
+
+  // scale factor: only do it after END_STARTUP_LONG time
+  gainFactor = 8192;  // 8192 = Q13(1.0)
+  if (inst->gainMap == 1 && inst->blockIndex > END_STARTUP_LONG &&
+      inst->energyIn > 0) {
+    // Q(-scaleEnergyOut)
+    energyOut = WebRtcSpl_Energy(inst->real, inst->anaLen, &scaleEnergyOut);
+    if (scaleEnergyOut == 0 && !(energyOut & 0x7f800000)) {
+      energyOut = WEBRTC_SPL_SHIFT_W32(
+          energyOut, 8 + scaleEnergyOut - inst->scaleEnergyIn);
+    } else {
+      // |energyIn| is currently in Q(|scaleEnergyIn|), but to later on end up
+      // with an |energyRatio| in Q8 we need to change the Q-domain to
+      // Q(-8-scaleEnergyOut).
+      inst->energyIn >>= 8 + scaleEnergyOut - inst->scaleEnergyIn;
+    }
+
+    RTC_DCHECK_GT(inst->energyIn, 0);
+    energyRatio = (energyOut + inst->energyIn / 2) / inst->energyIn;  // Q8
+    // Limit the ratio to [0, 1] in Q8, i.e., [0, 256]
+    energyRatio = WEBRTC_SPL_SAT(256, energyRatio, 0);
+
+    // all done in lookup tables now
+    RTC_DCHECK_LT(energyRatio, 257);
+    gainFactor1 = kFactor1Table[energyRatio];       // Q8
+    gainFactor2 = inst->factor2Table[energyRatio];  // Q8
+
+    // combine both scales with speech/noise prob: note prior (priorSpeechProb)
+    // is not frequency dependent
+
+    // factor = inst->priorSpeechProb*factor1 +
+    // (1.0-inst->priorSpeechProb)*factor2; // original code
+    tmp16no1 = (int16_t)(((16384 - inst->priorNonSpeechProb) * gainFactor1) >>
+                         14);  // in Q13, where 16384 = Q14(1.0)
+    tmp16no2 = (int16_t)((inst->priorNonSpeechProb * gainFactor2) >> 14);
+    gainFactor = tmp16no1 + tmp16no2;  // Q13
+  }                                    // out of flag_gain_map==1
+
+  // Synthesis, read out fully processed segment, and update synthesis buffer.
+  WebRtcNsx_SynthesisUpdate(inst, outFrame, gainFactor);
+}
+
+void WebRtcNsx_ProcessCore(NoiseSuppressionFixedC* inst,
+                           const int16_t* const* speechFrame,
+                           int num_bands,
+                           int16_t* const* outFrame) {
+  // main routine for noise suppression
+
+  uint32_t tmpU32no1, tmpU32no2, tmpU32no3;
+  uint32_t satMax, maxNoiseU32;
+  uint32_t tmpMagnU32, tmpNoiseU32;
+  uint32_t nearMagnEst;
+  uint32_t noiseUpdateU32;
+  uint32_t noiseU32[HALF_ANAL_BLOCKL];
+  uint32_t postLocSnr[HALF_ANAL_BLOCKL];
+  uint32_t priorLocSnr[HALF_ANAL_BLOCKL];
+  uint32_t prevNearSnr[HALF_ANAL_BLOCKL];
+  uint32_t curNearSnr;
+  uint32_t priorSnr;
+  uint32_t noise_estimate = 0;
+  uint32_t noise_estimate_avg = 0;
+  uint32_t numerator = 0;
+
+  int32_t tmp32no1, tmp32no2;
+  int32_t pink_noise_num_avg = 0;
+
+  uint16_t tmpU16no1;
+  uint16_t magnU16[HALF_ANAL_BLOCKL];
+  uint16_t prevNoiseU16[HALF_ANAL_BLOCKL];
+  uint16_t nonSpeechProbFinal[HALF_ANAL_BLOCKL];
+  uint16_t gammaNoise, prevGammaNoise;
+  uint16_t noiseSupFilterTmp[HALF_ANAL_BLOCKL];
+
+  int16_t qMagn, qNoise;
+  int16_t avgProbSpeechHB, gainModHB, avgFilterGainHB, gainTimeDomainHB;
+  int16_t pink_noise_exp_avg = 0;
+
+  size_t i, j;
+  int nShifts, postShifts;
+  int norm32no1, norm32no2;
+  int flag, sign;
+  int q_domain_to_use = 0;
+
+  // Code for ARMv7-Neon platform assumes the following:
+  RTC_DCHECK_GT(inst->anaLen, 0);
+  RTC_DCHECK_GT(inst->anaLen2, 0);
+  RTC_DCHECK_EQ(0, inst->anaLen % 16);
+  RTC_DCHECK_EQ(0, inst->anaLen2 % 8);
+  RTC_DCHECK_GT(inst->blockLen10ms, 0);
+  RTC_DCHECK_EQ(0, inst->blockLen10ms % 16);
+  RTC_DCHECK_EQ(inst->magnLen, inst->anaLen2 + 1);
+
+#ifdef NS_FILEDEBUG
+  if (fwrite(spframe, sizeof(short), inst->blockLen10ms, inst->infile) !=
+      inst->blockLen10ms) {
+    RTC_NOTREACHED();
+  }
+#endif
+
+  // Check that initialization has been done
+  RTC_DCHECK_EQ(1, inst->initFlag);
+  RTC_DCHECK_LE(num_bands - 1, NUM_HIGH_BANDS_MAX);
+
+  const int16_t* const* speechFrameHB = NULL;
+  int16_t* const* outFrameHB = NULL;
+  size_t num_high_bands = 0;
+  if (num_bands > 1) {
+    speechFrameHB = &speechFrame[1];
+    outFrameHB = &outFrame[1];
+    num_high_bands = (size_t)(num_bands - 1);
+  }
+
+  // Store speechFrame and transform to frequency domain
+  WebRtcNsx_DataAnalysis(inst, (int16_t*)speechFrame[0], magnU16);
+
+  if (inst->zeroInputSignal) {
+    WebRtcNsx_DataSynthesis(inst, outFrame[0]);
+
+    if (num_bands > 1) {
+      // update analysis buffer for H band
+      // append new data to buffer FX
+      for (i = 0; i < num_high_bands; ++i) {
+        int block_shift = inst->anaLen - inst->blockLen10ms;
+        memcpy(inst->dataBufHBFX[i], inst->dataBufHBFX[i] + inst->blockLen10ms,
+               block_shift * sizeof(*inst->dataBufHBFX[i]));
+        memcpy(inst->dataBufHBFX[i] + block_shift, speechFrameHB[i],
+               inst->blockLen10ms * sizeof(*inst->dataBufHBFX[i]));
+        for (j = 0; j < inst->blockLen10ms; j++) {
+          outFrameHB[i][j] = inst->dataBufHBFX[i][j];  // Q0
+        }
+      }
+    }  // end of H band gain computation
+    return;
+  }
+
+  // Update block index when we have something to process
+  inst->blockIndex++;
+  //
+
+  // Norm of magn
+  qMagn = inst->normData - inst->stages;
+
+  // Compute spectral flatness on input spectrum
+  WebRtcNsx_ComputeSpectralFlatness(inst, magnU16);
+
+  // quantile noise estimate
+  WebRtcNsx_NoiseEstimation(inst, magnU16, noiseU32, &qNoise);
+
+  // noise estimate from previous frame
+  for (i = 0; i < inst->magnLen; i++) {
+    prevNoiseU16[i] = (uint16_t)(inst->prevNoiseU32[i] >> 11);  // Q(prevQNoise)
+  }
+
+  if (inst->blockIndex < END_STARTUP_SHORT) {
+    // Noise Q-domain to be used later; see description at end of section.
+    q_domain_to_use = WEBRTC_SPL_MIN((int)qNoise, inst->minNorm - inst->stages);
+
+    // Calculate frequency independent parts in parametric noise estimate and
+    // calculate the estimate for the lower frequency band (same values for all
+    // frequency bins)
+    if (inst->pinkNoiseExp) {
+      pink_noise_exp_avg =
+          (int16_t)WebRtcSpl_DivW32W16(inst->pinkNoiseExp,
+                                       (int16_t)(inst->blockIndex + 1));  // Q14
+      pink_noise_num_avg =
+          WebRtcSpl_DivW32W16(inst->pinkNoiseNumerator,
+                              (int16_t)(inst->blockIndex + 1));  // Q11
+      WebRtcNsx_CalcParametricNoiseEstimate(
+          inst, pink_noise_exp_avg, pink_noise_num_avg, kStartBand,
+          &noise_estimate, &noise_estimate_avg);
+    } else {
+      // Use white noise estimate if we have poor pink noise parameter estimates
+      noise_estimate = inst->whiteNoiseLevel;  // Q(minNorm-stages)
+      noise_estimate_avg =
+          noise_estimate / (inst->blockIndex + 1);  // Q(minNorm-stages)
+    }
+    for (i = 0; i < inst->magnLen; i++) {
+      // Estimate the background noise using the pink noise parameters if
+      // permitted
+      if ((inst->pinkNoiseExp) && (i >= kStartBand)) {
+        // Reset noise_estimate
+        noise_estimate = 0;
+        noise_estimate_avg = 0;
+        // Calculate the parametric noise estimate for current frequency bin
+        WebRtcNsx_CalcParametricNoiseEstimate(
+            inst, pink_noise_exp_avg, pink_noise_num_avg, i, &noise_estimate,
+            &noise_estimate_avg);
+      }
+      // Calculate parametric Wiener filter
+      noiseSupFilterTmp[i] = inst->denoiseBound;
+      if (inst->initMagnEst[i]) {
+        // numerator = (initMagnEst - noise_estimate * overdrive)
+        // Result in Q(8+minNorm-stages)
+        tmpU32no1 = WEBRTC_SPL_UMUL_32_16(noise_estimate, inst->overdrive);
+        numerator = inst->initMagnEst[i] << 8;
+        if (numerator > tmpU32no1) {
+          // Suppression filter coefficient larger than zero, so calculate.
+          numerator -= tmpU32no1;
+
+          // Determine number of left shifts in numerator for best accuracy
+          // after division
+          nShifts = WebRtcSpl_NormU32(numerator);
+          nShifts = WEBRTC_SPL_SAT(6, nShifts, 0);
+
+          // Shift numerator to Q(nShifts+8+minNorm-stages)
+          numerator <<= nShifts;
+
+          // Shift denominator to Q(nShifts-6+minNorm-stages)
+          tmpU32no1 = inst->initMagnEst[i] >> (6 - nShifts);
+          if (tmpU32no1 == 0) {
+            // This is only possible if numerator = 0, in which case
+            // we don't need any division.
+            tmpU32no1 = 1;
+          }
+          tmpU32no2 = numerator / tmpU32no1;  // Q14
+          noiseSupFilterTmp[i] =
+              (uint16_t)WEBRTC_SPL_SAT(16384, tmpU32no2,
+                                       (uint32_t)(inst->denoiseBound));  // Q14
+        }
+      }
+      // Weight quantile noise 'noiseU32' with modeled noise
+      // 'noise_estimate_avg' 'noiseU32 is in Q(qNoise) and 'noise_estimate' in
+      // Q(minNorm-stages) To guarantee that we do not get wrap around when
+      // shifting to the same domain we use the lowest one. Furthermore, we need
+      // to save 6 bits for the weighting. 'noise_estimate_avg' can handle this
+      // operation by construction, but 'noiseU32' may not.
+
+      // Shift 'noiseU32' to 'q_domain_to_use'
+      tmpU32no1 = noiseU32[i] >> (qNoise - q_domain_to_use);
+      // Shift 'noise_estimate_avg' to 'q_domain_to_use'
+      tmpU32no2 = noise_estimate_avg >>
+                  (inst->minNorm - inst->stages - q_domain_to_use);
+      // Make a simple check to see if we have enough room for weighting
+      // 'tmpU32no1' without wrap around
+      nShifts = 0;
+      if (tmpU32no1 & 0xfc000000) {
+        tmpU32no1 >>= 6;
+        tmpU32no2 >>= 6;
+        nShifts = 6;
+      }
+      tmpU32no1 *= inst->blockIndex;
+      tmpU32no2 *= (END_STARTUP_SHORT - inst->blockIndex);
+      // Add them together and divide by startup length
+      noiseU32[i] =
+          WebRtcSpl_DivU32U16(tmpU32no1 + tmpU32no2, END_STARTUP_SHORT);
+      // Shift back if necessary
+      noiseU32[i] <<= nShifts;
+    }
+    // Update new Q-domain for 'noiseU32'
+    qNoise = q_domain_to_use;
+  }
+  // compute average signal during END_STARTUP_LONG time:
+  // used to normalize spectral difference measure
+  if (inst->blockIndex < END_STARTUP_LONG) {
+    // substituting division with shift ending up in Q(-2*stages)
+    inst->timeAvgMagnEnergyTmp +=
+        inst->magnEnergy >> (2 * inst->normData + inst->stages - 1);
+    inst->timeAvgMagnEnergy =
+        WebRtcSpl_DivU32U16(inst->timeAvgMagnEnergyTmp, inst->blockIndex + 1);
+  }
+
+  // start processing at frames == converged+1
+  // STEP 1: compute prior and post SNR based on quantile noise estimates
+
+  // compute direct decision (DD) estimate of prior SNR: needed for new method
+  satMax = (uint32_t)1048575;  // Largest possible value without getting
+                               // overflow despite shifting 12 steps
+  postShifts = 6 + qMagn - qNoise;
+  nShifts = 5 - inst->prevQMagn + inst->prevQNoise;
+  for (i = 0; i < inst->magnLen; i++) {
+    // FLOAT:
+    // post SNR
+    // postLocSnr[i] = 0.0;
+    // if (magn[i] > noise[i])
+    // {
+    //   postLocSnr[i] = magn[i] / (noise[i] + 0.0001);
+    // }
+    // // previous post SNR
+    // // previous estimate: based on previous frame with gain filter (smooth is
+    // previous filter)
+    //
+    // prevNearSnr[i] = inst->prevMagnU16[i] / (inst->noisePrev[i] + 0.0001) *
+    // (inst->smooth[i]);
+    //
+    // // DD estimate is sum of two terms: current estimate and previous
+    // estimate
+    // // directed decision update of priorSnr (or we actually store
+    // [2*priorSnr+1])
+    //
+    // priorLocSnr[i] = DD_PR_SNR * prevNearSnr[i] + (1.0 - DD_PR_SNR) *
+    // (postLocSnr[i] - 1.0);
+
+    // calculate post SNR: output in Q11
+    postLocSnr[i] = 2048;                   // 1.0 in Q11
+    tmpU32no1 = (uint32_t)magnU16[i] << 6;  // Q(6+qMagn)
+    if (postShifts < 0) {
+      tmpU32no2 = noiseU32[i] >> -postShifts;  // Q(6+qMagn)
+    } else {
+      tmpU32no2 = noiseU32[i] << postShifts;  // Q(6+qMagn)
+    }
+    if (tmpU32no1 > tmpU32no2) {
+      // Current magnitude larger than noise
+      tmpU32no1 <<= 11;  // Q(17+qMagn)
+      if (tmpU32no2 > 0) {
+        tmpU32no1 /= tmpU32no2;                             // Q11
+        postLocSnr[i] = WEBRTC_SPL_MIN(satMax, tmpU32no1);  // Q11
+      } else {
+        postLocSnr[i] = satMax;
+      }
+    }
+
+    // calculate prevNearSnr[i] and save for later instead of recalculating it
+    // later |nearMagnEst| in Q(prevQMagn + 14)
+    nearMagnEst = inst->prevMagnU16[i] * inst->noiseSupFilter[i];
+    tmpU32no1 = nearMagnEst << 3;                  // Q(prevQMagn+17)
+    tmpU32no2 = inst->prevNoiseU32[i] >> nShifts;  // Q(prevQMagn+6)
+
+    if (tmpU32no2 > 0) {
+      tmpU32no1 /= tmpU32no2;                         // Q11
+      tmpU32no1 = WEBRTC_SPL_MIN(satMax, tmpU32no1);  // Q11
+    } else {
+      tmpU32no1 = satMax;  // Q11
+    }
+    prevNearSnr[i] = tmpU32no1;  // Q11
+
+    // directed decision update of priorSnr
+    tmpU32no1 = WEBRTC_SPL_UMUL_32_16(prevNearSnr[i], DD_PR_SNR_Q11);  // Q22
+    tmpU32no2 = WEBRTC_SPL_UMUL_32_16(postLocSnr[i] - 2048,
+                                      ONE_MINUS_DD_PR_SNR_Q11);  // Q22
+    priorSnr = tmpU32no1 + tmpU32no2 + 512;  // Q22 (added 512 for rounding)
+    // priorLocSnr = 1 + 2*priorSnr
+    priorLocSnr[i] = 2048 + (priorSnr >> 10);  // Q11
+  }                                            // end of loop over frequencies
+  // done with step 1: DD computation of prior and post SNR
+
+  // STEP 2: compute speech/noise likelihood
+
+  // compute difference of input spectrum with learned/estimated noise spectrum
+  WebRtcNsx_ComputeSpectralDifference(inst, magnU16);
+  // compute histograms for determination of parameters (thresholds and weights
+  // for features) parameters are extracted once every window time
+  // (=inst->modelUpdate) counter update
+  inst->cntThresUpdate++;
+  flag = (int)(inst->cntThresUpdate == inst->modelUpdate);
+  // update histogram
+  WebRtcNsx_FeatureParameterExtraction(inst, flag);
+  // compute model parameters
+  if (flag) {
+    inst->cntThresUpdate = 0;  // Reset counter
+    // update every window:
+    // get normalization for spectral difference for next window estimate
+
+    // Shift to Q(-2*stages)
+    inst->curAvgMagnEnergy >>= STAT_UPDATES;
+
+    tmpU32no1 = (inst->curAvgMagnEnergy + inst->timeAvgMagnEnergy + 1) >>
+                1;  // Q(-2*stages)
+    // Update featureSpecDiff
+    if ((tmpU32no1 != inst->timeAvgMagnEnergy) && (inst->featureSpecDiff) &&
+        (inst->timeAvgMagnEnergy > 0)) {
+      norm32no1 = 0;
+      tmpU32no3 = tmpU32no1;
+      while (0xFFFF0000 & tmpU32no3) {
+        tmpU32no3 >>= 1;
+        norm32no1++;
+      }
+      tmpU32no2 = inst->featureSpecDiff;
+      while (0xFFFF0000 & tmpU32no2) {
+        tmpU32no2 >>= 1;
+        norm32no1++;
+      }
+      tmpU32no3 = WEBRTC_SPL_UMUL(tmpU32no3, tmpU32no2);
+      tmpU32no3 /= inst->timeAvgMagnEnergy;
+      if (WebRtcSpl_NormU32(tmpU32no3) < norm32no1) {
+        inst->featureSpecDiff = 0x007FFFFF;
+      } else {
+        inst->featureSpecDiff =
+            WEBRTC_SPL_MIN(0x007FFFFF, tmpU32no3 << norm32no1);
+      }
+    }
+
+    inst->timeAvgMagnEnergy = tmpU32no1;  // Q(-2*stages)
+    inst->curAvgMagnEnergy = 0;
+  }
+
+  // compute speech/noise probability
+  WebRtcNsx_SpeechNoiseProb(inst, nonSpeechProbFinal, priorLocSnr, postLocSnr);
+
+  // time-avg parameter for noise update
+  gammaNoise = NOISE_UPDATE_Q8;  // Q8
+
+  maxNoiseU32 = 0;
+  postShifts = inst->prevQNoise - qMagn;
+  nShifts = inst->prevQMagn - qMagn;
+  for (i = 0; i < inst->magnLen; i++) {
+    // temporary noise update: use it for speech frames if update value is less
+    // than previous the formula has been rewritten into: noiseUpdate =
+    // noisePrev[i] + (1 - gammaNoise) * nonSpeechProb * (magn[i] -
+    // noisePrev[i])
+
+    if (postShifts < 0) {
+      tmpU32no2 = magnU16[i] >> -postShifts;  // Q(prevQNoise)
+    } else {
+      tmpU32no2 = (uint32_t)magnU16[i] << postShifts;  // Q(prevQNoise)
+    }
+    if (prevNoiseU16[i] > tmpU32no2) {
+      sign = -1;
+      tmpU32no1 = prevNoiseU16[i] - tmpU32no2;
+    } else {
+      sign = 1;
+      tmpU32no1 = tmpU32no2 - prevNoiseU16[i];
+    }
+    noiseUpdateU32 = inst->prevNoiseU32[i];  // Q(prevQNoise+11)
+    tmpU32no3 = 0;
+    if ((tmpU32no1) && (nonSpeechProbFinal[i])) {
+      // This value will be used later, if gammaNoise changes
+      tmpU32no3 = WEBRTC_SPL_UMUL_32_16(
+          tmpU32no1, nonSpeechProbFinal[i]);  // Q(prevQNoise+8)
+      if (0x7c000000 & tmpU32no3) {
+        // Shifting required before multiplication
+        tmpU32no2 = (tmpU32no3 >> 5) * gammaNoise;  // Q(prevQNoise+11)
+      } else {
+        // We can do shifting after multiplication
+        tmpU32no2 = (tmpU32no3 * gammaNoise) >> 5;  // Q(prevQNoise+11)
+      }
+      if (sign > 0) {
+        noiseUpdateU32 += tmpU32no2;  // Q(prevQNoise+11)
+      } else {
+        // This operation is safe. We can never get wrap around, since worst
+        // case scenario means magnU16 = 0
+        noiseUpdateU32 -= tmpU32no2;  // Q(prevQNoise+11)
+      }
+    }
+
+    // increase gamma (i.e., less noise update) for frame likely to be speech
+    prevGammaNoise = gammaNoise;
+    gammaNoise = NOISE_UPDATE_Q8;
+    // time-constant based on speech/noise state
+    // increase gamma (i.e., less noise update) for frames likely to be speech
+    if (nonSpeechProbFinal[i] < ONE_MINUS_PROB_RANGE_Q8) {
+      gammaNoise = GAMMA_NOISE_TRANS_AND_SPEECH_Q8;
+    }
+
+    if (prevGammaNoise != gammaNoise) {
+      // new noise update
+      // this line is the same as above, only that the result is stored in a
+      // different variable and the gammaNoise has changed
+      //
+      // noiseUpdate = noisePrev[i] + (1 - gammaNoise) * nonSpeechProb *
+      // (magn[i] - noisePrev[i])
+
+      if (0x7c000000 & tmpU32no3) {
+        // Shifting required before multiplication
+        tmpU32no2 = (tmpU32no3 >> 5) * gammaNoise;  // Q(prevQNoise+11)
+      } else {
+        // We can do shifting after multiplication
+        tmpU32no2 = (tmpU32no3 * gammaNoise) >> 5;  // Q(prevQNoise+11)
+      }
+      if (sign > 0) {
+        tmpU32no1 = inst->prevNoiseU32[i] + tmpU32no2;  // Q(prevQNoise+11)
+      } else {
+        tmpU32no1 = inst->prevNoiseU32[i] - tmpU32no2;  // Q(prevQNoise+11)
+      }
+      if (noiseUpdateU32 > tmpU32no1) {
+        noiseUpdateU32 = tmpU32no1;  // Q(prevQNoise+11)
+      }
+    }
+    noiseU32[i] = noiseUpdateU32;  // Q(prevQNoise+11)
+    if (noiseUpdateU32 > maxNoiseU32) {
+      maxNoiseU32 = noiseUpdateU32;
+    }
+
+    // conservative noise update
+    // // original FLOAT code
+    // if (prob_speech < PROB_RANGE) {
+    // inst->avgMagnPause[i] = inst->avgMagnPause[i] + (1.0 -
+    // gamma_pause)*(magn[i] - inst->avgMagnPause[i]);
+    // }
+
+    tmp32no2 = WEBRTC_SPL_SHIFT_W32(inst->avgMagnPause[i], -nShifts);
+    if (nonSpeechProbFinal[i] > ONE_MINUS_PROB_RANGE_Q8) {
+      if (nShifts < 0) {
+        tmp32no1 = (int32_t)magnU16[i] - tmp32no2;  // Q(qMagn)
+        tmp32no1 *= ONE_MINUS_GAMMA_PAUSE_Q8;       // Q(8+prevQMagn+nShifts)
+        tmp32no1 = (tmp32no1 + 128) >> 8;           // Q(qMagn).
+      } else {
+        // In Q(qMagn+nShifts)
+        tmp32no1 = ((int32_t)magnU16[i] << nShifts) - inst->avgMagnPause[i];
+        tmp32no1 *= ONE_MINUS_GAMMA_PAUSE_Q8;  // Q(8+prevQMagn+nShifts)
+        tmp32no1 = (tmp32no1 + (128 << nShifts)) >> (8 + nShifts);  // Q(qMagn).
+      }
+      tmp32no2 += tmp32no1;  // Q(qMagn)
+    }
+    inst->avgMagnPause[i] = tmp32no2;
+  }  // end of frequency loop
+
+  norm32no1 = WebRtcSpl_NormU32(maxNoiseU32);
+  qNoise = inst->prevQNoise + norm32no1 - 5;
+  // done with step 2: noise update
+
+  // STEP 3: compute dd update of prior snr and post snr based on new noise
+  // estimate
+  nShifts = inst->prevQNoise + 11 - qMagn;
+  for (i = 0; i < inst->magnLen; i++) {
+    // FLOAT code
+    // // post and prior SNR
+    // curNearSnr = 0.0;
+    // if (magn[i] > noise[i])
+    // {
+    // curNearSnr = magn[i] / (noise[i] + 0.0001) - 1.0;
+    // }
+    // // DD estimate is sum of two terms: current estimate and previous
+    // estimate
+    // // directed decision update of snrPrior
+    // snrPrior = DD_PR_SNR * prevNearSnr[i] + (1.0 - DD_PR_SNR) * curNearSnr;
+    // // gain filter
+    // tmpFloat1 = inst->overdrive + snrPrior;
+    // tmpFloat2 = snrPrior / tmpFloat1;
+    // theFilter[i] = tmpFloat2;
+
+    // calculate curNearSnr again, this is necessary because a new noise
+    // estimate has been made since then. for the original
+    curNearSnr = 0;  // Q11
+    if (nShifts < 0) {
+      // This case is equivalent with magn < noise which implies curNearSnr = 0;
+      tmpMagnU32 = (uint32_t)magnU16[i];      // Q(qMagn)
+      tmpNoiseU32 = noiseU32[i] << -nShifts;  // Q(qMagn)
+    } else if (nShifts > 17) {
+      tmpMagnU32 = (uint32_t)magnU16[i] << 17;      // Q(qMagn+17)
+      tmpNoiseU32 = noiseU32[i] >> (nShifts - 17);  // Q(qMagn+17)
+    } else {
+      tmpMagnU32 = (uint32_t)magnU16[i] << nShifts;  // Q(qNoise_prev+11)
+      tmpNoiseU32 = noiseU32[i];                     // Q(qNoise_prev+11)
+    }
+    if (tmpMagnU32 > tmpNoiseU32) {
+      tmpU32no1 = tmpMagnU32 - tmpNoiseU32;  // Q(qCur)
+      norm32no2 = WEBRTC_SPL_MIN(11, WebRtcSpl_NormU32(tmpU32no1));
+      tmpU32no1 <<= norm32no2;                      // Q(qCur+norm32no2)
+      tmpU32no2 = tmpNoiseU32 >> (11 - norm32no2);  // Q(qCur+norm32no2-11)
+      if (tmpU32no2 > 0) {
+        tmpU32no1 /= tmpU32no2;  // Q11
+      }
+      curNearSnr = WEBRTC_SPL_MIN(satMax, tmpU32no1);  // Q11
+    }
+
+    // directed decision update of priorSnr
+    // FLOAT
+    // priorSnr = DD_PR_SNR * prevNearSnr + (1.0-DD_PR_SNR) * curNearSnr;
+
+    tmpU32no1 = WEBRTC_SPL_UMUL_32_16(prevNearSnr[i], DD_PR_SNR_Q11);  // Q22
+    tmpU32no2 =
+        WEBRTC_SPL_UMUL_32_16(curNearSnr, ONE_MINUS_DD_PR_SNR_Q11);  // Q22
+    priorSnr = tmpU32no1 + tmpU32no2;                                // Q22
+
+    // gain filter
+    tmpU32no1 = inst->overdrive + ((priorSnr + 8192) >> 14);  // Q8
+    RTC_DCHECK_GT(inst->overdrive, 0);
+    tmpU16no1 = (priorSnr + tmpU32no1 / 2) / tmpU32no1;  // Q14
+    inst->noiseSupFilter[i] = WEBRTC_SPL_SAT(
+        16384, tmpU16no1, inst->denoiseBound);  // 16384 = Q14(1.0) // Q14
+
+    // Weight in the parametric Wiener filter during startup
+    if (inst->blockIndex < END_STARTUP_SHORT) {
+      // Weight the two suppression filters
+      tmpU32no1 = inst->noiseSupFilter[i] * inst->blockIndex;
+      tmpU32no2 = noiseSupFilterTmp[i] * (END_STARTUP_SHORT - inst->blockIndex);
+      tmpU32no1 += tmpU32no2;
+      inst->noiseSupFilter[i] =
+          (uint16_t)WebRtcSpl_DivU32U16(tmpU32no1, END_STARTUP_SHORT);
+    }
+  }  // end of loop over frequencies
+  // done with step3
+
+  // save noise and magnitude spectrum for next frame
+  inst->prevQNoise = qNoise;
+  inst->prevQMagn = qMagn;
+  if (norm32no1 > 5) {
+    for (i = 0; i < inst->magnLen; i++) {
+      inst->prevNoiseU32[i] = noiseU32[i] << (norm32no1 - 5);  // Q(qNoise+11)
+      inst->prevMagnU16[i] = magnU16[i];                       // Q(qMagn)
+    }
+  } else {
+    for (i = 0; i < inst->magnLen; i++) {
+      inst->prevNoiseU32[i] = noiseU32[i] >> (5 - norm32no1);  // Q(qNoise+11)
+      inst->prevMagnU16[i] = magnU16[i];                       // Q(qMagn)
+    }
+  }
+
+  WebRtcNsx_DataSynthesis(inst, outFrame[0]);
+#ifdef NS_FILEDEBUG
+  if (fwrite(outframe, sizeof(short), inst->blockLen10ms, inst->outfile) !=
+      inst->blockLen10ms) {
+    RTC_NOTREACHED();
+  }
+#endif
+
+  // for H band:
+  // only update data buffer, then apply time-domain gain is applied derived
+  // from L band
+  if (num_bands > 1) {
+    // update analysis buffer for H band
+    // append new data to buffer FX
+    for (i = 0; i < num_high_bands; ++i) {
+      memcpy(
+          inst->dataBufHBFX[i], inst->dataBufHBFX[i] + inst->blockLen10ms,
+          (inst->anaLen - inst->blockLen10ms) * sizeof(*inst->dataBufHBFX[i]));
+      memcpy(inst->dataBufHBFX[i] + inst->anaLen - inst->blockLen10ms,
+             speechFrameHB[i],
+             inst->blockLen10ms * sizeof(*inst->dataBufHBFX[i]));
+    }
+    // range for averaging low band quantities for H band gain
+
+    gainTimeDomainHB = 16384;  // 16384 = Q14(1.0)
+    // average speech prob from low band
+    // average filter gain from low band
+    // avg over second half (i.e., 4->8kHz) of freq. spectrum
+    tmpU32no1 = 0;  // Q12
+    tmpU16no1 = 0;  // Q8
+    for (i = inst->anaLen2 - (inst->anaLen2 >> 2); i < inst->anaLen2; i++) {
+      tmpU16no1 += nonSpeechProbFinal[i];                // Q8
+      tmpU32no1 += (uint32_t)(inst->noiseSupFilter[i]);  // Q14
+    }
+    RTC_DCHECK_GE(inst->stages, 7);
+    avgProbSpeechHB = (4096 - (tmpU16no1 >> (inst->stages - 7)));  // Q12
+    avgFilterGainHB = (int16_t)(tmpU32no1 >> (inst->stages - 3));  // Q14
+
+    // // original FLOAT code
+    // // gain based on speech probability:
+    // avg_prob_speech_tt=(float)2.0*avg_prob_speech-(float)1.0;
+    // gain_mod=(float)0.5*((float)1.0+(float)tanh(avg_prob_speech_tt)); //
+    // between 0 and 1
+
+    // gain based on speech probability:
+    // original expression: "0.5 * (1 + tanh(2x-1))"
+    // avgProbSpeechHB has been anyway saturated to a value between 0 and 1 so
+    // the other cases don't have to be dealt with avgProbSpeechHB and gainModHB
+    // are in Q12, 3607 = Q12(0.880615234375) which is a zero point of |0.5 * (1
+    // + tanh(2x-1)) - x| - |0.5 * (1 + tanh(2x-1)) - 0.880615234375| meaning
+    // that from that point the error of approximating the expression with f(x)
+    // = x would be greater than the error of approximating the expression with
+    // f(x) = 0.880615234375 error: "|0.5 * (1 + tanh(2x-1)) - x| from x=0 to
+    // 0.880615234375" ->
+    // http://www.wolframalpha.com/input/?i=|0.5+*+(1+%2B+tanh(2x-1))+-+x|+from+x%3D0+to+0.880615234375
+    // and:  "|0.5 * (1 + tanh(2x-1)) - 0.880615234375| from x=0.880615234375 to
+    // 1" ->
+    // http://www.wolframalpha.com/input/?i=+|0.5+*+(1+%2B+tanh(2x-1))+-+0.880615234375|+from+x%3D0.880615234375+to+1
+    gainModHB = WEBRTC_SPL_MIN(avgProbSpeechHB, 3607);
+
+    // // original FLOAT code
+    // //combine gain with low band gain
+    // if (avg_prob_speech < (float)0.5) {
+    // gain_time_domain_HB=(float)0.5*gain_mod+(float)0.5*avg_filter_gain;
+    // }
+    // else {
+    // gain_time_domain_HB=(float)0.25*gain_mod+(float)0.75*avg_filter_gain;
+    // }
+
+    // combine gain with low band gain
+    if (avgProbSpeechHB < 2048) {
+      // 2048 = Q12(0.5)
+      // the next two lines in float are  "gain_time_domain = 0.5 * gain_mod +
+      // 0.5 * avg_filter_gain"; Q2(0.5) = 2 equals one left shift
+      gainTimeDomainHB = (gainModHB << 1) + (avgFilterGainHB >> 1);  // Q14
+    } else {
+      // "gain_time_domain = 0.25 * gain_mod + 0.75 * agv_filter_gain;"
+      gainTimeDomainHB = (int16_t)((3 * avgFilterGainHB) >> 2);  // 3 = Q2(0.75)
+      gainTimeDomainHB += gainModHB;                             // Q14
+    }
+    // make sure gain is within flooring range
+    gainTimeDomainHB =
+        WEBRTC_SPL_SAT(16384, gainTimeDomainHB,
+                       (int16_t)(inst->denoiseBound));  // 16384 = Q14(1.0)
+
+    // apply gain
+    for (i = 0; i < num_high_bands; ++i) {
+      for (j = 0; j < inst->blockLen10ms; j++) {
+        outFrameHB[i][j] = (int16_t)(
+            (gainTimeDomainHB * inst->dataBufHBFX[i][j]) >> 14);  // Q0
+      }
+    }
+  }  // end of H band gain computation
+}
diff --git a/modules/audio_processing/legacy_ns/nsx_core.h b/modules/audio_processing/legacy_ns/nsx_core.h
new file mode 100644
index 0000000..a3f224a
--- /dev/null
+++ b/modules/audio_processing/legacy_ns/nsx_core.h
@@ -0,0 +1,261 @@
+/*
+ *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_AUDIO_PROCESSING_LEGACY_NS_NSX_CORE_H_
+#define MODULES_AUDIO_PROCESSING_LEGACY_NS_NSX_CORE_H_
+
+#ifdef NS_FILEDEBUG
+#include <stdio.h>
+#endif
+
+#include "common_audio/signal_processing/include/signal_processing_library.h"
+#include "modules/audio_processing/legacy_ns/nsx_defines.h"
+
+typedef struct NoiseSuppressionFixedC_ {
+  uint32_t fs;
+
+  const int16_t* window;
+  int16_t analysisBuffer[ANAL_BLOCKL_MAX];
+  int16_t synthesisBuffer[ANAL_BLOCKL_MAX];
+  uint16_t noiseSupFilter[HALF_ANAL_BLOCKL];
+  uint16_t overdrive;    /* Q8 */
+  uint16_t denoiseBound; /* Q14 */
+  const int16_t* factor2Table;
+  int16_t noiseEstLogQuantile[SIMULT * HALF_ANAL_BLOCKL];
+  int16_t noiseEstDensity[SIMULT * HALF_ANAL_BLOCKL];
+  int16_t noiseEstCounter[SIMULT];
+  int16_t noiseEstQuantile[HALF_ANAL_BLOCKL];
+
+  size_t anaLen;
+  size_t anaLen2;
+  size_t magnLen;
+  int aggrMode;
+  int stages;
+  int initFlag;
+  int gainMap;
+
+  int32_t maxLrt;
+  int32_t minLrt;
+  // Log LRT factor with time-smoothing in Q8.
+  int32_t logLrtTimeAvgW32[HALF_ANAL_BLOCKL];
+  int32_t featureLogLrt;
+  int32_t thresholdLogLrt;
+  int16_t weightLogLrt;
+
+  uint32_t featureSpecDiff;
+  uint32_t thresholdSpecDiff;
+  int16_t weightSpecDiff;
+
+  uint32_t featureSpecFlat;
+  uint32_t thresholdSpecFlat;
+  int16_t weightSpecFlat;
+
+  // Conservative estimate of noise spectrum.
+  int32_t avgMagnPause[HALF_ANAL_BLOCKL];
+  uint32_t magnEnergy;
+  uint32_t sumMagn;
+  uint32_t curAvgMagnEnergy;
+  uint32_t timeAvgMagnEnergy;
+  uint32_t timeAvgMagnEnergyTmp;
+
+  uint32_t whiteNoiseLevel;  // Initial noise estimate.
+  // Initial magnitude spectrum estimate.
+  uint32_t initMagnEst[HALF_ANAL_BLOCKL];
+  // Pink noise parameters:
+  int32_t pinkNoiseNumerator;  // Numerator.
+  int32_t pinkNoiseExp;        // Power of freq.
+  int minNorm;                 // Smallest normalization factor.
+  int zeroInputSignal;         // Zero input signal flag.
+
+  // Noise spectrum from previous frame.
+  uint32_t prevNoiseU32[HALF_ANAL_BLOCKL];
+  // Magnitude spectrum from previous frame.
+  uint16_t prevMagnU16[HALF_ANAL_BLOCKL];
+  // Prior speech/noise probability in Q14.
+  int16_t priorNonSpeechProb;
+
+  int blockIndex;  // Frame index counter.
+  // Parameter for updating or estimating thresholds/weights for prior model.
+  int modelUpdate;
+  int cntThresUpdate;
+
+  // Histograms for parameter estimation.
+  int16_t histLrt[HIST_PAR_EST];
+  int16_t histSpecFlat[HIST_PAR_EST];
+  int16_t histSpecDiff[HIST_PAR_EST];
+
+  // Quantities for high band estimate.
+  int16_t dataBufHBFX[NUM_HIGH_BANDS_MAX][ANAL_BLOCKL_MAX];
+
+  int qNoise;
+  int prevQNoise;
+  int prevQMagn;
+  size_t blockLen10ms;
+
+  int16_t real[ANAL_BLOCKL_MAX];
+  int16_t imag[ANAL_BLOCKL_MAX];
+  int32_t energyIn;
+  int scaleEnergyIn;
+  int normData;
+
+  struct RealFFT* real_fft;
+} NoiseSuppressionFixedC;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/****************************************************************************
+ * WebRtcNsx_InitCore(...)
+ *
+ * This function initializes a noise suppression instance
+ *
+ * Input:
+ *      - inst          : Instance that should be initialized
+ *      - fs            : Sampling frequency
+ *
+ * Output:
+ *      - inst          : Initialized instance
+ *
+ * Return value         :  0 - Ok
+ *                        -1 - Error
+ */
+int32_t WebRtcNsx_InitCore(NoiseSuppressionFixedC* inst, uint32_t fs);
+
+/****************************************************************************
+ * WebRtcNsx_set_policy_core(...)
+ *
+ * This changes the aggressiveness of the noise suppression method.
+ *
+ * Input:
+ *      - inst       : Instance that should be initialized
+ *      - mode       : 0: Mild (6 dB), 1: Medium (10 dB), 2: Aggressive (15 dB)
+ *
+ * Output:
+ *      - inst       : Initialized instance
+ *
+ * Return value      :  0 - Ok
+ *                     -1 - Error
+ */
+int WebRtcNsx_set_policy_core(NoiseSuppressionFixedC* inst, int mode);
+
+/****************************************************************************
+ * WebRtcNsx_ProcessCore
+ *
+ * Do noise suppression.
+ *
+ * Input:
+ *      - inst          : Instance that should be initialized
+ *      - inFrame       : Input speech frame for each band
+ *      - num_bands     : Number of bands
+ *
+ * Output:
+ *      - inst          : Updated instance
+ *      - outFrame      : Output speech frame for each band
+ */
+void WebRtcNsx_ProcessCore(NoiseSuppressionFixedC* inst,
+                           const int16_t* const* inFrame,
+                           int num_bands,
+                           int16_t* const* outFrame);
+
+/****************************************************************************
+ * Some function pointers, for internal functions shared by ARM NEON and
+ * generic C code.
+ */
+// Noise Estimation.
+typedef void (*NoiseEstimation)(NoiseSuppressionFixedC* inst,
+                                uint16_t* magn,
+                                uint32_t* noise,
+                                int16_t* q_noise);
+extern NoiseEstimation WebRtcNsx_NoiseEstimation;
+
+// Filter the data in the frequency domain, and create spectrum.
+typedef void (*PrepareSpectrum)(NoiseSuppressionFixedC* inst,
+                                int16_t* freq_buff);
+extern PrepareSpectrum WebRtcNsx_PrepareSpectrum;
+
+// For the noise supression process, synthesis, read out fully processed
+// segment, and update synthesis buffer.
+typedef void (*SynthesisUpdate)(NoiseSuppressionFixedC* inst,
+                                int16_t* out_frame,
+                                int16_t gain_factor);
+extern SynthesisUpdate WebRtcNsx_SynthesisUpdate;
+
+// Update analysis buffer for lower band, and window data before FFT.
+typedef void (*AnalysisUpdate)(NoiseSuppressionFixedC* inst,
+                               int16_t* out,
+                               int16_t* new_speech);
+extern AnalysisUpdate WebRtcNsx_AnalysisUpdate;
+
+// Denormalize the real-valued signal |in|, the output from inverse FFT.
+typedef void (*Denormalize)(NoiseSuppressionFixedC* inst,
+                            int16_t* in,
+                            int factor);
+extern Denormalize WebRtcNsx_Denormalize;
+
+// Normalize the real-valued signal |in|, the input to forward FFT.
+typedef void (*NormalizeRealBuffer)(NoiseSuppressionFixedC* inst,
+                                    const int16_t* in,
+                                    int16_t* out);
+extern NormalizeRealBuffer WebRtcNsx_NormalizeRealBuffer;
+
+// Compute speech/noise probability.
+// Intended to be private.
+void WebRtcNsx_SpeechNoiseProb(NoiseSuppressionFixedC* inst,
+                               uint16_t* nonSpeechProbFinal,
+                               uint32_t* priorLocSnr,
+                               uint32_t* postLocSnr);
+
+#if defined(WEBRTC_HAS_NEON)
+// For the above function pointers, functions for generic platforms are declared
+// and defined as static in file nsx_core.c, while those for ARM Neon platforms
+// are declared below and defined in file nsx_core_neon.c.
+void WebRtcNsx_NoiseEstimationNeon(NoiseSuppressionFixedC* inst,
+                                   uint16_t* magn,
+                                   uint32_t* noise,
+                                   int16_t* q_noise);
+void WebRtcNsx_SynthesisUpdateNeon(NoiseSuppressionFixedC* inst,
+                                   int16_t* out_frame,
+                                   int16_t gain_factor);
+void WebRtcNsx_AnalysisUpdateNeon(NoiseSuppressionFixedC* inst,
+                                  int16_t* out,
+                                  int16_t* new_speech);
+void WebRtcNsx_PrepareSpectrumNeon(NoiseSuppressionFixedC* inst,
+                                   int16_t* freq_buff);
+#endif
+
+#if defined(MIPS32_LE)
+// For the above function pointers, functions for generic platforms are declared
+// and defined as static in file nsx_core.c, while those for MIPS platforms
+// are declared below and defined in file nsx_core_mips.c.
+void WebRtcNsx_SynthesisUpdate_mips(NoiseSuppressionFixedC* inst,
+                                    int16_t* out_frame,
+                                    int16_t gain_factor);
+void WebRtcNsx_AnalysisUpdate_mips(NoiseSuppressionFixedC* inst,
+                                   int16_t* out,
+                                   int16_t* new_speech);
+void WebRtcNsx_PrepareSpectrum_mips(NoiseSuppressionFixedC* inst,
+                                    int16_t* freq_buff);
+void WebRtcNsx_NormalizeRealBuffer_mips(NoiseSuppressionFixedC* inst,
+                                        const int16_t* in,
+                                        int16_t* out);
+#if defined(MIPS_DSP_R1_LE)
+void WebRtcNsx_Denormalize_mips(NoiseSuppressionFixedC* inst,
+                                int16_t* in,
+                                int factor);
+#endif
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // MODULES_AUDIO_PROCESSING_LEGACY_NS_NSX_CORE_H_
diff --git a/modules/audio_processing/legacy_ns/nsx_core_c.c b/modules/audio_processing/legacy_ns/nsx_core_c.c
new file mode 100644
index 0000000..2b0bb2f
--- /dev/null
+++ b/modules/audio_processing/legacy_ns/nsx_core_c.c
@@ -0,0 +1,259 @@
+/*
+ *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "rtc_base/checks.h"
+#include "modules/audio_processing/legacy_ns/noise_suppression_x.h"
+#include "modules/audio_processing/legacy_ns/nsx_core.h"
+#include "modules/audio_processing/legacy_ns/nsx_defines.h"
+
+static const int16_t kIndicatorTable[17] = {
+  0, 2017, 3809, 5227, 6258, 6963, 7424, 7718,
+  7901, 8014, 8084, 8126, 8152, 8168, 8177, 8183, 8187
+};
+
+// Compute speech/noise probability
+// speech/noise probability is returned in: probSpeechFinal
+//snrLocPrior is the prior SNR for each frequency (in Q11)
+//snrLocPost is the post SNR for each frequency (in Q11)
+void WebRtcNsx_SpeechNoiseProb(NoiseSuppressionFixedC* inst,
+                               uint16_t* nonSpeechProbFinal,
+                               uint32_t* priorLocSnr,
+                               uint32_t* postLocSnr) {
+  uint32_t zeros, num, den, tmpU32no1, tmpU32no2, tmpU32no3;
+  int32_t invLrtFX, indPriorFX, tmp32, tmp32no1, tmp32no2, besselTmpFX32;
+  int32_t frac32, logTmp;
+  int32_t logLrtTimeAvgKsumFX;
+  int16_t indPriorFX16;
+  int16_t tmp16, tmp16no1, tmp16no2, tmpIndFX, tableIndex, frac, intPart;
+  size_t i;
+  int normTmp, normTmp2, nShifts;
+
+  // compute feature based on average LR factor
+  // this is the average over all frequencies of the smooth log LRT
+  logLrtTimeAvgKsumFX = 0;
+  for (i = 0; i < inst->magnLen; i++) {
+    besselTmpFX32 = (int32_t)postLocSnr[i]; // Q11
+    normTmp = WebRtcSpl_NormU32(postLocSnr[i]);
+    num = postLocSnr[i] << normTmp;  // Q(11+normTmp)
+    if (normTmp > 10) {
+      den = priorLocSnr[i] << (normTmp - 11);  // Q(normTmp)
+    } else {
+      den = priorLocSnr[i] >> (11 - normTmp);  // Q(normTmp)
+    }
+    if (den > 0) {
+      besselTmpFX32 -= num / den;  // Q11
+    } else {
+      besselTmpFX32 = 0;
+    }
+
+    // inst->logLrtTimeAvg[i] += LRT_TAVG * (besselTmp - log(snrLocPrior)
+    //                                       - inst->logLrtTimeAvg[i]);
+    // Here, LRT_TAVG = 0.5
+    zeros = WebRtcSpl_NormU32(priorLocSnr[i]);
+    frac32 = (int32_t)(((priorLocSnr[i] << zeros) & 0x7FFFFFFF) >> 19);
+    tmp32 = (frac32 * frac32 * -43) >> 19;
+    tmp32 += ((int16_t)frac32 * 5412) >> 12;
+    frac32 = tmp32 + 37;
+    // tmp32 = log2(priorLocSnr[i])
+    tmp32 = (int32_t)(((31 - zeros) << 12) + frac32) - (11 << 12); // Q12
+    logTmp = (tmp32 * 178) >> 8;  // log2(priorLocSnr[i])*log(2)
+    // tmp32no1 = LRT_TAVG * (log(snrLocPrior) + inst->logLrtTimeAvg[i]) in Q12.
+    tmp32no1 = (logTmp + inst->logLrtTimeAvgW32[i]) / 2;
+    inst->logLrtTimeAvgW32[i] += (besselTmpFX32 - tmp32no1); // Q12
+
+    logLrtTimeAvgKsumFX += inst->logLrtTimeAvgW32[i]; // Q12
+  }
+  inst->featureLogLrt = (logLrtTimeAvgKsumFX * BIN_SIZE_LRT) >>
+      (inst->stages + 11);
+
+  // done with computation of LR factor
+
+  //
+  //compute the indicator functions
+  //
+
+  // average LRT feature
+  // FLOAT code
+  // indicator0 = 0.5 * (tanh(widthPrior *
+  //                      (logLrtTimeAvgKsum - threshPrior0)) + 1.0);
+  tmpIndFX = 16384; // Q14(1.0)
+  tmp32no1 = logLrtTimeAvgKsumFX - inst->thresholdLogLrt; // Q12
+  nShifts = 7 - inst->stages; // WIDTH_PR_MAP_SHIFT - inst->stages + 5;
+  //use larger width in tanh map for pause regions
+  if (tmp32no1 < 0) {
+    tmpIndFX = 0;
+    tmp32no1 = -tmp32no1;
+    //widthPrior = widthPrior * 2.0;
+    nShifts++;
+  }
+  tmp32no1 = WEBRTC_SPL_SHIFT_W32(tmp32no1, nShifts); // Q14
+  // compute indicator function: sigmoid map
+  if (tmp32no1 < (16 << 14) && tmp32no1 >= 0) {
+    tableIndex = (int16_t)(tmp32no1 >> 14);
+    tmp16no2 = kIndicatorTable[tableIndex];
+    tmp16no1 = kIndicatorTable[tableIndex + 1] - kIndicatorTable[tableIndex];
+    frac = (int16_t)(tmp32no1 & 0x00003fff); // Q14
+    tmp16no2 += (int16_t)((tmp16no1 * frac) >> 14);
+    if (tmpIndFX == 0) {
+      tmpIndFX = 8192 - tmp16no2; // Q14
+    } else {
+      tmpIndFX = 8192 + tmp16no2; // Q14
+    }
+  }
+  indPriorFX = inst->weightLogLrt * tmpIndFX;  // 6*Q14
+
+  //spectral flatness feature
+  if (inst->weightSpecFlat) {
+    tmpU32no1 = WEBRTC_SPL_UMUL(inst->featureSpecFlat, 400); // Q10
+    tmpIndFX = 16384; // Q14(1.0)
+    //use larger width in tanh map for pause regions
+    tmpU32no2 = inst->thresholdSpecFlat - tmpU32no1; //Q10
+    nShifts = 4;
+    if (inst->thresholdSpecFlat < tmpU32no1) {
+      tmpIndFX = 0;
+      tmpU32no2 = tmpU32no1 - inst->thresholdSpecFlat;
+      //widthPrior = widthPrior * 2.0;
+      nShifts++;
+    }
+    tmpU32no1 = WebRtcSpl_DivU32U16(tmpU32no2 << nShifts, 25);  // Q14
+    // compute indicator function: sigmoid map
+    // FLOAT code
+    // indicator1 = 0.5 * (tanh(sgnMap * widthPrior *
+    //                          (threshPrior1 - tmpFloat1)) + 1.0);
+    if (tmpU32no1 < (16 << 14)) {
+      tableIndex = (int16_t)(tmpU32no1 >> 14);
+      tmp16no2 = kIndicatorTable[tableIndex];
+      tmp16no1 = kIndicatorTable[tableIndex + 1] - kIndicatorTable[tableIndex];
+      frac = (int16_t)(tmpU32no1 & 0x00003fff); // Q14
+      tmp16no2 += (int16_t)((tmp16no1 * frac) >> 14);
+      if (tmpIndFX) {
+        tmpIndFX = 8192 + tmp16no2; // Q14
+      } else {
+        tmpIndFX = 8192 - tmp16no2; // Q14
+      }
+    }
+    indPriorFX += inst->weightSpecFlat * tmpIndFX;  // 6*Q14
+  }
+
+  //for template spectral-difference
+  if (inst->weightSpecDiff) {
+    tmpU32no1 = 0;
+    if (inst->featureSpecDiff) {
+      normTmp = WEBRTC_SPL_MIN(20 - inst->stages,
+                               WebRtcSpl_NormU32(inst->featureSpecDiff));
+      RTC_DCHECK_GE(normTmp, 0);
+      tmpU32no1 = inst->featureSpecDiff << normTmp;  // Q(normTmp-2*stages)
+      tmpU32no2 = inst->timeAvgMagnEnergy >> (20 - inst->stages - normTmp);
+      if (tmpU32no2 > 0) {
+        // Q(20 - inst->stages)
+        tmpU32no1 /= tmpU32no2;
+      } else {
+        tmpU32no1 = (uint32_t)(0x7fffffff);
+      }
+    }
+    tmpU32no3 = (inst->thresholdSpecDiff << 17) / 25;
+    tmpU32no2 = tmpU32no1 - tmpU32no3;
+    nShifts = 1;
+    tmpIndFX = 16384; // Q14(1.0)
+    //use larger width in tanh map for pause regions
+    if (tmpU32no2 & 0x80000000) {
+      tmpIndFX = 0;
+      tmpU32no2 = tmpU32no3 - tmpU32no1;
+      //widthPrior = widthPrior * 2.0;
+      nShifts--;
+    }
+    tmpU32no1 = tmpU32no2 >> nShifts;
+    // compute indicator function: sigmoid map
+    /* FLOAT code
+     indicator2 = 0.5 * (tanh(widthPrior * (tmpFloat1 - threshPrior2)) + 1.0);
+     */
+    if (tmpU32no1 < (16 << 14)) {
+      tableIndex = (int16_t)(tmpU32no1 >> 14);
+      tmp16no2 = kIndicatorTable[tableIndex];
+      tmp16no1 = kIndicatorTable[tableIndex + 1] - kIndicatorTable[tableIndex];
+      frac = (int16_t)(tmpU32no1 & 0x00003fff); // Q14
+      tmp16no2 += (int16_t)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(
+                    tmp16no1, frac, 14);
+      if (tmpIndFX) {
+        tmpIndFX = 8192 + tmp16no2;
+      } else {
+        tmpIndFX = 8192 - tmp16no2;
+      }
+    }
+    indPriorFX += inst->weightSpecDiff * tmpIndFX;  // 6*Q14
+  }
+
+  //combine the indicator function with the feature weights
+  // FLOAT code
+  // indPrior = 1 - (weightIndPrior0 * indicator0 + weightIndPrior1 *
+  //                 indicator1 + weightIndPrior2 * indicator2);
+  indPriorFX16 = WebRtcSpl_DivW32W16ResW16(98307 - indPriorFX, 6); // Q14
+  // done with computing indicator function
+
+  //compute the prior probability
+  // FLOAT code
+  // inst->priorNonSpeechProb += PRIOR_UPDATE *
+  //                             (indPriorNonSpeech - inst->priorNonSpeechProb);
+  tmp16 = indPriorFX16 - inst->priorNonSpeechProb; // Q14
+  inst->priorNonSpeechProb += (int16_t)((PRIOR_UPDATE_Q14 * tmp16) >> 14);
+
+  //final speech probability: combine prior model with LR factor:
+
+  memset(nonSpeechProbFinal, 0, sizeof(uint16_t) * inst->magnLen);
+
+  if (inst->priorNonSpeechProb > 0) {
+    for (i = 0; i < inst->magnLen; i++) {
+      // FLOAT code
+      // invLrt = exp(inst->logLrtTimeAvg[i]);
+      // invLrt = inst->priorSpeechProb * invLrt;
+      // nonSpeechProbFinal[i] = (1.0 - inst->priorSpeechProb) /
+      //                         (1.0 - inst->priorSpeechProb + invLrt);
+      // invLrt = (1.0 - inst->priorNonSpeechProb) * invLrt;
+      // nonSpeechProbFinal[i] = inst->priorNonSpeechProb /
+      //                         (inst->priorNonSpeechProb + invLrt);
+      if (inst->logLrtTimeAvgW32[i] < 65300) {
+        tmp32no1 = (inst->logLrtTimeAvgW32[i] * 23637) >> 14;  // Q12
+        intPart = (int16_t)(tmp32no1 >> 12);
+        if (intPart < -8) {
+          intPart = -8;
+        }
+        frac = (int16_t)(tmp32no1 & 0x00000fff); // Q12
+
+        // Quadratic approximation of 2^frac
+        tmp32no2 = (frac * frac * 44) >> 19;  // Q12.
+        tmp32no2 += (frac * 84) >> 7;  // Q12
+        invLrtFX = (1 << (8 + intPart)) +
+            WEBRTC_SPL_SHIFT_W32(tmp32no2, intPart - 4); // Q8
+
+        normTmp = WebRtcSpl_NormW32(invLrtFX);
+        normTmp2 = WebRtcSpl_NormW16((16384 - inst->priorNonSpeechProb));
+        if (normTmp + normTmp2 >= 7) {
+          if (normTmp + normTmp2 < 15) {
+            invLrtFX >>= 15 - normTmp2 - normTmp;
+            // Q(normTmp+normTmp2-7)
+            tmp32no1 = invLrtFX * (16384 - inst->priorNonSpeechProb);
+            // Q(normTmp+normTmp2+7)
+            invLrtFX = WEBRTC_SPL_SHIFT_W32(tmp32no1, 7 - normTmp - normTmp2);
+                                                                  // Q14
+          } else {
+            tmp32no1 = invLrtFX * (16384 - inst->priorNonSpeechProb);
+                                                                  // Q22
+            invLrtFX = tmp32no1 >> 8;  // Q14.
+          }
+
+          tmp32no1 = (int32_t)inst->priorNonSpeechProb << 8;  // Q22
+
+          nonSpeechProbFinal[i] = tmp32no1 /
+              (inst->priorNonSpeechProb + invLrtFX);  // Q8
+        }
+      }
+    }
+  }
+}
diff --git a/modules/audio_processing/legacy_ns/nsx_core_mips.c b/modules/audio_processing/legacy_ns/nsx_core_mips.c
new file mode 100644
index 0000000..af4c287
--- /dev/null
+++ b/modules/audio_processing/legacy_ns/nsx_core_mips.c
@@ -0,0 +1,1001 @@
+/*
+ *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <string.h>
+
+#include "rtc_base/checks.h"
+#include "modules/audio_processing/legacy_ns/noise_suppression_x.h"
+#include "modules/audio_processing/legacy_ns/nsx_core.h"
+
+static const int16_t kIndicatorTable[17] = {
+  0, 2017, 3809, 5227, 6258, 6963, 7424, 7718,
+  7901, 8014, 8084, 8126, 8152, 8168, 8177, 8183, 8187
+};
+
+// Compute speech/noise probability
+// speech/noise probability is returned in: probSpeechFinal
+//snrLocPrior is the prior SNR for each frequency (in Q11)
+//snrLocPost is the post SNR for each frequency (in Q11)
+void WebRtcNsx_SpeechNoiseProb(NoiseSuppressionFixedC* inst,
+                               uint16_t* nonSpeechProbFinal,
+                               uint32_t* priorLocSnr,
+                               uint32_t* postLocSnr) {
+  uint32_t tmpU32no1, tmpU32no2, tmpU32no3;
+  int32_t indPriorFX, tmp32no1;
+  int32_t logLrtTimeAvgKsumFX;
+  int16_t indPriorFX16;
+  int16_t tmp16, tmp16no1, tmp16no2, tmpIndFX, tableIndex, frac;
+  size_t i;
+  int normTmp, nShifts;
+
+  int32_t r0, r1, r2, r3, r4, r5, r6, r7, r8, r9;
+  int32_t const_max = 0x7fffffff;
+  int32_t const_neg43 = -43;
+  int32_t const_5412 = 5412;
+  int32_t const_11rsh12 = (11 << 12);
+  int32_t const_178 = 178;
+
+
+  // compute feature based on average LR factor
+  // this is the average over all frequencies of the smooth log LRT
+  logLrtTimeAvgKsumFX = 0;
+  for (i = 0; i < inst->magnLen; i++) {
+    r0 = postLocSnr[i]; // Q11
+    r1 = priorLocSnr[i];
+    r2 = inst->logLrtTimeAvgW32[i];
+
+    __asm __volatile(
+      ".set       push                                    \n\t"
+      ".set       noreorder                               \n\t"
+      "clz        %[r3],    %[r0]                         \n\t"
+      "clz        %[r5],    %[r1]                         \n\t"
+      "slti       %[r4],    %[r3],    32                  \n\t"
+      "slti       %[r6],    %[r5],    32                  \n\t"
+      "movz       %[r3],    $0,       %[r4]               \n\t"
+      "movz       %[r5],    $0,       %[r6]               \n\t"
+      "slti       %[r4],    %[r3],    11                  \n\t"
+      "addiu      %[r6],    %[r3],    -11                 \n\t"
+      "neg        %[r7],    %[r6]                         \n\t"
+      "sllv       %[r6],    %[r1],    %[r6]               \n\t"
+      "srav       %[r7],    %[r1],    %[r7]               \n\t"
+      "movn       %[r6],    %[r7],    %[r4]               \n\t"
+      "sllv       %[r1],    %[r1],    %[r5]               \n\t"
+      "and        %[r1],    %[r1],    %[const_max]        \n\t"
+      "sra        %[r1],    %[r1],    19                  \n\t"
+      "mul        %[r7],    %[r1],    %[r1]               \n\t"
+      "sllv       %[r3],    %[r0],    %[r3]               \n\t"
+      "divu       %[r8],    %[r3],    %[r6]               \n\t"
+      "slti       %[r6],    %[r6],    1                   \n\t"
+      "mul        %[r7],    %[r7],    %[const_neg43]      \n\t"
+      "sra        %[r7],    %[r7],    19                  \n\t"
+      "movz       %[r3],    %[r8],    %[r6]               \n\t"
+      "subu       %[r0],    %[r0],    %[r3]               \n\t"
+      "movn       %[r0],    $0,       %[r6]               \n\t"
+      "mul        %[r1],    %[r1],    %[const_5412]       \n\t"
+      "sra        %[r1],    %[r1],    12                  \n\t"
+      "addu       %[r7],    %[r7],    %[r1]               \n\t"
+      "addiu      %[r1],    %[r7],    37                  \n\t"
+      "addiu      %[r5],    %[r5],    -31                 \n\t"
+      "neg        %[r5],    %[r5]                         \n\t"
+      "sll        %[r5],    %[r5],    12                  \n\t"
+      "addu       %[r5],    %[r5],    %[r1]               \n\t"
+      "subu       %[r7],    %[r5],    %[const_11rsh12]    \n\t"
+      "mul        %[r7],    %[r7],    %[const_178]        \n\t"
+      "sra        %[r7],    %[r7],    8                   \n\t"
+      "addu       %[r7],    %[r7],    %[r2]               \n\t"
+      "sra        %[r7],    %[r7],    1                   \n\t"
+      "subu       %[r2],    %[r2],    %[r7]               \n\t"
+      "addu       %[r2],    %[r2],    %[r0]               \n\t"
+      ".set       pop                                     \n\t"
+      : [r0] "+r" (r0), [r1] "+r" (r1), [r2] "+r" (r2),
+        [r3] "=&r" (r3), [r4] "=&r" (r4), [r5] "=&r" (r5),
+        [r6] "=&r" (r6), [r7] "=&r" (r7), [r8] "=&r" (r8)
+      : [const_max] "r" (const_max), [const_neg43] "r" (const_neg43),
+        [const_5412] "r" (const_5412), [const_11rsh12] "r" (const_11rsh12),
+        [const_178] "r" (const_178)
+      : "hi", "lo"
+    );
+    inst->logLrtTimeAvgW32[i] = r2;
+    logLrtTimeAvgKsumFX += r2;
+  }
+
+  inst->featureLogLrt = (logLrtTimeAvgKsumFX * BIN_SIZE_LRT) >>
+      (inst->stages + 11);
+
+  // done with computation of LR factor
+
+  //
+  // compute the indicator functions
+  //
+
+  // average LRT feature
+  // FLOAT code
+  // indicator0 = 0.5 * (tanh(widthPrior *
+  //                      (logLrtTimeAvgKsum - threshPrior0)) + 1.0);
+  tmpIndFX = 16384; // Q14(1.0)
+  tmp32no1 = logLrtTimeAvgKsumFX - inst->thresholdLogLrt; // Q12
+  nShifts = 7 - inst->stages; // WIDTH_PR_MAP_SHIFT - inst->stages + 5;
+  //use larger width in tanh map for pause regions
+  if (tmp32no1 < 0) {
+    tmpIndFX = 0;
+    tmp32no1 = -tmp32no1;
+    //widthPrior = widthPrior * 2.0;
+    nShifts++;
+  }
+  tmp32no1 = WEBRTC_SPL_SHIFT_W32(tmp32no1, nShifts); // Q14
+  // compute indicator function: sigmoid map
+  if (tmp32no1 < (16 << 14) && tmp32no1 >= 0) {
+    tableIndex = (int16_t)(tmp32no1 >> 14);
+    tmp16no2 = kIndicatorTable[tableIndex];
+    tmp16no1 = kIndicatorTable[tableIndex + 1] - kIndicatorTable[tableIndex];
+    frac = (int16_t)(tmp32no1 & 0x00003fff); // Q14
+    tmp16no2 += (int16_t)((tmp16no1 * frac) >> 14);
+    if (tmpIndFX == 0) {
+      tmpIndFX = 8192 - tmp16no2; // Q14
+    } else {
+      tmpIndFX = 8192 + tmp16no2; // Q14
+    }
+  }
+  indPriorFX = inst->weightLogLrt * tmpIndFX;  // 6*Q14
+
+  //spectral flatness feature
+  if (inst->weightSpecFlat) {
+    tmpU32no1 = WEBRTC_SPL_UMUL(inst->featureSpecFlat, 400); // Q10
+    tmpIndFX = 16384; // Q14(1.0)
+    //use larger width in tanh map for pause regions
+    tmpU32no2 = inst->thresholdSpecFlat - tmpU32no1; //Q10
+    nShifts = 4;
+    if (inst->thresholdSpecFlat < tmpU32no1) {
+      tmpIndFX = 0;
+      tmpU32no2 = tmpU32no1 - inst->thresholdSpecFlat;
+      //widthPrior = widthPrior * 2.0;
+      nShifts++;
+    }
+    tmpU32no1 = WebRtcSpl_DivU32U16(tmpU32no2 << nShifts, 25);  //Q14
+    // compute indicator function: sigmoid map
+    // FLOAT code
+    // indicator1 = 0.5 * (tanh(sgnMap * widthPrior *
+    //                          (threshPrior1 - tmpFloat1)) + 1.0);
+    if (tmpU32no1 < (16 << 14)) {
+      tableIndex = (int16_t)(tmpU32no1 >> 14);
+      tmp16no2 = kIndicatorTable[tableIndex];
+      tmp16no1 = kIndicatorTable[tableIndex + 1] - kIndicatorTable[tableIndex];
+      frac = (int16_t)(tmpU32no1 & 0x00003fff); // Q14
+      tmp16no2 += (int16_t)((tmp16no1 * frac) >> 14);
+      if (tmpIndFX) {
+        tmpIndFX = 8192 + tmp16no2; // Q14
+      } else {
+        tmpIndFX = 8192 - tmp16no2; // Q14
+      }
+    }
+    indPriorFX += inst->weightSpecFlat * tmpIndFX;  // 6*Q14
+  }
+
+  //for template spectral-difference
+  if (inst->weightSpecDiff) {
+    tmpU32no1 = 0;
+    if (inst->featureSpecDiff) {
+      normTmp = WEBRTC_SPL_MIN(20 - inst->stages,
+                               WebRtcSpl_NormU32(inst->featureSpecDiff));
+      RTC_DCHECK_GE(normTmp, 0);
+      tmpU32no1 = inst->featureSpecDiff << normTmp;  // Q(normTmp-2*stages)
+      tmpU32no2 = inst->timeAvgMagnEnergy >> (20 - inst->stages - normTmp);
+      if (tmpU32no2 > 0) {
+        // Q(20 - inst->stages)
+        tmpU32no1 /= tmpU32no2;
+      } else {
+        tmpU32no1 = (uint32_t)(0x7fffffff);
+      }
+    }
+    tmpU32no3 = (inst->thresholdSpecDiff << 17) / 25;
+    tmpU32no2 = tmpU32no1 - tmpU32no3;
+    nShifts = 1;
+    tmpIndFX = 16384; // Q14(1.0)
+    //use larger width in tanh map for pause regions
+    if (tmpU32no2 & 0x80000000) {
+      tmpIndFX = 0;
+      tmpU32no2 = tmpU32no3 - tmpU32no1;
+      //widthPrior = widthPrior * 2.0;
+      nShifts--;
+    }
+    tmpU32no1 = tmpU32no2 >> nShifts;
+    // compute indicator function: sigmoid map
+    /* FLOAT code
+     indicator2 = 0.5 * (tanh(widthPrior * (tmpFloat1 - threshPrior2)) + 1.0);
+     */
+    if (tmpU32no1 < (16 << 14)) {
+      tableIndex = (int16_t)(tmpU32no1 >> 14);
+      tmp16no2 = kIndicatorTable[tableIndex];
+      tmp16no1 = kIndicatorTable[tableIndex + 1] - kIndicatorTable[tableIndex];
+      frac = (int16_t)(tmpU32no1 & 0x00003fff); // Q14
+      tmp16no2 += (int16_t)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(
+                    tmp16no1, frac, 14);
+      if (tmpIndFX) {
+        tmpIndFX = 8192 + tmp16no2;
+      } else {
+        tmpIndFX = 8192 - tmp16no2;
+      }
+    }
+    indPriorFX += inst->weightSpecDiff * tmpIndFX;  // 6*Q14
+  }
+
+  //combine the indicator function with the feature weights
+  // FLOAT code
+  // indPrior = 1 - (weightIndPrior0 * indicator0 + weightIndPrior1 *
+  //                 indicator1 + weightIndPrior2 * indicator2);
+  indPriorFX16 = WebRtcSpl_DivW32W16ResW16(98307 - indPriorFX, 6); // Q14
+  // done with computing indicator function
+
+  //compute the prior probability
+  // FLOAT code
+  // inst->priorNonSpeechProb += PRIOR_UPDATE *
+  //                             (indPriorNonSpeech - inst->priorNonSpeechProb);
+  tmp16 = indPriorFX16 - inst->priorNonSpeechProb; // Q14
+  inst->priorNonSpeechProb += (int16_t)((PRIOR_UPDATE_Q14 * tmp16) >> 14);
+
+  //final speech probability: combine prior model with LR factor:
+
+  memset(nonSpeechProbFinal, 0, sizeof(uint16_t) * inst->magnLen);
+
+  if (inst->priorNonSpeechProb > 0) {
+    r0 = inst->priorNonSpeechProb;
+    r1 = 16384 - r0;
+    int32_t const_23637 = 23637;
+    int32_t const_44 = 44;
+    int32_t const_84 = 84;
+    int32_t const_1 = 1;
+    int32_t const_neg8 = -8;
+    for (i = 0; i < inst->magnLen; i++) {
+      r2 = inst->logLrtTimeAvgW32[i];
+      if (r2 < 65300) {
+        __asm __volatile(
+          ".set         push                                      \n\t"
+          ".set         noreorder                                 \n\t"
+          "mul          %[r2],    %[r2],          %[const_23637]  \n\t"
+          "sll          %[r6],    %[r1],          16              \n\t"
+          "clz          %[r7],    %[r6]                           \n\t"
+          "clo          %[r8],    %[r6]                           \n\t"
+          "slt          %[r9],    %[r6],          $0              \n\t"
+          "movn         %[r7],    %[r8],          %[r9]           \n\t"
+          "sra          %[r2],    %[r2],          14              \n\t"
+          "andi         %[r3],    %[r2],          0xfff           \n\t"
+          "mul          %[r4],    %[r3],          %[r3]           \n\t"
+          "mul          %[r3],    %[r3],          %[const_84]     \n\t"
+          "sra          %[r2],    %[r2],          12              \n\t"
+          "slt          %[r5],    %[r2],          %[const_neg8]   \n\t"
+          "movn         %[r2],    %[const_neg8],  %[r5]           \n\t"
+          "mul          %[r4],    %[r4],          %[const_44]     \n\t"
+          "sra          %[r3],    %[r3],          7               \n\t"
+          "addiu        %[r7],    %[r7],          -1              \n\t"
+          "slti         %[r9],    %[r7],          31              \n\t"
+          "movz         %[r7],    $0,             %[r9]           \n\t"
+          "sra          %[r4],    %[r4],          19              \n\t"
+          "addu         %[r4],    %[r4],          %[r3]           \n\t"
+          "addiu        %[r3],    %[r2],          8               \n\t"
+          "addiu        %[r2],    %[r2],          -4              \n\t"
+          "neg          %[r5],    %[r2]                           \n\t"
+          "sllv         %[r6],    %[r4],          %[r2]           \n\t"
+          "srav         %[r5],    %[r4],          %[r5]           \n\t"
+          "slt          %[r2],    %[r2],          $0              \n\t"
+          "movn         %[r6],    %[r5],          %[r2]           \n\t"
+          "sllv         %[r3],    %[const_1],     %[r3]           \n\t"
+          "addu         %[r2],    %[r3],          %[r6]           \n\t"
+          "clz          %[r4],    %[r2]                           \n\t"
+          "clo          %[r5],    %[r2]                           \n\t"
+          "slt          %[r8],    %[r2],          $0              \n\t"
+          "movn         %[r4],    %[r5],          %[r8]           \n\t"
+          "addiu        %[r4],    %[r4],          -1              \n\t"
+          "slt          %[r5],    $0,             %[r2]           \n\t"
+          "or           %[r5],    %[r5],          %[r7]           \n\t"
+          "movz         %[r4],    $0,             %[r5]           \n\t"
+          "addiu        %[r6],    %[r7],          -7              \n\t"
+          "addu         %[r6],    %[r6],          %[r4]           \n\t"
+          "bltz         %[r6],    1f                              \n\t"
+          " nop                                                   \n\t"
+          "addiu        %[r4],    %[r6],          -8              \n\t"
+          "neg          %[r3],    %[r4]                           \n\t"
+          "srav         %[r5],    %[r2],          %[r3]           \n\t"
+          "mul          %[r5],    %[r5],          %[r1]           \n\t"
+          "mul          %[r2],    %[r2],          %[r1]           \n\t"
+          "slt          %[r4],    %[r4],          $0              \n\t"
+          "srav         %[r5],    %[r5],          %[r6]           \n\t"
+          "sra          %[r2],    %[r2],          8               \n\t"
+          "movn         %[r2],    %[r5],          %[r4]           \n\t"
+          "sll          %[r3],    %[r0],          8               \n\t"
+          "addu         %[r2],    %[r0],          %[r2]           \n\t"
+          "divu         %[r3],    %[r3],          %[r2]           \n\t"
+         "1:                                                      \n\t"
+          ".set         pop                                       \n\t"
+          : [r2] "+r" (r2), [r3] "=&r" (r3), [r4] "=&r" (r4),
+            [r5] "=&r" (r5), [r6] "=&r" (r6), [r7] "=&r" (r7),
+            [r8] "=&r" (r8), [r9] "=&r" (r9)
+          : [r0] "r" (r0), [r1] "r" (r1), [const_23637] "r" (const_23637),
+            [const_neg8] "r" (const_neg8), [const_84] "r" (const_84),
+            [const_1] "r" (const_1), [const_44] "r" (const_44)
+          : "hi", "lo"
+        );
+        nonSpeechProbFinal[i] = r3;
+      }
+    }
+  }
+}
+
+// Update analysis buffer for lower band, and window data before FFT.
+void WebRtcNsx_AnalysisUpdate_mips(NoiseSuppressionFixedC* inst,
+                                   int16_t* out,
+                                   int16_t* new_speech) {
+  int iters, after;
+  int anaLen = (int)inst->anaLen;
+  int *window = (int*)inst->window;
+  int *anaBuf = (int*)inst->analysisBuffer;
+  int *outBuf = (int*)out;
+  int r0, r1, r2, r3, r4, r5, r6, r7;
+#if defined(MIPS_DSP_R1_LE)
+  int r8;
+#endif
+
+  // For lower band update analysis buffer.
+  memcpy(inst->analysisBuffer, inst->analysisBuffer + inst->blockLen10ms,
+      (inst->anaLen - inst->blockLen10ms) * sizeof(*inst->analysisBuffer));
+  memcpy(inst->analysisBuffer + inst->anaLen - inst->blockLen10ms, new_speech,
+      inst->blockLen10ms * sizeof(*inst->analysisBuffer));
+
+  // Window data before FFT.
+#if defined(MIPS_DSP_R1_LE)
+  __asm __volatile(
+    ".set              push                                \n\t"
+    ".set              noreorder                           \n\t"
+    "sra               %[iters],   %[anaLen],    3         \n\t"
+   "1:                                                     \n\t"
+    "blez              %[iters],   2f                      \n\t"
+    " nop                                                  \n\t"
+    "lw                %[r0],      0(%[window])            \n\t"
+    "lw                %[r1],      0(%[anaBuf])            \n\t"
+    "lw                %[r2],      4(%[window])            \n\t"
+    "lw                %[r3],      4(%[anaBuf])            \n\t"
+    "lw                %[r4],      8(%[window])            \n\t"
+    "lw                %[r5],      8(%[anaBuf])            \n\t"
+    "lw                %[r6],      12(%[window])           \n\t"
+    "lw                %[r7],      12(%[anaBuf])           \n\t"
+    "muleq_s.w.phl     %[r8],      %[r0],        %[r1]     \n\t"
+    "muleq_s.w.phr     %[r0],      %[r0],        %[r1]     \n\t"
+    "muleq_s.w.phl     %[r1],      %[r2],        %[r3]     \n\t"
+    "muleq_s.w.phr     %[r2],      %[r2],        %[r3]     \n\t"
+    "muleq_s.w.phl     %[r3],      %[r4],        %[r5]     \n\t"
+    "muleq_s.w.phr     %[r4],      %[r4],        %[r5]     \n\t"
+    "muleq_s.w.phl     %[r5],      %[r6],        %[r7]     \n\t"
+    "muleq_s.w.phr     %[r6],      %[r6],        %[r7]     \n\t"
+#if defined(MIPS_DSP_R2_LE)
+    "precr_sra_r.ph.w  %[r8],      %[r0],        15        \n\t"
+    "precr_sra_r.ph.w  %[r1],      %[r2],        15        \n\t"
+    "precr_sra_r.ph.w  %[r3],      %[r4],        15        \n\t"
+    "precr_sra_r.ph.w  %[r5],      %[r6],        15        \n\t"
+    "sw                %[r8],      0(%[outBuf])            \n\t"
+    "sw                %[r1],      4(%[outBuf])            \n\t"
+    "sw                %[r3],      8(%[outBuf])            \n\t"
+    "sw                %[r5],      12(%[outBuf])           \n\t"
+#else
+    "shra_r.w          %[r8],      %[r8],        15        \n\t"
+    "shra_r.w          %[r0],      %[r0],        15        \n\t"
+    "shra_r.w          %[r1],      %[r1],        15        \n\t"
+    "shra_r.w          %[r2],      %[r2],        15        \n\t"
+    "shra_r.w          %[r3],      %[r3],        15        \n\t"
+    "shra_r.w          %[r4],      %[r4],        15        \n\t"
+    "shra_r.w          %[r5],      %[r5],        15        \n\t"
+    "shra_r.w          %[r6],      %[r6],        15        \n\t"
+    "sll               %[r0],      %[r0],        16        \n\t"
+    "sll               %[r2],      %[r2],        16        \n\t"
+    "sll               %[r4],      %[r4],        16        \n\t"
+    "sll               %[r6],      %[r6],        16        \n\t"
+    "packrl.ph         %[r0],      %[r8],        %[r0]     \n\t"
+    "packrl.ph         %[r2],      %[r1],        %[r2]     \n\t"
+    "packrl.ph         %[r4],      %[r3],        %[r4]     \n\t"
+    "packrl.ph         %[r6],      %[r5],        %[r6]     \n\t"
+    "sw                %[r0],      0(%[outBuf])            \n\t"
+    "sw                %[r2],      4(%[outBuf])            \n\t"
+    "sw                %[r4],      8(%[outBuf])            \n\t"
+    "sw                %[r6],      12(%[outBuf])           \n\t"
+#endif
+    "addiu             %[window],  %[window],    16        \n\t"
+    "addiu             %[anaBuf],  %[anaBuf],    16        \n\t"
+    "addiu             %[outBuf],  %[outBuf],    16        \n\t"
+    "b                 1b                                  \n\t"
+    " addiu            %[iters],   %[iters],     -1        \n\t"
+   "2:                                                     \n\t"
+    "andi              %[after],   %[anaLen],    7         \n\t"
+   "3:                                                     \n\t"
+    "blez              %[after],   4f                      \n\t"
+    " nop                                                  \n\t"
+    "lh                %[r0],      0(%[window])            \n\t"
+    "lh                %[r1],      0(%[anaBuf])            \n\t"
+    "mul               %[r0],      %[r0],        %[r1]     \n\t"
+    "addiu             %[window],  %[window],    2         \n\t"
+    "addiu             %[anaBuf],  %[anaBuf],    2         \n\t"
+    "addiu             %[outBuf],  %[outBuf],    2         \n\t"
+    "shra_r.w          %[r0],      %[r0],        14        \n\t"
+    "sh                %[r0],      -2(%[outBuf])           \n\t"
+    "b                 3b                                  \n\t"
+    " addiu            %[after],   %[after],     -1        \n\t"
+   "4:                                                     \n\t"
+    ".set              pop                                 \n\t"
+    : [r0] "=&r" (r0), [r1] "=&r" (r1), [r2] "=&r" (r2),
+      [r3] "=&r" (r3), [r4] "=&r" (r4), [r5] "=&r" (r5),
+      [r6] "=&r" (r6), [r7] "=&r" (r7), [r8] "=&r" (r8),
+      [iters] "=&r" (iters), [after] "=&r" (after),
+      [window] "+r" (window),[anaBuf] "+r" (anaBuf),
+      [outBuf] "+r" (outBuf)
+    : [anaLen] "r" (anaLen)
+    : "memory", "hi", "lo"
+  );
+#else
+  __asm  __volatile(
+    ".set           push                                    \n\t"
+    ".set           noreorder                               \n\t"
+    "sra            %[iters],   %[anaLen],      2           \n\t"
+   "1:                                                      \n\t"
+    "blez           %[iters],   2f                          \n\t"
+    " nop                                                   \n\t"
+    "lh             %[r0],      0(%[window])                \n\t"
+    "lh             %[r1],      0(%[anaBuf])                \n\t"
+    "lh             %[r2],      2(%[window])                \n\t"
+    "lh             %[r3],      2(%[anaBuf])                \n\t"
+    "lh             %[r4],      4(%[window])                \n\t"
+    "lh             %[r5],      4(%[anaBuf])                \n\t"
+    "lh             %[r6],      6(%[window])                \n\t"
+    "lh             %[r7],      6(%[anaBuf])                \n\t"
+    "mul            %[r0],      %[r0],          %[r1]       \n\t"
+    "mul            %[r2],      %[r2],          %[r3]       \n\t"
+    "mul            %[r4],      %[r4],          %[r5]       \n\t"
+    "mul            %[r6],      %[r6],          %[r7]       \n\t"
+    "addiu          %[window],  %[window],      8           \n\t"
+    "addiu          %[anaBuf],  %[anaBuf],      8           \n\t"
+    "addiu          %[r0],      %[r0],          0x2000      \n\t"
+    "addiu          %[r2],      %[r2],          0x2000      \n\t"
+    "addiu          %[r4],      %[r4],          0x2000      \n\t"
+    "addiu          %[r6],      %[r6],          0x2000      \n\t"
+    "sra            %[r0],      %[r0],          14          \n\t"
+    "sra            %[r2],      %[r2],          14          \n\t"
+    "sra            %[r4],      %[r4],          14          \n\t"
+    "sra            %[r6],      %[r6],          14          \n\t"
+    "sh             %[r0],      0(%[outBuf])                \n\t"
+    "sh             %[r2],      2(%[outBuf])                \n\t"
+    "sh             %[r4],      4(%[outBuf])                \n\t"
+    "sh             %[r6],      6(%[outBuf])                \n\t"
+    "addiu          %[outBuf],  %[outBuf],      8           \n\t"
+    "b              1b                                      \n\t"
+    " addiu         %[iters],   %[iters],       -1          \n\t"
+   "2:                                                      \n\t"
+    "andi           %[after],   %[anaLen],      3           \n\t"
+   "3:                                                      \n\t"
+    "blez           %[after],   4f                          \n\t"
+    " nop                                                   \n\t"
+    "lh             %[r0],      0(%[window])                \n\t"
+    "lh             %[r1],      0(%[anaBuf])                \n\t"
+    "mul            %[r0],      %[r0],          %[r1]       \n\t"
+    "addiu          %[window],  %[window],      2           \n\t"
+    "addiu          %[anaBuf],  %[anaBuf],      2           \n\t"
+    "addiu          %[outBuf],  %[outBuf],      2           \n\t"
+    "addiu          %[r0],      %[r0],          0x2000      \n\t"
+    "sra            %[r0],      %[r0],          14          \n\t"
+    "sh             %[r0],      -2(%[outBuf])               \n\t"
+    "b              3b                                      \n\t"
+    " addiu         %[after],   %[after],       -1          \n\t"
+   "4:                                                      \n\t"
+    ".set           pop                                     \n\t"
+    : [r0] "=&r" (r0), [r1] "=&r" (r1), [r2] "=&r" (r2),
+      [r3] "=&r" (r3), [r4] "=&r" (r4), [r5] "=&r" (r5),
+      [r6] "=&r" (r6), [r7] "=&r" (r7), [iters] "=&r" (iters),
+      [after] "=&r" (after), [window] "+r" (window),
+      [anaBuf] "+r" (anaBuf), [outBuf] "+r" (outBuf)
+    : [anaLen] "r" (anaLen)
+    : "memory", "hi", "lo"
+  );
+#endif
+}
+
+// For the noise supression process, synthesis, read out fully processed
+// segment, and update synthesis buffer.
+void WebRtcNsx_SynthesisUpdate_mips(NoiseSuppressionFixedC* inst,
+                                    int16_t* out_frame,
+                                    int16_t gain_factor) {
+  int iters = (int)inst->blockLen10ms >> 2;
+  int after = inst->blockLen10ms & 3;
+  int r0, r1, r2, r3, r4, r5, r6, r7;
+  int16_t *window = (int16_t*)inst->window;
+  int16_t *real = inst->real;
+  int16_t *synthBuf = inst->synthesisBuffer;
+  int16_t *out = out_frame;
+  int sat_pos = 0x7fff;
+  int sat_neg = 0xffff8000;
+  int block10 = (int)inst->blockLen10ms;
+  int anaLen = (int)inst->anaLen;
+
+  __asm __volatile(
+    ".set       push                                        \n\t"
+    ".set       noreorder                                   \n\t"
+   "1:                                                      \n\t"
+    "blez       %[iters],   2f                              \n\t"
+    " nop                                                   \n\t"
+    "lh         %[r0],      0(%[window])                    \n\t"
+    "lh         %[r1],      0(%[real])                      \n\t"
+    "lh         %[r2],      2(%[window])                    \n\t"
+    "lh         %[r3],      2(%[real])                      \n\t"
+    "lh         %[r4],      4(%[window])                    \n\t"
+    "lh         %[r5],      4(%[real])                      \n\t"
+    "lh         %[r6],      6(%[window])                    \n\t"
+    "lh         %[r7],      6(%[real])                      \n\t"
+    "mul        %[r0],      %[r0],          %[r1]           \n\t"
+    "mul        %[r2],      %[r2],          %[r3]           \n\t"
+    "mul        %[r4],      %[r4],          %[r5]           \n\t"
+    "mul        %[r6],      %[r6],          %[r7]           \n\t"
+    "addiu      %[r0],      %[r0],          0x2000          \n\t"
+    "addiu      %[r2],      %[r2],          0x2000          \n\t"
+    "addiu      %[r4],      %[r4],          0x2000          \n\t"
+    "addiu      %[r6],      %[r6],          0x2000          \n\t"
+    "sra        %[r0],      %[r0],          14              \n\t"
+    "sra        %[r2],      %[r2],          14              \n\t"
+    "sra        %[r4],      %[r4],          14              \n\t"
+    "sra        %[r6],      %[r6],          14              \n\t"
+    "mul        %[r0],      %[r0],          %[gain_factor]  \n\t"
+    "mul        %[r2],      %[r2],          %[gain_factor]  \n\t"
+    "mul        %[r4],      %[r4],          %[gain_factor]  \n\t"
+    "mul        %[r6],      %[r6],          %[gain_factor]  \n\t"
+    "addiu      %[r0],      %[r0],          0x1000          \n\t"
+    "addiu      %[r2],      %[r2],          0x1000          \n\t"
+    "addiu      %[r4],      %[r4],          0x1000          \n\t"
+    "addiu      %[r6],      %[r6],          0x1000          \n\t"
+    "sra        %[r0],      %[r0],          13              \n\t"
+    "sra        %[r2],      %[r2],          13              \n\t"
+    "sra        %[r4],      %[r4],          13              \n\t"
+    "sra        %[r6],      %[r6],          13              \n\t"
+    "slt        %[r1],      %[r0],          %[sat_pos]      \n\t"
+    "slt        %[r3],      %[r2],          %[sat_pos]      \n\t"
+    "slt        %[r5],      %[r4],          %[sat_pos]      \n\t"
+    "slt        %[r7],      %[r6],          %[sat_pos]      \n\t"
+    "movz       %[r0],      %[sat_pos],     %[r1]           \n\t"
+    "movz       %[r2],      %[sat_pos],     %[r3]           \n\t"
+    "movz       %[r4],      %[sat_pos],     %[r5]           \n\t"
+    "movz       %[r6],      %[sat_pos],     %[r7]           \n\t"
+    "lh         %[r1],      0(%[synthBuf])                  \n\t"
+    "lh         %[r3],      2(%[synthBuf])                  \n\t"
+    "lh         %[r5],      4(%[synthBuf])                  \n\t"
+    "lh         %[r7],      6(%[synthBuf])                  \n\t"
+    "addu       %[r0],      %[r0],          %[r1]           \n\t"
+    "addu       %[r2],      %[r2],          %[r3]           \n\t"
+    "addu       %[r4],      %[r4],          %[r5]           \n\t"
+    "addu       %[r6],      %[r6],          %[r7]           \n\t"
+    "slt        %[r1],      %[r0],          %[sat_pos]      \n\t"
+    "slt        %[r3],      %[r2],          %[sat_pos]      \n\t"
+    "slt        %[r5],      %[r4],          %[sat_pos]      \n\t"
+    "slt        %[r7],      %[r6],          %[sat_pos]      \n\t"
+    "movz       %[r0],      %[sat_pos],     %[r1]           \n\t"
+    "movz       %[r2],      %[sat_pos],     %[r3]           \n\t"
+    "movz       %[r4],      %[sat_pos],     %[r5]           \n\t"
+    "movz       %[r6],      %[sat_pos],     %[r7]           \n\t"
+    "slt        %[r1],      %[r0],          %[sat_neg]      \n\t"
+    "slt        %[r3],      %[r2],          %[sat_neg]      \n\t"
+    "slt        %[r5],      %[r4],          %[sat_neg]      \n\t"
+    "slt        %[r7],      %[r6],          %[sat_neg]      \n\t"
+    "movn       %[r0],      %[sat_neg],     %[r1]           \n\t"
+    "movn       %[r2],      %[sat_neg],     %[r3]           \n\t"
+    "movn       %[r4],      %[sat_neg],     %[r5]           \n\t"
+    "movn       %[r6],      %[sat_neg],     %[r7]           \n\t"
+    "sh         %[r0],      0(%[synthBuf])                  \n\t"
+    "sh         %[r2],      2(%[synthBuf])                  \n\t"
+    "sh         %[r4],      4(%[synthBuf])                  \n\t"
+    "sh         %[r6],      6(%[synthBuf])                  \n\t"
+    "sh         %[r0],      0(%[out])                       \n\t"
+    "sh         %[r2],      2(%[out])                       \n\t"
+    "sh         %[r4],      4(%[out])                       \n\t"
+    "sh         %[r6],      6(%[out])                       \n\t"
+    "addiu      %[window],  %[window],      8               \n\t"
+    "addiu      %[real],    %[real],        8               \n\t"
+    "addiu      %[synthBuf],%[synthBuf],    8               \n\t"
+    "addiu      %[out],     %[out],         8               \n\t"
+    "b          1b                                          \n\t"
+    " addiu     %[iters],   %[iters],       -1              \n\t"
+   "2:                                                      \n\t"
+    "blez       %[after],   3f                              \n\t"
+    " subu      %[block10], %[anaLen],      %[block10]      \n\t"
+    "lh         %[r0],      0(%[window])                    \n\t"
+    "lh         %[r1],      0(%[real])                      \n\t"
+    "mul        %[r0],      %[r0],          %[r1]           \n\t"
+    "addiu      %[window],  %[window],      2               \n\t"
+    "addiu      %[real],    %[real],        2               \n\t"
+    "addiu      %[r0],      %[r0],          0x2000          \n\t"
+    "sra        %[r0],      %[r0],          14              \n\t"
+    "mul        %[r0],      %[r0],          %[gain_factor]  \n\t"
+    "addiu      %[r0],      %[r0],          0x1000          \n\t"
+    "sra        %[r0],      %[r0],          13              \n\t"
+    "slt        %[r1],      %[r0],          %[sat_pos]      \n\t"
+    "movz       %[r0],      %[sat_pos],     %[r1]           \n\t"
+    "lh         %[r1],      0(%[synthBuf])                  \n\t"
+    "addu       %[r0],      %[r0],          %[r1]           \n\t"
+    "slt        %[r1],      %[r0],          %[sat_pos]      \n\t"
+    "movz       %[r0],      %[sat_pos],     %[r1]           \n\t"
+    "slt        %[r1],      %[r0],          %[sat_neg]      \n\t"
+    "movn       %[r0],      %[sat_neg],     %[r1]           \n\t"
+    "sh         %[r0],      0(%[synthBuf])                  \n\t"
+    "sh         %[r0],      0(%[out])                       \n\t"
+    "addiu      %[synthBuf],%[synthBuf],    2               \n\t"
+    "addiu      %[out],     %[out],         2               \n\t"
+    "b          2b                                          \n\t"
+    " addiu     %[after],   %[after],       -1              \n\t"
+   "3:                                                      \n\t"
+    "sra        %[iters],   %[block10],     2               \n\t"
+   "4:                                                      \n\t"
+    "blez       %[iters],   5f                              \n\t"
+    " andi      %[after],   %[block10],     3               \n\t"
+    "lh         %[r0],      0(%[window])                    \n\t"
+    "lh         %[r1],      0(%[real])                      \n\t"
+    "lh         %[r2],      2(%[window])                    \n\t"
+    "lh         %[r3],      2(%[real])                      \n\t"
+    "lh         %[r4],      4(%[window])                    \n\t"
+    "lh         %[r5],      4(%[real])                      \n\t"
+    "lh         %[r6],      6(%[window])                    \n\t"
+    "lh         %[r7],      6(%[real])                      \n\t"
+    "mul        %[r0],      %[r0],          %[r1]           \n\t"
+    "mul        %[r2],      %[r2],          %[r3]           \n\t"
+    "mul        %[r4],      %[r4],          %[r5]           \n\t"
+    "mul        %[r6],      %[r6],          %[r7]           \n\t"
+    "addiu      %[r0],      %[r0],          0x2000          \n\t"
+    "addiu      %[r2],      %[r2],          0x2000          \n\t"
+    "addiu      %[r4],      %[r4],          0x2000          \n\t"
+    "addiu      %[r6],      %[r6],          0x2000          \n\t"
+    "sra        %[r0],      %[r0],          14              \n\t"
+    "sra        %[r2],      %[r2],          14              \n\t"
+    "sra        %[r4],      %[r4],          14              \n\t"
+    "sra        %[r6],      %[r6],          14              \n\t"
+    "mul        %[r0],      %[r0],          %[gain_factor]  \n\t"
+    "mul        %[r2],      %[r2],          %[gain_factor]  \n\t"
+    "mul        %[r4],      %[r4],          %[gain_factor]  \n\t"
+    "mul        %[r6],      %[r6],          %[gain_factor]  \n\t"
+    "addiu      %[r0],      %[r0],          0x1000          \n\t"
+    "addiu      %[r2],      %[r2],          0x1000          \n\t"
+    "addiu      %[r4],      %[r4],          0x1000          \n\t"
+    "addiu      %[r6],      %[r6],          0x1000          \n\t"
+    "sra        %[r0],      %[r0],          13              \n\t"
+    "sra        %[r2],      %[r2],          13              \n\t"
+    "sra        %[r4],      %[r4],          13              \n\t"
+    "sra        %[r6],      %[r6],          13              \n\t"
+    "slt        %[r1],      %[r0],          %[sat_pos]      \n\t"
+    "slt        %[r3],      %[r2],          %[sat_pos]      \n\t"
+    "slt        %[r5],      %[r4],          %[sat_pos]      \n\t"
+    "slt        %[r7],      %[r6],          %[sat_pos]      \n\t"
+    "movz       %[r0],      %[sat_pos],     %[r1]           \n\t"
+    "movz       %[r2],      %[sat_pos],     %[r3]           \n\t"
+    "movz       %[r4],      %[sat_pos],     %[r5]           \n\t"
+    "movz       %[r6],      %[sat_pos],     %[r7]           \n\t"
+    "lh         %[r1],      0(%[synthBuf])                  \n\t"
+    "lh         %[r3],      2(%[synthBuf])                  \n\t"
+    "lh         %[r5],      4(%[synthBuf])                  \n\t"
+    "lh         %[r7],      6(%[synthBuf])                  \n\t"
+    "addu       %[r0],      %[r0],          %[r1]           \n\t"
+    "addu       %[r2],      %[r2],          %[r3]           \n\t"
+    "addu       %[r4],      %[r4],          %[r5]           \n\t"
+    "addu       %[r6],      %[r6],          %[r7]           \n\t"
+    "slt        %[r1],      %[r0],          %[sat_pos]      \n\t"
+    "slt        %[r3],      %[r2],          %[sat_pos]      \n\t"
+    "slt        %[r5],      %[r4],          %[sat_pos]      \n\t"
+    "slt        %[r7],      %[r6],          %[sat_pos]      \n\t"
+    "movz       %[r0],      %[sat_pos],     %[r1]           \n\t"
+    "movz       %[r2],      %[sat_pos],     %[r3]           \n\t"
+    "movz       %[r4],      %[sat_pos],     %[r5]           \n\t"
+    "movz       %[r6],      %[sat_pos],     %[r7]           \n\t"
+    "slt        %[r1],      %[r0],          %[sat_neg]      \n\t"
+    "slt        %[r3],      %[r2],          %[sat_neg]      \n\t"
+    "slt        %[r5],      %[r4],          %[sat_neg]      \n\t"
+    "slt        %[r7],      %[r6],          %[sat_neg]      \n\t"
+    "movn       %[r0],      %[sat_neg],     %[r1]           \n\t"
+    "movn       %[r2],      %[sat_neg],     %[r3]           \n\t"
+    "movn       %[r4],      %[sat_neg],     %[r5]           \n\t"
+    "movn       %[r6],      %[sat_neg],     %[r7]           \n\t"
+    "sh         %[r0],      0(%[synthBuf])                  \n\t"
+    "sh         %[r2],      2(%[synthBuf])                  \n\t"
+    "sh         %[r4],      4(%[synthBuf])                  \n\t"
+    "sh         %[r6],      6(%[synthBuf])                  \n\t"
+    "addiu      %[window],  %[window],      8               \n\t"
+    "addiu      %[real],    %[real],        8               \n\t"
+    "addiu      %[synthBuf],%[synthBuf],    8               \n\t"
+    "b          4b                                          \n\t"
+    " addiu     %[iters],   %[iters],       -1              \n\t"
+   "5:                                                      \n\t"
+    "blez       %[after],   6f                              \n\t"
+    " nop                                                   \n\t"
+    "lh         %[r0],      0(%[window])                    \n\t"
+    "lh         %[r1],      0(%[real])                      \n\t"
+    "mul        %[r0],      %[r0],          %[r1]           \n\t"
+    "addiu      %[window],  %[window],      2               \n\t"
+    "addiu      %[real],    %[real],        2               \n\t"
+    "addiu      %[r0],      %[r0],          0x2000          \n\t"
+    "sra        %[r0],      %[r0],          14              \n\t"
+    "mul        %[r0],      %[r0],          %[gain_factor]  \n\t"
+    "addiu      %[r0],      %[r0],          0x1000          \n\t"
+    "sra        %[r0],      %[r0],          13              \n\t"
+    "slt        %[r1],      %[r0],          %[sat_pos]      \n\t"
+    "movz       %[r0],      %[sat_pos],     %[r1]           \n\t"
+    "lh         %[r1],      0(%[synthBuf])                  \n\t"
+    "addu       %[r0],      %[r0],          %[r1]           \n\t"
+    "slt        %[r1],      %[r0],          %[sat_pos]      \n\t"
+    "movz       %[r0],      %[sat_pos],     %[r1]           \n\t"
+    "slt        %[r1],      %[r0],          %[sat_neg]      \n\t"
+    "movn       %[r0],      %[sat_neg],     %[r1]           \n\t"
+    "sh         %[r0],      0(%[synthBuf])                  \n\t"
+    "addiu      %[synthBuf],%[synthBuf],    2               \n\t"
+    "b          2b                                          \n\t"
+    " addiu     %[after],   %[after],       -1              \n\t"
+   "6:                                                      \n\t"
+    ".set       pop                                         \n\t"
+    : [r0] "=&r" (r0), [r1] "=&r" (r1), [r2] "=&r" (r2),
+      [r3] "=&r" (r3), [r4] "=&r" (r4), [r5] "=&r" (r5),
+      [r6] "=&r" (r6), [r7] "=&r" (r7), [iters] "+r" (iters),
+      [after] "+r" (after), [block10] "+r" (block10),
+      [window] "+r" (window), [real] "+r" (real),
+      [synthBuf] "+r" (synthBuf), [out] "+r" (out)
+    : [gain_factor] "r" (gain_factor), [sat_pos] "r" (sat_pos),
+      [sat_neg] "r" (sat_neg), [anaLen] "r" (anaLen)
+    : "memory", "hi", "lo"
+  );
+
+  // update synthesis buffer
+  memcpy(inst->synthesisBuffer, inst->synthesisBuffer + inst->blockLen10ms,
+      (inst->anaLen - inst->blockLen10ms) * sizeof(*inst->synthesisBuffer));
+  WebRtcSpl_ZerosArrayW16(inst->synthesisBuffer
+      + inst->anaLen - inst->blockLen10ms, inst->blockLen10ms);
+}
+
+// Filter the data in the frequency domain, and create spectrum.
+void WebRtcNsx_PrepareSpectrum_mips(NoiseSuppressionFixedC* inst,
+                                    int16_t* freq_buf) {
+  uint16_t *noiseSupFilter = inst->noiseSupFilter;
+  int16_t *real = inst->real;
+  int16_t *imag = inst->imag;
+  int32_t loop_count = 2;
+  int16_t tmp_1, tmp_2, tmp_3, tmp_4, tmp_5, tmp_6;
+  int16_t tmp16 = (int16_t)(inst->anaLen << 1) - 4;
+  int16_t* freq_buf_f = freq_buf;
+  int16_t* freq_buf_s = &freq_buf[tmp16];
+
+  __asm __volatile (
+    ".set       push                                                 \n\t"
+    ".set       noreorder                                            \n\t"
+    //first sample
+    "lh         %[tmp_1],           0(%[noiseSupFilter])             \n\t"
+    "lh         %[tmp_2],           0(%[real])                       \n\t"
+    "lh         %[tmp_3],           0(%[imag])                       \n\t"
+    "mul        %[tmp_2],           %[tmp_2],             %[tmp_1]   \n\t"
+    "mul        %[tmp_3],           %[tmp_3],             %[tmp_1]   \n\t"
+    "sra        %[tmp_2],           %[tmp_2],             14         \n\t"
+    "sra        %[tmp_3],           %[tmp_3],             14         \n\t"
+    "sh         %[tmp_2],           0(%[real])                       \n\t"
+    "sh         %[tmp_3],           0(%[imag])                       \n\t"
+    "negu       %[tmp_3],           %[tmp_3]                         \n\t"
+    "sh         %[tmp_2],           0(%[freq_buf_f])                 \n\t"
+    "sh         %[tmp_3],           2(%[freq_buf_f])                 \n\t"
+    "addiu      %[real],            %[real],              2          \n\t"
+    "addiu      %[imag],            %[imag],              2          \n\t"
+    "addiu      %[noiseSupFilter],  %[noiseSupFilter],    2          \n\t"
+    "addiu      %[freq_buf_f],      %[freq_buf_f],        4          \n\t"
+   "1:                                                               \n\t"
+    "lh         %[tmp_1],           0(%[noiseSupFilter])             \n\t"
+    "lh         %[tmp_2],           0(%[real])                       \n\t"
+    "lh         %[tmp_3],           0(%[imag])                       \n\t"
+    "lh         %[tmp_4],           2(%[noiseSupFilter])             \n\t"
+    "lh         %[tmp_5],           2(%[real])                       \n\t"
+    "lh         %[tmp_6],           2(%[imag])                       \n\t"
+    "mul        %[tmp_2],           %[tmp_2],             %[tmp_1]   \n\t"
+    "mul        %[tmp_3],           %[tmp_3],             %[tmp_1]   \n\t"
+    "mul        %[tmp_5],           %[tmp_5],             %[tmp_4]   \n\t"
+    "mul        %[tmp_6],           %[tmp_6],             %[tmp_4]   \n\t"
+    "addiu      %[loop_count],      %[loop_count],        2          \n\t"
+    "sra        %[tmp_2],           %[tmp_2],             14         \n\t"
+    "sra        %[tmp_3],           %[tmp_3],             14         \n\t"
+    "sra        %[tmp_5],           %[tmp_5],             14         \n\t"
+    "sra        %[tmp_6],           %[tmp_6],             14         \n\t"
+    "addiu      %[noiseSupFilter],  %[noiseSupFilter],    4          \n\t"
+    "sh         %[tmp_2],           0(%[real])                       \n\t"
+    "sh         %[tmp_2],           4(%[freq_buf_s])                 \n\t"
+    "sh         %[tmp_3],           0(%[imag])                       \n\t"
+    "sh         %[tmp_3],           6(%[freq_buf_s])                 \n\t"
+    "negu       %[tmp_3],           %[tmp_3]                         \n\t"
+    "sh         %[tmp_5],           2(%[real])                       \n\t"
+    "sh         %[tmp_5],           0(%[freq_buf_s])                 \n\t"
+    "sh         %[tmp_6],           2(%[imag])                       \n\t"
+    "sh         %[tmp_6],           2(%[freq_buf_s])                 \n\t"
+    "negu       %[tmp_6],           %[tmp_6]                         \n\t"
+    "addiu      %[freq_buf_s],      %[freq_buf_s],        -8         \n\t"
+    "addiu      %[real],            %[real],              4          \n\t"
+    "addiu      %[imag],            %[imag],              4          \n\t"
+    "sh         %[tmp_2],           0(%[freq_buf_f])                 \n\t"
+    "sh         %[tmp_3],           2(%[freq_buf_f])                 \n\t"
+    "sh         %[tmp_5],           4(%[freq_buf_f])                 \n\t"
+    "sh         %[tmp_6],           6(%[freq_buf_f])                 \n\t"
+    "blt        %[loop_count],      %[loop_size],         1b         \n\t"
+    " addiu     %[freq_buf_f],      %[freq_buf_f],        8          \n\t"
+    //last two samples:
+    "lh         %[tmp_1],           0(%[noiseSupFilter])             \n\t"
+    "lh         %[tmp_2],           0(%[real])                       \n\t"
+    "lh         %[tmp_3],           0(%[imag])                       \n\t"
+    "lh         %[tmp_4],           2(%[noiseSupFilter])             \n\t"
+    "lh         %[tmp_5],           2(%[real])                       \n\t"
+    "lh         %[tmp_6],           2(%[imag])                       \n\t"
+    "mul        %[tmp_2],           %[tmp_2],             %[tmp_1]   \n\t"
+    "mul        %[tmp_3],           %[tmp_3],             %[tmp_1]   \n\t"
+    "mul        %[tmp_5],           %[tmp_5],             %[tmp_4]   \n\t"
+    "mul        %[tmp_6],           %[tmp_6],             %[tmp_4]   \n\t"
+    "sra        %[tmp_2],           %[tmp_2],             14         \n\t"
+    "sra        %[tmp_3],           %[tmp_3],             14         \n\t"
+    "sra        %[tmp_5],           %[tmp_5],             14         \n\t"
+    "sra        %[tmp_6],           %[tmp_6],             14         \n\t"
+    "sh         %[tmp_2],           0(%[real])                       \n\t"
+    "sh         %[tmp_2],           4(%[freq_buf_s])                 \n\t"
+    "sh         %[tmp_3],           0(%[imag])                       \n\t"
+    "sh         %[tmp_3],           6(%[freq_buf_s])                 \n\t"
+    "negu       %[tmp_3],           %[tmp_3]                         \n\t"
+    "sh         %[tmp_2],           0(%[freq_buf_f])                 \n\t"
+    "sh         %[tmp_3],           2(%[freq_buf_f])                 \n\t"
+    "sh         %[tmp_5],           4(%[freq_buf_f])                 \n\t"
+    "sh         %[tmp_6],           6(%[freq_buf_f])                 \n\t"
+    "sh         %[tmp_5],           2(%[real])                       \n\t"
+    "sh         %[tmp_6],           2(%[imag])                       \n\t"
+    ".set       pop                                                  \n\t"
+    : [real] "+r" (real), [imag] "+r" (imag),
+      [freq_buf_f] "+r" (freq_buf_f), [freq_buf_s] "+r" (freq_buf_s),
+      [loop_count] "+r" (loop_count), [noiseSupFilter] "+r" (noiseSupFilter),
+      [tmp_1] "=&r" (tmp_1), [tmp_2] "=&r" (tmp_2), [tmp_3] "=&r" (tmp_3),
+      [tmp_4] "=&r" (tmp_4), [tmp_5] "=&r" (tmp_5), [tmp_6] "=&r" (tmp_6)
+    : [loop_size] "r" (inst->anaLen2)
+    : "memory", "hi", "lo"
+  );
+}
+
+#if defined(MIPS_DSP_R1_LE)
+// Denormalize the real-valued signal |in|, the output from inverse FFT.
+void WebRtcNsx_Denormalize_mips(NoiseSuppressionFixedC* inst,
+                                int16_t* in,
+                                int factor) {
+  int32_t r0, r1, r2, r3, t0;
+  int len = (int)inst->anaLen;
+  int16_t *out = &inst->real[0];
+  int shift = factor - inst->normData;
+
+  __asm __volatile (
+    ".set          push                                \n\t"
+    ".set          noreorder                           \n\t"
+    "beqz          %[len],     8f                      \n\t"
+    " nop                                              \n\t"
+    "bltz          %[shift],   4f                      \n\t"
+    " sra          %[t0],      %[len],      2          \n\t"
+    "beqz          %[t0],      2f                      \n\t"
+    " andi         %[len],     %[len],      3          \n\t"
+   "1:                                                 \n\t"
+    "lh            %[r0],      0(%[in])                \n\t"
+    "lh            %[r1],      2(%[in])                \n\t"
+    "lh            %[r2],      4(%[in])                \n\t"
+    "lh            %[r3],      6(%[in])                \n\t"
+    "shllv_s.ph    %[r0],      %[r0],       %[shift]   \n\t"
+    "shllv_s.ph    %[r1],      %[r1],       %[shift]   \n\t"
+    "shllv_s.ph    %[r2],      %[r2],       %[shift]   \n\t"
+    "shllv_s.ph    %[r3],      %[r3],       %[shift]   \n\t"
+    "addiu         %[in],      %[in],       8          \n\t"
+    "addiu         %[t0],      %[t0],       -1         \n\t"
+    "sh            %[r0],      0(%[out])               \n\t"
+    "sh            %[r1],      2(%[out])               \n\t"
+    "sh            %[r2],      4(%[out])               \n\t"
+    "sh            %[r3],      6(%[out])               \n\t"
+    "bgtz          %[t0],      1b                      \n\t"
+    " addiu        %[out],     %[out],      8          \n\t"
+   "2:                                                 \n\t"
+    "beqz          %[len],     8f                      \n\t"
+    " nop                                              \n\t"
+   "3:                                                 \n\t"
+    "lh            %[r0],      0(%[in])                \n\t"
+    "addiu         %[in],      %[in],       2          \n\t"
+    "addiu         %[len],     %[len],      -1         \n\t"
+    "shllv_s.ph    %[r0],      %[r0],       %[shift]   \n\t"
+    "addiu         %[out],     %[out],      2          \n\t"
+    "bgtz          %[len],     3b                      \n\t"
+    " sh           %[r0],      -2(%[out])              \n\t"
+    "b             8f                                  \n\t"
+   "4:                                                 \n\t"
+    "negu          %[shift],   %[shift]                \n\t"
+    "beqz          %[t0],      6f                      \n\t"
+    " andi         %[len],     %[len],      3          \n\t"
+   "5:                                                 \n\t"
+    "lh            %[r0],      0(%[in])                \n\t"
+    "lh            %[r1],      2(%[in])                \n\t"
+    "lh            %[r2],      4(%[in])                \n\t"
+    "lh            %[r3],      6(%[in])                \n\t"
+    "srav          %[r0],      %[r0],       %[shift]   \n\t"
+    "srav          %[r1],      %[r1],       %[shift]   \n\t"
+    "srav          %[r2],      %[r2],       %[shift]   \n\t"
+    "srav          %[r3],      %[r3],       %[shift]   \n\t"
+    "addiu         %[in],      %[in],       8          \n\t"
+    "addiu         %[t0],      %[t0],       -1         \n\t"
+    "sh            %[r0],      0(%[out])               \n\t"
+    "sh            %[r1],      2(%[out])               \n\t"
+    "sh            %[r2],      4(%[out])               \n\t"
+    "sh            %[r3],      6(%[out])               \n\t"
+    "bgtz          %[t0],      5b                      \n\t"
+    " addiu        %[out],     %[out],      8          \n\t"
+   "6:                                                 \n\t"
+    "beqz          %[len],     8f                      \n\t"
+    " nop                                              \n\t"
+   "7:                                                 \n\t"
+    "lh            %[r0],      0(%[in])                \n\t"
+    "addiu         %[in],      %[in],       2          \n\t"
+    "addiu         %[len],     %[len],      -1         \n\t"
+    "srav          %[r0],      %[r0],       %[shift]   \n\t"
+    "addiu         %[out],     %[out],      2          \n\t"
+    "bgtz          %[len],     7b                      \n\t"
+    " sh           %[r0],      -2(%[out])              \n\t"
+   "8:                                                 \n\t"
+    ".set          pop                                 \n\t"
+    : [t0] "=&r" (t0), [r0] "=&r" (r0), [r1] "=&r" (r1),
+      [r2] "=&r" (r2), [r3] "=&r" (r3)
+    : [len] "r" (len), [shift] "r" (shift), [in] "r" (in),
+      [out] "r" (out)
+    : "memory"
+  );
+}
+#endif
+
+// Normalize the real-valued signal |in|, the input to forward FFT.
+void WebRtcNsx_NormalizeRealBuffer_mips(NoiseSuppressionFixedC* inst,
+                                        const int16_t* in,
+                                        int16_t* out) {
+  int32_t r0, r1, r2, r3, t0;
+  int len = (int)inst->anaLen;
+  int shift = inst->normData;
+
+  __asm __volatile (
+    ".set          push                                \n\t"
+    ".set          noreorder                           \n\t"
+    "beqz          %[len],     4f                      \n\t"
+    " sra          %[t0],      %[len],      2          \n\t"
+    "beqz          %[t0],      2f                      \n\t"
+    " andi         %[len],     %[len],      3          \n\t"
+   "1:                                                 \n\t"
+    "lh            %[r0],      0(%[in])                \n\t"
+    "lh            %[r1],      2(%[in])                \n\t"
+    "lh            %[r2],      4(%[in])                \n\t"
+    "lh            %[r3],      6(%[in])                \n\t"
+    "sllv          %[r0],      %[r0],       %[shift]   \n\t"
+    "sllv          %[r1],      %[r1],       %[shift]   \n\t"
+    "sllv          %[r2],      %[r2],       %[shift]   \n\t"
+    "sllv          %[r3],      %[r3],       %[shift]   \n\t"
+    "addiu         %[in],      %[in],       8          \n\t"
+    "addiu         %[t0],      %[t0],       -1         \n\t"
+    "sh            %[r0],      0(%[out])               \n\t"
+    "sh            %[r1],      2(%[out])               \n\t"
+    "sh            %[r2],      4(%[out])               \n\t"
+    "sh            %[r3],      6(%[out])               \n\t"
+    "bgtz          %[t0],      1b                      \n\t"
+    " addiu        %[out],     %[out],      8          \n\t"
+   "2:                                                 \n\t"
+    "beqz          %[len],     4f                      \n\t"
+    " nop                                              \n\t"
+   "3:                                                 \n\t"
+    "lh            %[r0],      0(%[in])                \n\t"
+    "addiu         %[in],      %[in],       2          \n\t"
+    "addiu         %[len],     %[len],      -1         \n\t"
+    "sllv          %[r0],      %[r0],       %[shift]   \n\t"
+    "addiu         %[out],     %[out],      2          \n\t"
+    "bgtz          %[len],     3b                      \n\t"
+    " sh           %[r0],      -2(%[out])              \n\t"
+   "4:                                                 \n\t"
+    ".set          pop                                 \n\t"
+    : [t0] "=&r" (t0), [r0] "=&r" (r0), [r1] "=&r" (r1),
+      [r2] "=&r" (r2), [r3] "=&r" (r3)
+    : [len] "r" (len), [shift] "r" (shift), [in] "r" (in),
+      [out] "r" (out)
+    : "memory"
+  );
+}
diff --git a/modules/audio_processing/legacy_ns/nsx_core_neon.c b/modules/audio_processing/legacy_ns/nsx_core_neon.c
new file mode 100644
index 0000000..244111c
--- /dev/null
+++ b/modules/audio_processing/legacy_ns/nsx_core_neon.c
@@ -0,0 +1,606 @@
+/*
+ *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/audio_processing/legacy_ns/nsx_core.h"
+
+#include <arm_neon.h>
+
+#include "rtc_base/checks.h"
+
+// Constants to compensate for shifting signal log(2^shifts).
+const int16_t WebRtcNsx_kLogTable[9] = {
+  0, 177, 355, 532, 710, 887, 1065, 1242, 1420
+};
+
+const int16_t WebRtcNsx_kCounterDiv[201] = {
+  32767, 16384, 10923, 8192, 6554, 5461, 4681, 4096, 3641, 3277, 2979, 2731,
+  2521, 2341, 2185, 2048, 1928, 1820, 1725, 1638, 1560, 1489, 1425, 1365, 1311,
+  1260, 1214, 1170, 1130, 1092, 1057, 1024, 993, 964, 936, 910, 886, 862, 840,
+  819, 799, 780, 762, 745, 728, 712, 697, 683, 669, 655, 643, 630, 618, 607,
+  596, 585, 575, 565, 555, 546, 537, 529, 520, 512, 504, 496, 489, 482, 475,
+  468, 462, 455, 449, 443, 437, 431, 426, 420, 415, 410, 405, 400, 395, 390,
+  386, 381, 377, 372, 368, 364, 360, 356, 352, 349, 345, 341, 338, 334, 331,
+  328, 324, 321, 318, 315, 312, 309, 306, 303, 301, 298, 295, 293, 290, 287,
+  285, 282, 280, 278, 275, 273, 271, 269, 266, 264, 262, 260, 258, 256, 254,
+  252, 250, 248, 246, 245, 243, 241, 239, 237, 236, 234, 232, 231, 229, 228,
+  226, 224, 223, 221, 220, 218, 217, 216, 214, 213, 211, 210, 209, 207, 206,
+  205, 204, 202, 201, 200, 199, 197, 196, 195, 194, 193, 192, 191, 189, 188,
+  187, 186, 185, 184, 183, 182, 181, 180, 179, 178, 177, 176, 175, 174, 173,
+  172, 172, 171, 170, 169, 168, 167, 166, 165, 165, 164, 163
+};
+
+const int16_t WebRtcNsx_kLogTableFrac[256] = {
+  0, 1, 3, 4, 6, 7, 9, 10, 11, 13, 14, 16, 17, 18, 20, 21,
+  22, 24, 25, 26, 28, 29, 30, 32, 33, 34, 36, 37, 38, 40, 41, 42,
+  44, 45, 46, 47, 49, 50, 51, 52, 54, 55, 56, 57, 59, 60, 61, 62,
+  63, 65, 66, 67, 68, 69, 71, 72, 73, 74, 75, 77, 78, 79, 80, 81,
+  82, 84, 85, 86, 87, 88, 89, 90, 92, 93, 94, 95, 96, 97, 98, 99,
+  100, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 116,
+  117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131,
+  132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146,
+  147, 148, 149, 150, 151, 152, 153, 154, 155, 155, 156, 157, 158, 159, 160,
+  161, 162, 163, 164, 165, 166, 167, 168, 169, 169, 170, 171, 172, 173, 174,
+  175, 176, 177, 178, 178, 179, 180, 181, 182, 183, 184, 185, 185, 186, 187,
+  188, 189, 190, 191, 192, 192, 193, 194, 195, 196, 197, 198, 198, 199, 200,
+  201, 202, 203, 203, 204, 205, 206, 207, 208, 208, 209, 210, 211, 212, 212,
+  213, 214, 215, 216, 216, 217, 218, 219, 220, 220, 221, 222, 223, 224, 224,
+  225, 226, 227, 228, 228, 229, 230, 231, 231, 232, 233, 234, 234, 235, 236,
+  237, 238, 238, 239, 240, 241, 241, 242, 243, 244, 244, 245, 246, 247, 247,
+  248, 249, 249, 250, 251, 252, 252, 253, 254, 255, 255
+};
+
+// Update the noise estimation information.
+static void UpdateNoiseEstimateNeon(NoiseSuppressionFixedC* inst, int offset) {
+  const int16_t kExp2Const = 11819; // Q13
+  int16_t* ptr_noiseEstLogQuantile = NULL;
+  int16_t* ptr_noiseEstQuantile = NULL;
+  int16x4_t kExp2Const16x4 = vdup_n_s16(kExp2Const);
+  int32x4_t twentyOne32x4 = vdupq_n_s32(21);
+  int32x4_t constA32x4 = vdupq_n_s32(0x1fffff);
+  int32x4_t constB32x4 = vdupq_n_s32(0x200000);
+
+  int16_t tmp16 = WebRtcSpl_MaxValueW16(inst->noiseEstLogQuantile + offset,
+                                        inst->magnLen);
+
+  // Guarantee a Q-domain as high as possible and still fit in int16
+  inst->qNoise = 14 - (int) WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(kExp2Const,
+                                                                 tmp16,
+                                                                 21);
+
+  int32x4_t qNoise32x4 = vdupq_n_s32(inst->qNoise);
+
+  for (ptr_noiseEstLogQuantile = &inst->noiseEstLogQuantile[offset],
+       ptr_noiseEstQuantile = &inst->noiseEstQuantile[0];
+       ptr_noiseEstQuantile < &inst->noiseEstQuantile[inst->magnLen - 3];
+       ptr_noiseEstQuantile += 4, ptr_noiseEstLogQuantile += 4) {
+
+    // tmp32no2 = kExp2Const * inst->noiseEstLogQuantile[offset + i];
+    int16x4_t v16x4 = vld1_s16(ptr_noiseEstLogQuantile);
+    int32x4_t v32x4B = vmull_s16(v16x4, kExp2Const16x4);
+
+    // tmp32no1 = (0x00200000 | (tmp32no2 & 0x001FFFFF)); // 2^21 + frac
+    int32x4_t v32x4A = vandq_s32(v32x4B, constA32x4);
+    v32x4A = vorrq_s32(v32x4A, constB32x4);
+
+    // tmp16 = (int16_t)(tmp32no2 >> 21);
+    v32x4B = vshrq_n_s32(v32x4B, 21);
+
+    // tmp16 -= 21;// shift 21 to get result in Q0
+    v32x4B = vsubq_s32(v32x4B, twentyOne32x4);
+
+    // tmp16 += (int16_t) inst->qNoise;
+    // shift to get result in Q(qNoise)
+    v32x4B = vaddq_s32(v32x4B, qNoise32x4);
+
+    // if (tmp16 < 0) {
+    //   tmp32no1 >>= -tmp16;
+    // } else {
+    //   tmp32no1 <<= tmp16;
+    // }
+    v32x4B = vshlq_s32(v32x4A, v32x4B);
+
+    // tmp16 = WebRtcSpl_SatW32ToW16(tmp32no1);
+    v16x4 = vqmovn_s32(v32x4B);
+
+    //inst->noiseEstQuantile[i] = tmp16;
+    vst1_s16(ptr_noiseEstQuantile, v16x4);
+  }
+
+  // Last iteration:
+
+  // inst->quantile[i]=exp(inst->lquantile[offset+i]);
+  // in Q21
+  int32_t tmp32no2 = kExp2Const * *ptr_noiseEstLogQuantile;
+  int32_t tmp32no1 = (0x00200000 | (tmp32no2 & 0x001FFFFF)); // 2^21 + frac
+
+  tmp16 = (int16_t)(tmp32no2 >> 21);
+  tmp16 -= 21;// shift 21 to get result in Q0
+  tmp16 += (int16_t) inst->qNoise; //shift to get result in Q(qNoise)
+  if (tmp16 < 0) {
+    tmp32no1 >>= -tmp16;
+  } else {
+    tmp32no1 <<= tmp16;
+  }
+  *ptr_noiseEstQuantile = WebRtcSpl_SatW32ToW16(tmp32no1);
+}
+
+// Noise Estimation
+void WebRtcNsx_NoiseEstimationNeon(NoiseSuppressionFixedC* inst,
+                                   uint16_t* magn,
+                                   uint32_t* noise,
+                                   int16_t* q_noise) {
+  int16_t lmagn[HALF_ANAL_BLOCKL], counter, countDiv;
+  int16_t countProd, delta, zeros, frac;
+  int16_t log2, tabind, logval, tmp16, tmp16no1, tmp16no2;
+  const int16_t log2_const = 22713;
+  const int16_t width_factor = 21845;
+
+  size_t i, s, offset;
+
+  tabind = inst->stages - inst->normData;
+  RTC_DCHECK_LT(tabind, 9);
+  RTC_DCHECK_GT(tabind, -9);
+  if (tabind < 0) {
+    logval = -WebRtcNsx_kLogTable[-tabind];
+  } else {
+    logval = WebRtcNsx_kLogTable[tabind];
+  }
+
+  int16x8_t logval_16x8 = vdupq_n_s16(logval);
+
+  // lmagn(i)=log(magn(i))=log(2)*log2(magn(i))
+  // magn is in Q(-stages), and the real lmagn values are:
+  // real_lmagn(i)=log(magn(i)*2^stages)=log(magn(i))+log(2^stages)
+  // lmagn in Q8
+  for (i = 0; i < inst->magnLen; i++) {
+    if (magn[i]) {
+      zeros = WebRtcSpl_NormU32((uint32_t)magn[i]);
+      frac = (int16_t)((((uint32_t)magn[i] << zeros)
+                        & 0x7FFFFFFF) >> 23);
+      RTC_DCHECK_LT(frac, 256);
+      // log2(magn(i))
+      log2 = (int16_t)(((31 - zeros) << 8)
+                       + WebRtcNsx_kLogTableFrac[frac]);
+      // log2(magn(i))*log(2)
+      lmagn[i] = (int16_t)((log2 * log2_const) >> 15);
+      // + log(2^stages)
+      lmagn[i] += logval;
+    } else {
+      lmagn[i] = logval;
+    }
+  }
+
+  int16x4_t Q3_16x4  = vdup_n_s16(3);
+  int16x8_t WIDTHQ8_16x8 = vdupq_n_s16(WIDTH_Q8);
+  int16x8_t WIDTHFACTOR_16x8 = vdupq_n_s16(width_factor);
+
+  int16_t factor = FACTOR_Q7;
+  if (inst->blockIndex < END_STARTUP_LONG)
+    factor = FACTOR_Q7_STARTUP;
+
+  // Loop over simultaneous estimates
+  for (s = 0; s < SIMULT; s++) {
+    offset = s * inst->magnLen;
+
+    // Get counter values from state
+    counter = inst->noiseEstCounter[s];
+    RTC_DCHECK_LT(counter, 201);
+    countDiv = WebRtcNsx_kCounterDiv[counter];
+    countProd = (int16_t)(counter * countDiv);
+
+    // quant_est(...)
+    int16_t deltaBuff[8];
+    int16x4_t tmp16x4_0;
+    int16x4_t tmp16x4_1;
+    int16x4_t countDiv_16x4 = vdup_n_s16(countDiv);
+    int16x8_t countProd_16x8 = vdupq_n_s16(countProd);
+    int16x8_t tmp16x8_0 = vdupq_n_s16(countDiv);
+    int16x8_t prod16x8 = vqrdmulhq_s16(WIDTHFACTOR_16x8, tmp16x8_0);
+    int16x8_t tmp16x8_1;
+    int16x8_t tmp16x8_2;
+    int16x8_t tmp16x8_3;
+    uint16x8_t tmp16x8_4;
+    int32x4_t tmp32x4;
+
+    for (i = 0; i + 7 < inst->magnLen; i += 8) {
+      // Compute delta.
+      // Smaller step size during startup. This prevents from using
+      // unrealistic values causing overflow.
+      tmp16x8_0 = vdupq_n_s16(factor);
+      vst1q_s16(deltaBuff, tmp16x8_0);
+
+      int j;
+      for (j = 0; j < 8; j++) {
+        if (inst->noiseEstDensity[offset + i + j] > 512) {
+          // Get values for deltaBuff by shifting intead of dividing.
+          int factor = WebRtcSpl_NormW16(inst->noiseEstDensity[offset + i + j]);
+          deltaBuff[j] = (int16_t)(FACTOR_Q16 >> (14 - factor));
+        }
+      }
+
+      // Update log quantile estimate
+
+      // tmp16 = (int16_t)((delta * countDiv) >> 14);
+      tmp32x4 = vmull_s16(vld1_s16(&deltaBuff[0]), countDiv_16x4);
+      tmp16x4_1 = vshrn_n_s32(tmp32x4, 14);
+      tmp32x4 = vmull_s16(vld1_s16(&deltaBuff[4]), countDiv_16x4);
+      tmp16x4_0 = vshrn_n_s32(tmp32x4, 14);
+      tmp16x8_0 = vcombine_s16(tmp16x4_1, tmp16x4_0); // Keep for several lines.
+
+      // prepare for the "if" branch
+      // tmp16 += 2;
+      // tmp16_1 = (Word16)(tmp16>>2);
+      tmp16x8_1 = vrshrq_n_s16(tmp16x8_0, 2);
+
+      // inst->noiseEstLogQuantile[offset+i] + tmp16_1;
+      tmp16x8_2 = vld1q_s16(&inst->noiseEstLogQuantile[offset + i]); // Keep
+      tmp16x8_1 = vaddq_s16(tmp16x8_2, tmp16x8_1); // Keep for several lines
+
+      // Prepare for the "else" branch
+      // tmp16 += 1;
+      // tmp16_1 = (Word16)(tmp16>>1);
+      tmp16x8_0 = vrshrq_n_s16(tmp16x8_0, 1);
+
+      // tmp16_2 = (int16_t)((tmp16_1 * 3) >> 1);
+      tmp32x4 = vmull_s16(vget_low_s16(tmp16x8_0), Q3_16x4);
+      tmp16x4_1 = vshrn_n_s32(tmp32x4, 1);
+
+      // tmp16_2 = (int16_t)((tmp16_1 * 3) >> 1);
+      tmp32x4 = vmull_s16(vget_high_s16(tmp16x8_0), Q3_16x4);
+      tmp16x4_0 = vshrn_n_s32(tmp32x4, 1);
+
+      // inst->noiseEstLogQuantile[offset + i] - tmp16_2;
+      tmp16x8_0 = vcombine_s16(tmp16x4_1, tmp16x4_0); // keep
+      tmp16x8_0 = vsubq_s16(tmp16x8_2, tmp16x8_0);
+
+      // logval is the smallest fixed point representation we can have. Values
+      // below that will correspond to values in the interval [0, 1], which
+      // can't possibly occur.
+      tmp16x8_0 = vmaxq_s16(tmp16x8_0, logval_16x8);
+
+      // Do the if-else branches:
+      tmp16x8_3 = vld1q_s16(&lmagn[i]); // keep for several lines
+      tmp16x8_4 = vcgtq_s16(tmp16x8_3, tmp16x8_2);
+      tmp16x8_2 = vbslq_s16(tmp16x8_4, tmp16x8_1, tmp16x8_0);
+      vst1q_s16(&inst->noiseEstLogQuantile[offset + i], tmp16x8_2);
+
+      // Update density estimate
+      // tmp16_1 + tmp16_2
+      tmp16x8_1 = vld1q_s16(&inst->noiseEstDensity[offset + i]);
+      tmp16x8_0 = vqrdmulhq_s16(tmp16x8_1, countProd_16x8);
+      tmp16x8_0 = vaddq_s16(tmp16x8_0, prod16x8);
+
+      // lmagn[i] - inst->noiseEstLogQuantile[offset + i]
+      tmp16x8_3 = vsubq_s16(tmp16x8_3, tmp16x8_2);
+      tmp16x8_3 = vabsq_s16(tmp16x8_3);
+      tmp16x8_4 = vcgtq_s16(WIDTHQ8_16x8, tmp16x8_3);
+      tmp16x8_1 = vbslq_s16(tmp16x8_4, tmp16x8_0, tmp16x8_1);
+      vst1q_s16(&inst->noiseEstDensity[offset + i], tmp16x8_1);
+    }  // End loop over magnitude spectrum
+
+    // Last iteration over magnitude spectrum:
+    // compute delta
+    if (inst->noiseEstDensity[offset + i] > 512) {
+      // Get values for deltaBuff by shifting intead of dividing.
+      int factor = WebRtcSpl_NormW16(inst->noiseEstDensity[offset + i]);
+      delta = (int16_t)(FACTOR_Q16 >> (14 - factor));
+    } else {
+      delta = FACTOR_Q7;
+      if (inst->blockIndex < END_STARTUP_LONG) {
+        // Smaller step size during startup. This prevents from using
+        // unrealistic values causing overflow.
+        delta = FACTOR_Q7_STARTUP;
+      }
+    }
+    // update log quantile estimate
+    tmp16 = (int16_t)((delta * countDiv) >> 14);
+    if (lmagn[i] > inst->noiseEstLogQuantile[offset + i]) {
+      // +=QUANTILE*delta/(inst->counter[s]+1) QUANTILE=0.25, =1 in Q2
+      // CounterDiv=1/(inst->counter[s]+1) in Q15
+      tmp16 += 2;
+      inst->noiseEstLogQuantile[offset + i] += tmp16 / 4;
+    } else {
+      tmp16 += 1;
+      // *(1-QUANTILE), in Q2 QUANTILE=0.25, 1-0.25=0.75=3 in Q2
+      // TODO(bjornv): investigate why we need to truncate twice.
+      tmp16no2 = (int16_t)((tmp16 / 2) * 3 / 2);
+      inst->noiseEstLogQuantile[offset + i] -= tmp16no2;
+      if (inst->noiseEstLogQuantile[offset + i] < logval) {
+        // logval is the smallest fixed point representation we can have.
+        // Values below that will correspond to values in the interval
+        // [0, 1], which can't possibly occur.
+        inst->noiseEstLogQuantile[offset + i] = logval;
+      }
+    }
+
+    // update density estimate
+    if (WEBRTC_SPL_ABS_W16(lmagn[i] - inst->noiseEstLogQuantile[offset + i])
+        < WIDTH_Q8) {
+      tmp16no1 = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(
+                   inst->noiseEstDensity[offset + i], countProd, 15);
+      tmp16no2 = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(
+                   width_factor, countDiv, 15);
+      inst->noiseEstDensity[offset + i] = tmp16no1 + tmp16no2;
+    }
+
+
+    if (counter >= END_STARTUP_LONG) {
+      inst->noiseEstCounter[s] = 0;
+      if (inst->blockIndex >= END_STARTUP_LONG) {
+        UpdateNoiseEstimateNeon(inst, offset);
+      }
+    }
+    inst->noiseEstCounter[s]++;
+
+  }  // end loop over simultaneous estimates
+
+  // Sequentially update the noise during startup
+  if (inst->blockIndex < END_STARTUP_LONG) {
+    UpdateNoiseEstimateNeon(inst, offset);
+  }
+
+  for (i = 0; i < inst->magnLen; i++) {
+    noise[i] = (uint32_t)(inst->noiseEstQuantile[i]); // Q(qNoise)
+  }
+  (*q_noise) = (int16_t)inst->qNoise;
+}
+
+// Filter the data in the frequency domain, and create spectrum.
+void WebRtcNsx_PrepareSpectrumNeon(NoiseSuppressionFixedC* inst,
+                                   int16_t* freq_buf) {
+  RTC_DCHECK_EQ(1, inst->magnLen % 8);
+  RTC_DCHECK_EQ(0, inst->anaLen2 % 16);
+
+  // (1) Filtering.
+
+  // Fixed point C code for the next block is as follows:
+  // for (i = 0; i < inst->magnLen; i++) {
+  //   inst->real[i] = (int16_t)((inst->real[i] *
+  //      (int16_t)(inst->noiseSupFilter[i])) >> 14);  // Q(normData-stages)
+  //   inst->imag[i] = (int16_t)((inst->imag[i] *
+  //      (int16_t)(inst->noiseSupFilter[i])) >> 14);  // Q(normData-stages)
+  // }
+
+  int16_t* preal = &inst->real[0];
+  int16_t* pimag = &inst->imag[0];
+  int16_t* pns_filter = (int16_t*)&inst->noiseSupFilter[0];
+  int16_t* pimag_end = pimag + inst->magnLen - 4;
+
+  while (pimag < pimag_end) {
+    int16x8_t real = vld1q_s16(preal);
+    int16x8_t imag = vld1q_s16(pimag);
+    int16x8_t ns_filter = vld1q_s16(pns_filter);
+
+    int32x4_t tmp_r_0 = vmull_s16(vget_low_s16(real), vget_low_s16(ns_filter));
+    int32x4_t tmp_i_0 = vmull_s16(vget_low_s16(imag), vget_low_s16(ns_filter));
+    int32x4_t tmp_r_1 = vmull_s16(vget_high_s16(real),
+                                  vget_high_s16(ns_filter));
+    int32x4_t tmp_i_1 = vmull_s16(vget_high_s16(imag),
+                                  vget_high_s16(ns_filter));
+
+    int16x4_t result_r_0 = vshrn_n_s32(tmp_r_0, 14);
+    int16x4_t result_i_0 = vshrn_n_s32(tmp_i_0, 14);
+    int16x4_t result_r_1 = vshrn_n_s32(tmp_r_1, 14);
+    int16x4_t result_i_1 = vshrn_n_s32(tmp_i_1, 14);
+
+    vst1q_s16(preal, vcombine_s16(result_r_0, result_r_1));
+    vst1q_s16(pimag, vcombine_s16(result_i_0, result_i_1));
+    preal += 8;
+    pimag += 8;
+    pns_filter += 8;
+  }
+
+  // Filter the last element
+  *preal = (int16_t)((*preal * *pns_filter) >> 14);
+  *pimag = (int16_t)((*pimag * *pns_filter) >> 14);
+
+  // (2) Create spectrum.
+
+  // Fixed point C code for the rest of the function is as follows:
+  // freq_buf[0] = inst->real[0];
+  // freq_buf[1] = -inst->imag[0];
+  // for (i = 1, j = 2; i < inst->anaLen2; i += 1, j += 2) {
+  //   freq_buf[j] = inst->real[i];
+  //   freq_buf[j + 1] = -inst->imag[i];
+  // }
+  // freq_buf[inst->anaLen] = inst->real[inst->anaLen2];
+  // freq_buf[inst->anaLen + 1] = -inst->imag[inst->anaLen2];
+
+  preal = &inst->real[0];
+  pimag = &inst->imag[0];
+  pimag_end = pimag + inst->anaLen2;
+  int16_t * freq_buf_start = freq_buf;
+  while (pimag < pimag_end) {
+    // loop unroll
+    int16x8x2_t real_imag_0;
+    int16x8x2_t real_imag_1;
+    real_imag_0.val[1] = vld1q_s16(pimag);
+    real_imag_0.val[0] = vld1q_s16(preal);
+    preal += 8;
+    pimag += 8;
+    real_imag_1.val[1] = vld1q_s16(pimag);
+    real_imag_1.val[0] = vld1q_s16(preal);
+    preal += 8;
+    pimag += 8;
+
+    real_imag_0.val[1] = vnegq_s16(real_imag_0.val[1]);
+    real_imag_1.val[1] = vnegq_s16(real_imag_1.val[1]);
+    vst2q_s16(freq_buf_start, real_imag_0);
+    freq_buf_start += 16;
+    vst2q_s16(freq_buf_start, real_imag_1);
+    freq_buf_start += 16;
+  }
+  freq_buf[inst->anaLen] = inst->real[inst->anaLen2];
+  freq_buf[inst->anaLen + 1] = -inst->imag[inst->anaLen2];
+}
+
+// For the noise supress process, synthesis, read out fully processed segment,
+// and update synthesis buffer.
+void WebRtcNsx_SynthesisUpdateNeon(NoiseSuppressionFixedC* inst,
+                                   int16_t* out_frame,
+                                   int16_t gain_factor) {
+  RTC_DCHECK_EQ(0, inst->anaLen % 16);
+  RTC_DCHECK_EQ(0, inst->blockLen10ms % 16);
+
+  int16_t* preal_start = inst->real;
+  const int16_t* pwindow = inst->window;
+  int16_t* preal_end = preal_start + inst->anaLen;
+  int16_t* psynthesis_buffer = inst->synthesisBuffer;
+
+  while (preal_start < preal_end) {
+    // Loop unroll.
+    int16x8_t window_0 = vld1q_s16(pwindow);
+    int16x8_t real_0 = vld1q_s16(preal_start);
+    int16x8_t synthesis_buffer_0 = vld1q_s16(psynthesis_buffer);
+
+    int16x8_t window_1 = vld1q_s16(pwindow + 8);
+    int16x8_t real_1 = vld1q_s16(preal_start + 8);
+    int16x8_t synthesis_buffer_1 = vld1q_s16(psynthesis_buffer + 8);
+
+    int32x4_t tmp32a_0_low = vmull_s16(vget_low_s16(real_0),
+                                       vget_low_s16(window_0));
+    int32x4_t tmp32a_0_high = vmull_s16(vget_high_s16(real_0),
+                                        vget_high_s16(window_0));
+
+    int32x4_t tmp32a_1_low = vmull_s16(vget_low_s16(real_1),
+                                       vget_low_s16(window_1));
+    int32x4_t tmp32a_1_high = vmull_s16(vget_high_s16(real_1),
+                                        vget_high_s16(window_1));
+
+    int16x4_t tmp16a_0_low = vqrshrn_n_s32(tmp32a_0_low, 14);
+    int16x4_t tmp16a_0_high = vqrshrn_n_s32(tmp32a_0_high, 14);
+
+    int16x4_t tmp16a_1_low = vqrshrn_n_s32(tmp32a_1_low, 14);
+    int16x4_t tmp16a_1_high = vqrshrn_n_s32(tmp32a_1_high, 14);
+
+    int32x4_t tmp32b_0_low = vmull_n_s16(tmp16a_0_low, gain_factor);
+    int32x4_t tmp32b_0_high = vmull_n_s16(tmp16a_0_high, gain_factor);
+
+    int32x4_t tmp32b_1_low = vmull_n_s16(tmp16a_1_low, gain_factor);
+    int32x4_t tmp32b_1_high = vmull_n_s16(tmp16a_1_high, gain_factor);
+
+    int16x4_t tmp16b_0_low = vqrshrn_n_s32(tmp32b_0_low, 13);
+    int16x4_t tmp16b_0_high = vqrshrn_n_s32(tmp32b_0_high, 13);
+
+    int16x4_t tmp16b_1_low = vqrshrn_n_s32(tmp32b_1_low, 13);
+    int16x4_t tmp16b_1_high = vqrshrn_n_s32(tmp32b_1_high, 13);
+
+    synthesis_buffer_0 = vqaddq_s16(vcombine_s16(tmp16b_0_low, tmp16b_0_high),
+                                    synthesis_buffer_0);
+    synthesis_buffer_1 = vqaddq_s16(vcombine_s16(tmp16b_1_low, tmp16b_1_high),
+                                    synthesis_buffer_1);
+    vst1q_s16(psynthesis_buffer, synthesis_buffer_0);
+    vst1q_s16(psynthesis_buffer + 8, synthesis_buffer_1);
+
+    pwindow += 16;
+    preal_start += 16;
+    psynthesis_buffer += 16;
+  }
+
+  // Read out fully processed segment.
+  int16_t * p_start = inst->synthesisBuffer;
+  int16_t * p_end = inst->synthesisBuffer + inst->blockLen10ms;
+  int16_t * p_frame = out_frame;
+  while (p_start < p_end) {
+    int16x8_t frame_0 = vld1q_s16(p_start);
+    vst1q_s16(p_frame, frame_0);
+    p_start += 8;
+    p_frame += 8;
+  }
+
+  // Update synthesis buffer.
+  int16_t* p_start_src = inst->synthesisBuffer + inst->blockLen10ms;
+  int16_t* p_end_src = inst->synthesisBuffer + inst->anaLen;
+  int16_t* p_start_dst = inst->synthesisBuffer;
+  while (p_start_src < p_end_src) {
+    int16x8_t frame = vld1q_s16(p_start_src);
+    vst1q_s16(p_start_dst, frame);
+    p_start_src += 8;
+    p_start_dst += 8;
+  }
+
+  p_start = inst->synthesisBuffer + inst->anaLen - inst->blockLen10ms;
+  p_end = p_start + inst->blockLen10ms;
+  int16x8_t zero = vdupq_n_s16(0);
+  for (;p_start < p_end; p_start += 8) {
+    vst1q_s16(p_start, zero);
+  }
+}
+
+// Update analysis buffer for lower band, and window data before FFT.
+void WebRtcNsx_AnalysisUpdateNeon(NoiseSuppressionFixedC* inst,
+                                  int16_t* out,
+                                  int16_t* new_speech) {
+  RTC_DCHECK_EQ(0, inst->blockLen10ms % 16);
+  RTC_DCHECK_EQ(0, inst->anaLen % 16);
+
+  // For lower band update analysis buffer.
+  // memcpy(inst->analysisBuffer, inst->analysisBuffer + inst->blockLen10ms,
+  //     (inst->anaLen - inst->blockLen10ms) * sizeof(*inst->analysisBuffer));
+  int16_t* p_start_src = inst->analysisBuffer + inst->blockLen10ms;
+  int16_t* p_end_src = inst->analysisBuffer + inst->anaLen;
+  int16_t* p_start_dst = inst->analysisBuffer;
+  while (p_start_src < p_end_src) {
+    int16x8_t frame = vld1q_s16(p_start_src);
+    vst1q_s16(p_start_dst, frame);
+
+    p_start_src += 8;
+    p_start_dst += 8;
+  }
+
+  // memcpy(inst->analysisBuffer + inst->anaLen - inst->blockLen10ms,
+  //     new_speech, inst->blockLen10ms * sizeof(*inst->analysisBuffer));
+  p_start_src = new_speech;
+  p_end_src = new_speech + inst->blockLen10ms;
+  p_start_dst = inst->analysisBuffer + inst->anaLen - inst->blockLen10ms;
+  while (p_start_src < p_end_src) {
+    int16x8_t frame = vld1q_s16(p_start_src);
+    vst1q_s16(p_start_dst, frame);
+
+    p_start_src += 8;
+    p_start_dst += 8;
+  }
+
+  // Window data before FFT.
+  int16_t* p_start_window = (int16_t*) inst->window;
+  int16_t* p_start_buffer = inst->analysisBuffer;
+  int16_t* p_end_buffer = inst->analysisBuffer + inst->anaLen;
+  int16_t* p_start_out = out;
+
+  // Load the first element to reduce pipeline bubble.
+  int16x8_t window = vld1q_s16(p_start_window);
+  int16x8_t buffer = vld1q_s16(p_start_buffer);
+  p_start_window += 8;
+  p_start_buffer += 8;
+
+  while (p_start_buffer < p_end_buffer) {
+    // Unroll loop.
+    int32x4_t tmp32_low = vmull_s16(vget_low_s16(window), vget_low_s16(buffer));
+    int32x4_t tmp32_high = vmull_s16(vget_high_s16(window),
+                                     vget_high_s16(buffer));
+    window = vld1q_s16(p_start_window);
+    buffer = vld1q_s16(p_start_buffer);
+
+    int16x4_t result_low = vrshrn_n_s32(tmp32_low, 14);
+    int16x4_t result_high = vrshrn_n_s32(tmp32_high, 14);
+    vst1q_s16(p_start_out, vcombine_s16(result_low, result_high));
+
+    p_start_buffer += 8;
+    p_start_window += 8;
+    p_start_out += 8;
+  }
+  int32x4_t tmp32_low = vmull_s16(vget_low_s16(window), vget_low_s16(buffer));
+  int32x4_t tmp32_high = vmull_s16(vget_high_s16(window),
+                                   vget_high_s16(buffer));
+
+  int16x4_t result_low = vrshrn_n_s32(tmp32_low, 14);
+  int16x4_t result_high = vrshrn_n_s32(tmp32_high, 14);
+  vst1q_s16(p_start_out, vcombine_s16(result_low, result_high));
+}
diff --git a/modules/audio_processing/legacy_ns/nsx_defines.h b/modules/audio_processing/legacy_ns/nsx_defines.h
new file mode 100644
index 0000000..18db311
--- /dev/null
+++ b/modules/audio_processing/legacy_ns/nsx_defines.h
@@ -0,0 +1,74 @@
+/*
+ *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_AUDIO_PROCESSING_LEGACY_NS_NSX_DEFINES_H_
+#define MODULES_AUDIO_PROCESSING_LEGACY_NS_NSX_DEFINES_H_
+
+#define ANAL_BLOCKL_MAX 256  /* Max analysis block length */
+#define HALF_ANAL_BLOCKL 129 /* Half max analysis block length + 1 */
+#define NUM_HIGH_BANDS_MAX 2 /* Max number of high bands */
+#define SIMULT 3
+#define END_STARTUP_LONG 200
+#define END_STARTUP_SHORT 50
+#define FACTOR_Q16 2621440     /* 40 in Q16 */
+#define FACTOR_Q7 5120         /* 40 in Q7 */
+#define FACTOR_Q7_STARTUP 1024 /* 8 in Q7 */
+#define WIDTH_Q8 3             /* 0.01 in Q8 (or 25 ) */
+
+/* PARAMETERS FOR NEW METHOD */
+#define DD_PR_SNR_Q11 2007         /* ~= Q11(0.98) DD update of prior SNR */
+#define ONE_MINUS_DD_PR_SNR_Q11 41 /* DD update of prior SNR */
+#define SPECT_FLAT_TAVG_Q14 \
+  4915 /* (0.30) tavg parameter for spectral flatness measure */
+#define SPECT_DIFF_TAVG_Q8 \
+  77 /* (0.30) tavg parameter for spectral flatness measure */
+#define PRIOR_UPDATE_Q14 1638 /* Q14(0.1) Update parameter of prior model */
+#define NOISE_UPDATE_Q8 26    /* 26 ~= Q8(0.1) Update parameter for noise */
+
+/* Probability threshold for noise state in speech/noise likelihood. */
+#define ONE_MINUS_PROB_RANGE_Q8 205 /* 205 ~= Q8(0.8) */
+#define HIST_PAR_EST 1000 /* Histogram size for estimation of parameters */
+
+/* FEATURE EXTRACTION CONFIG  */
+/* Bin size of histogram */
+#define BIN_SIZE_LRT 10
+/* Scale parameters: multiply dominant peaks of the histograms by scale factor
+ * to obtain. */
+/* Thresholds for prior model */
+#define FACTOR_1_LRT_DIFF \
+  6 /* For LRT and spectral difference (5 times bigger) */
+/* For spectral_flatness: used when noise is flatter than speech (10 times
+ * bigger). */
+#define FACTOR_2_FLAT_Q10 922
+/* Peak limit for spectral flatness (varies between 0 and 1) */
+#define THRES_PEAK_FLAT 24 /* * 2 * BIN_SIZE_FLAT_FX */
+/* Limit on spacing of two highest peaks in histogram: spacing determined by bin
+ * size. */
+#define LIM_PEAK_SPACE_FLAT_DIFF 4 /* * 2 * BIN_SIZE_DIFF_FX */
+/* Limit on relevance of second peak */
+#define LIM_PEAK_WEIGHT_FLAT_DIFF 2
+#define THRES_FLUCT_LRT \
+  10240 /* = 20 * inst->modelUpdate; fluctuation limit of LRT feat. */
+/* Limit on the max and min values for the feature thresholds */
+#define MAX_FLAT_Q10 38912 /*  * 2 * BIN_SIZE_FLAT_FX */
+#define MIN_FLAT_Q10 4096  /*  * 2 * BIN_SIZE_FLAT_FX */
+#define MAX_DIFF 100       /* * 2 * BIN_SIZE_DIFF_FX */
+#define MIN_DIFF 16        /* * 2 * BIN_SIZE_DIFF_FX */
+/* Criteria of weight of histogram peak  to accept/reject feature */
+#define THRES_WEIGHT_FLAT_DIFF \
+  154 /*(int)(0.3*(inst->modelUpdate)) for flatness and difference */
+
+#define STAT_UPDATES 9 /* Update every 512 = 1 << 9 block */
+#define ONE_MINUS_GAMMA_PAUSE_Q8 \
+  13 /* ~= Q8(0.05) Update for conservative noise estimate */
+#define GAMMA_NOISE_TRANS_AND_SPEECH_Q8 \
+  3 /* ~= Q8(0.01) Update for transition and noise region */
+
+#endif /* MODULES_AUDIO_PROCESSING_LEGACY_NS_NSX_DEFINES_H_ */
diff --git a/modules/audio_processing/legacy_ns/windows_private.h b/modules/audio_processing/legacy_ns/windows_private.h
new file mode 100644
index 0000000..21bb7d4
--- /dev/null
+++ b/modules/audio_processing/legacy_ns/windows_private.h
@@ -0,0 +1,553 @@
+/*
+ *  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_AUDIO_PROCESSING_LEGACY_NS_WINDOWS_PRIVATE_H_
+#define MODULES_AUDIO_PROCESSING_LEGACY_NS_WINDOWS_PRIVATE_H_
+
+// Hanning window for 4ms 16kHz
+static const float kHanning64w128[128] = {
+    0.00000000000000f, 0.02454122852291f, 0.04906767432742f, 0.07356456359967f,
+    0.09801714032956f, 0.12241067519922f, 0.14673047445536f, 0.17096188876030f,
+    0.19509032201613f, 0.21910124015687f, 0.24298017990326f, 0.26671275747490f,
+    0.29028467725446f, 0.31368174039889f, 0.33688985339222f, 0.35989503653499f,
+    0.38268343236509f, 0.40524131400499f, 0.42755509343028f, 0.44961132965461f,
+    0.47139673682600f, 0.49289819222978f, 0.51410274419322f, 0.53499761988710f,
+    0.55557023301960f, 0.57580819141785f, 0.59569930449243f, 0.61523159058063f,
+    0.63439328416365f, 0.65317284295378f, 0.67155895484702f, 0.68954054473707f,
+    0.70710678118655f, 0.72424708295147f, 0.74095112535496f, 0.75720884650648f,
+    0.77301045336274f, 0.78834642762661f, 0.80320753148064f, 0.81758481315158f,
+    0.83146961230255f, 0.84485356524971f, 0.85772861000027f, 0.87008699110871f,
+    0.88192126434835f, 0.89322430119552f, 0.90398929312344f, 0.91420975570353f,
+    0.92387953251129f, 0.93299279883474f, 0.94154406518302f, 0.94952818059304f,
+    0.95694033573221f, 0.96377606579544f, 0.97003125319454f, 0.97570213003853f,
+    0.98078528040323f, 0.98527764238894f, 0.98917650996478f, 0.99247953459871f,
+    0.99518472667220f, 0.99729045667869f, 0.99879545620517f, 0.99969881869620f,
+    1.00000000000000f, 0.99969881869620f, 0.99879545620517f, 0.99729045667869f,
+    0.99518472667220f, 0.99247953459871f, 0.98917650996478f, 0.98527764238894f,
+    0.98078528040323f, 0.97570213003853f, 0.97003125319454f, 0.96377606579544f,
+    0.95694033573221f, 0.94952818059304f, 0.94154406518302f, 0.93299279883474f,
+    0.92387953251129f, 0.91420975570353f, 0.90398929312344f, 0.89322430119552f,
+    0.88192126434835f, 0.87008699110871f, 0.85772861000027f, 0.84485356524971f,
+    0.83146961230255f, 0.81758481315158f, 0.80320753148064f, 0.78834642762661f,
+    0.77301045336274f, 0.75720884650648f, 0.74095112535496f, 0.72424708295147f,
+    0.70710678118655f, 0.68954054473707f, 0.67155895484702f, 0.65317284295378f,
+    0.63439328416365f, 0.61523159058063f, 0.59569930449243f, 0.57580819141785f,
+    0.55557023301960f, 0.53499761988710f, 0.51410274419322f, 0.49289819222978f,
+    0.47139673682600f, 0.44961132965461f, 0.42755509343028f, 0.40524131400499f,
+    0.38268343236509f, 0.35989503653499f, 0.33688985339222f, 0.31368174039889f,
+    0.29028467725446f, 0.26671275747490f, 0.24298017990326f, 0.21910124015687f,
+    0.19509032201613f, 0.17096188876030f, 0.14673047445536f, 0.12241067519922f,
+    0.09801714032956f, 0.07356456359967f, 0.04906767432742f, 0.02454122852291f};
+
+// hybrib Hanning & flat window
+static const float kBlocks80w128[128] = {
+    0.00000000f, 0.03271908f, 0.06540313f, 0.09801714f, 0.13052619f,
+    0.16289547f, 0.19509032f, 0.22707626f, 0.25881905f, 0.29028468f,
+    0.32143947f, 0.35225005f, 0.38268343f, 0.41270703f, 0.44228869f,
+    0.47139674f, 0.50000000f, 0.52806785f, 0.55557023f, 0.58247770f,
+    0.60876143f, 0.63439328f, 0.65934582f, 0.68359230f, 0.70710678f,
+    0.72986407f, 0.75183981f, 0.77301045f, 0.79335334f, 0.81284668f,
+    0.83146961f, 0.84920218f, 0.86602540f, 0.88192126f, 0.89687274f,
+    0.91086382f, 0.92387953f, 0.93590593f, 0.94693013f, 0.95694034f,
+    0.96592583f, 0.97387698f, 0.98078528f, 0.98664333f, 0.99144486f,
+    0.99518473f, 0.99785892f, 0.99946459f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 0.99946459f, 0.99785892f, 0.99518473f, 0.99144486f,
+    0.98664333f, 0.98078528f, 0.97387698f, 0.96592583f, 0.95694034f,
+    0.94693013f, 0.93590593f, 0.92387953f, 0.91086382f, 0.89687274f,
+    0.88192126f, 0.86602540f, 0.84920218f, 0.83146961f, 0.81284668f,
+    0.79335334f, 0.77301045f, 0.75183981f, 0.72986407f, 0.70710678f,
+    0.68359230f, 0.65934582f, 0.63439328f, 0.60876143f, 0.58247770f,
+    0.55557023f, 0.52806785f, 0.50000000f, 0.47139674f, 0.44228869f,
+    0.41270703f, 0.38268343f, 0.35225005f, 0.32143947f, 0.29028468f,
+    0.25881905f, 0.22707626f, 0.19509032f, 0.16289547f, 0.13052619f,
+    0.09801714f, 0.06540313f, 0.03271908f};
+
+// hybrib Hanning & flat window
+static const float kBlocks160w256[256] = {
+    0.00000000f, 0.01636173f, 0.03271908f, 0.04906767f, 0.06540313f,
+    0.08172107f, 0.09801714f, 0.11428696f, 0.13052619f, 0.14673047f,
+    0.16289547f, 0.17901686f, 0.19509032f, 0.21111155f, 0.22707626f,
+    0.24298018f, 0.25881905f, 0.27458862f, 0.29028468f, 0.30590302f,
+    0.32143947f, 0.33688985f, 0.35225005f, 0.36751594f, 0.38268343f,
+    0.39774847f, 0.41270703f, 0.42755509f, 0.44228869f, 0.45690388f,
+    0.47139674f, 0.48576339f, 0.50000000f, 0.51410274f, 0.52806785f,
+    0.54189158f, 0.55557023f, 0.56910015f, 0.58247770f, 0.59569930f,
+    0.60876143f, 0.62166057f, 0.63439328f, 0.64695615f, 0.65934582f,
+    0.67155895f, 0.68359230f, 0.69544264f, 0.70710678f, 0.71858162f,
+    0.72986407f, 0.74095113f, 0.75183981f, 0.76252720f, 0.77301045f,
+    0.78328675f, 0.79335334f, 0.80320753f, 0.81284668f, 0.82226822f,
+    0.83146961f, 0.84044840f, 0.84920218f, 0.85772861f, 0.86602540f,
+    0.87409034f, 0.88192126f, 0.88951608f, 0.89687274f, 0.90398929f,
+    0.91086382f, 0.91749450f, 0.92387953f, 0.93001722f, 0.93590593f,
+    0.94154407f, 0.94693013f, 0.95206268f, 0.95694034f, 0.96156180f,
+    0.96592583f, 0.97003125f, 0.97387698f, 0.97746197f, 0.98078528f,
+    0.98384601f, 0.98664333f, 0.98917651f, 0.99144486f, 0.99344778f,
+    0.99518473f, 0.99665524f, 0.99785892f, 0.99879546f, 0.99946459f,
+    0.99986614f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 0.99986614f, 0.99946459f, 0.99879546f, 0.99785892f,
+    0.99665524f, 0.99518473f, 0.99344778f, 0.99144486f, 0.98917651f,
+    0.98664333f, 0.98384601f, 0.98078528f, 0.97746197f, 0.97387698f,
+    0.97003125f, 0.96592583f, 0.96156180f, 0.95694034f, 0.95206268f,
+    0.94693013f, 0.94154407f, 0.93590593f, 0.93001722f, 0.92387953f,
+    0.91749450f, 0.91086382f, 0.90398929f, 0.89687274f, 0.88951608f,
+    0.88192126f, 0.87409034f, 0.86602540f, 0.85772861f, 0.84920218f,
+    0.84044840f, 0.83146961f, 0.82226822f, 0.81284668f, 0.80320753f,
+    0.79335334f, 0.78328675f, 0.77301045f, 0.76252720f, 0.75183981f,
+    0.74095113f, 0.72986407f, 0.71858162f, 0.70710678f, 0.69544264f,
+    0.68359230f, 0.67155895f, 0.65934582f, 0.64695615f, 0.63439328f,
+    0.62166057f, 0.60876143f, 0.59569930f, 0.58247770f, 0.56910015f,
+    0.55557023f, 0.54189158f, 0.52806785f, 0.51410274f, 0.50000000f,
+    0.48576339f, 0.47139674f, 0.45690388f, 0.44228869f, 0.42755509f,
+    0.41270703f, 0.39774847f, 0.38268343f, 0.36751594f, 0.35225005f,
+    0.33688985f, 0.32143947f, 0.30590302f, 0.29028468f, 0.27458862f,
+    0.25881905f, 0.24298018f, 0.22707626f, 0.21111155f, 0.19509032f,
+    0.17901686f, 0.16289547f, 0.14673047f, 0.13052619f, 0.11428696f,
+    0.09801714f, 0.08172107f, 0.06540313f, 0.04906767f, 0.03271908f,
+    0.01636173f};
+
+// hybrib Hanning & flat window: for 20ms
+static const float kBlocks320w512[512] = {
+    0.00000000f, 0.00818114f, 0.01636173f, 0.02454123f, 0.03271908f,
+    0.04089475f, 0.04906767f, 0.05723732f, 0.06540313f, 0.07356456f,
+    0.08172107f, 0.08987211f, 0.09801714f, 0.10615561f, 0.11428696f,
+    0.12241068f, 0.13052619f, 0.13863297f, 0.14673047f, 0.15481816f,
+    0.16289547f, 0.17096189f, 0.17901686f, 0.18705985f, 0.19509032f,
+    0.20310773f, 0.21111155f, 0.21910124f, 0.22707626f, 0.23503609f,
+    0.24298018f, 0.25090801f, 0.25881905f, 0.26671276f, 0.27458862f,
+    0.28244610f, 0.29028468f, 0.29810383f, 0.30590302f, 0.31368174f,
+    0.32143947f, 0.32917568f, 0.33688985f, 0.34458148f, 0.35225005f,
+    0.35989504f, 0.36751594f, 0.37511224f, 0.38268343f, 0.39022901f,
+    0.39774847f, 0.40524131f, 0.41270703f, 0.42014512f, 0.42755509f,
+    0.43493645f, 0.44228869f, 0.44961133f, 0.45690388f, 0.46416584f,
+    0.47139674f, 0.47859608f, 0.48576339f, 0.49289819f, 0.50000000f,
+    0.50706834f, 0.51410274f, 0.52110274f, 0.52806785f, 0.53499762f,
+    0.54189158f, 0.54874927f, 0.55557023f, 0.56235401f, 0.56910015f,
+    0.57580819f, 0.58247770f, 0.58910822f, 0.59569930f, 0.60225052f,
+    0.60876143f, 0.61523159f, 0.62166057f, 0.62804795f, 0.63439328f,
+    0.64069616f, 0.64695615f, 0.65317284f, 0.65934582f, 0.66547466f,
+    0.67155895f, 0.67759830f, 0.68359230f, 0.68954054f, 0.69544264f,
+    0.70129818f, 0.70710678f, 0.71286806f, 0.71858162f, 0.72424708f,
+    0.72986407f, 0.73543221f, 0.74095113f, 0.74642045f, 0.75183981f,
+    0.75720885f, 0.76252720f, 0.76779452f, 0.77301045f, 0.77817464f,
+    0.78328675f, 0.78834643f, 0.79335334f, 0.79830715f, 0.80320753f,
+    0.80805415f, 0.81284668f, 0.81758481f, 0.82226822f, 0.82689659f,
+    0.83146961f, 0.83598698f, 0.84044840f, 0.84485357f, 0.84920218f,
+    0.85349396f, 0.85772861f, 0.86190585f, 0.86602540f, 0.87008699f,
+    0.87409034f, 0.87803519f, 0.88192126f, 0.88574831f, 0.88951608f,
+    0.89322430f, 0.89687274f, 0.90046115f, 0.90398929f, 0.90745693f,
+    0.91086382f, 0.91420976f, 0.91749450f, 0.92071783f, 0.92387953f,
+    0.92697940f, 0.93001722f, 0.93299280f, 0.93590593f, 0.93875641f,
+    0.94154407f, 0.94426870f, 0.94693013f, 0.94952818f, 0.95206268f,
+    0.95453345f, 0.95694034f, 0.95928317f, 0.96156180f, 0.96377607f,
+    0.96592583f, 0.96801094f, 0.97003125f, 0.97198664f, 0.97387698f,
+    0.97570213f, 0.97746197f, 0.97915640f, 0.98078528f, 0.98234852f,
+    0.98384601f, 0.98527764f, 0.98664333f, 0.98794298f, 0.98917651f,
+    0.99034383f, 0.99144486f, 0.99247953f, 0.99344778f, 0.99434953f,
+    0.99518473f, 0.99595331f, 0.99665524f, 0.99729046f, 0.99785892f,
+    0.99836060f, 0.99879546f, 0.99916346f, 0.99946459f, 0.99969882f,
+    0.99986614f, 0.99996653f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
+    1.00000000f, 0.99996653f, 0.99986614f, 0.99969882f, 0.99946459f,
+    0.99916346f, 0.99879546f, 0.99836060f, 0.99785892f, 0.99729046f,
+    0.99665524f, 0.99595331f, 0.99518473f, 0.99434953f, 0.99344778f,
+    0.99247953f, 0.99144486f, 0.99034383f, 0.98917651f, 0.98794298f,
+    0.98664333f, 0.98527764f, 0.98384601f, 0.98234852f, 0.98078528f,
+    0.97915640f, 0.97746197f, 0.97570213f, 0.97387698f, 0.97198664f,
+    0.97003125f, 0.96801094f, 0.96592583f, 0.96377607f, 0.96156180f,
+    0.95928317f, 0.95694034f, 0.95453345f, 0.95206268f, 0.94952818f,
+    0.94693013f, 0.94426870f, 0.94154407f, 0.93875641f, 0.93590593f,
+    0.93299280f, 0.93001722f, 0.92697940f, 0.92387953f, 0.92071783f,
+    0.91749450f, 0.91420976f, 0.91086382f, 0.90745693f, 0.90398929f,
+    0.90046115f, 0.89687274f, 0.89322430f, 0.88951608f, 0.88574831f,
+    0.88192126f, 0.87803519f, 0.87409034f, 0.87008699f, 0.86602540f,
+    0.86190585f, 0.85772861f, 0.85349396f, 0.84920218f, 0.84485357f,
+    0.84044840f, 0.83598698f, 0.83146961f, 0.82689659f, 0.82226822f,
+    0.81758481f, 0.81284668f, 0.80805415f, 0.80320753f, 0.79830715f,
+    0.79335334f, 0.78834643f, 0.78328675f, 0.77817464f, 0.77301045f,
+    0.76779452f, 0.76252720f, 0.75720885f, 0.75183981f, 0.74642045f,
+    0.74095113f, 0.73543221f, 0.72986407f, 0.72424708f, 0.71858162f,
+    0.71286806f, 0.70710678f, 0.70129818f, 0.69544264f, 0.68954054f,
+    0.68359230f, 0.67759830f, 0.67155895f, 0.66547466f, 0.65934582f,
+    0.65317284f, 0.64695615f, 0.64069616f, 0.63439328f, 0.62804795f,
+    0.62166057f, 0.61523159f, 0.60876143f, 0.60225052f, 0.59569930f,
+    0.58910822f, 0.58247770f, 0.57580819f, 0.56910015f, 0.56235401f,
+    0.55557023f, 0.54874927f, 0.54189158f, 0.53499762f, 0.52806785f,
+    0.52110274f, 0.51410274f, 0.50706834f, 0.50000000f, 0.49289819f,
+    0.48576339f, 0.47859608f, 0.47139674f, 0.46416584f, 0.45690388f,
+    0.44961133f, 0.44228869f, 0.43493645f, 0.42755509f, 0.42014512f,
+    0.41270703f, 0.40524131f, 0.39774847f, 0.39022901f, 0.38268343f,
+    0.37511224f, 0.36751594f, 0.35989504f, 0.35225005f, 0.34458148f,
+    0.33688985f, 0.32917568f, 0.32143947f, 0.31368174f, 0.30590302f,
+    0.29810383f, 0.29028468f, 0.28244610f, 0.27458862f, 0.26671276f,
+    0.25881905f, 0.25090801f, 0.24298018f, 0.23503609f, 0.22707626f,
+    0.21910124f, 0.21111155f, 0.20310773f, 0.19509032f, 0.18705985f,
+    0.17901686f, 0.17096189f, 0.16289547f, 0.15481816f, 0.14673047f,
+    0.13863297f, 0.13052619f, 0.12241068f, 0.11428696f, 0.10615561f,
+    0.09801714f, 0.08987211f, 0.08172107f, 0.07356456f, 0.06540313f,
+    0.05723732f, 0.04906767f, 0.04089475f, 0.03271908f, 0.02454123f,
+    0.01636173f, 0.00818114f};
+
+// Hanning window: for 15ms at 16kHz with symmetric zeros
+static const float kBlocks240w512[512] = {
+    0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f,
+    0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f,
+    0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f,
+    0.00000000f, 0.00000000f, 0.00654494f, 0.01308960f, 0.01963369f,
+    0.02617695f, 0.03271908f, 0.03925982f, 0.04579887f, 0.05233596f,
+    0.05887080f, 0.06540313f, 0.07193266f, 0.07845910f, 0.08498218f,
+    0.09150162f, 0.09801714f, 0.10452846f, 0.11103531f, 0.11753740f,
+    0.12403446f, 0.13052620f, 0.13701233f, 0.14349262f, 0.14996676f,
+    0.15643448f, 0.16289547f, 0.16934951f, 0.17579629f, 0.18223552f,
+    0.18866697f, 0.19509032f, 0.20150533f, 0.20791170f, 0.21430916f,
+    0.22069745f, 0.22707628f, 0.23344538f, 0.23980446f, 0.24615330f,
+    0.25249159f, 0.25881904f, 0.26513544f, 0.27144045f, 0.27773386f,
+    0.28401536f, 0.29028466f, 0.29654160f, 0.30278578f, 0.30901700f,
+    0.31523499f, 0.32143945f, 0.32763019f, 0.33380687f, 0.33996925f,
+    0.34611708f, 0.35225007f, 0.35836795f, 0.36447051f, 0.37055743f,
+    0.37662852f, 0.38268346f, 0.38872197f, 0.39474389f, 0.40074885f,
+    0.40673664f, 0.41270703f, 0.41865975f, 0.42459452f, 0.43051112f,
+    0.43640924f, 0.44228873f, 0.44814920f, 0.45399052f, 0.45981237f,
+    0.46561453f, 0.47139674f, 0.47715878f, 0.48290035f, 0.48862126f,
+    0.49432120f, 0.50000000f, 0.50565743f, 0.51129311f, 0.51690692f,
+    0.52249855f, 0.52806789f, 0.53361452f, 0.53913832f, 0.54463905f,
+    0.55011642f, 0.55557024f, 0.56100029f, 0.56640625f, 0.57178795f,
+    0.57714522f, 0.58247769f, 0.58778524f, 0.59306765f, 0.59832460f,
+    0.60355598f, 0.60876143f, 0.61394083f, 0.61909395f, 0.62422055f,
+    0.62932038f, 0.63439333f, 0.63943899f, 0.64445734f, 0.64944810f,
+    0.65441096f, 0.65934587f, 0.66425246f, 0.66913062f, 0.67398012f,
+    0.67880076f, 0.68359232f, 0.68835455f, 0.69308740f, 0.69779050f,
+    0.70246369f, 0.70710677f, 0.71171963f, 0.71630198f, 0.72085363f,
+    0.72537440f, 0.72986406f, 0.73432255f, 0.73874950f, 0.74314487f,
+    0.74750835f, 0.75183982f, 0.75613910f, 0.76040596f, 0.76464027f,
+    0.76884186f, 0.77301043f, 0.77714598f, 0.78124821f, 0.78531694f,
+    0.78935206f, 0.79335338f, 0.79732066f, 0.80125386f, 0.80515265f,
+    0.80901700f, 0.81284672f, 0.81664157f, 0.82040149f, 0.82412618f,
+    0.82781565f, 0.83146966f, 0.83508795f, 0.83867061f, 0.84221727f,
+    0.84572780f, 0.84920216f, 0.85264021f, 0.85604161f, 0.85940641f,
+    0.86273444f, 0.86602545f, 0.86927933f, 0.87249607f, 0.87567532f,
+    0.87881714f, 0.88192129f, 0.88498765f, 0.88801610f, 0.89100653f,
+    0.89395881f, 0.89687276f, 0.89974827f, 0.90258533f, 0.90538365f,
+    0.90814316f, 0.91086388f, 0.91354549f, 0.91618794f, 0.91879123f,
+    0.92135513f, 0.92387950f, 0.92636442f, 0.92880958f, 0.93121493f,
+    0.93358046f, 0.93590593f, 0.93819135f, 0.94043654f, 0.94264150f,
+    0.94480604f, 0.94693011f, 0.94901365f, 0.95105654f, 0.95305866f,
+    0.95501995f, 0.95694035f, 0.95881975f, 0.96065807f, 0.96245527f,
+    0.96421117f, 0.96592581f, 0.96759909f, 0.96923089f, 0.97082120f,
+    0.97236991f, 0.97387701f, 0.97534233f, 0.97676587f, 0.97814763f,
+    0.97948742f, 0.98078531f, 0.98204112f, 0.98325491f, 0.98442656f,
+    0.98555607f, 0.98664331f, 0.98768836f, 0.98869103f, 0.98965138f,
+    0.99056935f, 0.99144489f, 0.99227792f, 0.99306846f, 0.99381649f,
+    0.99452192f, 0.99518472f, 0.99580491f, 0.99638247f, 0.99691731f,
+    0.99740952f, 0.99785894f, 0.99826562f, 0.99862951f, 0.99895066f,
+    0.99922901f, 0.99946457f, 0.99965733f, 0.99980724f, 0.99991435f,
+    0.99997860f, 1.00000000f, 0.99997860f, 0.99991435f, 0.99980724f,
+    0.99965733f, 0.99946457f, 0.99922901f, 0.99895066f, 0.99862951f,
+    0.99826562f, 0.99785894f, 0.99740946f, 0.99691731f, 0.99638247f,
+    0.99580491f, 0.99518472f, 0.99452192f, 0.99381644f, 0.99306846f,
+    0.99227792f, 0.99144489f, 0.99056935f, 0.98965138f, 0.98869103f,
+    0.98768836f, 0.98664331f, 0.98555607f, 0.98442656f, 0.98325491f,
+    0.98204112f, 0.98078525f, 0.97948742f, 0.97814757f, 0.97676587f,
+    0.97534227f, 0.97387695f, 0.97236991f, 0.97082120f, 0.96923089f,
+    0.96759909f, 0.96592581f, 0.96421117f, 0.96245521f, 0.96065807f,
+    0.95881969f, 0.95694029f, 0.95501995f, 0.95305860f, 0.95105648f,
+    0.94901365f, 0.94693011f, 0.94480604f, 0.94264150f, 0.94043654f,
+    0.93819129f, 0.93590593f, 0.93358046f, 0.93121493f, 0.92880952f,
+    0.92636436f, 0.92387950f, 0.92135507f, 0.91879123f, 0.91618794f,
+    0.91354543f, 0.91086382f, 0.90814310f, 0.90538365f, 0.90258527f,
+    0.89974827f, 0.89687276f, 0.89395875f, 0.89100647f, 0.88801610f,
+    0.88498759f, 0.88192123f, 0.87881714f, 0.87567532f, 0.87249595f,
+    0.86927933f, 0.86602539f, 0.86273432f, 0.85940641f, 0.85604161f,
+    0.85264009f, 0.84920216f, 0.84572780f, 0.84221715f, 0.83867055f,
+    0.83508795f, 0.83146954f, 0.82781565f, 0.82412612f, 0.82040137f,
+    0.81664157f, 0.81284660f, 0.80901700f, 0.80515265f, 0.80125374f,
+    0.79732066f, 0.79335332f, 0.78935200f, 0.78531694f, 0.78124815f,
+    0.77714586f, 0.77301049f, 0.76884180f, 0.76464021f, 0.76040596f,
+    0.75613904f, 0.75183970f, 0.74750835f, 0.74314481f, 0.73874938f,
+    0.73432249f, 0.72986400f, 0.72537428f, 0.72085363f, 0.71630186f,
+    0.71171951f, 0.70710677f, 0.70246363f, 0.69779032f, 0.69308734f,
+    0.68835449f, 0.68359220f, 0.67880070f, 0.67398006f, 0.66913044f,
+    0.66425240f, 0.65934575f, 0.65441096f, 0.64944804f, 0.64445722f,
+    0.63943905f, 0.63439327f, 0.62932026f, 0.62422055f, 0.61909389f,
+    0.61394072f, 0.60876143f, 0.60355592f, 0.59832448f, 0.59306765f,
+    0.58778518f, 0.58247757f, 0.57714522f, 0.57178789f, 0.56640613f,
+    0.56100023f, 0.55557019f, 0.55011630f, 0.54463905f, 0.53913826f,
+    0.53361434f, 0.52806783f, 0.52249849f, 0.51690674f, 0.51129305f,
+    0.50565726f, 0.50000006f, 0.49432117f, 0.48862115f, 0.48290038f,
+    0.47715873f, 0.47139663f, 0.46561456f, 0.45981231f, 0.45399037f,
+    0.44814920f, 0.44228864f, 0.43640912f, 0.43051112f, 0.42459446f,
+    0.41865960f, 0.41270703f, 0.40673658f, 0.40074870f, 0.39474386f,
+    0.38872188f, 0.38268328f, 0.37662849f, 0.37055734f, 0.36447033f,
+    0.35836792f, 0.35224995f, 0.34611690f, 0.33996922f, 0.33380675f,
+    0.32763001f, 0.32143945f, 0.31523487f, 0.30901679f, 0.30278572f,
+    0.29654145f, 0.29028472f, 0.28401530f, 0.27773371f, 0.27144048f,
+    0.26513538f, 0.25881892f, 0.25249159f, 0.24615324f, 0.23980433f,
+    0.23344538f, 0.22707619f, 0.22069728f, 0.21430916f, 0.20791161f,
+    0.20150517f, 0.19509031f, 0.18866688f, 0.18223536f, 0.17579627f,
+    0.16934940f, 0.16289529f, 0.15643445f, 0.14996666f, 0.14349243f,
+    0.13701232f, 0.13052608f, 0.12403426f, 0.11753736f, 0.11103519f,
+    0.10452849f, 0.09801710f, 0.09150149f, 0.08498220f, 0.07845904f,
+    0.07193252f, 0.06540315f, 0.05887074f, 0.05233581f, 0.04579888f,
+    0.03925974f, 0.03271893f, 0.02617695f, 0.01963361f, 0.01308943f,
+    0.00654493f, 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f,
+    0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f,
+    0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f,
+    0.00000000f, 0.00000000f};
+
+// Hanning window: for 30ms with 1024 fft with symmetric zeros at 16kHz
+static const float kBlocks480w1024[1024] = {
+    0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f,
+    0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f,
+    0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f,
+    0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f,
+    0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f,
+    0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f,
+    0.00000000f, 0.00000000f, 0.00000000f, 0.00327249f, 0.00654494f,
+    0.00981732f, 0.01308960f, 0.01636173f, 0.01963369f, 0.02290544f,
+    0.02617695f, 0.02944817f, 0.03271908f, 0.03598964f, 0.03925982f,
+    0.04252957f, 0.04579887f, 0.04906768f, 0.05233596f, 0.05560368f,
+    0.05887080f, 0.06213730f, 0.06540313f, 0.06866825f, 0.07193266f,
+    0.07519628f, 0.07845910f, 0.08172107f, 0.08498218f, 0.08824237f,
+    0.09150162f, 0.09475989f, 0.09801714f, 0.10127335f, 0.10452846f,
+    0.10778246f, 0.11103531f, 0.11428697f, 0.11753740f, 0.12078657f,
+    0.12403446f, 0.12728101f, 0.13052620f, 0.13376999f, 0.13701233f,
+    0.14025325f, 0.14349262f, 0.14673047f, 0.14996676f, 0.15320145f,
+    0.15643448f, 0.15966582f, 0.16289547f, 0.16612339f, 0.16934951f,
+    0.17257382f, 0.17579629f, 0.17901687f, 0.18223552f, 0.18545224f,
+    0.18866697f, 0.19187967f, 0.19509032f, 0.19829889f, 0.20150533f,
+    0.20470962f, 0.20791170f, 0.21111156f, 0.21430916f, 0.21750447f,
+    0.22069745f, 0.22388805f, 0.22707628f, 0.23026206f, 0.23344538f,
+    0.23662618f, 0.23980446f, 0.24298020f, 0.24615330f, 0.24932377f,
+    0.25249159f, 0.25565669f, 0.25881904f, 0.26197866f, 0.26513544f,
+    0.26828939f, 0.27144045f, 0.27458861f, 0.27773386f, 0.28087610f,
+    0.28401536f, 0.28715158f, 0.29028466f, 0.29341471f, 0.29654160f,
+    0.29966527f, 0.30278578f, 0.30590302f, 0.30901700f, 0.31212768f,
+    0.31523499f, 0.31833893f, 0.32143945f, 0.32453656f, 0.32763019f,
+    0.33072028f, 0.33380687f, 0.33688986f, 0.33996925f, 0.34304500f,
+    0.34611708f, 0.34918544f, 0.35225007f, 0.35531089f, 0.35836795f,
+    0.36142117f, 0.36447051f, 0.36751595f, 0.37055743f, 0.37359497f,
+    0.37662852f, 0.37965801f, 0.38268346f, 0.38570479f, 0.38872197f,
+    0.39173502f, 0.39474389f, 0.39774847f, 0.40074885f, 0.40374491f,
+    0.40673664f, 0.40972406f, 0.41270703f, 0.41568562f, 0.41865975f,
+    0.42162940f, 0.42459452f, 0.42755508f, 0.43051112f, 0.43346250f,
+    0.43640924f, 0.43935132f, 0.44228873f, 0.44522133f, 0.44814920f,
+    0.45107228f, 0.45399052f, 0.45690390f, 0.45981237f, 0.46271592f,
+    0.46561453f, 0.46850815f, 0.47139674f, 0.47428030f, 0.47715878f,
+    0.48003215f, 0.48290035f, 0.48576337f, 0.48862126f, 0.49147385f,
+    0.49432120f, 0.49716330f, 0.50000000f, 0.50283140f, 0.50565743f,
+    0.50847799f, 0.51129311f, 0.51410276f, 0.51690692f, 0.51970553f,
+    0.52249855f, 0.52528602f, 0.52806789f, 0.53084403f, 0.53361452f,
+    0.53637928f, 0.53913832f, 0.54189163f, 0.54463905f, 0.54738063f,
+    0.55011642f, 0.55284631f, 0.55557024f, 0.55828828f, 0.56100029f,
+    0.56370628f, 0.56640625f, 0.56910014f, 0.57178795f, 0.57446963f,
+    0.57714522f, 0.57981455f, 0.58247769f, 0.58513463f, 0.58778524f,
+    0.59042960f, 0.59306765f, 0.59569931f, 0.59832460f, 0.60094351f,
+    0.60355598f, 0.60616195f, 0.60876143f, 0.61135441f, 0.61394083f,
+    0.61652070f, 0.61909395f, 0.62166059f, 0.62422055f, 0.62677383f,
+    0.62932038f, 0.63186020f, 0.63439333f, 0.63691956f, 0.63943899f,
+    0.64195162f, 0.64445734f, 0.64695615f, 0.64944810f, 0.65193301f,
+    0.65441096f, 0.65688187f, 0.65934587f, 0.66180271f, 0.66425246f,
+    0.66669512f, 0.66913062f, 0.67155898f, 0.67398012f, 0.67639405f,
+    0.67880076f, 0.68120021f, 0.68359232f, 0.68597710f, 0.68835455f,
+    0.69072467f, 0.69308740f, 0.69544262f, 0.69779050f, 0.70013082f,
+    0.70246369f, 0.70478904f, 0.70710677f, 0.70941699f, 0.71171963f,
+    0.71401459f, 0.71630198f, 0.71858168f, 0.72085363f, 0.72311789f,
+    0.72537440f, 0.72762316f, 0.72986406f, 0.73209721f, 0.73432255f,
+    0.73653996f, 0.73874950f, 0.74095118f, 0.74314487f, 0.74533057f,
+    0.74750835f, 0.74967808f, 0.75183982f, 0.75399351f, 0.75613910f,
+    0.75827658f, 0.76040596f, 0.76252723f, 0.76464027f, 0.76674515f,
+    0.76884186f, 0.77093029f, 0.77301043f, 0.77508241f, 0.77714598f,
+    0.77920127f, 0.78124821f, 0.78328675f, 0.78531694f, 0.78733873f,
+    0.78935206f, 0.79135692f, 0.79335338f, 0.79534125f, 0.79732066f,
+    0.79929149f, 0.80125386f, 0.80320752f, 0.80515265f, 0.80708915f,
+    0.80901700f, 0.81093621f, 0.81284672f, 0.81474853f, 0.81664157f,
+    0.81852591f, 0.82040149f, 0.82226825f, 0.82412618f, 0.82597536f,
+    0.82781565f, 0.82964706f, 0.83146966f, 0.83328325f, 0.83508795f,
+    0.83688378f, 0.83867061f, 0.84044838f, 0.84221727f, 0.84397703f,
+    0.84572780f, 0.84746957f, 0.84920216f, 0.85092574f, 0.85264021f,
+    0.85434544f, 0.85604161f, 0.85772866f, 0.85940641f, 0.86107504f,
+    0.86273444f, 0.86438453f, 0.86602545f, 0.86765707f, 0.86927933f,
+    0.87089235f, 0.87249607f, 0.87409031f, 0.87567532f, 0.87725097f,
+    0.87881714f, 0.88037390f, 0.88192129f, 0.88345921f, 0.88498765f,
+    0.88650668f, 0.88801610f, 0.88951612f, 0.89100653f, 0.89248741f,
+    0.89395881f, 0.89542055f, 0.89687276f, 0.89831537f, 0.89974827f,
+    0.90117162f, 0.90258533f, 0.90398932f, 0.90538365f, 0.90676826f,
+    0.90814316f, 0.90950841f, 0.91086388f, 0.91220951f, 0.91354549f,
+    0.91487163f, 0.91618794f, 0.91749454f, 0.91879123f, 0.92007810f,
+    0.92135513f, 0.92262226f, 0.92387950f, 0.92512691f, 0.92636442f,
+    0.92759192f, 0.92880958f, 0.93001723f, 0.93121493f, 0.93240267f,
+    0.93358046f, 0.93474817f, 0.93590593f, 0.93705362f, 0.93819135f,
+    0.93931901f, 0.94043654f, 0.94154406f, 0.94264150f, 0.94372880f,
+    0.94480604f, 0.94587320f, 0.94693011f, 0.94797695f, 0.94901365f,
+    0.95004016f, 0.95105654f, 0.95206273f, 0.95305866f, 0.95404440f,
+    0.95501995f, 0.95598525f, 0.95694035f, 0.95788521f, 0.95881975f,
+    0.95974404f, 0.96065807f, 0.96156180f, 0.96245527f, 0.96333838f,
+    0.96421117f, 0.96507370f, 0.96592581f, 0.96676767f, 0.96759909f,
+    0.96842021f, 0.96923089f, 0.97003126f, 0.97082120f, 0.97160077f,
+    0.97236991f, 0.97312868f, 0.97387701f, 0.97461486f, 0.97534233f,
+    0.97605932f, 0.97676587f, 0.97746199f, 0.97814763f, 0.97882277f,
+    0.97948742f, 0.98014158f, 0.98078531f, 0.98141843f, 0.98204112f,
+    0.98265332f, 0.98325491f, 0.98384601f, 0.98442656f, 0.98499662f,
+    0.98555607f, 0.98610497f, 0.98664331f, 0.98717111f, 0.98768836f,
+    0.98819500f, 0.98869103f, 0.98917651f, 0.98965138f, 0.99011570f,
+    0.99056935f, 0.99101239f, 0.99144489f, 0.99186671f, 0.99227792f,
+    0.99267852f, 0.99306846f, 0.99344778f, 0.99381649f, 0.99417448f,
+    0.99452192f, 0.99485862f, 0.99518472f, 0.99550015f, 0.99580491f,
+    0.99609905f, 0.99638247f, 0.99665523f, 0.99691731f, 0.99716878f,
+    0.99740952f, 0.99763954f, 0.99785894f, 0.99806762f, 0.99826562f,
+    0.99845290f, 0.99862951f, 0.99879545f, 0.99895066f, 0.99909520f,
+    0.99922901f, 0.99935216f, 0.99946457f, 0.99956632f, 0.99965733f,
+    0.99973762f, 0.99980724f, 0.99986613f, 0.99991435f, 0.99995178f,
+    0.99997860f, 0.99999464f, 1.00000000f, 0.99999464f, 0.99997860f,
+    0.99995178f, 0.99991435f, 0.99986613f, 0.99980724f, 0.99973762f,
+    0.99965733f, 0.99956632f, 0.99946457f, 0.99935216f, 0.99922901f,
+    0.99909520f, 0.99895066f, 0.99879545f, 0.99862951f, 0.99845290f,
+    0.99826562f, 0.99806762f, 0.99785894f, 0.99763954f, 0.99740946f,
+    0.99716872f, 0.99691731f, 0.99665523f, 0.99638247f, 0.99609905f,
+    0.99580491f, 0.99550015f, 0.99518472f, 0.99485862f, 0.99452192f,
+    0.99417448f, 0.99381644f, 0.99344778f, 0.99306846f, 0.99267852f,
+    0.99227792f, 0.99186671f, 0.99144489f, 0.99101239f, 0.99056935f,
+    0.99011564f, 0.98965138f, 0.98917651f, 0.98869103f, 0.98819494f,
+    0.98768836f, 0.98717111f, 0.98664331f, 0.98610497f, 0.98555607f,
+    0.98499656f, 0.98442656f, 0.98384601f, 0.98325491f, 0.98265326f,
+    0.98204112f, 0.98141843f, 0.98078525f, 0.98014158f, 0.97948742f,
+    0.97882277f, 0.97814757f, 0.97746193f, 0.97676587f, 0.97605932f,
+    0.97534227f, 0.97461486f, 0.97387695f, 0.97312862f, 0.97236991f,
+    0.97160077f, 0.97082120f, 0.97003126f, 0.96923089f, 0.96842015f,
+    0.96759909f, 0.96676761f, 0.96592581f, 0.96507365f, 0.96421117f,
+    0.96333838f, 0.96245521f, 0.96156180f, 0.96065807f, 0.95974404f,
+    0.95881969f, 0.95788515f, 0.95694029f, 0.95598525f, 0.95501995f,
+    0.95404440f, 0.95305860f, 0.95206267f, 0.95105648f, 0.95004016f,
+    0.94901365f, 0.94797695f, 0.94693011f, 0.94587314f, 0.94480604f,
+    0.94372880f, 0.94264150f, 0.94154406f, 0.94043654f, 0.93931895f,
+    0.93819129f, 0.93705362f, 0.93590593f, 0.93474817f, 0.93358046f,
+    0.93240267f, 0.93121493f, 0.93001723f, 0.92880952f, 0.92759192f,
+    0.92636436f, 0.92512691f, 0.92387950f, 0.92262226f, 0.92135507f,
+    0.92007804f, 0.91879123f, 0.91749448f, 0.91618794f, 0.91487157f,
+    0.91354543f, 0.91220951f, 0.91086382f, 0.90950835f, 0.90814310f,
+    0.90676820f, 0.90538365f, 0.90398932f, 0.90258527f, 0.90117157f,
+    0.89974827f, 0.89831525f, 0.89687276f, 0.89542055f, 0.89395875f,
+    0.89248741f, 0.89100647f, 0.88951600f, 0.88801610f, 0.88650662f,
+    0.88498759f, 0.88345915f, 0.88192123f, 0.88037384f, 0.87881714f,
+    0.87725091f, 0.87567532f, 0.87409031f, 0.87249595f, 0.87089223f,
+    0.86927933f, 0.86765701f, 0.86602539f, 0.86438447f, 0.86273432f,
+    0.86107504f, 0.85940641f, 0.85772860f, 0.85604161f, 0.85434544f,
+    0.85264009f, 0.85092574f, 0.84920216f, 0.84746951f, 0.84572780f,
+    0.84397697f, 0.84221715f, 0.84044844f, 0.83867055f, 0.83688372f,
+    0.83508795f, 0.83328319f, 0.83146954f, 0.82964706f, 0.82781565f,
+    0.82597530f, 0.82412612f, 0.82226813f, 0.82040137f, 0.81852591f,
+    0.81664157f, 0.81474847f, 0.81284660f, 0.81093609f, 0.80901700f,
+    0.80708915f, 0.80515265f, 0.80320752f, 0.80125374f, 0.79929143f,
+    0.79732066f, 0.79534125f, 0.79335332f, 0.79135686f, 0.78935200f,
+    0.78733861f, 0.78531694f, 0.78328675f, 0.78124815f, 0.77920121f,
+    0.77714586f, 0.77508223f, 0.77301049f, 0.77093029f, 0.76884180f,
+    0.76674509f, 0.76464021f, 0.76252711f, 0.76040596f, 0.75827658f,
+    0.75613904f, 0.75399339f, 0.75183970f, 0.74967796f, 0.74750835f,
+    0.74533057f, 0.74314481f, 0.74095106f, 0.73874938f, 0.73653996f,
+    0.73432249f, 0.73209721f, 0.72986400f, 0.72762305f, 0.72537428f,
+    0.72311789f, 0.72085363f, 0.71858162f, 0.71630186f, 0.71401453f,
+    0.71171951f, 0.70941705f, 0.70710677f, 0.70478898f, 0.70246363f,
+    0.70013070f, 0.69779032f, 0.69544268f, 0.69308734f, 0.69072461f,
+    0.68835449f, 0.68597704f, 0.68359220f, 0.68120021f, 0.67880070f,
+    0.67639399f, 0.67398006f, 0.67155886f, 0.66913044f, 0.66669512f,
+    0.66425240f, 0.66180259f, 0.65934575f, 0.65688181f, 0.65441096f,
+    0.65193301f, 0.64944804f, 0.64695609f, 0.64445722f, 0.64195150f,
+    0.63943905f, 0.63691956f, 0.63439327f, 0.63186014f, 0.62932026f,
+    0.62677372f, 0.62422055f, 0.62166059f, 0.61909389f, 0.61652064f,
+    0.61394072f, 0.61135429f, 0.60876143f, 0.60616189f, 0.60355592f,
+    0.60094339f, 0.59832448f, 0.59569913f, 0.59306765f, 0.59042960f,
+    0.58778518f, 0.58513451f, 0.58247757f, 0.57981461f, 0.57714522f,
+    0.57446963f, 0.57178789f, 0.56910002f, 0.56640613f, 0.56370628f,
+    0.56100023f, 0.55828822f, 0.55557019f, 0.55284619f, 0.55011630f,
+    0.54738069f, 0.54463905f, 0.54189152f, 0.53913826f, 0.53637916f,
+    0.53361434f, 0.53084403f, 0.52806783f, 0.52528596f, 0.52249849f,
+    0.51970541f, 0.51690674f, 0.51410276f, 0.51129305f, 0.50847787f,
+    0.50565726f, 0.50283122f, 0.50000006f, 0.49716327f, 0.49432117f,
+    0.49147379f, 0.48862115f, 0.48576325f, 0.48290038f, 0.48003212f,
+    0.47715873f, 0.47428021f, 0.47139663f, 0.46850798f, 0.46561456f,
+    0.46271589f, 0.45981231f, 0.45690379f, 0.45399037f, 0.45107210f,
+    0.44814920f, 0.44522130f, 0.44228864f, 0.43935123f, 0.43640912f,
+    0.43346232f, 0.43051112f, 0.42755505f, 0.42459446f, 0.42162928f,
+    0.41865960f, 0.41568545f, 0.41270703f, 0.40972400f, 0.40673658f,
+    0.40374479f, 0.40074870f, 0.39774850f, 0.39474386f, 0.39173496f,
+    0.38872188f, 0.38570464f, 0.38268328f, 0.37965804f, 0.37662849f,
+    0.37359491f, 0.37055734f, 0.36751580f, 0.36447033f, 0.36142117f,
+    0.35836792f, 0.35531086f, 0.35224995f, 0.34918529f, 0.34611690f,
+    0.34304500f, 0.33996922f, 0.33688980f, 0.33380675f, 0.33072016f,
+    0.32763001f, 0.32453656f, 0.32143945f, 0.31833887f, 0.31523487f,
+    0.31212750f, 0.30901679f, 0.30590302f, 0.30278572f, 0.29966521f,
+    0.29654145f, 0.29341453f, 0.29028472f, 0.28715155f, 0.28401530f,
+    0.28087601f, 0.27773371f, 0.27458847f, 0.27144048f, 0.26828936f,
+    0.26513538f, 0.26197854f, 0.25881892f, 0.25565651f, 0.25249159f,
+    0.24932374f, 0.24615324f, 0.24298008f, 0.23980433f, 0.23662600f,
+    0.23344538f, 0.23026201f, 0.22707619f, 0.22388794f, 0.22069728f,
+    0.21750426f, 0.21430916f, 0.21111152f, 0.20791161f, 0.20470949f,
+    0.20150517f, 0.19829892f, 0.19509031f, 0.19187963f, 0.18866688f,
+    0.18545210f, 0.18223536f, 0.17901689f, 0.17579627f, 0.17257376f,
+    0.16934940f, 0.16612324f, 0.16289529f, 0.15966584f, 0.15643445f,
+    0.15320137f, 0.14996666f, 0.14673033f, 0.14349243f, 0.14025325f,
+    0.13701232f, 0.13376991f, 0.13052608f, 0.12728085f, 0.12403426f,
+    0.12078657f, 0.11753736f, 0.11428688f, 0.11103519f, 0.10778230f,
+    0.10452849f, 0.10127334f, 0.09801710f, 0.09475980f, 0.09150149f,
+    0.08824220f, 0.08498220f, 0.08172106f, 0.07845904f, 0.07519618f,
+    0.07193252f, 0.06866808f, 0.06540315f, 0.06213728f, 0.05887074f,
+    0.05560357f, 0.05233581f, 0.04906749f, 0.04579888f, 0.04252954f,
+    0.03925974f, 0.03598953f, 0.03271893f, 0.02944798f, 0.02617695f,
+    0.02290541f, 0.01963361f, 0.01636161f, 0.01308943f, 0.00981712f,
+    0.00654493f, 0.00327244f, 0.00000000f, 0.00000000f, 0.00000000f,
+    0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f,
+    0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f,
+    0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f,
+    0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f,
+    0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f,
+    0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f};
+
+#endif  // MODULES_AUDIO_PROCESSING_LEGACY_NS_WINDOWS_PRIVATE_H_