Add noise suppression settings to AudioProcessing::Config

This Config configuration will eventually replace the AudioProcessing::noise_suppression() interface.

This also introduces a proxy NoiseSuppression, returned by AudioProcessing::noise_suppression.
Without this proxy, ApplyConfig could overwrite NS settings for clients who currently use noise_suppression(). For example, the following code will not preserve the noise suppression level:

apm->noise_suppression()->set_level(NoiseSuppression::kHigh);
auto cfg = apm->GetConfig();
apm->ApplyConfig(cfg);

The NoiseSuppression instance returned by noise_suppression() has no way to update the config inside APM, so GetConfig() will return an out-of-date config which is then re-applied. This CL adds a proxy that makes this update, by forwarding Enable() and set_level() calls to ApplyConfig().

Drive-by change: AudioProcessing::Config substructs are reordered to mirror the capture processing pipeline.

Tested: Ran ToT and this CL builds of audioproc_f and verified identical settings/aecdumps.
Bug: webrtc:9947
Change-Id: I823eade894be115c254d656562564108b2b63b1f
Reviewed-on: https://webrtc-review.googlesource.com/c/116521
Reviewed-by: Alex Loiko <aleloi@webrtc.org>
Commit-Queue: Sam Zackrisson <saza@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#26248}
This commit is contained in:
Sam Zackrisson 2019-01-11 15:10:32 +01:00 committed by Commit Bot
parent cd7c21bfad
commit 235131303b
10 changed files with 212 additions and 53 deletions

View File

