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_noise_suppression.cc b/modules/audio_processing/legacy_noise_suppression.cc
new file mode 100644
index 0000000..b2c8853
--- /dev/null
+++ b/modules/audio_processing/legacy_noise_suppression.cc
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2019 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_noise_suppression.h"
+
+#include "modules/audio_processing/audio_buffer.h"
+#include "rtc_base/checks.h"
+#if defined(WEBRTC_NS_FLOAT)
+#include "modules/audio_processing/legacy_ns/noise_suppression.h"
+
+#define NS_CREATE WebRtcNs_Create
+#define NS_FREE WebRtcNs_Free
+#define NS_INIT WebRtcNs_Init
+#define NS_SET_POLICY WebRtcNs_set_policy
+typedef NsHandle NsState;
+#elif defined(WEBRTC_NS_FIXED)
+#include "modules/audio_processing/legacy_ns/noise_suppression_x.h"
+
+#define NS_CREATE WebRtcNsx_Create
+#define NS_FREE WebRtcNsx_Free
+#define NS_INIT WebRtcNsx_Init
+#define NS_SET_POLICY WebRtcNsx_set_policy
+typedef NsxHandle NsState;
+#endif
+
+namespace webrtc {
+namespace {
+int NoiseSuppressionLevelToPolicy(NoiseSuppression::Level level) {
+ switch (level) {
+ case NoiseSuppression::Level::kLow:
+ return 0;
+ case NoiseSuppression::Level::kModerate:
+ return 1;
+ case NoiseSuppression::Level::kHigh:
+ return 2;
+ case NoiseSuppression::Level::kVeryHigh:
+ return 3;
+ default:
+ RTC_NOTREACHED();
+ }
+ return 1;
+}
+} // namespace
+
+class NoiseSuppression::Suppressor {
+ public:
+ explicit Suppressor(int sample_rate_hz) {
+ state_ = NS_CREATE();
+ RTC_CHECK(state_);
+ int error = NS_INIT(state_, sample_rate_hz);
+ RTC_DCHECK_EQ(0, error);
+ }
+ ~Suppressor() { NS_FREE(state_); }
+
+ Suppressor(Suppressor&) = delete;
+ Suppressor& operator=(Suppressor&) = delete;
+
+ NsState* state() { return state_; }
+
+ private:
+ NsState* state_ = nullptr;
+};
+
+NoiseSuppression::NoiseSuppression(size_t channels,
+ int sample_rate_hz,
+ Level level) {
+ const int policy = NoiseSuppressionLevelToPolicy(level);
+ for (size_t i = 0; i < channels; ++i) {
+ suppressors_.push_back(std::make_unique<Suppressor>(sample_rate_hz));
+ int error = NS_SET_POLICY(suppressors_[i]->state(), policy);
+ RTC_DCHECK_EQ(0, error);
+ }
+}
+
+NoiseSuppression::~NoiseSuppression() {}
+
+void NoiseSuppression::AnalyzeCaptureAudio(AudioBuffer* audio) {
+ RTC_DCHECK(audio);
+#if defined(WEBRTC_NS_FLOAT)
+ RTC_DCHECK_GE(160, audio->num_frames_per_band());
+ RTC_DCHECK_EQ(suppressors_.size(), audio->num_channels());
+ for (size_t i = 0; i < suppressors_.size(); i++) {
+ WebRtcNs_Analyze(suppressors_[i]->state(),
+ audio->split_bands_const(i)[kBand0To8kHz]);
+ }
+#endif
+}
+
+void NoiseSuppression::ProcessCaptureAudio(AudioBuffer* audio) {
+ RTC_DCHECK(audio);
+ RTC_DCHECK_GE(160, audio->num_frames_per_band());
+ RTC_DCHECK_EQ(suppressors_.size(), audio->num_channels());
+ for (size_t i = 0; i < suppressors_.size(); i++) {
+#if defined(WEBRTC_NS_FLOAT)
+ WebRtcNs_Process(suppressors_[i]->state(), audio->split_bands_const(i),
+ audio->num_bands(), audio->split_bands(i));
+#elif defined(WEBRTC_NS_FIXED)
+ int16_t split_band_data[AudioBuffer::kMaxNumBands]
+ [AudioBuffer::kMaxSplitFrameLength];
+ int16_t* split_bands[AudioBuffer::kMaxNumBands] = {
+ split_band_data[0], split_band_data[1], split_band_data[2]};
+ audio->ExportSplitChannelData(i, split_bands);
+
+ WebRtcNsx_Process(suppressors_[i]->state(), split_bands, audio->num_bands(),
+ split_bands);
+
+ audio->ImportSplitChannelData(i, split_bands);
+#endif
+ }
+}
+
+float NoiseSuppression::speech_probability() const {
+#if defined(WEBRTC_NS_FLOAT)
+ float probability_average = 0.0f;
+ for (auto& suppressor : suppressors_) {
+ probability_average +=
+ WebRtcNs_prior_speech_probability(suppressor->state());
+ }
+ if (!suppressors_.empty()) {
+ probability_average /= suppressors_.size();
+ }
+ return probability_average;
+#elif defined(WEBRTC_NS_FIXED)
+ // TODO(peah): Returning error code as a float! Remove this.
+ // Currently not available for the fixed point implementation.
+ return AudioProcessing::kUnsupportedFunctionError;
+#endif
+}
+
+std::vector<float> NoiseSuppression::NoiseEstimate() {
+ std::vector<float> noise_estimate;
+#if defined(WEBRTC_NS_FLOAT)
+ const float kNumChannelsFraction = 1.f / suppressors_.size();
+ noise_estimate.assign(WebRtcNs_num_freq(), 0.f);
+ for (auto& suppressor : suppressors_) {
+ const float* noise = WebRtcNs_noise_estimate(suppressor->state());
+ for (size_t i = 0; i < noise_estimate.size(); ++i) {
+ noise_estimate[i] += kNumChannelsFraction * noise[i];
+ }
+ }
+#elif defined(WEBRTC_NS_FIXED)
+ noise_estimate.assign(WebRtcNsx_num_freq(), 0.f);
+ for (auto& suppressor : suppressors_) {
+ int q_noise;
+ const uint32_t* noise =
+ WebRtcNsx_noise_estimate(suppressor->state(), &q_noise);
+ const float kNormalizationFactor =
+ 1.f / ((1 << q_noise) * suppressors_.size());
+ for (size_t i = 0; i < noise_estimate.size(); ++i) {
+ noise_estimate[i] += kNormalizationFactor * noise[i];
+ }
+ }
+#endif
+ return noise_estimate;
+}
+
+size_t NoiseSuppression::num_noise_bins() {
+#if defined(WEBRTC_NS_FLOAT)
+ return WebRtcNs_num_freq();
+#elif defined(WEBRTC_NS_FIXED)
+ return WebRtcNsx_num_freq();
+#endif
+}
+
+} // namespace webrtc