mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-13 13:50:40 +01:00

The AdaptiveAgc often boosts the signal outside of Float S16 range. It is expected, which is why we have a limiter after it in the process chain. But it turns out that this happens regularly even for simple input examples. The output signal peaks can be as high as +4 dBFs for a single speaker example (which should be easy). It leads to excessive gain modulation by the limiter. This CL is a new tuning designed to produce a safer gain. After this, we shouldn't hit the saturation region of the limiter as often. But we will still maintain a high gain. We have a 'configurable kill-switch': the settings can be changed via field trials WebRTC-Audio-Agc2Force(Initial|Extra)SaturationMargin. Bug: webrtc:7494, chromium:892043 Change-Id: I5014377050c74c32ae8998282991141eae31cf58 Reviewed-on: https://webrtc-review.googlesource.com/c/102922 Commit-Queue: Alex Loiko <aleloi@webrtc.org> Reviewed-by: Sam Zackrisson <saza@webrtc.org> Cr-Commit-Position: refs/heads/master@{#25006}
149 lines
5.7 KiB
C++
149 lines
5.7 KiB
C++
/*
|
|
* Copyright (c) 2018 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/agc2/saturation_protector.h"
|
|
|
|
#include <algorithm>
|
|
|
|
#include "modules/audio_processing/agc2/agc2_common.h"
|
|
#include "modules/audio_processing/logging/apm_data_dumper.h"
|
|
#include "rtc_base/gunit.h"
|
|
|
|
namespace webrtc {
|
|
namespace {
|
|
float RunOnConstantLevel(int num_iterations,
|
|
VadWithLevel::LevelAndProbability vad_data,
|
|
float estimated_level_dbfs,
|
|
SaturationProtector* saturation_protector) {
|
|
float last_margin = saturation_protector->LastMargin();
|
|
float max_difference = 0.f;
|
|
for (int i = 0; i < num_iterations; ++i) {
|
|
saturation_protector->UpdateMargin(vad_data, estimated_level_dbfs);
|
|
const float new_margin = saturation_protector->LastMargin();
|
|
max_difference =
|
|
std::max(max_difference, std::abs(new_margin - last_margin));
|
|
last_margin = new_margin;
|
|
saturation_protector->DebugDumpEstimate();
|
|
}
|
|
return max_difference;
|
|
}
|
|
} // namespace
|
|
|
|
TEST(AutomaticGainController2SaturationProtector, ProtectorShouldNotCrash) {
|
|
ApmDataDumper apm_data_dumper(0);
|
|
SaturationProtector saturation_protector(&apm_data_dumper);
|
|
VadWithLevel::LevelAndProbability vad_data(1.f, -20.f, -10.f);
|
|
|
|
saturation_protector.UpdateMargin(vad_data, -20.f);
|
|
static_cast<void>(saturation_protector.LastMargin());
|
|
saturation_protector.DebugDumpEstimate();
|
|
}
|
|
|
|
// Check that the estimate converges to the ratio between peaks and
|
|
// level estimator values after a while.
|
|
TEST(AutomaticGainController2SaturationProtector,
|
|
ProtectorEstimatesCrestRatio) {
|
|
ApmDataDumper apm_data_dumper(0);
|
|
SaturationProtector saturation_protector(&apm_data_dumper);
|
|
|
|
constexpr float kPeakLevel = -20.f;
|
|
const float kCrestFactor = GetInitialSaturationMarginDb() + 1.f;
|
|
const float kSpeechLevel = kPeakLevel - kCrestFactor;
|
|
const float kMaxDifference =
|
|
0.5 * std::abs(GetInitialSaturationMarginDb() - kCrestFactor);
|
|
|
|
static_cast<void>(RunOnConstantLevel(
|
|
2000, VadWithLevel::LevelAndProbability(1.f, -90.f, kPeakLevel),
|
|
kSpeechLevel, &saturation_protector));
|
|
|
|
EXPECT_NEAR(
|
|
saturation_protector.LastMargin() - GetExtraSaturationMarginOffsetDb(),
|
|
kCrestFactor, kMaxDifference);
|
|
}
|
|
|
|
TEST(AutomaticGainController2SaturationProtector, ProtectorChangesSlowly) {
|
|
ApmDataDumper apm_data_dumper(0);
|
|
SaturationProtector saturation_protector(&apm_data_dumper);
|
|
|
|
constexpr float kPeakLevel = -20.f;
|
|
const float kCrestFactor = GetInitialSaturationMarginDb() - 5.f;
|
|
const float kOtherCrestFactor = GetInitialSaturationMarginDb();
|
|
const float kSpeechLevel = kPeakLevel - kCrestFactor;
|
|
const float kOtherSpeechLevel = kPeakLevel - kOtherCrestFactor;
|
|
|
|
constexpr int kNumIterations = 1000;
|
|
float max_difference = RunOnConstantLevel(
|
|
kNumIterations, VadWithLevel::LevelAndProbability(1.f, -90.f, kPeakLevel),
|
|
kSpeechLevel, &saturation_protector);
|
|
|
|
max_difference =
|
|
std::max(RunOnConstantLevel(
|
|
kNumIterations,
|
|
VadWithLevel::LevelAndProbability(1.f, -90.f, kPeakLevel),
|
|
kOtherSpeechLevel, &saturation_protector),
|
|
max_difference);
|
|
|
|
constexpr float kMaxChangeSpeedDbPerSecond = 0.5; // 1 db / 2 seconds.
|
|
|
|
EXPECT_LE(max_difference,
|
|
kMaxChangeSpeedDbPerSecond / 1000 * kFrameDurationMs);
|
|
}
|
|
|
|
TEST(AutomaticGainController2SaturationProtector,
|
|
ProtectorAdaptsToDelayedChanges) {
|
|
ApmDataDumper apm_data_dumper(0);
|
|
SaturationProtector saturation_protector(&apm_data_dumper);
|
|
|
|
constexpr int kDelayIterations = kFullBufferSizeMs / kFrameDurationMs;
|
|
constexpr float kInitialSpeechLevelDbfs = -30;
|
|
constexpr float kLaterSpeechLevelDbfs = -15;
|
|
|
|
// First run on initial level.
|
|
float max_difference = RunOnConstantLevel(
|
|
kDelayIterations,
|
|
VadWithLevel::LevelAndProbability(
|
|
1.f, -90.f, kInitialSpeechLevelDbfs + GetInitialSaturationMarginDb()),
|
|
kInitialSpeechLevelDbfs, &saturation_protector);
|
|
|
|
// Then peak changes, but not RMS.
|
|
max_difference =
|
|
std::max(RunOnConstantLevel(
|
|
kDelayIterations,
|
|
VadWithLevel::LevelAndProbability(
|
|
1.f, -90.f,
|
|
kLaterSpeechLevelDbfs + GetInitialSaturationMarginDb()),
|
|
kInitialSpeechLevelDbfs, &saturation_protector),
|
|
max_difference);
|
|
|
|
// Then both change.
|
|
max_difference =
|
|
std::max(RunOnConstantLevel(
|
|
kDelayIterations,
|
|
VadWithLevel::LevelAndProbability(
|
|
1.f, -90.f,
|
|
kLaterSpeechLevelDbfs + GetInitialSaturationMarginDb()),
|
|
kLaterSpeechLevelDbfs, &saturation_protector),
|
|
max_difference);
|
|
|
|
// The saturation protector expects that the RMS changes roughly
|
|
// 'kFullBufferSizeMs' after peaks change. This is to account for
|
|
// delay introduces by the level estimator. Therefore, the input
|
|
// above is 'normal' and 'expected', and shouldn't influence the
|
|
// margin by much.
|
|
|
|
const float total_difference = std::abs(saturation_protector.LastMargin() -
|
|
GetExtraSaturationMarginOffsetDb() -
|
|
GetInitialSaturationMarginDb());
|
|
|
|
EXPECT_LE(total_difference, 0.05f);
|
|
EXPECT_LE(max_difference, 0.01f);
|
|
}
|
|
|
|
} // namespace webrtc
|