@ -134,6 +134,7 @@ rtc_static_library("audio_processing") {
":audio_processing_statistics",
":config",
":gain_control_interface",
":noise_suppression_proxy",
"../..:webrtc_common",
"../../api:array_view",
"../../api/audio:aec3_config",
@ -189,6 +190,17 @@ rtc_source_set("gain_control_interface") {
]
}
rtc_source_set("noise_suppression_proxy") {
sources = [
"noise_suppression_proxy.cc",
"noise_suppression_proxy.h",
]
deps = [
":api",
"../../rtc_base:macromagic",
]
}
rtc_source_set("audio_processing_statistics") {
visibility = [ "*" ]
sources = [

View File

@ -34,6 +34,7 @@
#include "modules/audio_processing/logging/apm_data_dumper.h"
#include "modules/audio_processing/low_cut_filter.h"
#include "modules/audio_processing/noise_suppression_impl.h"
#include "modules/audio_processing/noise_suppression_proxy.h"
#include "modules/audio_processing/residual_echo_detector.h"
#include "modules/audio_processing/transient/transient_suppressor.h"
#include "modules/audio_processing/voice_detection_impl.h"
@ -107,6 +108,23 @@ int FindNativeProcessRateToUse(int minimum_rate, bool band_splitting_required) {
return uppermost_native_rate;
}
NoiseSuppression::Level NsConfigLevelToInterfaceLevel(
AudioProcessing::Config::NoiseSuppression::Level level) {
using NsConfig = AudioProcessing::Config::NoiseSuppression;
switch (level) {
case NsConfig::kLow:
return NoiseSuppression::kLow;
case NsConfig::kModerate:
return NoiseSuppression::kModerate;
case NsConfig::kHigh:
return NoiseSuppression::kHigh;
case NsConfig::kVeryHigh:
return NoiseSuppression::kVeryHigh;
default:
RTC_NOTREACHED();
}
}
// Maximum lengths that frame of samples being passed from the render side to
// the capture side can have (does not apply to AEC3).
static const size_t kMaxAllowedValuesOfSamplesPerBand = 160;
@ -232,9 +250,12 @@ bool AudioProcessingImpl::ApmSubmoduleStates::LowCutFilteringRequired() const {
struct AudioProcessingImpl::ApmPublicSubmodules {
ApmPublicSubmodules() {}
// Accessed externally of APM without any lock acquired.
// TODO(bugs.webrtc.org/9947): Move these submodules into private_submodules_
// when their pointer-to-submodule API functions are gone.
std::unique_ptr<GainControlImpl> gain_control;
std::unique_ptr<LevelEstimatorImpl> level_estimator;
std::unique_ptr<NoiseSuppressionImpl> noise_suppression;
std::unique_ptr<NoiseSuppressionProxy> noise_suppression_proxy;
std::unique_ptr<VoiceDetectionImpl> voice_detection;
std::unique_ptr<GainControlForExperimentalAgc>
gain_control_for_experimental_agc;
@ -380,6 +401,8 @@ AudioProcessingImpl::AudioProcessingImpl(
new LevelEstimatorImpl(&crit_capture_));
public_submodules_->noise_suppression.reset(
new NoiseSuppressionImpl(&crit_capture_));
public_submodules_->noise_suppression_proxy.reset(new NoiseSuppressionProxy(
this, public_submodules_->noise_suppression.get()));
public_submodules_->voice_detection.reset(
new VoiceDetectionImpl(&crit_capture_));
public_submodules_->gain_control_for_experimental_agc.reset(
@ -664,6 +687,11 @@ void AudioProcessingImpl::ApplyConfig(const AudioProcessing::Config& config) {
? EchoCancellationImpl::SuppressionLevel::kModerateSuppression
: EchoCancellationImpl::SuppressionLevel::kHighSuppression);
public_submodules_->noise_suppression->Enable(
config.noise_suppression.enabled);
public_submodules_->noise_suppression->set_level(
NsConfigLevelToInterfaceLevel(config.noise_suppression.level));
InitializeLowCutFilter();
RTC_LOG(LS_INFO) << "Highpass filter activated: "
@ -1690,7 +1718,7 @@ LevelEstimator* AudioProcessingImpl::level_estimator() const {
}
NoiseSuppression* AudioProcessingImpl::noise_suppression() const {
return public_submodules_->noise_suppression.get();
return public_submodules_->noise_suppression_proxy.get();
}
VoiceDetection* AudioProcessingImpl::voice_detection() const {

View File

@ -527,21 +527,20 @@ bool AudioProcessingImplLockTest::MaybeEndTest() {
void AudioProcessingImplLockTest::SetUp() {
test_config_ = static_cast<TestConfig>(GetParam());
ASSERT_EQ(apm_->kNoError, apm_->level_estimator()->Enable(true));
ASSERT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true));
ASSERT_EQ(apm_->kNoError,
apm_->gain_control()->set_mode(GainControl::kAdaptiveDigital));
ASSERT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true));
ASSERT_EQ(apm_->kNoError, apm_->noise_suppression()->Enable(true));
ASSERT_EQ(apm_->kNoError, apm_->voice_detection()->Enable(true));
AudioProcessing::Config apm_config;
AudioProcessing::Config apm_config = apm_->GetConfig();
apm_config.echo_canceller.enabled =
(test_config_.aec_type != AecType::AecTurnedOff);
apm_config.echo_canceller.mobile_mode =
(test_config_.aec_type == AecType::BasicWebRtcAecSettingsWithAecMobile);
apm_config.noise_suppression.enabled = true;
apm_config.voice_detection.enabled = true;
apm_config.level_estimation.enabled = true;
apm_->ApplyConfig(apm_config);
Config config;
@ -584,7 +583,7 @@ bool StatsProcessor::Process() {
EXPECT_FALSE(apm_config.echo_canceller.enabled);
}
EXPECT_TRUE(apm_->gain_control()->is_enabled());
EXPECT_TRUE(apm_->noise_suppression()->is_enabled());
EXPECT_TRUE(apm_config.noise_suppression.enabled);
// The below return values are not testable.
apm_->noise_suppression()->speech_probability();

View File

@ -240,6 +240,17 @@ class AudioProcessing : public rtc::RefCountInterface {
// by changing the default values in the AudioProcessing::Config struct.
// The config is applied by passing the struct to the ApplyConfig method.
struct Config {
// Enabled the pre-amplifier. It amplifies the capture signal
// before any other processing is done.
struct PreAmplifier {
bool enabled = false;
float fixed_gain_factor = 1.f;
} pre_amplifier;
struct HighPassFilter {
bool enabled = false;
} high_pass_filter;
struct EchoCanceller {
bool enabled = false;
bool mobile_mode = false;
@ -248,20 +259,17 @@ class AudioProcessing : public rtc::RefCountInterface {
bool legacy_moderate_suppression_level = false;
} echo_canceller;
struct ResidualEchoDetector {
bool enabled = true;
} residual_echo_detector;
struct HighPassFilter {
// Enables background noise suppression.
struct NoiseSuppression {
bool enabled = false;
} high_pass_filter;
enum Level { kLow, kModerate, kHigh, kVeryHigh };
Level level = kModerate;
} noise_suppression;
// Enabled the pre-amplifier. It amplifies the capture signal
// before any other processing is done.
struct PreAmplifier {
// Enables reporting of |has_voice| in webrtc::AudioProcessingStats.
struct VoiceDetection {
bool enabled = false;
float fixed_gain_factor = 1.f;
} pre_amplifier;
} voice_detection;
// Enables the next generation AGC functionality. This feature replaces the
// standard methods of gain control in the previous AGC. Enabling this
@ -283,16 +291,15 @@ class AudioProcessing : public rtc::RefCountInterface {
} adaptive_digital;
} gain_controller2;
struct ResidualEchoDetector {
bool enabled = true;
} residual_echo_detector;
// Enables reporting of |output_rms_dbfs| in webrtc::AudioProcessingStats.
struct LevelEstimation {
bool enabled = false;
} level_estimation;
// Enables reporting of |has_voice| in webrtc::AudioProcessingStats.
struct VoiceDetection {
bool enabled = false;
} voice_detection;
// Explicit copy assignment implementation to avoid issues with memory
// sanitizer complaints in case of self-assignment.
// TODO(peah): Add buildflag to ensure that this is only included for memory

View File

@ -0,0 +1,71 @@
/*
* 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/noise_suppression_proxy.h"
namespace webrtc {
NoiseSuppressionProxy::NoiseSuppressionProxy(AudioProcessing* apm,
NoiseSuppression* ns)
: apm_(apm), ns_(ns) {}
NoiseSuppressionProxy::~NoiseSuppressionProxy() {}
int NoiseSuppressionProxy::Enable(bool enable) {
AudioProcessing::Config config = apm_->GetConfig();
if (config.noise_suppression.enabled != enable) {
config.noise_suppression.enabled = enable;
apm_->ApplyConfig(config);
}
return AudioProcessing::kNoError;
}
bool NoiseSuppressionProxy::is_enabled() const {
return ns_->is_enabled();
}
int NoiseSuppressionProxy::set_level(Level level) {
AudioProcessing::Config config = apm_->GetConfig();
using NsConfig = AudioProcessing::Config::NoiseSuppression;
NsConfig::Level new_level;
switch (level) {
case NoiseSuppression::kLow:
new_level = NsConfig::kLow;
break;
case NoiseSuppression::kModerate:
new_level = NsConfig::kModerate;
break;
case NoiseSuppression::kHigh:
new_level = NsConfig::kHigh;
break;
case NoiseSuppression::kVeryHigh:
new_level = NsConfig::kVeryHigh;
break;
default:
RTC_NOTREACHED();
}
if (config.noise_suppression.level != new_level) {
config.noise_suppression.level = new_level;
apm_->ApplyConfig(config);
}
return AudioProcessing::kNoError;
}
NoiseSuppression::Level NoiseSuppressionProxy::level() const {
return ns_->level();
}
float NoiseSuppressionProxy::speech_probability() const {
return ns_->speech_probability();
}
std::vector<float> NoiseSuppressionProxy::NoiseEstimate() {
return ns_->NoiseEstimate();
}
} // namespace webrtc

View File

@ -0,0 +1,45 @@
/*
* 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.
*/
#ifndef MODULES_AUDIO_PROCESSING_NOISE_SUPPRESSION_PROXY_H_
#define MODULES_AUDIO_PROCESSING_NOISE_SUPPRESSION_PROXY_H_
#include <vector>
#include "modules/audio_processing/include/audio_processing.h"
#include "rtc_base/constructormagic.h"
namespace webrtc {
// This class ensures interoperability with the pointer-to-submodule interface
// AudioProcessing::noise_suppression() and AudioProcessing::ApplyConfig:
// Enable(..) and set_level(..) calls are applied via
// AudioProcessing::ApplyConfig, while all other function calls are forwarded
// directly to a wrapped NoiseSuppression instance.
class NoiseSuppressionProxy : public NoiseSuppression {
public:
NoiseSuppressionProxy(AudioProcessing* apm, NoiseSuppression* ns);
~NoiseSuppressionProxy() override;
// NoiseSuppression implementation.
int Enable(bool enable) override;
bool is_enabled() const override;
int set_level(Level level) override;
Level level() const override;
float speech_probability() const override;
std::vector<float> NoiseEstimate() override;
private:
AudioProcessing* apm_;
NoiseSuppression* ns_;
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(NoiseSuppressionProxy);
};
} // namespace webrtc
#endif // MODULES_AUDIO_PROCESSING_NOISE_SUPPRESSION_PROXY_H_

View File

@ -424,8 +424,7 @@ void AecDumpBasedSimulator::HandleMessage(
if (msg.has_ns_enabled() || settings_.use_ns) {
bool enable = settings_.use_ns ? *settings_.use_ns : msg.ns_enabled();
RTC_CHECK_EQ(AudioProcessing::kNoError,
ap_->noise_suppression()->Enable(enable));
apm_config.noise_suppression.enabled = enable;
if (settings_.use_verbose_logging) {
std::cout << " ns_enabled: " << (enable ? "true" : "false")
<< std::endl;
@ -434,9 +433,8 @@ void AecDumpBasedSimulator::HandleMessage(
if (msg.has_ns_level() || settings_.ns_level) {
int level = settings_.ns_level ? *settings_.ns_level : msg.ns_level();
RTC_CHECK_EQ(AudioProcessing::kNoError,
ap_->noise_suppression()->set_level(
static_cast<NoiseSuppression::Level>(level)));
apm_config.noise_suppression.level =
static_cast<AudioProcessing::Config::NoiseSuppression::Level>(level);
if (settings_.use_verbose_logging) {
std::cout << " ns_level: " << level << std::endl;
}

View File

@ -219,6 +219,26 @@ void DebugDumpReplayer::ConfigureApm(const audioproc::Config& msg) {
msg.aec_suppression_level()) ==
EchoCancellationImpl::SuppressionLevel::kModerateSuppression;
// HPF configs.
RTC_CHECK(msg.has_hpf_enabled());
apm_config.high_pass_filter.enabled = msg.hpf_enabled();
// Preamp configs.
RTC_CHECK(msg.has_pre_amplifier_enabled());
apm_config.pre_amplifier.enabled = msg.pre_amplifier_enabled();
apm_config.pre_amplifier.fixed_gain_factor =
msg.pre_amplifier_fixed_gain_factor();
// NS configs.
RTC_CHECK(msg.has_ns_enabled());
RTC_CHECK(msg.has_ns_level());
apm_config.noise_suppression.enabled = msg.ns_enabled();
apm_config.noise_suppression.level =
static_cast<AudioProcessing::Config::NoiseSuppression::Level>(
msg.ns_level());
apm_->ApplyConfig(apm_config);
// AGC configs.
RTC_CHECK(msg.has_agc_enabled());
RTC_CHECK_EQ(AudioProcessing::kNoError,
@ -232,27 +252,6 @@ void DebugDumpReplayer::ConfigureApm(const audioproc::Config& msg) {
RTC_CHECK(msg.has_agc_limiter_enabled());
RTC_CHECK_EQ(AudioProcessing::kNoError,
apm_->gain_control()->enable_limiter(msg.agc_limiter_enabled()));
// HPF configs.
RTC_CHECK(msg.has_hpf_enabled());
apm_config.high_pass_filter.enabled = msg.hpf_enabled();
// NS configs.
RTC_CHECK(msg.has_ns_enabled());
RTC_CHECK_EQ(AudioProcessing::kNoError,
apm_->noise_suppression()->Enable(msg.ns_enabled()));
RTC_CHECK(msg.has_ns_level());
RTC_CHECK_EQ(AudioProcessing::kNoError,
apm_->noise_suppression()->set_level(
static_cast<NoiseSuppression::Level>(msg.ns_level())));
RTC_CHECK(msg.has_pre_amplifier_enabled());
apm_config.pre_amplifier.enabled = msg.pre_amplifier_enabled();
apm_config.pre_amplifier.fixed_gain_factor =
msg.pre_amplifier_fixed_gain_factor();
apm_->ApplyConfig(apm_config);
}
void DebugDumpReplayer::LoadNextMessage() {

View File

@ -588,8 +588,9 @@ TEST_F(DebugDumpTest, ToggleNs) {
generator.StartRecording();
generator.Process(100);
NoiseSuppression* ns = generator.apm()->noise_suppression();
EXPECT_EQ(AudioProcessing::kNoError, ns->Enable(!ns->is_enabled()));
AudioProcessing::Config apm_config = generator.apm()->GetConfig();
apm_config.noise_suppression.enabled = !apm_config.noise_suppression.enabled;
generator.apm()->ApplyConfig(apm_config);
generator.Process(100);
generator.StopRecording();

View File

@ -171,12 +171,11 @@ std::unique_ptr<AudioProcessing> CreateApm(test::FuzzDataHelper* fuzz_data,
kPeak;
apm_config.gain_controller2.adaptive_digital.use_saturation_protector =
use_agc2_adaptive_digital_saturation_protector;
apm_config.noise_suppression.enabled = use_ns;
apm_config.voice_detection.enabled = use_vad;
apm->ApplyConfig(apm_config);
apm->gain_control()->Enable(use_agc);
apm->noise_suppression()->Enable(use_ns);
apm->level_estimator()->Enable(use_le);
apm->voice_detection()->Enable(use_vad);
apm->gain_control()->enable_limiter(use_agc_limiter);