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

Make sure that the input volume controller implementations exhibit the adaptive behavior regardless of the sample rate and the number of channels. The newly added tests check that: - a downward adjustment takes place with clipping input - an upward adjustment takes place with a too low speech level - a downward adjustment takes place with a too high speech level Bug: webrtc:14761 Change-Id: I1795e74c5f219e15107e928ebaca2bfa75214526 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/287301 Commit-Queue: Alessio Bazzica <alessiob@webrtc.org> Reviewed-by: Hanna Silen <silen@webrtc.org> Cr-Commit-Position: refs/heads/main@{#38930}
1857 lines
79 KiB
C++
1857 lines
79 KiB
C++
/*
|
|
* 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 "modules/audio_processing/agc2/input_volume_controller.h"
|
|
|
|
#include <algorithm>
|
|
#include <fstream>
|
|
#include <limits>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "rtc_base/numerics/safe_minmax.h"
|
|
#include "rtc_base/strings/string_builder.h"
|
|
#include "system_wrappers/include/metrics.h"
|
|
#include "test/field_trial.h"
|
|
#include "test/gmock.h"
|
|
#include "test/gtest.h"
|
|
#include "test/testsupport/file_utils.h"
|
|
|
|
using ::testing::_;
|
|
using ::testing::AtLeast;
|
|
using ::testing::DoAll;
|
|
using ::testing::Return;
|
|
using ::testing::SetArgPointee;
|
|
|
|
namespace webrtc {
|
|
namespace {
|
|
|
|
constexpr int kSampleRateHz = 32000;
|
|
constexpr int kNumChannels = 1;
|
|
constexpr int kInitialInputVolume = 128;
|
|
constexpr int kClippedMin = 165; // Arbitrary, but different from the default.
|
|
constexpr float kAboveClippedThreshold = 0.2f;
|
|
constexpr int kMinMicLevel = 20;
|
|
constexpr int kClippedLevelStep = 15;
|
|
constexpr float kClippedRatioThreshold = 0.1f;
|
|
constexpr int kClippedWaitFrames = 300;
|
|
constexpr float kHighSpeechProbability = 0.7f;
|
|
constexpr float kLowSpeechProbability = 0.1f;
|
|
constexpr float kSpeechLevel = -25.0f;
|
|
constexpr float kSpeechProbabilityThreshold = 0.5f;
|
|
constexpr float kSpeechRatioThreshold = 0.8f;
|
|
|
|
constexpr float kMinSample = std::numeric_limits<int16_t>::min();
|
|
constexpr float kMaxSample = std::numeric_limits<int16_t>::max();
|
|
|
|
using ClippingPredictorConfig = AudioProcessing::Config::GainController1::
|
|
AnalogGainController::ClippingPredictor;
|
|
|
|
using InputVolumeControllerConfig = InputVolumeController::Config;
|
|
|
|
constexpr ClippingPredictorConfig kDefaultClippingPredictorConfig{};
|
|
|
|
std::unique_ptr<InputVolumeController> CreateInputVolumeController(
|
|
int clipped_level_step = kClippedLevelStep,
|
|
float clipped_ratio_threshold = kClippedRatioThreshold,
|
|
int clipped_wait_frames = kClippedWaitFrames,
|
|
bool enable_clipping_predictor = false,
|
|
int update_input_volume_wait_frames = 0) {
|
|
InputVolumeControllerConfig config{
|
|
.min_input_volume = kMinMicLevel,
|
|
.clipped_level_min = kClippedMin,
|
|
.clipped_level_step = clipped_level_step,
|
|
.clipped_ratio_threshold = clipped_ratio_threshold,
|
|
.clipped_wait_frames = clipped_wait_frames,
|
|
.enable_clipping_predictor = enable_clipping_predictor,
|
|
.target_range_max_dbfs = -18,
|
|
.target_range_min_dbfs = -30,
|
|
.update_input_volume_wait_frames = update_input_volume_wait_frames,
|
|
.speech_probability_threshold = kSpeechProbabilityThreshold,
|
|
.speech_ratio_threshold = kSpeechRatioThreshold,
|
|
};
|
|
|
|
return std::make_unique<InputVolumeController>(/*num_capture_channels=*/1,
|
|
config);
|
|
}
|
|
|
|
// (Over)writes `samples_value` for the samples in `audio_buffer`.
|
|
// When `clipped_ratio`, a value in [0, 1], is greater than 0, the corresponding
|
|
// fraction of the frame is set to a full scale value to simulate clipping.
|
|
void WriteAudioBufferSamples(float samples_value,
|
|
float clipped_ratio,
|
|
AudioBuffer& audio_buffer) {
|
|
RTC_DCHECK_GE(samples_value, kMinSample);
|
|
RTC_DCHECK_LE(samples_value, kMaxSample);
|
|
RTC_DCHECK_GE(clipped_ratio, 0.0f);
|
|
RTC_DCHECK_LE(clipped_ratio, 1.0f);
|
|
int num_channels = audio_buffer.num_channels();
|
|
int num_samples = audio_buffer.num_frames();
|
|
int num_clipping_samples = clipped_ratio * num_samples;
|
|
for (int ch = 0; ch < num_channels; ++ch) {
|
|
int i = 0;
|
|
for (; i < num_clipping_samples; ++i) {
|
|
audio_buffer.channels()[ch][i] = 32767.0f;
|
|
}
|
|
for (; i < num_samples; ++i) {
|
|
audio_buffer.channels()[ch][i] = samples_value;
|
|
}
|
|
}
|
|
}
|
|
|
|
// (Over)writes samples in `audio_buffer`. Alternates samples `samples_value`
|
|
// and zero.
|
|
void WriteAlternatingAudioBufferSamples(float samples_value,
|
|
AudioBuffer& audio_buffer) {
|
|
RTC_DCHECK_GE(samples_value, kMinSample);
|
|
RTC_DCHECK_LE(samples_value, kMaxSample);
|
|
const int num_channels = audio_buffer.num_channels();
|
|
const int num_frames = audio_buffer.num_frames();
|
|
for (int ch = 0; ch < num_channels; ++ch) {
|
|
for (int i = 0; i < num_frames; i += 2) {
|
|
audio_buffer.channels()[ch][i] = samples_value;
|
|
audio_buffer.channels()[ch][i + 1] = 0.0f;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Reads a given number of 10 ms chunks from a PCM file and feeds them to
|
|
// `InputVolumeController`.
|
|
class SpeechSamplesReader {
|
|
private:
|
|
// Recording properties.
|
|
static constexpr int kPcmSampleRateHz = 16000;
|
|
static constexpr int kPcmNumChannels = 1;
|
|
static constexpr int kPcmBytesPerSamples = sizeof(int16_t);
|
|
|
|
public:
|
|
SpeechSamplesReader()
|
|
: is_(test::ResourcePath("audio_processing/agc/agc_audio", "pcm"),
|
|
std::ios::binary | std::ios::ate),
|
|
audio_buffer_(kPcmSampleRateHz,
|
|
kPcmNumChannels,
|
|
kPcmSampleRateHz,
|
|
kPcmNumChannels,
|
|
kPcmSampleRateHz,
|
|
kPcmNumChannels),
|
|
buffer_(audio_buffer_.num_frames()),
|
|
buffer_num_bytes_(buffer_.size() * kPcmBytesPerSamples) {
|
|
RTC_CHECK(is_);
|
|
}
|
|
|
|
// Reads `num_frames` 10 ms frames from the beginning of the PCM file, applies
|
|
// `gain_db` and feeds the frames into `controller` by calling
|
|
// `AnalyzeInputAudio()` and `RecommendInputVolume()` for each frame. Reads
|
|
// the number of 10 ms frames available in the PCM file if `num_frames` is too
|
|
// large - i.e., does not loop. `speech_probability` and `speech_level_dbfs`
|
|
// are passed to `RecommendInputVolume()`.
|
|
int Feed(int num_frames,
|
|
int applied_input_volume,
|
|
int gain_db,
|
|
float speech_probability,
|
|
absl::optional<float> speech_level_dbfs,
|
|
InputVolumeController& controller) {
|
|
RTC_DCHECK(controller.capture_output_used());
|
|
|
|
float gain = std::pow(10.0f, gain_db / 20.0f); // From dB to linear gain.
|
|
is_.seekg(0, is_.beg); // Start from the beginning of the PCM file.
|
|
|
|
// Read and feed frames.
|
|
for (int i = 0; i < num_frames; ++i) {
|
|
is_.read(reinterpret_cast<char*>(buffer_.data()), buffer_num_bytes_);
|
|
if (is_.gcount() < buffer_num_bytes_) {
|
|
// EOF reached. Stop.
|
|
break;
|
|
}
|
|
// Apply gain and copy samples into `audio_buffer_`.
|
|
std::transform(buffer_.begin(), buffer_.end(),
|
|
audio_buffer_.channels()[0], [gain](int16_t v) -> float {
|
|
return rtc::SafeClamp(static_cast<float>(v) * gain,
|
|
kMinSample, kMaxSample);
|
|
});
|
|
controller.AnalyzeInputAudio(applied_input_volume, audio_buffer_);
|
|
const auto recommended_input_volume = controller.RecommendInputVolume(
|
|
speech_probability, speech_level_dbfs);
|
|
|
|
// Expect no errors: Applied volume set for every frame;
|
|
// `RecommendInputVolume()` returns a non-empty value.
|
|
EXPECT_TRUE(recommended_input_volume.has_value());
|
|
|
|
applied_input_volume = *recommended_input_volume;
|
|
}
|
|
return applied_input_volume;
|
|
}
|
|
|
|
private:
|
|
std::ifstream is_;
|
|
AudioBuffer audio_buffer_;
|
|
std::vector<int16_t> buffer_;
|
|
const std::streamsize buffer_num_bytes_;
|
|
};
|
|
|
|
// Runs the MonoInputVolumeControl processing sequence following the API
|
|
// contract. Returns the updated recommended input volume.
|
|
float UpdateRecommendedInputVolume(MonoInputVolumeController& mono_controller,
|
|
int applied_input_volume,
|
|
float speech_probability,
|
|
absl::optional<float> rms_error_dbfs) {
|
|
mono_controller.set_stream_analog_level(applied_input_volume);
|
|
EXPECT_EQ(mono_controller.recommended_analog_level(), applied_input_volume);
|
|
mono_controller.Process(rms_error_dbfs, speech_probability);
|
|
return mono_controller.recommended_analog_level();
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// TODO(bugs.webrtc.org/12874): Use constexpr struct with designated
|
|
// initializers once fixed.
|
|
constexpr InputVolumeControllerConfig GetInputVolumeControllerTestConfig() {
|
|
InputVolumeControllerConfig config{
|
|
.clipped_level_min = kClippedMin,
|
|
.clipped_level_step = kClippedLevelStep,
|
|
.clipped_ratio_threshold = kClippedRatioThreshold,
|
|
.clipped_wait_frames = kClippedWaitFrames,
|
|
.enable_clipping_predictor = kDefaultClippingPredictorConfig.enabled,
|
|
.target_range_max_dbfs = -18,
|
|
.target_range_min_dbfs = -30,
|
|
.update_input_volume_wait_frames = 0,
|
|
.speech_probability_threshold = 0.5f,
|
|
.speech_ratio_threshold = 1.0f,
|
|
};
|
|
return config;
|
|
}
|
|
|
|
// Helper class that provides an `InputVolumeController` instance with an
|
|
// `AudioBuffer` instance and `CallAgcSequence()`, a helper method that runs the
|
|
// `InputVolumeController` instance on the `AudioBuffer` one by sticking to the
|
|
// API contract.
|
|
class InputVolumeControllerTestHelper {
|
|
public:
|
|
// Ctor. Initializes `audio_buffer` with zeros.
|
|
// TODO(bugs.webrtc.org/7494): Remove the default argument.
|
|
InputVolumeControllerTestHelper(const InputVolumeController::Config& config =
|
|
GetInputVolumeControllerTestConfig())
|
|
: audio_buffer(kSampleRateHz,
|
|
kNumChannels,
|
|
kSampleRateHz,
|
|
kNumChannels,
|
|
kSampleRateHz,
|
|
kNumChannels),
|
|
controller(/*num_capture_channels=*/1, config) {
|
|
controller.Initialize();
|
|
WriteAudioBufferSamples(/*samples_value=*/0.0f, /*clipped_ratio=*/0.0f,
|
|
audio_buffer);
|
|
}
|
|
|
|
// Calls the sequence of `InputVolumeController` methods according to the API
|
|
// contract, namely:
|
|
// - Sets the applied input volume;
|
|
// - Uses `audio_buffer` to call `AnalyzeInputAudio()` and
|
|
// `RecommendInputVolume()`;
|
|
// Returns the recommended input volume.
|
|
absl::optional<int> CallAgcSequence(int applied_input_volume,
|
|
float speech_probability,
|
|
absl::optional<float> speech_level_dbfs,
|
|
int num_calls = 1) {
|
|
RTC_DCHECK_GE(num_calls, 1);
|
|
absl::optional<int> volume = applied_input_volume;
|
|
for (int i = 0; i < num_calls; ++i) {
|
|
// Repeat the initial volume if `RecommendInputVolume()` doesn't return a
|
|
// value.
|
|
controller.AnalyzeInputAudio(volume.value_or(applied_input_volume),
|
|
audio_buffer);
|
|
volume = controller.RecommendInputVolume(speech_probability,
|
|
speech_level_dbfs);
|
|
|
|
// Allow deviation from the API contract: `RecommendInputVolume()` doesn't
|
|
// return a recommended input volume.
|
|
if (volume.has_value()) {
|
|
EXPECT_EQ(*volume, controller.recommended_input_volume());
|
|
}
|
|
}
|
|
return volume;
|
|
}
|
|
|
|
// Deprecated.
|
|
// TODO(bugs.webrtc.org/7494): Let the caller write `audio_buffer` and use
|
|
// `CallAgcSequence()`.
|
|
int CallRecommendInputVolume(int num_calls,
|
|
int initial_volume,
|
|
float speech_probability,
|
|
absl::optional<float> speech_level_dbfs) {
|
|
RTC_DCHECK(controller.capture_output_used());
|
|
|
|
// Create non-clipping audio for `AnalyzeInputAudio()`.
|
|
WriteAlternatingAudioBufferSamples(0.1f * kMaxSample, audio_buffer);
|
|
int volume = initial_volume;
|
|
for (int i = 0; i < num_calls; ++i) {
|
|
controller.AnalyzeInputAudio(volume, audio_buffer);
|
|
const auto recommended_input_volume = controller.RecommendInputVolume(
|
|
speech_probability, speech_level_dbfs);
|
|
|
|
// Expect no errors: Applied volume set for every frame;
|
|
// `RecommendInputVolume()` returns a non-empty value.
|
|
EXPECT_TRUE(recommended_input_volume.has_value());
|
|
|
|
volume = *recommended_input_volume;
|
|
}
|
|
return volume;
|
|
}
|
|
|
|
// Deprecated.
|
|
// TODO(bugs.webrtc.org/7494): Let the caller write `audio_buffer` and use
|
|
// `CallAgcSequence()`.
|
|
void CallAnalyzeInputAudio(int num_calls, float clipped_ratio) {
|
|
RTC_DCHECK(controller.capture_output_used());
|
|
|
|
RTC_DCHECK_GE(clipped_ratio, 0.0f);
|
|
RTC_DCHECK_LE(clipped_ratio, 1.0f);
|
|
WriteAudioBufferSamples(/*samples_value=*/0.0f, clipped_ratio,
|
|
audio_buffer);
|
|
for (int i = 0; i < num_calls; ++i) {
|
|
controller.AnalyzeInputAudio(controller.recommended_input_volume(),
|
|
audio_buffer);
|
|
}
|
|
}
|
|
|
|
AudioBuffer audio_buffer;
|
|
InputVolumeController controller;
|
|
};
|
|
|
|
class InputVolumeControllerChannelSampleRateTest
|
|
: public ::testing::TestWithParam<std::tuple<int, int>> {
|
|
protected:
|
|
int GetNumChannels() const { return std::get<0>(GetParam()); }
|
|
int GetSampleRateHz() const { return std::get<1>(GetParam()); }
|
|
};
|
|
|
|
TEST_P(InputVolumeControllerChannelSampleRateTest, CheckIsAlive) {
|
|
const int num_channels = GetNumChannels();
|
|
const int sample_rate_hz = GetSampleRateHz();
|
|
|
|
constexpr InputVolumeController::Config kConfig{.enable_clipping_predictor =
|
|
true};
|
|
InputVolumeController controller(num_channels, kConfig);
|
|
controller.Initialize();
|
|
AudioBuffer buffer(sample_rate_hz, num_channels, sample_rate_hz, num_channels,
|
|
sample_rate_hz, num_channels);
|
|
|
|
constexpr int kStartupVolume = 100;
|
|
int applied_initial_volume = kStartupVolume;
|
|
|
|
// Trigger a downward adaptation with clipping.
|
|
constexpr int kLevelWithinTargetDbfs =
|
|
(kConfig.target_range_min_dbfs + kConfig.target_range_max_dbfs) / 2;
|
|
WriteAlternatingAudioBufferSamples(/*samples_value=*/kMaxSample, buffer);
|
|
const int initial_volume1 = applied_initial_volume;
|
|
for (int i = 0; i < 400; ++i) {
|
|
controller.AnalyzeInputAudio(applied_initial_volume, buffer);
|
|
auto recommended_input_volume = controller.RecommendInputVolume(
|
|
kLowSpeechProbability,
|
|
/*speech_level_dbfs=*/kLevelWithinTargetDbfs);
|
|
ASSERT_TRUE(recommended_input_volume.has_value());
|
|
applied_initial_volume = *recommended_input_volume;
|
|
}
|
|
ASSERT_LT(controller.recommended_input_volume(), initial_volume1);
|
|
|
|
// Fill in audio that does not clip.
|
|
WriteAlternatingAudioBufferSamples(/*samples_value=*/1234.5f, buffer);
|
|
|
|
// Trigger an upward adaptation.
|
|
const int initial_volume2 = controller.recommended_input_volume();
|
|
for (int i = 0; i < kConfig.clipped_wait_frames; ++i) {
|
|
controller.AnalyzeInputAudio(applied_initial_volume, buffer);
|
|
auto recommended_input_volume = controller.RecommendInputVolume(
|
|
kHighSpeechProbability,
|
|
/*speech_level_dbfs=*/kConfig.target_range_min_dbfs - 5);
|
|
ASSERT_TRUE(recommended_input_volume.has_value());
|
|
applied_initial_volume = *recommended_input_volume;
|
|
}
|
|
EXPECT_GT(controller.recommended_input_volume(), initial_volume2);
|
|
|
|
// Trigger a downward adaptation.
|
|
const int initial_volume = controller.recommended_input_volume();
|
|
for (int i = 0; i < kConfig.update_input_volume_wait_frames; ++i) {
|
|
controller.AnalyzeInputAudio(applied_initial_volume, buffer);
|
|
auto recommended_input_volume = controller.RecommendInputVolume(
|
|
kHighSpeechProbability,
|
|
/*speech_level_dbfs=*/kConfig.target_range_max_dbfs + 5);
|
|
ASSERT_TRUE(recommended_input_volume.has_value());
|
|
applied_initial_volume = *recommended_input_volume;
|
|
}
|
|
EXPECT_LT(controller.recommended_input_volume(), initial_volume);
|
|
}
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
,
|
|
InputVolumeControllerChannelSampleRateTest,
|
|
::testing::Combine(::testing::Values(1, 2, 3, 6),
|
|
::testing::Values(8000, 16000, 32000, 48000)));
|
|
|
|
class InputVolumeControllerParametrizedTest
|
|
: public ::testing::TestWithParam<int> {};
|
|
|
|
TEST_P(InputVolumeControllerParametrizedTest,
|
|
StartupMinVolumeConfigurationRespectedWhenAppliedInputVolumeAboveMin) {
|
|
InputVolumeControllerTestHelper helper(
|
|
/*config=*/{.min_input_volume = GetParam()});
|
|
|
|
EXPECT_EQ(*helper.CallAgcSequence(/*applied_input_volume=*/128,
|
|
/*speech_probability=*/0.9f,
|
|
/*speech_level_dbfs=*/-80),
|
|
128);
|
|
}
|
|
|
|
TEST_P(
|
|
InputVolumeControllerParametrizedTest,
|
|
StartupMinVolumeConfigurationRespectedWhenAppliedInputVolumeMaybeBelowMin) {
|
|
InputVolumeControllerTestHelper helper(
|
|
/*config=*/{.min_input_volume = GetParam()});
|
|
|
|
EXPECT_GE(*helper.CallAgcSequence(/*applied_input_volume=*/10,
|
|
/*speech_probability=*/0.9f,
|
|
/*speech_level_dbfs=*/-80),
|
|
10);
|
|
}
|
|
|
|
TEST_P(InputVolumeControllerParametrizedTest,
|
|
StartupMinVolumeRespectedWhenAppliedVolumeNonZero) {
|
|
const int kMinInputVolume = GetParam();
|
|
InputVolumeControllerTestHelper helper(
|
|
/*config=*/{.min_input_volume = kMinInputVolume,
|
|
.target_range_min_dbfs = -30,
|
|
.update_input_volume_wait_frames = 1,
|
|
.speech_probability_threshold = 0.5f,
|
|
.speech_ratio_threshold = 0.5f});
|
|
|
|
// Volume change possible; speech level below the digital gain window.
|
|
int volume = *helper.CallAgcSequence(/*applied_input_volume=*/1,
|
|
/*speech_probability=*/0.9f,
|
|
/*speech_level_dbfs=*/-80);
|
|
|
|
EXPECT_EQ(volume, kMinInputVolume);
|
|
}
|
|
|
|
TEST_P(InputVolumeControllerParametrizedTest,
|
|
MinVolumeRepeatedlyRespectedWhenAppliedVolumeNonZero) {
|
|
const int kMinInputVolume = GetParam();
|
|
InputVolumeControllerTestHelper helper(
|
|
/*config=*/{.min_input_volume = kMinInputVolume,
|
|
.target_range_min_dbfs = -30,
|
|
.update_input_volume_wait_frames = 1,
|
|
.speech_probability_threshold = 0.5f,
|
|
.speech_ratio_threshold = 0.5f});
|
|
|
|
// Volume change possible; speech level below the digital gain window.
|
|
for (int i = 0; i < 100; ++i) {
|
|
const int volume = *helper.CallAgcSequence(/*applied_input_volume=*/1,
|
|
/*speech_probability=*/0.9f,
|
|
/*speech_level_dbfs=*/-80);
|
|
EXPECT_GE(volume, kMinInputVolume);
|
|
}
|
|
}
|
|
|
|
TEST_P(InputVolumeControllerParametrizedTest,
|
|
StartupMinVolumeRespectedOnceWhenAppliedVolumeZero) {
|
|
const int kMinInputVolume = GetParam();
|
|
InputVolumeControllerTestHelper helper(
|
|
/*config=*/{.min_input_volume = kMinInputVolume,
|
|
.target_range_min_dbfs = -30,
|
|
.update_input_volume_wait_frames = 1,
|
|
.speech_probability_threshold = 0.5f,
|
|
.speech_ratio_threshold = 0.5f});
|
|
|
|
int volume = *helper.CallAgcSequence(/*applied_input_volume=*/0,
|
|
/*speech_probability=*/0.9f,
|
|
/*speech_level_dbfs=*/-80);
|
|
|
|
EXPECT_EQ(volume, kMinInputVolume);
|
|
|
|
// No change of volume regardless of a speech level below the digital gain
|
|
// window; applied volume is zero.
|
|
volume = *helper.CallAgcSequence(/*applied_input_volume=*/0,
|
|
/*speech_probability=*/0.9f,
|
|
/*speech_level_dbfs=*/-80);
|
|
|
|
EXPECT_EQ(volume, 0);
|
|
}
|
|
|
|
TEST_P(InputVolumeControllerParametrizedTest, MicVolumeResponseToRmsError) {
|
|
InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig();
|
|
config.min_input_volume = GetParam();
|
|
InputVolumeControllerTestHelper helper(config);
|
|
int volume = *helper.CallAgcSequence(kInitialInputVolume,
|
|
kHighSpeechProbability, kSpeechLevel);
|
|
|
|
// Inside the digital gain's window; no change of volume.
|
|
volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
|
|
kHighSpeechProbability, -23.0f);
|
|
|
|
// Inside the digital gain's window; no change of volume.
|
|
volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
|
|
kHighSpeechProbability, -28.0f);
|
|
|
|
// Above the digital gain's window; volume should be increased.
|
|
volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
|
|
kHighSpeechProbability, -29.0f);
|
|
EXPECT_EQ(volume, 128);
|
|
|
|
volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
|
|
kHighSpeechProbability, -38.0f);
|
|
EXPECT_EQ(volume, 156);
|
|
|
|
// Inside the digital gain's window; no change of volume.
|
|
volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
|
|
kHighSpeechProbability, -23.0f);
|
|
volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
|
|
kHighSpeechProbability, -18.0f);
|
|
|
|
// Below the digial gain's window; volume should be decreased.
|
|
volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
|
|
kHighSpeechProbability, -17.0f);
|
|
EXPECT_EQ(volume, 155);
|
|
|
|
volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
|
|
kHighSpeechProbability, -17.0f);
|
|
EXPECT_EQ(volume, 151);
|
|
|
|
volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
|
|
kHighSpeechProbability, -9.0f);
|
|
EXPECT_EQ(volume, 119);
|
|
}
|
|
|
|
TEST_P(InputVolumeControllerParametrizedTest, MicVolumeIsLimited) {
|
|
InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig();
|
|
const int min_input_volume = GetParam();
|
|
config.min_input_volume = min_input_volume;
|
|
InputVolumeControllerTestHelper helper(config);
|
|
int volume = *helper.CallAgcSequence(kInitialInputVolume,
|
|
kHighSpeechProbability, kSpeechLevel);
|
|
|
|
// Maximum upwards change is limited.
|
|
volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
|
|
kHighSpeechProbability, -48.0f);
|
|
EXPECT_EQ(volume, 183);
|
|
|
|
volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
|
|
kHighSpeechProbability, -48.0f);
|
|
EXPECT_EQ(volume, 243);
|
|
|
|
// Won't go higher than the maximum.
|
|
volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
|
|
kHighSpeechProbability, -48.0f);
|
|
EXPECT_EQ(volume, 255);
|
|
|
|
volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
|
|
kHighSpeechProbability, -17.0f);
|
|
EXPECT_EQ(volume, 254);
|
|
|
|
// Maximum downwards change is limited.
|
|
volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
|
|
kHighSpeechProbability, 22.0f);
|
|
EXPECT_EQ(volume, 194);
|
|
|
|
volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
|
|
kHighSpeechProbability, 22.0f);
|
|
EXPECT_EQ(volume, 137);
|
|
|
|
volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
|
|
kHighSpeechProbability, 22.0f);
|
|
EXPECT_EQ(volume, 88);
|
|
|
|
volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
|
|
kHighSpeechProbability, 22.0f);
|
|
EXPECT_EQ(volume, 54);
|
|
|
|
volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
|
|
kHighSpeechProbability, 22.0f);
|
|
EXPECT_EQ(volume, 33);
|
|
|
|
// Won't go lower than the minimum.
|
|
volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
|
|
kHighSpeechProbability, 22.0f);
|
|
EXPECT_EQ(volume, std::max(18, min_input_volume));
|
|
|
|
volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
|
|
kHighSpeechProbability, 22.0f);
|
|
EXPECT_EQ(volume, std::max(12, min_input_volume));
|
|
}
|
|
|
|
TEST_P(InputVolumeControllerParametrizedTest, NoActionWhileMuted) {
|
|
InputVolumeControllerTestHelper helper_1(
|
|
/*config=*/{.min_input_volume = GetParam()});
|
|
InputVolumeControllerTestHelper helper_2(
|
|
/*config=*/{.min_input_volume = GetParam()});
|
|
|
|
int volume_1 = *helper_1.CallAgcSequence(/*applied_input_volume=*/255,
|
|
kHighSpeechProbability, kSpeechLevel,
|
|
/*num_calls=*/1);
|
|
int volume_2 = *helper_2.CallAgcSequence(/*applied_input_volume=*/255,
|
|
kHighSpeechProbability, kSpeechLevel,
|
|
/*num_calls=*/1);
|
|
|
|
EXPECT_EQ(volume_1, 255);
|
|
EXPECT_EQ(volume_2, 255);
|
|
|
|
helper_2.controller.HandleCaptureOutputUsedChange(false);
|
|
|
|
WriteAlternatingAudioBufferSamples(kMaxSample, helper_1.audio_buffer);
|
|
WriteAlternatingAudioBufferSamples(kMaxSample, helper_2.audio_buffer);
|
|
|
|
volume_1 =
|
|
*helper_1.CallAgcSequence(volume_1, kHighSpeechProbability, kSpeechLevel,
|
|
/*num_calls=*/1);
|
|
volume_2 =
|
|
*helper_2.CallAgcSequence(volume_2, kHighSpeechProbability, kSpeechLevel,
|
|
/*num_calls=*/1);
|
|
|
|
EXPECT_LT(volume_1, 255);
|
|
EXPECT_EQ(volume_2, 255);
|
|
}
|
|
|
|
TEST_P(InputVolumeControllerParametrizedTest,
|
|
UnmutingChecksVolumeWithoutRaising) {
|
|
InputVolumeControllerTestHelper helper(
|
|
/*config=*/{.min_input_volume = GetParam()});
|
|
helper.CallAgcSequence(kInitialInputVolume, kHighSpeechProbability,
|
|
kSpeechLevel);
|
|
|
|
helper.controller.HandleCaptureOutputUsedChange(false);
|
|
helper.controller.HandleCaptureOutputUsedChange(true);
|
|
|
|
constexpr int kInputVolume = 127;
|
|
|
|
// SetMicVolume should not be called.
|
|
EXPECT_EQ(
|
|
helper.CallRecommendInputVolume(/*num_calls=*/1, kInputVolume,
|
|
kHighSpeechProbability, kSpeechLevel),
|
|
kInputVolume);
|
|
}
|
|
|
|
TEST_P(InputVolumeControllerParametrizedTest, UnmutingRaisesTooLowVolume) {
|
|
const int min_input_volume = GetParam();
|
|
InputVolumeControllerTestHelper helper(
|
|
/*config=*/{.min_input_volume = min_input_volume});
|
|
helper.CallAgcSequence(kInitialInputVolume, kHighSpeechProbability,
|
|
kSpeechLevel);
|
|
|
|
helper.controller.HandleCaptureOutputUsedChange(false);
|
|
helper.controller.HandleCaptureOutputUsedChange(true);
|
|
|
|
constexpr int kInputVolume = 11;
|
|
|
|
EXPECT_EQ(
|
|
helper.CallRecommendInputVolume(/*num_calls=*/1, kInputVolume,
|
|
kHighSpeechProbability, kSpeechLevel),
|
|
min_input_volume);
|
|
}
|
|
|
|
TEST_P(InputVolumeControllerParametrizedTest,
|
|
ManualLevelChangeResultsInNoSetMicCall) {
|
|
InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig();
|
|
config.min_input_volume = GetParam();
|
|
InputVolumeControllerTestHelper helper(config);
|
|
int volume = *helper.CallAgcSequence(kInitialInputVolume,
|
|
kHighSpeechProbability, kSpeechLevel);
|
|
|
|
// GetMicVolume returns a value outside of the quantization slack, indicating
|
|
// a manual volume change.
|
|
ASSERT_NE(volume, 154);
|
|
volume = helper.CallRecommendInputVolume(
|
|
/*num_calls=*/1, /*initial_volume=*/154, kHighSpeechProbability, -29.0f);
|
|
EXPECT_EQ(volume, 154);
|
|
|
|
// Do the same thing, except downwards now.
|
|
volume = helper.CallRecommendInputVolume(
|
|
/*num_calls=*/1, /*initial_volume=*/100, kHighSpeechProbability, -17.0f);
|
|
EXPECT_EQ(volume, 100);
|
|
|
|
// And finally verify the AGC continues working without a manual change.
|
|
volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
|
|
kHighSpeechProbability, -17.0f);
|
|
EXPECT_EQ(volume, 99);
|
|
}
|
|
|
|
TEST_P(InputVolumeControllerParametrizedTest,
|
|
RecoveryAfterManualLevelChangeFromMax) {
|
|
InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig();
|
|
config.min_input_volume = GetParam();
|
|
InputVolumeControllerTestHelper helper(config);
|
|
int volume = *helper.CallAgcSequence(kInitialInputVolume,
|
|
kHighSpeechProbability, kSpeechLevel);
|
|
|
|
// Force the mic up to max volume. Takes a few steps due to the residual
|
|
// gain limitation.
|
|
volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
|
|
kHighSpeechProbability, -48.0f);
|
|
EXPECT_EQ(volume, 183);
|
|
volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
|
|
kHighSpeechProbability, -48.0f);
|
|
EXPECT_EQ(volume, 243);
|
|
volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
|
|
kHighSpeechProbability, -48.0f);
|
|
EXPECT_EQ(volume, 255);
|
|
|
|
// Manual change does not result in SetMicVolume call.
|
|
volume = helper.CallRecommendInputVolume(
|
|
/*num_calls=*/1, /*initial_volume=*/50, kHighSpeechProbability, -17.0f);
|
|
EXPECT_EQ(helper.controller.recommended_input_volume(), 50);
|
|
|
|
// Continues working as usual afterwards.
|
|
volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
|
|
kHighSpeechProbability, -38.0f);
|
|
|
|
EXPECT_EQ(volume, 65);
|
|
}
|
|
|
|
// Checks that the minimum input volume is enforced during the upward adjustment
|
|
// of the input volume.
|
|
TEST_P(InputVolumeControllerParametrizedTest,
|
|
EnforceMinInputVolumeDuringUpwardsAdjustment) {
|
|
const int min_input_volume = GetParam();
|
|
InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig();
|
|
config.min_input_volume = min_input_volume;
|
|
InputVolumeControllerTestHelper helper(config);
|
|
int volume = *helper.CallAgcSequence(kInitialInputVolume,
|
|
kHighSpeechProbability, kSpeechLevel);
|
|
|
|
// Manual change below min, but strictly positive, otherwise no action will be
|
|
// taken.
|
|
volume = helper.CallRecommendInputVolume(
|
|
/*num_calls=*/1, /*initial_volume=*/1, kHighSpeechProbability, -17.0f);
|
|
|
|
// Trigger an upward adjustment of the input volume.
|
|
EXPECT_EQ(volume, min_input_volume);
|
|
volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
|
|
kHighSpeechProbability, -29.0f);
|
|
EXPECT_EQ(volume, min_input_volume);
|
|
volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
|
|
kHighSpeechProbability, -30.0f);
|
|
EXPECT_EQ(volume, min_input_volume);
|
|
|
|
// After a number of consistently low speech level observations, the input
|
|
// volume is eventually raised above the minimum.
|
|
volume = helper.CallRecommendInputVolume(/*num_calls=*/10, volume,
|
|
kHighSpeechProbability, -38.0f);
|
|
EXPECT_GT(volume, min_input_volume);
|
|
}
|
|
|
|
// Checks that, when the min mic level override is specified, AGC immediately
|
|
// applies the minimum mic level after the mic level is manually set below the
|
|
// minimum gain to enforce.
|
|
TEST_P(InputVolumeControllerParametrizedTest,
|
|
RecoveryAfterManualLevelChangeBelowMin) {
|
|
const int min_input_volume = GetParam();
|
|
InputVolumeControllerTestHelper helper(
|
|
/*config=*/{.min_input_volume = min_input_volume});
|
|
int volume = *helper.CallAgcSequence(kInitialInputVolume,
|
|
kHighSpeechProbability, kSpeechLevel);
|
|
|
|
// Manual change below min, but strictly positive, otherwise
|
|
// AGC won't take any action.
|
|
volume = helper.CallRecommendInputVolume(
|
|
/*num_calls=*/1, /*initial_volume=*/1, kHighSpeechProbability, -17.0f);
|
|
EXPECT_EQ(volume, min_input_volume);
|
|
}
|
|
|
|
TEST_P(InputVolumeControllerParametrizedTest, NoClippingHasNoImpact) {
|
|
InputVolumeControllerTestHelper helper(
|
|
/*config=*/{.min_input_volume = GetParam()});
|
|
helper.CallAgcSequence(kInitialInputVolume, kHighSpeechProbability,
|
|
kSpeechLevel);
|
|
|
|
helper.CallAnalyzeInputAudio(/*num_calls=*/100, /*clipped_ratio=*/0);
|
|
EXPECT_EQ(helper.controller.recommended_input_volume(), 128);
|
|
}
|
|
|
|
TEST_P(InputVolumeControllerParametrizedTest,
|
|
ClippingUnderThresholdHasNoImpact) {
|
|
InputVolumeControllerTestHelper helper(
|
|
/*config=*/{.min_input_volume = GetParam()});
|
|
helper.CallAgcSequence(kInitialInputVolume, kHighSpeechProbability,
|
|
kSpeechLevel);
|
|
|
|
helper.CallAnalyzeInputAudio(/*num_calls=*/1, /*clipped_ratio=*/0.099);
|
|
EXPECT_EQ(helper.controller.recommended_input_volume(), 128);
|
|
}
|
|
|
|
TEST_P(InputVolumeControllerParametrizedTest, ClippingLowersVolume) {
|
|
InputVolumeControllerTestHelper helper(
|
|
/*config=*/{.min_input_volume = GetParam()});
|
|
helper.CallAgcSequence(/*applied_input_volume=*/255, kHighSpeechProbability,
|
|
kSpeechLevel);
|
|
|
|
helper.CallAnalyzeInputAudio(/*num_calls=*/1, /*clipped_ratio=*/0.2);
|
|
EXPECT_EQ(helper.controller.recommended_input_volume(), 240);
|
|
}
|
|
|
|
TEST_P(InputVolumeControllerParametrizedTest,
|
|
WaitingPeriodBetweenClippingChecks) {
|
|
InputVolumeControllerTestHelper helper(
|
|
/*config=*/{.min_input_volume = GetParam()});
|
|
helper.CallAgcSequence(/*applied_input_volume=*/255, kHighSpeechProbability,
|
|
kSpeechLevel);
|
|
|
|
helper.CallAnalyzeInputAudio(/*num_calls=*/1,
|
|
/*clipped_ratio=*/kAboveClippedThreshold);
|
|
EXPECT_EQ(helper.controller.recommended_input_volume(), 240);
|
|
|
|
helper.CallAnalyzeInputAudio(/*num_calls=*/300,
|
|
/*clipped_ratio=*/kAboveClippedThreshold);
|
|
EXPECT_EQ(helper.controller.recommended_input_volume(), 240);
|
|
|
|
helper.CallAnalyzeInputAudio(/*num_calls=*/1,
|
|
/*clipped_ratio=*/kAboveClippedThreshold);
|
|
EXPECT_EQ(helper.controller.recommended_input_volume(), 225);
|
|
}
|
|
|
|
TEST_P(InputVolumeControllerParametrizedTest, ClippingLoweringIsLimited) {
|
|
InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig();
|
|
config.min_input_volume = GetParam();
|
|
InputVolumeControllerTestHelper helper(config);
|
|
helper.CallAgcSequence(/*applied_input_volume=*/180, kHighSpeechProbability,
|
|
kSpeechLevel);
|
|
|
|
helper.CallAnalyzeInputAudio(/*num_calls=*/1,
|
|
/*clipped_ratio=*/kAboveClippedThreshold);
|
|
EXPECT_EQ(helper.controller.recommended_input_volume(), kClippedMin);
|
|
|
|
helper.CallAnalyzeInputAudio(/*num_calls=*/1000,
|
|
/*clipped_ratio=*/kAboveClippedThreshold);
|
|
EXPECT_EQ(helper.controller.recommended_input_volume(), kClippedMin);
|
|
}
|
|
|
|
TEST_P(InputVolumeControllerParametrizedTest,
|
|
ClippingMaxIsRespectedWhenEqualToLevel) {
|
|
InputVolumeControllerTestHelper helper(
|
|
/*config=*/{.min_input_volume = GetParam()});
|
|
helper.CallAgcSequence(/*applied_input_volume=*/255, kHighSpeechProbability,
|
|
kSpeechLevel);
|
|
|
|
helper.CallAnalyzeInputAudio(/*num_calls=*/1,
|
|
/*clipped_ratio=*/kAboveClippedThreshold);
|
|
EXPECT_EQ(helper.controller.recommended_input_volume(), 240);
|
|
|
|
helper.CallRecommendInputVolume(/*num_calls=*/10, /*initial_volume=*/240,
|
|
kHighSpeechProbability, -48.0f);
|
|
EXPECT_EQ(helper.controller.recommended_input_volume(), 240);
|
|
}
|
|
|
|
TEST_P(InputVolumeControllerParametrizedTest,
|
|
ClippingMaxIsRespectedWhenHigherThanLevel) {
|
|
InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig();
|
|
config.min_input_volume = GetParam();
|
|
InputVolumeControllerTestHelper helper(config);
|
|
helper.CallAgcSequence(/*applied_input_volume=*/200, kHighSpeechProbability,
|
|
kSpeechLevel);
|
|
|
|
helper.CallAnalyzeInputAudio(/*num_calls=*/1,
|
|
/*clipped_ratio=*/kAboveClippedThreshold);
|
|
int volume = helper.controller.recommended_input_volume();
|
|
EXPECT_EQ(volume, 185);
|
|
|
|
volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
|
|
kHighSpeechProbability, -58.0f);
|
|
EXPECT_EQ(volume, 240);
|
|
volume = helper.CallRecommendInputVolume(/*num_calls=*/10, volume,
|
|
kHighSpeechProbability, -58.0f);
|
|
EXPECT_EQ(volume, 240);
|
|
}
|
|
|
|
TEST_P(InputVolumeControllerParametrizedTest, UserCanRaiseVolumeAfterClipping) {
|
|
InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig();
|
|
config.min_input_volume = GetParam();
|
|
InputVolumeControllerTestHelper helper(config);
|
|
helper.CallAgcSequence(/*applied_input_volume=*/225, kHighSpeechProbability,
|
|
kSpeechLevel);
|
|
|
|
helper.CallAnalyzeInputAudio(/*num_calls=*/1,
|
|
/*clipped_ratio=*/kAboveClippedThreshold);
|
|
EXPECT_EQ(helper.controller.recommended_input_volume(), 210);
|
|
|
|
// User changed the volume.
|
|
int volume = helper.CallRecommendInputVolume(
|
|
/*num_calls=*/1, /*initial_volume-*/ 250, kHighSpeechProbability, -32.0f);
|
|
EXPECT_EQ(volume, 250);
|
|
|
|
// Move down...
|
|
volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
|
|
kHighSpeechProbability, -8.0f);
|
|
EXPECT_EQ(volume, 210);
|
|
// And back up to the new max established by the user.
|
|
volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
|
|
kHighSpeechProbability, -58.0f);
|
|
EXPECT_EQ(volume, 250);
|
|
// Will not move above new maximum.
|
|
volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
|
|
kHighSpeechProbability, -48.0f);
|
|
EXPECT_EQ(volume, 250);
|
|
}
|
|
|
|
TEST_P(InputVolumeControllerParametrizedTest,
|
|
ClippingDoesNotPullLowVolumeBackUp) {
|
|
InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig();
|
|
config.min_input_volume = GetParam();
|
|
InputVolumeControllerTestHelper helper(config);
|
|
helper.CallAgcSequence(/*applied_input_volume=*/80, kHighSpeechProbability,
|
|
kSpeechLevel);
|
|
|
|
int initial_volume = helper.controller.recommended_input_volume();
|
|
helper.CallAnalyzeInputAudio(/*num_calls=*/1,
|
|
/*clipped_ratio=*/kAboveClippedThreshold);
|
|
EXPECT_EQ(helper.controller.recommended_input_volume(), initial_volume);
|
|
}
|
|
|
|
TEST_P(InputVolumeControllerParametrizedTest, TakesNoActionOnZeroMicVolume) {
|
|
InputVolumeControllerTestHelper helper(
|
|
/*config=*/{.min_input_volume = GetParam()});
|
|
helper.CallAgcSequence(kInitialInputVolume, kHighSpeechProbability,
|
|
kSpeechLevel);
|
|
|
|
EXPECT_EQ(
|
|
helper.CallRecommendInputVolume(/*num_calls=*/10, /*initial_volume=*/0,
|
|
kHighSpeechProbability, -48.0f),
|
|
0);
|
|
}
|
|
|
|
TEST_P(InputVolumeControllerParametrizedTest, ClippingDetectionLowersVolume) {
|
|
InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig();
|
|
config.min_input_volume = GetParam();
|
|
InputVolumeControllerTestHelper helper(config);
|
|
int volume = *helper.CallAgcSequence(/*applied_input_volume=*/255,
|
|
kHighSpeechProbability, kSpeechLevel,
|
|
/*num_calls=*/1);
|
|
|
|
EXPECT_EQ(volume, 255);
|
|
|
|
WriteAlternatingAudioBufferSamples(0.99f * kMaxSample, helper.audio_buffer);
|
|
volume = *helper.CallAgcSequence(volume, kHighSpeechProbability, kSpeechLevel,
|
|
/*num_calls=*/100);
|
|
|
|
EXPECT_EQ(volume, 255);
|
|
|
|
WriteAlternatingAudioBufferSamples(kMaxSample, helper.audio_buffer);
|
|
volume = *helper.CallAgcSequence(volume, kHighSpeechProbability, kSpeechLevel,
|
|
/*num_calls=*/100);
|
|
|
|
EXPECT_EQ(volume, 240);
|
|
}
|
|
|
|
// TODO(bugs.webrtc.org/12774): Test the bahavior of `clipped_level_step`.
|
|
// TODO(bugs.webrtc.org/12774): Test the bahavior of `clipped_ratio_threshold`.
|
|
// TODO(bugs.webrtc.org/12774): Test the bahavior of `clipped_wait_frames`.
|
|
// Verifies that configurable clipping parameters are initialized as intended.
|
|
TEST_P(InputVolumeControllerParametrizedTest, ClippingParametersVerified) {
|
|
std::unique_ptr<InputVolumeController> controller =
|
|
CreateInputVolumeController(kClippedLevelStep, kClippedRatioThreshold,
|
|
kClippedWaitFrames);
|
|
controller->Initialize();
|
|
EXPECT_EQ(controller->clipped_level_step_, kClippedLevelStep);
|
|
EXPECT_EQ(controller->clipped_ratio_threshold_, kClippedRatioThreshold);
|
|
EXPECT_EQ(controller->clipped_wait_frames_, kClippedWaitFrames);
|
|
std::unique_ptr<InputVolumeController> controller_custom =
|
|
CreateInputVolumeController(/*clipped_level_step=*/10,
|
|
/*clipped_ratio_threshold=*/0.2f,
|
|
/*clipped_wait_frames=*/50);
|
|
controller_custom->Initialize();
|
|
EXPECT_EQ(controller_custom->clipped_level_step_, 10);
|
|
EXPECT_EQ(controller_custom->clipped_ratio_threshold_, 0.2f);
|
|
EXPECT_EQ(controller_custom->clipped_wait_frames_, 50);
|
|
}
|
|
|
|
TEST_P(InputVolumeControllerParametrizedTest,
|
|
DisableClippingPredictorDisablesClippingPredictor) {
|
|
std::unique_ptr<InputVolumeController> controller =
|
|
CreateInputVolumeController(kClippedLevelStep, kClippedRatioThreshold,
|
|
kClippedWaitFrames,
|
|
/*enable_clipping_predictor=*/false);
|
|
controller->Initialize();
|
|
|
|
EXPECT_FALSE(controller->clipping_predictor_enabled());
|
|
EXPECT_FALSE(controller->use_clipping_predictor_step());
|
|
}
|
|
|
|
TEST_P(InputVolumeControllerParametrizedTest,
|
|
EnableClippingPredictorEnablesClippingPredictor) {
|
|
std::unique_ptr<InputVolumeController> controller =
|
|
CreateInputVolumeController(kClippedLevelStep, kClippedRatioThreshold,
|
|
kClippedWaitFrames,
|
|
/*enable_clipping_predictor=*/true);
|
|
controller->Initialize();
|
|
|
|
EXPECT_TRUE(controller->clipping_predictor_enabled());
|
|
EXPECT_TRUE(controller->use_clipping_predictor_step());
|
|
}
|
|
|
|
TEST_P(InputVolumeControllerParametrizedTest,
|
|
DisableClippingPredictorDoesNotLowerVolume) {
|
|
int volume = 255;
|
|
InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig();
|
|
config.enable_clipping_predictor = false;
|
|
auto helper = InputVolumeControllerTestHelper(config);
|
|
helper.controller.Initialize();
|
|
|
|
EXPECT_FALSE(helper.controller.clipping_predictor_enabled());
|
|
EXPECT_FALSE(helper.controller.use_clipping_predictor_step());
|
|
|
|
// Expect no change if clipping prediction is enabled.
|
|
for (int j = 0; j < 31; ++j) {
|
|
WriteAlternatingAudioBufferSamples(0.99f * kMaxSample, helper.audio_buffer);
|
|
volume =
|
|
*helper.CallAgcSequence(volume, kLowSpeechProbability, kSpeechLevel,
|
|
/*num_calls=*/5);
|
|
|
|
WriteAudioBufferSamples(0.99f * kMaxSample, /*clipped_ratio=*/0.0f,
|
|
helper.audio_buffer);
|
|
volume =
|
|
*helper.CallAgcSequence(volume, kLowSpeechProbability, kSpeechLevel,
|
|
/*num_calls=*/5);
|
|
|
|
EXPECT_EQ(volume, 255);
|
|
}
|
|
}
|
|
|
|
// TODO(bugs.webrtc.org/7494): Split into several smaller tests.
|
|
TEST_P(InputVolumeControllerParametrizedTest,
|
|
UsedClippingPredictionsProduceLowerAnalogLevels) {
|
|
constexpr int kInitialLevel = 255;
|
|
constexpr float kCloseToClippingPeakRatio = 0.99f;
|
|
int volume_1 = kInitialLevel;
|
|
int volume_2 = kInitialLevel;
|
|
|
|
// Create two helpers, one with clipping prediction and one without.
|
|
auto config_1 = GetInputVolumeControllerTestConfig();
|
|
auto config_2 = GetInputVolumeControllerTestConfig();
|
|
config_1.enable_clipping_predictor = true;
|
|
config_2.enable_clipping_predictor = false;
|
|
auto helper_1 = InputVolumeControllerTestHelper(config_1);
|
|
auto helper_2 = InputVolumeControllerTestHelper(config_2);
|
|
helper_1.controller.Initialize();
|
|
helper_2.controller.Initialize();
|
|
|
|
EXPECT_TRUE(helper_1.controller.clipping_predictor_enabled());
|
|
EXPECT_FALSE(helper_2.controller.clipping_predictor_enabled());
|
|
EXPECT_TRUE(helper_1.controller.use_clipping_predictor_step());
|
|
|
|
// Expect a change if clipping prediction is enabled.
|
|
WriteAlternatingAudioBufferSamples(kCloseToClippingPeakRatio * kMaxSample,
|
|
helper_1.audio_buffer);
|
|
WriteAlternatingAudioBufferSamples(kCloseToClippingPeakRatio * kMaxSample,
|
|
helper_2.audio_buffer);
|
|
volume_1 = *helper_1.CallAgcSequence(volume_1, kLowSpeechProbability,
|
|
kSpeechLevel, 5);
|
|
volume_2 = *helper_2.CallAgcSequence(volume_2, kLowSpeechProbability,
|
|
kSpeechLevel, 5);
|
|
|
|
WriteAudioBufferSamples(kCloseToClippingPeakRatio * kMaxSample,
|
|
/*clipped_ratio=*/0.0f, helper_1.audio_buffer);
|
|
WriteAudioBufferSamples(kCloseToClippingPeakRatio * kMaxSample,
|
|
/*clipped_ratio=*/0.0f, helper_2.audio_buffer);
|
|
volume_1 = *helper_1.CallAgcSequence(volume_1, kLowSpeechProbability,
|
|
kSpeechLevel, 5);
|
|
volume_2 = *helper_2.CallAgcSequence(volume_2, kLowSpeechProbability,
|
|
kSpeechLevel, 5);
|
|
|
|
EXPECT_EQ(volume_1, kInitialLevel - kClippedLevelStep);
|
|
EXPECT_EQ(volume_2, kInitialLevel);
|
|
|
|
// Expect no change during waiting.
|
|
for (int i = 0; i < kClippedWaitFrames / 10; ++i) {
|
|
WriteAlternatingAudioBufferSamples(kCloseToClippingPeakRatio * kMaxSample,
|
|
helper_1.audio_buffer);
|
|
WriteAlternatingAudioBufferSamples(kCloseToClippingPeakRatio * kMaxSample,
|
|
helper_2.audio_buffer);
|
|
volume_1 = *helper_1.CallAgcSequence(volume_1, kLowSpeechProbability,
|
|
kSpeechLevel, 5);
|
|
volume_2 = *helper_2.CallAgcSequence(volume_2, kLowSpeechProbability,
|
|
kSpeechLevel, 5);
|
|
|
|
WriteAudioBufferSamples(kCloseToClippingPeakRatio * kMaxSample,
|
|
/*clipped_ratio=*/0.0f, helper_1.audio_buffer);
|
|
WriteAudioBufferSamples(kCloseToClippingPeakRatio * kMaxSample,
|
|
/*clipped_ratio=*/0.0f, helper_2.audio_buffer);
|
|
volume_1 = *helper_1.CallAgcSequence(volume_1, kLowSpeechProbability,
|
|
kSpeechLevel, 5);
|
|
volume_2 = *helper_2.CallAgcSequence(volume_2, kLowSpeechProbability,
|
|
kSpeechLevel, 5);
|
|
|
|
EXPECT_EQ(volume_1, kInitialLevel - kClippedLevelStep);
|
|
EXPECT_EQ(volume_2, kInitialLevel);
|
|
}
|
|
|
|
// Expect a change when the prediction step is used.
|
|
WriteAlternatingAudioBufferSamples(kCloseToClippingPeakRatio * kMaxSample,
|
|
helper_1.audio_buffer);
|
|
WriteAlternatingAudioBufferSamples(kCloseToClippingPeakRatio * kMaxSample,
|
|
helper_2.audio_buffer);
|
|
volume_1 = *helper_1.CallAgcSequence(volume_1, kLowSpeechProbability,
|
|
kSpeechLevel, 5);
|
|
volume_2 = *helper_2.CallAgcSequence(volume_2, kLowSpeechProbability,
|
|
kSpeechLevel, 5);
|
|
|
|
WriteAudioBufferSamples(kCloseToClippingPeakRatio * kMaxSample,
|
|
/*clipped_ratio=*/0.0f, helper_1.audio_buffer);
|
|
WriteAudioBufferSamples(kCloseToClippingPeakRatio * kMaxSample,
|
|
/*clipped_ratio=*/0.0f, helper_2.audio_buffer);
|
|
volume_1 = *helper_1.CallAgcSequence(volume_1, kLowSpeechProbability,
|
|
kSpeechLevel, 5);
|
|
volume_2 = *helper_2.CallAgcSequence(volume_2, kLowSpeechProbability,
|
|
kSpeechLevel, 5);
|
|
|
|
EXPECT_EQ(volume_1, kInitialLevel - 2 * kClippedLevelStep);
|
|
EXPECT_EQ(volume_2, kInitialLevel);
|
|
|
|
// Expect no change when clipping is not detected or predicted.
|
|
for (int i = 0; i < 2 * kClippedWaitFrames / 10; ++i) {
|
|
WriteAlternatingAudioBufferSamples(/*samples_value=*/0.0f,
|
|
helper_1.audio_buffer);
|
|
WriteAlternatingAudioBufferSamples(/*samples_value=*/0.0f,
|
|
helper_2.audio_buffer);
|
|
volume_1 = *helper_1.CallAgcSequence(volume_1, kLowSpeechProbability,
|
|
kSpeechLevel, 5);
|
|
volume_2 = *helper_2.CallAgcSequence(volume_2, kLowSpeechProbability,
|
|
kSpeechLevel, 5);
|
|
|
|
WriteAudioBufferSamples(/*samples_value=*/0.0f, /*clipped_ratio=*/0.0f,
|
|
helper_1.audio_buffer);
|
|
WriteAudioBufferSamples(/*samples_value=*/0.0f, /*clipped_ratio=*/0.0f,
|
|
helper_2.audio_buffer);
|
|
volume_1 = *helper_1.CallAgcSequence(volume_1, kLowSpeechProbability,
|
|
kSpeechLevel, 5);
|
|
volume_2 = *helper_2.CallAgcSequence(volume_2, kLowSpeechProbability,
|
|
kSpeechLevel, 5);
|
|
}
|
|
|
|
EXPECT_EQ(volume_1, kInitialLevel - 2 * kClippedLevelStep);
|
|
EXPECT_EQ(volume_2, kInitialLevel);
|
|
|
|
// Expect a change for clipping frames.
|
|
WriteAlternatingAudioBufferSamples(kMaxSample, helper_1.audio_buffer);
|
|
WriteAlternatingAudioBufferSamples(kMaxSample, helper_2.audio_buffer);
|
|
volume_1 = *helper_1.CallAgcSequence(volume_1, kLowSpeechProbability,
|
|
kSpeechLevel, 1);
|
|
volume_2 = *helper_2.CallAgcSequence(volume_2, kLowSpeechProbability,
|
|
kSpeechLevel, 1);
|
|
|
|
EXPECT_EQ(volume_1, kInitialLevel - 3 * kClippedLevelStep);
|
|
EXPECT_EQ(volume_2, kInitialLevel - kClippedLevelStep);
|
|
|
|
// Expect no change during waiting.
|
|
for (int i = 0; i < kClippedWaitFrames / 10; ++i) {
|
|
WriteAlternatingAudioBufferSamples(kMaxSample, helper_1.audio_buffer);
|
|
WriteAlternatingAudioBufferSamples(kMaxSample, helper_2.audio_buffer);
|
|
volume_1 = *helper_1.CallAgcSequence(volume_1, kLowSpeechProbability,
|
|
kSpeechLevel, 5);
|
|
volume_2 = *helper_2.CallAgcSequence(volume_2, kLowSpeechProbability,
|
|
kSpeechLevel, 5);
|
|
|
|
WriteAudioBufferSamples(kMaxSample, /*clipped_ratio=*/1.0f,
|
|
helper_1.audio_buffer);
|
|
WriteAudioBufferSamples(kMaxSample, /*clipped_ratio=*/1.0f,
|
|
helper_2.audio_buffer);
|
|
volume_1 = *helper_1.CallAgcSequence(volume_1, kLowSpeechProbability,
|
|
kSpeechLevel, 5);
|
|
volume_2 = *helper_2.CallAgcSequence(volume_2, kLowSpeechProbability,
|
|
kSpeechLevel, 5);
|
|
}
|
|
|
|
EXPECT_EQ(volume_1, kInitialLevel - 3 * kClippedLevelStep);
|
|
EXPECT_EQ(volume_2, kInitialLevel - kClippedLevelStep);
|
|
|
|
// Expect a change for clipping frames.
|
|
WriteAlternatingAudioBufferSamples(kMaxSample, helper_1.audio_buffer);
|
|
WriteAlternatingAudioBufferSamples(kMaxSample, helper_2.audio_buffer);
|
|
volume_1 = *helper_1.CallAgcSequence(volume_1, kLowSpeechProbability,
|
|
kSpeechLevel, 1);
|
|
volume_2 = *helper_2.CallAgcSequence(volume_2, kLowSpeechProbability,
|
|
kSpeechLevel, 1);
|
|
|
|
EXPECT_EQ(volume_1, kInitialLevel - 4 * kClippedLevelStep);
|
|
EXPECT_EQ(volume_2, kInitialLevel - 2 * kClippedLevelStep);
|
|
}
|
|
|
|
// Checks that passing an empty speech level has no effect on the input volume.
|
|
TEST_P(InputVolumeControllerParametrizedTest, EmptyRmsErrorHasNoEffect) {
|
|
InputVolumeController controller(kNumChannels,
|
|
GetInputVolumeControllerTestConfig());
|
|
controller.Initialize();
|
|
|
|
// Feed speech with low energy that would trigger an upward adapation of
|
|
// the analog level if an speech level was not low and the RMS level empty.
|
|
constexpr int kNumFrames = 125;
|
|
constexpr int kGainDb = -20;
|
|
SpeechSamplesReader reader;
|
|
int volume = reader.Feed(kNumFrames, kInitialInputVolume, kGainDb,
|
|
kLowSpeechProbability, absl::nullopt, controller);
|
|
|
|
// Check that no adaptation occurs.
|
|
ASSERT_EQ(volume, kInitialInputVolume);
|
|
}
|
|
|
|
// Checks that the recommended input volume is not updated unless enough
|
|
// frames have been processed after the previous update.
|
|
TEST(InputVolumeControllerTest, UpdateInputVolumeWaitFramesIsEffective) {
|
|
constexpr int kInputVolume = kInitialInputVolume;
|
|
std::unique_ptr<InputVolumeController> controller_wait_0 =
|
|
CreateInputVolumeController(kClippedLevelStep, kClippedRatioThreshold,
|
|
kClippedWaitFrames,
|
|
/*enable_clipping_predictor=*/false,
|
|
/*update_input_volume_wait_frames=*/0);
|
|
std::unique_ptr<InputVolumeController> controller_wait_100 =
|
|
CreateInputVolumeController(kClippedLevelStep, kClippedRatioThreshold,
|
|
kClippedWaitFrames,
|
|
/*enable_clipping_predictor=*/false,
|
|
/*update_input_volume_wait_frames=*/100);
|
|
controller_wait_0->Initialize();
|
|
controller_wait_100->Initialize();
|
|
|
|
SpeechSamplesReader reader_1;
|
|
SpeechSamplesReader reader_2;
|
|
int volume_wait_0 = reader_1.Feed(
|
|
/*num_frames=*/99, kInputVolume, /*gain_db=*/0, kHighSpeechProbability,
|
|
/*speech_level_dbfs=*/-42.0f, *controller_wait_0);
|
|
int volume_wait_100 = reader_2.Feed(
|
|
/*num_frames=*/99, kInputVolume, /*gain_db=*/0, kHighSpeechProbability,
|
|
/*speech_level_dbfs=*/-42.0f, *controller_wait_100);
|
|
|
|
// Check that adaptation only occurs if enough frames have been processed.
|
|
ASSERT_GT(volume_wait_0, kInputVolume);
|
|
ASSERT_EQ(volume_wait_100, kInputVolume);
|
|
|
|
volume_wait_0 =
|
|
reader_1.Feed(/*num_frames=*/1, volume_wait_0,
|
|
/*gain_db=*/0, kHighSpeechProbability,
|
|
/*speech_level_dbfs=*/-42.0f, *controller_wait_0);
|
|
volume_wait_100 =
|
|
reader_2.Feed(/*num_frames=*/1, volume_wait_100,
|
|
/*gain_db=*/0, kHighSpeechProbability,
|
|
/*speech_level_dbfs=*/-42.0f, *controller_wait_100);
|
|
|
|
// Check that adaptation only occurs when enough frames have been processed.
|
|
ASSERT_GT(volume_wait_0, kInputVolume);
|
|
ASSERT_GT(volume_wait_100, kInputVolume);
|
|
}
|
|
|
|
INSTANTIATE_TEST_SUITE_P(,
|
|
InputVolumeControllerParametrizedTest,
|
|
::testing::Values(12, 20));
|
|
|
|
TEST(InputVolumeControllerTest,
|
|
MinInputVolumeEnforcedWithClippingWhenAboveClippedLevelMin) {
|
|
InputVolumeControllerTestHelper helper(
|
|
/*config=*/{.min_input_volume = 80, .clipped_level_min = 70});
|
|
|
|
// Trigger a downward adjustment caused by clipping input. Use a low speech
|
|
// probability to limit the volume changes to clipping handling.
|
|
WriteAudioBufferSamples(/*samples_value=*/4000.0f, /*clipped_ratio=*/0.8f,
|
|
helper.audio_buffer);
|
|
constexpr int kNumCalls = 800;
|
|
helper.CallAgcSequence(/*applied_input_volume=*/100, kLowSpeechProbability,
|
|
/*speech_level_dbfs=*/-18.0f, kNumCalls);
|
|
|
|
EXPECT_EQ(helper.controller.recommended_input_volume(), 80);
|
|
}
|
|
|
|
TEST(InputVolumeControllerTest,
|
|
ClippedlevelMinEnforcedWithClippingWhenAboveMinInputVolume) {
|
|
InputVolumeControllerTestHelper helper(
|
|
/*config=*/{.min_input_volume = 70, .clipped_level_min = 80});
|
|
|
|
// Trigger a downward adjustment caused by clipping input. Use a low speech
|
|
// probability to limit the volume changes to clipping handling.
|
|
WriteAudioBufferSamples(/*samples_value=*/4000.0f, /*clipped_ratio=*/0.8f,
|
|
helper.audio_buffer);
|
|
constexpr int kNumCalls = 800;
|
|
helper.CallAgcSequence(/*applied_input_volume=*/100, kLowSpeechProbability,
|
|
/*speech_level_dbfs=*/-18.0f, kNumCalls);
|
|
|
|
EXPECT_EQ(helper.controller.recommended_input_volume(), 80);
|
|
}
|
|
|
|
TEST(InputVolumeControllerTest, SpeechRatioThresholdIsEffective) {
|
|
constexpr int kInputVolume = kInitialInputVolume;
|
|
// Create two input volume controllers with 10 frames between volume updates
|
|
// and the minimum speech ratio of 0.8 and speech probability threshold 0.5.
|
|
std::unique_ptr<InputVolumeController> controller_1 =
|
|
CreateInputVolumeController(kClippedLevelStep, kClippedRatioThreshold,
|
|
kClippedWaitFrames,
|
|
/*enable_clipping_predictor=*/false,
|
|
/*update_input_volume_wait_frames=*/10);
|
|
std::unique_ptr<InputVolumeController> controller_2 =
|
|
CreateInputVolumeController(kClippedLevelStep, kClippedRatioThreshold,
|
|
kClippedWaitFrames,
|
|
/*enable_clipping_predictor=*/false,
|
|
/*update_input_volume_wait_frames=*/10);
|
|
controller_1->Initialize();
|
|
controller_2->Initialize();
|
|
|
|
SpeechSamplesReader reader_1;
|
|
SpeechSamplesReader reader_2;
|
|
|
|
int volume_1 = reader_1.Feed(/*num_frames=*/1, kInputVolume, /*gain_db=*/0,
|
|
/*speech_probability=*/0.7f,
|
|
/*speech_level_dbfs=*/-42.0f, *controller_1);
|
|
int volume_2 = reader_2.Feed(/*num_frames=*/1, kInputVolume, /*gain_db=*/0,
|
|
/*speech_probability=*/0.4f,
|
|
/*speech_level_dbfs=*/-42.0f, *controller_2);
|
|
|
|
ASSERT_EQ(volume_1, kInputVolume);
|
|
ASSERT_EQ(volume_2, kInputVolume);
|
|
|
|
volume_1 = reader_1.Feed(/*num_frames=*/2, volume_1, /*gain_db=*/0,
|
|
/*speech_probability=*/0.4f,
|
|
/*speech_level_dbfs=*/-42.0f, *controller_1);
|
|
volume_2 = reader_2.Feed(/*num_frames=*/2, volume_2, /*gain_db=*/0,
|
|
/*speech_probability=*/0.4f,
|
|
/*speech_level_dbfs=*/-42.0f, *controller_2);
|
|
|
|
ASSERT_EQ(volume_1, kInputVolume);
|
|
ASSERT_EQ(volume_2, kInputVolume);
|
|
|
|
volume_1 = reader_1.Feed(
|
|
/*num_frames=*/7, volume_1, /*gain_db=*/0,
|
|
/*speech_probability=*/0.7f, /*speech_level_dbfs=*/-42.0f, *controller_1);
|
|
volume_2 = reader_2.Feed(
|
|
/*num_frames=*/7, volume_2, /*gain_db=*/0,
|
|
/*speech_probability=*/0.7f, /*speech_level_dbfs=*/-42.0f, *controller_2);
|
|
|
|
ASSERT_GT(volume_1, kInputVolume);
|
|
ASSERT_EQ(volume_2, kInputVolume);
|
|
}
|
|
|
|
TEST(InputVolumeControllerTest, SpeechProbabilityThresholdIsEffective) {
|
|
constexpr int kInputVolume = kInitialInputVolume;
|
|
// Create two input volume controllers with the exact same settings and
|
|
// 10 frames between volume updates.
|
|
std::unique_ptr<InputVolumeController> controller_1 =
|
|
CreateInputVolumeController(kClippedLevelStep, kClippedRatioThreshold,
|
|
kClippedWaitFrames,
|
|
/*enable_clipping_predictor=*/false,
|
|
/*update_input_volume_wait_frames=*/10);
|
|
std::unique_ptr<InputVolumeController> controller_2 =
|
|
CreateInputVolumeController(kClippedLevelStep, kClippedRatioThreshold,
|
|
kClippedWaitFrames,
|
|
/*enable_clipping_predictor=*/false,
|
|
/*update_input_volume_wait_frames=*/10);
|
|
controller_1->Initialize();
|
|
controller_2->Initialize();
|
|
|
|
SpeechSamplesReader reader_1;
|
|
SpeechSamplesReader reader_2;
|
|
|
|
// Process with two sets of inputs: Use `reader_1` to process inputs
|
|
// that make the volume to be adjusted after enough frames have been
|
|
// processsed and `reader_2` to process inputs that won't make the volume
|
|
// to be adjusted.
|
|
int volume_1 = reader_1.Feed(/*num_frames=*/1, kInputVolume, /*gain_db=*/0,
|
|
/*speech_probability=*/0.5f,
|
|
/*speech_level_dbfs=*/-42.0f, *controller_1);
|
|
int volume_2 = reader_2.Feed(/*num_frames=*/1, kInputVolume, /*gain_db=*/0,
|
|
/*speech_probability=*/0.49f,
|
|
/*speech_level_dbfs=*/-42.0f, *controller_2);
|
|
|
|
ASSERT_EQ(volume_1, kInputVolume);
|
|
ASSERT_EQ(volume_2, kInputVolume);
|
|
|
|
reader_1.Feed(/*num_frames=*/2, volume_1, /*gain_db=*/0,
|
|
/*speech_probability=*/0.49f, /*speech_level_dbfs=*/-42.0f,
|
|
*controller_1);
|
|
reader_2.Feed(/*num_frames=*/2, volume_2, /*gain_db=*/0,
|
|
/*speech_probability=*/0.49f, /*speech_level_dbfs=*/-42.0f,
|
|
*controller_2);
|
|
|
|
ASSERT_EQ(volume_1, kInputVolume);
|
|
ASSERT_EQ(volume_2, kInputVolume);
|
|
|
|
volume_1 = reader_1.Feed(
|
|
/*num_frames=*/7, volume_1, /*gain_db=*/0,
|
|
/*speech_probability=*/0.5f, /*speech_level_dbfs=*/-42.0f, *controller_1);
|
|
volume_2 = reader_2.Feed(
|
|
/*num_frames=*/7, volume_2, /*gain_db=*/0,
|
|
/*speech_probability=*/0.5f, /*speech_level_dbfs=*/-42.0f, *controller_2);
|
|
|
|
ASSERT_GT(volume_1, kInputVolume);
|
|
ASSERT_EQ(volume_2, kInputVolume);
|
|
}
|
|
|
|
TEST(InputVolumeControllerTest,
|
|
DoNotLogRecommendedInputVolumeOnChangeToMatchTarget) {
|
|
metrics::Reset();
|
|
|
|
SpeechSamplesReader reader;
|
|
auto controller = CreateInputVolumeController();
|
|
controller->Initialize();
|
|
// Trigger a downward volume change by inputting audio that clips. Pass a
|
|
// speech level that falls in the target range to make sure that the
|
|
// adaptation is not made to match the target range.
|
|
constexpr int kStartupVolume = 255;
|
|
const int volume = reader.Feed(/*num_frames=*/14, kStartupVolume,
|
|
/*gain_db=*/50, kHighSpeechProbability,
|
|
/*speech_level_dbfs=*/-20.0f, *controller);
|
|
ASSERT_LT(volume, kStartupVolume);
|
|
EXPECT_METRIC_THAT(
|
|
metrics::Samples(
|
|
"WebRTC.Audio.Apm.RecommendedInputVolume.OnChangeToMatchTarget"),
|
|
::testing::IsEmpty());
|
|
}
|
|
|
|
TEST(InputVolumeControllerTest,
|
|
LogRecommendedInputVolumeOnUpwardChangeToMatchTarget) {
|
|
metrics::Reset();
|
|
|
|
SpeechSamplesReader reader;
|
|
auto controller = CreateInputVolumeController();
|
|
controller->Initialize();
|
|
constexpr int kStartupVolume = 100;
|
|
// Trigger an upward volume change by inputting audio that does not clip and
|
|
// by passing a speech level below the target range.
|
|
const int volume = reader.Feed(/*num_frames=*/14, kStartupVolume,
|
|
/*gain_db=*/-6, kHighSpeechProbability,
|
|
/*speech_level_dbfs=*/-50.0f, *controller);
|
|
ASSERT_GT(volume, kStartupVolume);
|
|
EXPECT_METRIC_THAT(
|
|
metrics::Samples(
|
|
"WebRTC.Audio.Apm.RecommendedInputVolume.OnChangeToMatchTarget"),
|
|
::testing::Not(::testing::IsEmpty()));
|
|
}
|
|
|
|
TEST(InputVolumeControllerTest,
|
|
LogRecommendedInputVolumeOnDownwardChangeToMatchTarget) {
|
|
metrics::Reset();
|
|
|
|
SpeechSamplesReader reader;
|
|
auto controller = CreateInputVolumeController();
|
|
controller->Initialize();
|
|
constexpr int kStartupVolume = 100;
|
|
// Trigger a downward volume change by inputting audio that does not clip and
|
|
// by passing a speech level above the target range.
|
|
const int volume = reader.Feed(/*num_frames=*/14, kStartupVolume,
|
|
/*gain_db=*/-6, kHighSpeechProbability,
|
|
/*speech_level_dbfs=*/-5.0f, *controller);
|
|
ASSERT_LT(volume, kStartupVolume);
|
|
EXPECT_METRIC_THAT(
|
|
metrics::Samples(
|
|
"WebRTC.Audio.Apm.RecommendedInputVolume.OnChangeToMatchTarget"),
|
|
::testing::Not(::testing::IsEmpty()));
|
|
}
|
|
|
|
TEST(MonoInputVolumeControllerTest, CheckHandleClippingLowersVolume) {
|
|
constexpr int kInitialInputVolume = 100;
|
|
constexpr int kInputVolumeStep = 29;
|
|
MonoInputVolumeController mono_controller(
|
|
/*clipped_level_min=*/70,
|
|
/*min_mic_level=*/32,
|
|
/*update_input_volume_wait_frames=*/3, kHighSpeechProbability,
|
|
kSpeechRatioThreshold);
|
|
mono_controller.Initialize();
|
|
|
|
UpdateRecommendedInputVolume(mono_controller, kInitialInputVolume,
|
|
kLowSpeechProbability,
|
|
/*rms_error_dbfs*/ -10.0f);
|
|
|
|
mono_controller.HandleClipping(kInputVolumeStep);
|
|
|
|
EXPECT_EQ(mono_controller.recommended_analog_level(),
|
|
kInitialInputVolume - kInputVolumeStep);
|
|
}
|
|
|
|
TEST(MonoInputVolumeControllerTest,
|
|
CheckProcessNegativeRmsErrorDecreasesInputVolume) {
|
|
constexpr int kInitialInputVolume = 100;
|
|
MonoInputVolumeController mono_controller(
|
|
/*clipped_level_min=*/64,
|
|
/*min_mic_level=*/32,
|
|
/*update_input_volume_wait_frames=*/3, kHighSpeechProbability,
|
|
kSpeechRatioThreshold);
|
|
mono_controller.Initialize();
|
|
|
|
int volume = UpdateRecommendedInputVolume(
|
|
mono_controller, kInitialInputVolume, kHighSpeechProbability, -10.0f);
|
|
volume = UpdateRecommendedInputVolume(mono_controller, volume,
|
|
kHighSpeechProbability, -10.0f);
|
|
volume = UpdateRecommendedInputVolume(mono_controller, volume,
|
|
kHighSpeechProbability, -10.0f);
|
|
|
|
EXPECT_LT(volume, kInitialInputVolume);
|
|
}
|
|
|
|
TEST(MonoInputVolumeControllerTest,
|
|
CheckProcessPositiveRmsErrorIncreasesInputVolume) {
|
|
constexpr int kInitialInputVolume = 100;
|
|
MonoInputVolumeController mono_controller(
|
|
/*clipped_level_min=*/64,
|
|
/*min_mic_level=*/32,
|
|
/*update_input_volume_wait_frames=*/3, kHighSpeechProbability,
|
|
kSpeechRatioThreshold);
|
|
mono_controller.Initialize();
|
|
|
|
int volume = UpdateRecommendedInputVolume(
|
|
mono_controller, kInitialInputVolume, kHighSpeechProbability, 10.0f);
|
|
volume = UpdateRecommendedInputVolume(mono_controller, volume,
|
|
kHighSpeechProbability, 10.0f);
|
|
volume = UpdateRecommendedInputVolume(mono_controller, volume,
|
|
kHighSpeechProbability, 10.0f);
|
|
|
|
EXPECT_GT(volume, kInitialInputVolume);
|
|
}
|
|
|
|
TEST(MonoInputVolumeControllerTest,
|
|
CheckProcessNegativeRmsErrorDecreasesInputVolumeWithLimit) {
|
|
constexpr int kInitialInputVolume = 100;
|
|
MonoInputVolumeController mono_controller_1(
|
|
/*clipped_level_min=*/64,
|
|
/*min_mic_level=*/32,
|
|
/*update_input_volume_wait_frames=*/2, kHighSpeechProbability,
|
|
kSpeechRatioThreshold);
|
|
MonoInputVolumeController mono_controller_2(
|
|
/*clipped_level_min=*/64,
|
|
/*min_mic_level=*/32,
|
|
/*update_input_volume_wait_frames=*/2, kHighSpeechProbability,
|
|
kSpeechRatioThreshold);
|
|
MonoInputVolumeController mono_controller_3(
|
|
/*clipped_level_min=*/64,
|
|
/*min_mic_level=*/32,
|
|
/*update_input_volume_wait_frames=*/2,
|
|
/*speech_probability_threshold=*/0.7,
|
|
/*speech_ratio_threshold=*/0.8);
|
|
mono_controller_1.Initialize();
|
|
mono_controller_2.Initialize();
|
|
mono_controller_3.Initialize();
|
|
|
|
// Process RMS errors in the range
|
|
// [`-kMaxResidualGainChange`, `kMaxResidualGainChange`].
|
|
int volume_1 = UpdateRecommendedInputVolume(
|
|
mono_controller_1, kInitialInputVolume, kHighSpeechProbability, -14.0f);
|
|
volume_1 = UpdateRecommendedInputVolume(mono_controller_1, volume_1,
|
|
kHighSpeechProbability, -14.0f);
|
|
// Process RMS errors outside the range
|
|
// [`-kMaxResidualGainChange`, `kMaxResidualGainChange`].
|
|
int volume_2 = UpdateRecommendedInputVolume(
|
|
mono_controller_2, kInitialInputVolume, kHighSpeechProbability, -15.0f);
|
|
int volume_3 = UpdateRecommendedInputVolume(
|
|
mono_controller_3, kInitialInputVolume, kHighSpeechProbability, -30.0f);
|
|
volume_2 = UpdateRecommendedInputVolume(mono_controller_2, volume_2,
|
|
kHighSpeechProbability, -15.0f);
|
|
volume_3 = UpdateRecommendedInputVolume(mono_controller_3, volume_3,
|
|
kHighSpeechProbability, -30.0f);
|
|
|
|
EXPECT_LT(volume_1, kInitialInputVolume);
|
|
EXPECT_LT(volume_2, volume_1);
|
|
EXPECT_EQ(volume_2, volume_3);
|
|
}
|
|
|
|
TEST(MonoInputVolumeControllerTest,
|
|
CheckProcessPositiveRmsErrorIncreasesInputVolumeWithLimit) {
|
|
constexpr int kInitialInputVolume = 100;
|
|
MonoInputVolumeController mono_controller_1(
|
|
/*clipped_level_min=*/64,
|
|
/*min_mic_level=*/32,
|
|
/*update_input_volume_wait_frames=*/2, kHighSpeechProbability,
|
|
kSpeechRatioThreshold);
|
|
MonoInputVolumeController mono_controller_2(
|
|
/*clipped_level_min=*/64,
|
|
/*min_mic_level=*/32,
|
|
/*update_input_volume_wait_frames=*/2, kHighSpeechProbability,
|
|
kSpeechRatioThreshold);
|
|
MonoInputVolumeController mono_controller_3(
|
|
/*clipped_level_min=*/64,
|
|
/*min_mic_level=*/32,
|
|
/*update_input_volume_wait_frames=*/2, kHighSpeechProbability,
|
|
kSpeechRatioThreshold);
|
|
mono_controller_1.Initialize();
|
|
mono_controller_2.Initialize();
|
|
mono_controller_3.Initialize();
|
|
|
|
// Process RMS errors in the range
|
|
// [`-kMaxResidualGainChange`, `kMaxResidualGainChange`].
|
|
int volume_1 = UpdateRecommendedInputVolume(
|
|
mono_controller_1, kInitialInputVolume, kHighSpeechProbability, 14.0f);
|
|
volume_1 = UpdateRecommendedInputVolume(mono_controller_1, volume_1,
|
|
kHighSpeechProbability, 14.0f);
|
|
// Process RMS errors outside the range
|
|
// [`-kMaxResidualGainChange`, `kMaxResidualGainChange`].
|
|
int volume_2 = UpdateRecommendedInputVolume(
|
|
mono_controller_2, kInitialInputVolume, kHighSpeechProbability, 15.0f);
|
|
int volume_3 = UpdateRecommendedInputVolume(
|
|
mono_controller_3, kInitialInputVolume, kHighSpeechProbability, 30.0f);
|
|
volume_2 = UpdateRecommendedInputVolume(mono_controller_2, volume_2,
|
|
kHighSpeechProbability, 15.0f);
|
|
volume_3 = UpdateRecommendedInputVolume(mono_controller_3, volume_3,
|
|
kHighSpeechProbability, 30.0f);
|
|
|
|
EXPECT_GT(volume_1, kInitialInputVolume);
|
|
EXPECT_GT(volume_2, volume_1);
|
|
EXPECT_EQ(volume_2, volume_3);
|
|
}
|
|
|
|
TEST(MonoInputVolumeControllerTest,
|
|
CheckProcessRmsErrorDecreasesInputVolumeRepeatedly) {
|
|
constexpr int kInitialInputVolume = 100;
|
|
MonoInputVolumeController mono_controller(
|
|
/*clipped_level_min=*/64,
|
|
/*min_mic_level=*/32,
|
|
/*update_input_volume_wait_frames=*/2, kHighSpeechProbability,
|
|
kSpeechRatioThreshold);
|
|
mono_controller.Initialize();
|
|
|
|
int volume_before = UpdateRecommendedInputVolume(
|
|
mono_controller, kInitialInputVolume, kHighSpeechProbability, -10.0f);
|
|
volume_before = UpdateRecommendedInputVolume(mono_controller, volume_before,
|
|
kHighSpeechProbability, -10.0f);
|
|
|
|
EXPECT_LT(volume_before, kInitialInputVolume);
|
|
|
|
int volume_after = UpdateRecommendedInputVolume(
|
|
mono_controller, volume_before, kHighSpeechProbability, -10.0f);
|
|
volume_after = UpdateRecommendedInputVolume(mono_controller, volume_after,
|
|
kHighSpeechProbability, -10.0f);
|
|
|
|
EXPECT_LT(volume_after, volume_before);
|
|
}
|
|
|
|
TEST(MonoInputVolumeControllerTest,
|
|
CheckProcessPositiveRmsErrorIncreasesInputVolumeRepeatedly) {
|
|
constexpr int kInitialInputVolume = 100;
|
|
MonoInputVolumeController mono_controller(
|
|
/*clipped_level_min=*/64,
|
|
/*min_mic_level=*/32,
|
|
/*update_input_volume_wait_frames=*/2, kHighSpeechProbability,
|
|
kSpeechRatioThreshold);
|
|
mono_controller.Initialize();
|
|
|
|
int volume_before = UpdateRecommendedInputVolume(
|
|
mono_controller, kInitialInputVolume, kHighSpeechProbability, 10.0f);
|
|
volume_before = UpdateRecommendedInputVolume(mono_controller, volume_before,
|
|
kHighSpeechProbability, 10.0f);
|
|
|
|
EXPECT_GT(volume_before, kInitialInputVolume);
|
|
|
|
int volume_after = UpdateRecommendedInputVolume(
|
|
mono_controller, volume_before, kHighSpeechProbability, 10.0f);
|
|
volume_after = UpdateRecommendedInputVolume(mono_controller, volume_after,
|
|
kHighSpeechProbability, 10.0f);
|
|
|
|
EXPECT_GT(volume_after, volume_before);
|
|
}
|
|
|
|
TEST(MonoInputVolumeControllerTest, CheckClippedLevelMinIsEffective) {
|
|
constexpr int kInitialInputVolume = 100;
|
|
constexpr int kClippedLevelMin = 70;
|
|
MonoInputVolumeController mono_controller_1(
|
|
kClippedLevelMin,
|
|
/*min_mic_level=*/84,
|
|
/*update_input_volume_wait_frames=*/2, kHighSpeechProbability,
|
|
kSpeechRatioThreshold);
|
|
MonoInputVolumeController mono_controller_2(
|
|
kClippedLevelMin,
|
|
/*min_mic_level=*/84,
|
|
/*update_input_volume_wait_frames=*/2, kHighSpeechProbability,
|
|
kSpeechRatioThreshold);
|
|
mono_controller_1.Initialize();
|
|
mono_controller_2.Initialize();
|
|
|
|
// Process one frame to reset the state for `HandleClipping()`.
|
|
EXPECT_EQ(UpdateRecommendedInputVolume(mono_controller_1, kInitialInputVolume,
|
|
kLowSpeechProbability, -10.0f),
|
|
kInitialInputVolume);
|
|
EXPECT_EQ(UpdateRecommendedInputVolume(mono_controller_2, kInitialInputVolume,
|
|
kLowSpeechProbability, -10.0f),
|
|
kInitialInputVolume);
|
|
|
|
mono_controller_1.HandleClipping(29);
|
|
mono_controller_2.HandleClipping(31);
|
|
|
|
EXPECT_EQ(mono_controller_2.recommended_analog_level(), kClippedLevelMin);
|
|
EXPECT_LT(mono_controller_2.recommended_analog_level(),
|
|
mono_controller_1.recommended_analog_level());
|
|
}
|
|
|
|
TEST(MonoInputVolumeControllerTest, CheckMinMicLevelIsEffective) {
|
|
constexpr int kInitialInputVolume = 100;
|
|
constexpr int kMinMicLevel = 64;
|
|
MonoInputVolumeController mono_controller_1(
|
|
/*clipped_level_min=*/64, kMinMicLevel,
|
|
/*update_input_volume_wait_frames=*/2, kHighSpeechProbability,
|
|
kSpeechRatioThreshold);
|
|
MonoInputVolumeController mono_controller_2(
|
|
/*clipped_level_min=*/64, kMinMicLevel,
|
|
/*update_input_volume_wait_frames=*/2, kHighSpeechProbability,
|
|
kSpeechRatioThreshold);
|
|
mono_controller_1.Initialize();
|
|
mono_controller_2.Initialize();
|
|
|
|
int volume_1 = UpdateRecommendedInputVolume(
|
|
mono_controller_1, kInitialInputVolume, kHighSpeechProbability, -10.0f);
|
|
int volume_2 = UpdateRecommendedInputVolume(
|
|
mono_controller_2, kInitialInputVolume, kHighSpeechProbability, -10.0f);
|
|
|
|
EXPECT_EQ(volume_1, kInitialInputVolume);
|
|
EXPECT_EQ(volume_2, kInitialInputVolume);
|
|
|
|
volume_1 = UpdateRecommendedInputVolume(mono_controller_1, volume_1,
|
|
kHighSpeechProbability, -10.0f);
|
|
volume_2 = UpdateRecommendedInputVolume(mono_controller_2, volume_2,
|
|
kHighSpeechProbability, -30.0f);
|
|
|
|
EXPECT_LT(volume_1, kInitialInputVolume);
|
|
EXPECT_LT(volume_2, volume_1);
|
|
EXPECT_EQ(volume_2, kMinMicLevel);
|
|
}
|
|
|
|
TEST(MonoInputVolumeControllerTest,
|
|
CheckUpdateInputVolumeWaitFramesIsEffective) {
|
|
constexpr int kInitialInputVolume = 100;
|
|
MonoInputVolumeController mono_controller_1(
|
|
/*clipped_level_min=*/64,
|
|
/*min_mic_level=*/84,
|
|
/*update_input_volume_wait_frames=*/1, kHighSpeechProbability,
|
|
kSpeechRatioThreshold);
|
|
MonoInputVolumeController mono_controller_2(
|
|
/*clipped_level_min=*/64,
|
|
/*min_mic_level=*/84,
|
|
/*update_input_volume_wait_frames=*/3, kHighSpeechProbability,
|
|
kSpeechRatioThreshold);
|
|
mono_controller_1.Initialize();
|
|
mono_controller_2.Initialize();
|
|
|
|
int volume_1 = UpdateRecommendedInputVolume(
|
|
mono_controller_1, kInitialInputVolume, kHighSpeechProbability, -10.0f);
|
|
int volume_2 = UpdateRecommendedInputVolume(
|
|
mono_controller_2, kInitialInputVolume, kHighSpeechProbability, -10.0f);
|
|
|
|
EXPECT_EQ(volume_1, kInitialInputVolume);
|
|
EXPECT_EQ(volume_2, kInitialInputVolume);
|
|
|
|
volume_1 = UpdateRecommendedInputVolume(mono_controller_1, volume_1,
|
|
kHighSpeechProbability, -10.0f);
|
|
volume_2 = UpdateRecommendedInputVolume(mono_controller_2, volume_2,
|
|
kHighSpeechProbability, -10.0f);
|
|
|
|
EXPECT_LT(volume_1, kInitialInputVolume);
|
|
EXPECT_EQ(volume_2, kInitialInputVolume);
|
|
|
|
volume_2 = UpdateRecommendedInputVolume(mono_controller_2, volume_2,
|
|
kHighSpeechProbability, -10.0f);
|
|
|
|
EXPECT_LT(volume_2, kInitialInputVolume);
|
|
}
|
|
|
|
TEST(MonoInputVolumeControllerTest,
|
|
CheckSpeechProbabilityThresholdIsEffective) {
|
|
constexpr int kInitialInputVolume = 100;
|
|
constexpr float kSpeechProbabilityThreshold = 0.8f;
|
|
MonoInputVolumeController mono_controller_1(
|
|
/*clipped_level_min=*/64,
|
|
/*min_mic_level=*/84,
|
|
/*update_input_volume_wait_frames=*/2, kSpeechProbabilityThreshold,
|
|
kSpeechRatioThreshold);
|
|
MonoInputVolumeController mono_controller_2(
|
|
/*clipped_level_min=*/64,
|
|
/*min_mic_level=*/84,
|
|
/*update_input_volume_wait_frames=*/2, kSpeechProbabilityThreshold,
|
|
kSpeechRatioThreshold);
|
|
mono_controller_1.Initialize();
|
|
mono_controller_2.Initialize();
|
|
|
|
int volume_1 =
|
|
UpdateRecommendedInputVolume(mono_controller_1, kInitialInputVolume,
|
|
kSpeechProbabilityThreshold, -10.0f);
|
|
int volume_2 =
|
|
UpdateRecommendedInputVolume(mono_controller_2, kInitialInputVolume,
|
|
kSpeechProbabilityThreshold, -10.0f);
|
|
|
|
EXPECT_EQ(volume_1, kInitialInputVolume);
|
|
EXPECT_EQ(volume_2, kInitialInputVolume);
|
|
|
|
volume_1 = UpdateRecommendedInputVolume(
|
|
mono_controller_1, volume_1, kSpeechProbabilityThreshold - 0.1f, -10.0f);
|
|
volume_2 = UpdateRecommendedInputVolume(mono_controller_2, volume_2,
|
|
kSpeechProbabilityThreshold, -10.0f);
|
|
|
|
EXPECT_EQ(volume_1, kInitialInputVolume);
|
|
EXPECT_LT(volume_2, volume_1);
|
|
}
|
|
|
|
TEST(MonoInputVolumeControllerTest, CheckSpeechRatioThresholdIsEffective) {
|
|
constexpr int kInitialInputVolume = 100;
|
|
MonoInputVolumeController mono_controller_1(
|
|
/*clipped_level_min=*/64,
|
|
/*min_mic_level=*/84,
|
|
/*update_input_volume_wait_frames=*/4, kHighSpeechProbability,
|
|
/*speech_ratio_threshold=*/0.75f);
|
|
MonoInputVolumeController mono_controller_2(
|
|
/*clipped_level_min=*/64,
|
|
/*min_mic_level=*/84,
|
|
/*update_input_volume_wait_frames=*/4, kHighSpeechProbability,
|
|
/*speech_ratio_threshold=*/0.75f);
|
|
mono_controller_1.Initialize();
|
|
mono_controller_2.Initialize();
|
|
|
|
int volume_1 = UpdateRecommendedInputVolume(
|
|
mono_controller_1, kInitialInputVolume, kHighSpeechProbability, -10.0f);
|
|
int volume_2 = UpdateRecommendedInputVolume(
|
|
mono_controller_2, kInitialInputVolume, kHighSpeechProbability, -10.0f);
|
|
|
|
volume_1 = UpdateRecommendedInputVolume(mono_controller_1, volume_1,
|
|
kHighSpeechProbability, -10.0f);
|
|
volume_2 = UpdateRecommendedInputVolume(mono_controller_2, volume_2,
|
|
kHighSpeechProbability, -10.0f);
|
|
|
|
volume_1 = UpdateRecommendedInputVolume(mono_controller_1, volume_1,
|
|
kLowSpeechProbability, -10.0f);
|
|
volume_2 = UpdateRecommendedInputVolume(mono_controller_2, volume_2,
|
|
kLowSpeechProbability, -10.0f);
|
|
|
|
EXPECT_EQ(volume_1, kInitialInputVolume);
|
|
EXPECT_EQ(volume_2, kInitialInputVolume);
|
|
|
|
volume_1 = UpdateRecommendedInputVolume(mono_controller_1, volume_1,
|
|
kLowSpeechProbability, -10.0f);
|
|
volume_2 = UpdateRecommendedInputVolume(mono_controller_2, volume_2,
|
|
kHighSpeechProbability, -10.0f);
|
|
|
|
EXPECT_EQ(volume_1, kInitialInputVolume);
|
|
EXPECT_LT(volume_2, volume_1);
|
|
}
|
|
|
|
TEST(MonoInputVolumeControllerTest,
|
|
CheckProcessEmptyRmsErrorDoesNotLowerVolume) {
|
|
constexpr int kInitialInputVolume = 100;
|
|
MonoInputVolumeController mono_controller_1(
|
|
/*clipped_level_min=*/64,
|
|
/*min_mic_level=*/84,
|
|
/*update_input_volume_wait_frames=*/2, kHighSpeechProbability,
|
|
kSpeechRatioThreshold);
|
|
MonoInputVolumeController mono_controller_2(
|
|
/*clipped_level_min=*/64,
|
|
/*min_mic_level=*/84,
|
|
/*update_input_volume_wait_frames=*/2, kHighSpeechProbability,
|
|
kSpeechRatioThreshold);
|
|
mono_controller_1.Initialize();
|
|
mono_controller_2.Initialize();
|
|
|
|
int volume_1 = UpdateRecommendedInputVolume(
|
|
mono_controller_1, kInitialInputVolume, kHighSpeechProbability, -10.0f);
|
|
int volume_2 = UpdateRecommendedInputVolume(
|
|
mono_controller_2, kInitialInputVolume, kHighSpeechProbability, -10.0f);
|
|
|
|
EXPECT_EQ(volume_1, kInitialInputVolume);
|
|
EXPECT_EQ(volume_2, kInitialInputVolume);
|
|
|
|
volume_1 = UpdateRecommendedInputVolume(
|
|
mono_controller_1, volume_1, kHighSpeechProbability, absl::nullopt);
|
|
volume_2 = UpdateRecommendedInputVolume(mono_controller_2, volume_2,
|
|
kHighSpeechProbability, -10.0f);
|
|
|
|
EXPECT_EQ(volume_1, kInitialInputVolume);
|
|
EXPECT_LT(volume_2, volume_1);
|
|
}
|
|
|
|
} // namespace webrtc
|