This CL introduces a new APM sub-module named AGC2 that does not use the band

split domain and only implements floating point operations (to avoid spectral
leakage issues and unnecessary complexity).

The goal of this CL is adding the new sub-module into APM without providing an
implementation that could replace the existing gain control modules. The focus
is in fact on initialization, reset, and configuration of AGC2.

The module itself only applies a hard-coded gain value. This behavior will
change in the coming CLs.

BUG=webrtc:7494

Review-Url: https://codereview.webrtc.org/2848593002
Cr-Commit-Position: refs/heads/master@{#18222}
This commit is contained in:
alessiob
2017-05-22 06:57:06 -07:00
committed by Commit bot
parent c0876aab46
commit 3ec96df907
12 changed files with 358 additions and 3 deletions

View File

@ -108,6 +108,10 @@ rtc_static_library("audio_processing") {
"agc/loudness_histogram.h",
"agc/utility.cc",
"agc/utility.h",
"agc2/digital_gain_applier.cc",
"agc2/digital_gain_applier.h",
"agc2/gain_controller2.cc",
"agc2/gain_controller2.h",
"audio_buffer.cc",
"audio_buffer.h",
"audio_processing_impl.cc",
@ -612,6 +616,7 @@ if (rtc_include_tests) {
"aec3/suppression_filter_unittest.cc",
"aec3/suppression_gain_unittest.cc",
"aec3/vector_math_unittest.cc",
"agc2/gain_controller2_unittest.cc",
"audio_processing_impl_locking_unittest.cc",
"audio_processing_impl_unittest.cc",
"audio_processing_unittest.cc",

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) 2017 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 "webrtc/modules/audio_processing/agc2/digital_gain_applier.h"
#include <algorithm>
namespace webrtc {
namespace {
constexpr float kMaxSampleValue = 32767.0f;
constexpr float kMinSampleValue = -32767.0f;
} // namespace
DigitalGainApplier::DigitalGainApplier() = default;
void DigitalGainApplier::Process(float gain, rtc::ArrayView<float> samples) {
if (gain == 1.f) { return; }
for (auto& v : samples) { v *= gain; }
LimitToAllowedRange(samples);
}
void DigitalGainApplier::LimitToAllowedRange(rtc::ArrayView<float> x) {
for (auto& v : x) {
v = std::max(kMinSampleValue, v);
v = std::min(kMaxSampleValue, v);
}
}
} // namespace webrtc

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2017 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 WEBRTC_MODULES_AUDIO_PROCESSING_AGC2_DIGITAL_GAIN_APPLIER_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_AGC2_DIGITAL_GAIN_APPLIER_H_
#include "webrtc/base/array_view.h"
#include "webrtc/modules/audio_processing/audio_buffer.h"
namespace webrtc {
class DigitalGainApplier {
public:
DigitalGainApplier();
// Applies the specified gain to an array of samples.
void Process(float gain, rtc::ArrayView<float> samples);
private:
void LimitToAllowedRange(rtc::ArrayView<float> x);
};
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AGC2_DIGITAL_GAIN_APPLIER_H_

View File

@ -0,0 +1,65 @@
/*
* Copyright (c) 2017 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 "webrtc/modules/audio_processing/agc2/gain_controller2.h"
#include "webrtc/base/atomicops.h"
#include "webrtc/base/checks.h"
#include "webrtc/modules/audio_processing/audio_buffer.h"
#include "webrtc/modules/audio_processing/logging/apm_data_dumper.h"
namespace webrtc {
namespace {
constexpr float kGain = 0.5f;
} // namespace
int GainController2::instance_count_ = 0;
GainController2::GainController2(int sample_rate_hz)
: sample_rate_hz_(sample_rate_hz),
data_dumper_(new ApmDataDumper(
rtc::AtomicOps::Increment(&instance_count_))),
digital_gain_applier_(),
gain_(kGain) {
RTC_DCHECK(sample_rate_hz_ == AudioProcessing::kSampleRate8kHz ||
sample_rate_hz_ == AudioProcessing::kSampleRate16kHz ||
sample_rate_hz_ == AudioProcessing::kSampleRate32kHz ||
sample_rate_hz_ == AudioProcessing::kSampleRate48kHz);
data_dumper_->InitiateNewSetOfRecordings();
data_dumper_->DumpRaw("gain_", 1, &gain_);
}
GainController2::~GainController2() = default;
void GainController2::Process(AudioBuffer* audio) {
for (size_t k = 0; k < audio->num_channels(); ++k) {
auto channel_view = rtc::ArrayView<float>(
audio->channels_f()[k], audio->num_frames());
digital_gain_applier_.Process(gain_, channel_view);
}
}
bool GainController2::Validate(
const AudioProcessing::Config::GainController2& config) {
return true;
}
std::string GainController2::ToString(
const AudioProcessing::Config::GainController2& config) {
std::stringstream ss;
ss << "{"
<< "enabled: " << (config.enabled ? "true" : "false") << "}";
return ss.str();
}
} // namespace webrtc

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2017 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 WEBRTC_MODULES_AUDIO_PROCESSING_AGC2_GAIN_CONTROLLER2_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_AGC2_GAIN_CONTROLLER2_H_
#include <memory>
#include <string>
#include "webrtc/base/constructormagic.h"
#include "webrtc/modules/audio_processing/include/audio_processing.h"
#include "webrtc/modules/audio_processing/agc2/digital_gain_applier.h"
namespace webrtc {
class ApmDataDumper;
class AudioBuffer;
// Gain Controller 2 aims to automatically adjust levels by acting on the
// microphone gain and/or applying digital gain.
//
// It temporarily implements a hard-coded gain mode only.
class GainController2 {
public:
explicit GainController2(int sample_rate_hz);
~GainController2();
int sample_rate_hz() { return sample_rate_hz_; }
void Process(AudioBuffer* audio);
static bool Validate(const AudioProcessing::Config::GainController2& config);
static std::string ToString(
const AudioProcessing::Config::GainController2& config);
private:
int sample_rate_hz_;
std::unique_ptr<ApmDataDumper> data_dumper_;
DigitalGainApplier digital_gain_applier_;
static int instance_count_;
// TODO(alessiob): Remove once a meaningful gain controller mode is
// implemented.
const float gain_;
RTC_DISALLOW_COPY_AND_ASSIGN(GainController2);
};
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AGC2_GAIN_CONTROLLER2_H_

View File

@ -0,0 +1,99 @@
/*
* Copyright (c) 2017 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 <memory>
#include <string>
#include "webrtc/base/array_view.h"
#include "webrtc/modules/audio_processing/audio_buffer.h"
#include "webrtc/modules/audio_processing/agc2/gain_controller2.h"
#include "webrtc/modules/audio_processing/agc2/digital_gain_applier.h"
#include "webrtc/test/gtest.h"
namespace webrtc {
namespace test {
namespace {
constexpr size_t kNumFrames = 480u;
constexpr size_t kStereo = 2u;
void SetAudioBufferSamples(float value, AudioBuffer* ab) {
for (size_t k = 0; k < ab->num_channels(); ++k) {
auto channel = rtc::ArrayView<float>(ab->channels_f()[k], ab->num_frames());
for (auto& sample : channel) { sample = value; }
}
}
template<typename Functor>
bool CheckAudioBufferSamples(Functor validator, AudioBuffer* ab) {
for (size_t k = 0; k < ab->num_channels(); ++k) {
auto channel = rtc::ArrayView<float>(ab->channels_f()[k], ab->num_frames());
for (auto& sample : channel) { if (!validator(sample)) { return false; } }
}
return true;
}
bool TestDigitalGainApplier(float sample_value, float gain, float expected) {
AudioBuffer ab(kNumFrames, kStereo, kNumFrames, kStereo, kNumFrames);
SetAudioBufferSamples(sample_value, &ab);
DigitalGainApplier gain_applier;
for (size_t k = 0; k < ab.num_channels(); ++k) {
auto channel_view = rtc::ArrayView<float>(
ab.channels_f()[k], ab.num_frames());
gain_applier.Process(gain, channel_view);
}
auto check_expectation = [expected](float sample) {
return sample == expected; };
return CheckAudioBufferSamples(check_expectation, &ab);
}
} // namespace
TEST(GainController2, Instance) {
std::unique_ptr<GainController2> gain_controller2;
gain_controller2.reset(new GainController2(
AudioProcessing::kSampleRate48kHz));
}
TEST(GainController2, ToString) {
AudioProcessing::Config config;
config.gain_controller2.enabled = false;
EXPECT_EQ("{enabled: false}",
GainController2::ToString(config.gain_controller2));
config.gain_controller2.enabled = true;
EXPECT_EQ("{enabled: true}",
GainController2::ToString(config.gain_controller2));
}
TEST(GainController2, DigitalGainApplierProcess) {
EXPECT_TRUE(TestDigitalGainApplier(1000.0f, 0.5, 500.0f));
}
TEST(GainController2, DigitalGainApplierCheckClipping) {
EXPECT_TRUE(TestDigitalGainApplier(30000.0f, 1.5, 32767.0f));
EXPECT_TRUE(TestDigitalGainApplier(-30000.0f, 1.5, -32767.0f));
}
TEST(GainController2, Usage) {
std::unique_ptr<GainController2> gain_controller2;
gain_controller2.reset(new GainController2(
AudioProcessing::kSampleRate48kHz));
AudioBuffer ab(kNumFrames, kStereo, kNumFrames, kStereo, kNumFrames);
SetAudioBufferSamples(1000.0f, &ab);
gain_controller2->Process(&ab);
}
} // namespace test
} // namespace webrtc

View File

@ -12,6 +12,7 @@
#include <math.h>
#include <algorithm>
#include <string>
#include "webrtc/base/checks.h"
#include "webrtc/base/logging.h"
@ -23,6 +24,7 @@
#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h"
#include "webrtc/modules/audio_processing/aec/aec_core.h"
#include "webrtc/modules/audio_processing/aec3/echo_canceller3.h"
#include "webrtc/modules/audio_processing/agc2/gain_controller2.h"
#include "webrtc/modules/audio_processing/agc/agc_manager_direct.h"
#include "webrtc/modules/audio_processing/audio_buffer.h"
#include "webrtc/modules/audio_processing/beamformer/nonlinear_beamformer.h"
@ -171,6 +173,7 @@ bool AudioProcessingImpl::ApmSubmoduleStates::Update(
bool intelligibility_enhancer_enabled,
bool beamformer_enabled,
bool adaptive_gain_controller_enabled,
bool gain_controller2_enabled,
bool level_controller_enabled,
bool echo_canceller3_enabled,
bool voice_activity_detector_enabled,
@ -189,6 +192,8 @@ bool AudioProcessingImpl::ApmSubmoduleStates::Update(
changed |= (beamformer_enabled != beamformer_enabled_);
changed |=
(adaptive_gain_controller_enabled != adaptive_gain_controller_enabled_);
changed |=
(gain_controller2_enabled != gain_controller2_enabled_);
changed |= (level_controller_enabled != level_controller_enabled_);
changed |= (echo_canceller3_enabled != echo_canceller3_enabled_);
changed |= (level_estimator_enabled != level_estimator_enabled_);
@ -204,6 +209,7 @@ bool AudioProcessingImpl::ApmSubmoduleStates::Update(
intelligibility_enhancer_enabled_ = intelligibility_enhancer_enabled;
beamformer_enabled_ = beamformer_enabled;
adaptive_gain_controller_enabled_ = adaptive_gain_controller_enabled;
gain_controller2_enabled_ = gain_controller2_enabled;
level_controller_enabled_ = level_controller_enabled;
echo_canceller3_enabled_ = echo_canceller3_enabled;
level_estimator_enabled_ = level_estimator_enabled;
@ -275,6 +281,7 @@ struct AudioProcessingImpl::ApmPrivateSubmodules {
// Accessed internally from capture or during initialization
std::unique_ptr<NonlinearBeamformer> beamformer;
std::unique_ptr<AgcManagerDirect> agc_manager;
std::unique_ptr<GainController2> gain_controller2;
std::unique_ptr<LowCutFilter> low_cut_filter;
std::unique_ptr<LevelController> level_controller;
std::unique_ptr<ResidualEchoDetector> residual_echo_detector;
@ -519,6 +526,7 @@ int AudioProcessingImpl::InitializeLocked() {
InitializeLevelController();
InitializeResidualEchoDetector();
InitializeEchoCanceller3();
InitializeGainController2();
#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP
if (debug_dump_.debug_file->is_open()) {
@ -673,6 +681,25 @@ void AudioProcessingImpl::ApplyConfig(const AudioProcessing::Config& config) {
LOG(LS_INFO) << "Echo canceller 3 activated: "
<< capture_nonlocked_.echo_canceller3_enabled;
}
config_ok = GainController2::Validate(config_.gain_controller2);
if (!config_ok) {
LOG(LS_ERROR) << "AudioProcessing module config error" << std::endl
<< "gain_controller2: "
<< GainController2::ToString(config_.gain_controller2)
<< std::endl
<< "Reverting to default parameter set";
config_.gain_controller2 = AudioProcessing::Config::GainController2();
}
if (config.gain_controller2.enabled !=
capture_nonlocked_.gain_controller2_enabled) {
capture_nonlocked_.gain_controller2_enabled =
config_.gain_controller2.enabled;
InitializeGainController2();
LOG(LS_INFO) << "Gain controller 2 activated: "
<< capture_nonlocked_.gain_controller2_enabled;
}
}
void AudioProcessingImpl::SetExtraOptions(const webrtc::Config& config) {
@ -690,7 +717,7 @@ void AudioProcessingImpl::SetExtraOptions(const webrtc::Config& config) {
}
#if WEBRTC_INTELLIGIBILITY_ENHANCER
if(capture_nonlocked_.intelligibility_enabled !=
if (capture_nonlocked_.intelligibility_enabled !=
config.Get<Intelligibility>().enabled) {
capture_nonlocked_.intelligibility_enabled =
config.Get<Intelligibility>().enabled;
@ -1295,6 +1322,10 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() {
capture_.key_pressed);
}
if (capture_nonlocked_.gain_controller2_enabled) {
private_submodules_->gain_controller2->Process(capture_buffer);
}
if (capture_nonlocked_.level_controller_enabled) {
private_submodules_->level_controller->Process(capture_buffer);
}
@ -1716,6 +1747,7 @@ bool AudioProcessingImpl::UpdateActiveSubmoduleStates() {
capture_nonlocked_.intelligibility_enabled,
capture_nonlocked_.beamformer_enabled,
public_submodules_->gain_control->is_enabled(),
capture_nonlocked_.gain_controller2_enabled,
capture_nonlocked_.level_controller_enabled,
capture_nonlocked_.echo_canceller3_enabled,
public_submodules_->voice_detection->is_enabled(),
@ -1766,6 +1798,7 @@ void AudioProcessingImpl::InitializeLowCutFilter() {
private_submodules_->low_cut_filter.reset();
}
}
void AudioProcessingImpl::InitializeEchoCanceller3() {
if (capture_nonlocked_.echo_canceller3_enabled) {
private_submodules_->echo_canceller3.reset(
@ -1775,6 +1808,15 @@ void AudioProcessingImpl::InitializeEchoCanceller3() {
}
}
void AudioProcessingImpl::InitializeGainController2() {
if (capture_nonlocked_.gain_controller2_enabled) {
private_submodules_->gain_controller2.reset(
new GainController2(proc_sample_rate_hz()));
} else {
private_submodules_->gain_controller2.reset();
}
}
void AudioProcessingImpl::InitializeLevelController() {
private_submodules_->level_controller->Initialize(proc_sample_rate_hz());
}

View File

@ -41,9 +41,7 @@ RTC_POP_IGNORING_WUNDEF()
namespace webrtc {
class AgcManagerDirect;
class AudioConverter;
class NonlinearBeamformer;
class AudioProcessingImpl : public AudioProcessing {
@ -167,6 +165,7 @@ class AudioProcessingImpl : public AudioProcessing {
bool intelligibility_enhancer_enabled,
bool beamformer_enabled,
bool adaptive_gain_controller_enabled,
bool gain_controller2_enabled,
bool level_controller_enabled,
bool echo_canceller3_enabled,
bool voice_activity_detector_enabled,
@ -186,6 +185,7 @@ class AudioProcessingImpl : public AudioProcessing {
bool intelligibility_enhancer_enabled_ = false;
bool beamformer_enabled_ = false;
bool adaptive_gain_controller_enabled_ = false;
bool gain_controller2_enabled_ = false;
bool level_controller_enabled_ = false;
bool echo_canceller3_enabled_ = false;
bool level_estimator_enabled_ = false;
@ -254,6 +254,7 @@ class AudioProcessingImpl : public AudioProcessing {
EXCLUSIVE_LOCKS_REQUIRED(crit_render_, crit_capture_);
void InitializeLowCutFilter() EXCLUSIVE_LOCKS_REQUIRED(crit_capture_);
void InitializeEchoCanceller3() EXCLUSIVE_LOCKS_REQUIRED(crit_capture_);
void InitializeGainController2();
void EmptyQueuedRenderAudio();
void AllocateRenderQueue()
@ -390,6 +391,7 @@ class AudioProcessingImpl : public AudioProcessing {
bool intelligibility_enabled;
bool level_controller_enabled = false;
bool echo_canceller3_enabled = false;
bool gain_controller2_enabled = false;
} capture_nonlocked_;
struct ApmRenderState {

View File

@ -267,6 +267,14 @@ class AudioProcessing {
struct EchoCanceller3 {
bool enabled = false;
} echo_canceller3;
// Enables the next generation AGC functionality. This feature replaces the
// standard methods of gain control in the previous AGC.
// The functionality is not yet activated in the code and turning this on
// does not yet have the desired behavior.
struct GainController2 {
bool enabled = false;
} gain_controller2;
};
// TODO(mgraczyk): Remove once all methods that use ChannelLayout are gone.

View File

@ -270,6 +270,9 @@ void AudioProcessingSimulator::CreateAudioProcessor() {
if (settings_.use_aec3) {
apm_config.echo_canceller3.enabled = *settings_.use_aec3;
}
if (settings_.use_agc2) {
apm_config.gain_controller2.enabled = *settings_.use_agc2;
}
if (settings_.use_lc) {
apm_config.level_controller.enabled = *settings_.use_lc;
}

View File

@ -50,6 +50,7 @@ struct SimulationSettings {
rtc::Optional<bool> use_ed; // Residual Echo Detector.
rtc::Optional<std::string> ed_graph_output_filename;
rtc::Optional<bool> use_agc;
rtc::Optional<bool> use_agc2;
rtc::Optional<bool> use_hpf;
rtc::Optional<bool> use_ns;
rtc::Optional<bool> use_ts;

View File

@ -78,6 +78,9 @@ DEFINE_string(ed_graph, "", "Output filename for graph of echo likelihood");
DEFINE_int32(agc,
kParameterNotSpecifiedValue,
"Activate (1) or deactivate(0) the AGC");
DEFINE_int32(agc2,
kParameterNotSpecifiedValue,
"Activate (1) or deactivate(0) the AGC2");
DEFINE_int32(hpf,
kParameterNotSpecifiedValue,
"Activate (1) or deactivate(0) the high-pass filter");
@ -230,6 +233,7 @@ SimulationSettings CreateSettings() {
SetSettingIfFlagSet(FLAGS_ed, &settings.use_ed);
SetSettingIfSpecified(FLAGS_ed_graph, &settings.ed_graph_output_filename);
SetSettingIfFlagSet(FLAGS_agc, &settings.use_agc);
SetSettingIfFlagSet(FLAGS_agc2, &settings.use_agc2);
SetSettingIfFlagSet(FLAGS_hpf, &settings.use_hpf);
SetSettingIfFlagSet(FLAGS_ns, &settings.use_ns);
SetSettingIfFlagSet(FLAGS_ts, &settings.use_ts);