mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-12 21:30:45 +01:00

Bug: webrtc:15622 Change-Id: I2d4c20c726af1a052e161b7689a73d1e5e3eb191 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/325526 Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org> Commit-Queue: Harald Alvestrand <hta@webrtc.org> Cr-Commit-Position: refs/heads/main@{#41067}
1563 lines
64 KiB
C++
1563 lines
64 KiB
C++
/*
|
|
* Copyright (c) 2014 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/audio_processing_impl.h"
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <memory>
|
|
#include <tuple>
|
|
|
|
#include "absl/types/optional.h"
|
|
#include "api/make_ref_counted.h"
|
|
#include "api/scoped_refptr.h"
|
|
#include "modules/audio_processing/include/audio_processing.h"
|
|
#include "modules/audio_processing/optionally_built_submodule_creators.h"
|
|
#include "modules/audio_processing/test/audio_processing_builder_for_testing.h"
|
|
#include "modules/audio_processing/test/echo_canceller_test_tools.h"
|
|
#include "modules/audio_processing/test/echo_control_mock.h"
|
|
#include "modules/audio_processing/test/test_utils.h"
|
|
#include "rtc_base/checks.h"
|
|
#include "rtc_base/random.h"
|
|
#include "rtc_base/strings/string_builder.h"
|
|
#include "test/field_trial.h"
|
|
#include "test/gmock.h"
|
|
#include "test/gtest.h"
|
|
|
|
namespace webrtc {
|
|
namespace {
|
|
|
|
using ::testing::Invoke;
|
|
using ::testing::NotNull;
|
|
|
|
class MockInitialize : public AudioProcessingImpl {
|
|
public:
|
|
MockInitialize() : AudioProcessingImpl() {}
|
|
|
|
MOCK_METHOD(void, InitializeLocked, (), (override));
|
|
void RealInitializeLocked() {
|
|
AssertLockedForTest();
|
|
AudioProcessingImpl::InitializeLocked();
|
|
}
|
|
|
|
MOCK_METHOD(void, AddRef, (), (const, override));
|
|
MOCK_METHOD(RefCountReleaseStatus, Release, (), (const, override));
|
|
};
|
|
|
|
// Creates MockEchoControl instances and provides a raw pointer access to
|
|
// the next created one. The raw pointer is meant to be used with gmock.
|
|
// Returning a pointer of the next created MockEchoControl instance is necessary
|
|
// for the following reasons: (i) gmock expectations must be set before any call
|
|
// occurs, (ii) APM is initialized the first time that
|
|
// AudioProcessingImpl::ProcessStream() is called and the initialization leads
|
|
// to the creation of a new EchoControl object.
|
|
class MockEchoControlFactory : public EchoControlFactory {
|
|
public:
|
|
MockEchoControlFactory() : next_mock_(std::make_unique<MockEchoControl>()) {}
|
|
// Returns a pointer to the next MockEchoControl that this factory creates.
|
|
MockEchoControl* GetNext() const { return next_mock_.get(); }
|
|
std::unique_ptr<EchoControl> Create(int sample_rate_hz,
|
|
int num_render_channels,
|
|
int num_capture_channels) override {
|
|
std::unique_ptr<EchoControl> mock = std::move(next_mock_);
|
|
next_mock_ = std::make_unique<MockEchoControl>();
|
|
return mock;
|
|
}
|
|
|
|
private:
|
|
std::unique_ptr<MockEchoControl> next_mock_;
|
|
};
|
|
|
|
// Mocks EchoDetector and records the first samples of the last analyzed render
|
|
// stream frame. Used to check what data is read by an EchoDetector
|
|
// implementation injected into an APM.
|
|
class TestEchoDetector : public EchoDetector {
|
|
public:
|
|
TestEchoDetector()
|
|
: analyze_render_audio_called_(false),
|
|
last_render_audio_first_sample_(0.f) {}
|
|
~TestEchoDetector() override = default;
|
|
void AnalyzeRenderAudio(rtc::ArrayView<const float> render_audio) override {
|
|
last_render_audio_first_sample_ = render_audio[0];
|
|
analyze_render_audio_called_ = true;
|
|
}
|
|
void AnalyzeCaptureAudio(rtc::ArrayView<const float> capture_audio) override {
|
|
}
|
|
void Initialize(int capture_sample_rate_hz,
|
|
int num_capture_channels,
|
|
int render_sample_rate_hz,
|
|
int num_render_channels) override {}
|
|
EchoDetector::Metrics GetMetrics() const override { return {}; }
|
|
// Returns true if AnalyzeRenderAudio() has been called at least once.
|
|
bool analyze_render_audio_called() const {
|
|
return analyze_render_audio_called_;
|
|
}
|
|
// Returns the first sample of the last analyzed render frame.
|
|
float last_render_audio_first_sample() const {
|
|
return last_render_audio_first_sample_;
|
|
}
|
|
|
|
private:
|
|
bool analyze_render_audio_called_;
|
|
float last_render_audio_first_sample_;
|
|
};
|
|
|
|
// Mocks CustomProcessing and applies ProcessSample() to all the samples.
|
|
// Meant to be injected into an APM to modify samples in a known and detectable
|
|
// way.
|
|
class TestRenderPreProcessor : public CustomProcessing {
|
|
public:
|
|
TestRenderPreProcessor() = default;
|
|
~TestRenderPreProcessor() = default;
|
|
void Initialize(int sample_rate_hz, int num_channels) override {}
|
|
void Process(AudioBuffer* audio) override {
|
|
for (size_t k = 0; k < audio->num_channels(); ++k) {
|
|
rtc::ArrayView<float> channel_view(audio->channels()[k],
|
|
audio->num_frames());
|
|
std::transform(channel_view.begin(), channel_view.end(),
|
|
channel_view.begin(), ProcessSample);
|
|
}
|
|
}
|
|
std::string ToString() const override { return "TestRenderPreProcessor"; }
|
|
void SetRuntimeSetting(AudioProcessing::RuntimeSetting setting) override {}
|
|
// Modifies a sample. This member is used in Process() to modify a frame and
|
|
// it is publicly visible to enable tests.
|
|
static constexpr float ProcessSample(float x) { return 2.f * x; }
|
|
};
|
|
|
|
// Runs `apm` input processing for volume adjustments for `num_frames` random
|
|
// frames starting from the volume `initial_volume`. This includes three steps:
|
|
// 1) Set the input volume 2) Process the stream 3) Set the new recommended
|
|
// input volume. Returns the new recommended input volume.
|
|
int ProcessInputVolume(AudioProcessing& apm,
|
|
int num_frames,
|
|
int initial_volume) {
|
|
constexpr int kSampleRateHz = 48000;
|
|
constexpr int kNumChannels = 1;
|
|
std::array<float, kSampleRateHz / 100> buffer;
|
|
float* channel_pointers[] = {buffer.data()};
|
|
StreamConfig stream_config(/*sample_rate_hz=*/kSampleRateHz,
|
|
/*num_channels=*/kNumChannels);
|
|
int recommended_input_volume = initial_volume;
|
|
for (int i = 0; i < num_frames; ++i) {
|
|
Random random_generator(2341U);
|
|
RandomizeSampleVector(&random_generator, buffer);
|
|
|
|
apm.set_stream_analog_level(recommended_input_volume);
|
|
apm.ProcessStream(channel_pointers, stream_config, stream_config,
|
|
channel_pointers);
|
|
recommended_input_volume = apm.recommended_stream_analog_level();
|
|
}
|
|
return recommended_input_volume;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TEST(AudioProcessingImplTest, AudioParameterChangeTriggersInit) {
|
|
MockInitialize mock;
|
|
ON_CALL(mock, InitializeLocked)
|
|
.WillByDefault(Invoke(&mock, &MockInitialize::RealInitializeLocked));
|
|
|
|
EXPECT_CALL(mock, InitializeLocked).Times(1);
|
|
mock.Initialize();
|
|
|
|
constexpr size_t kMaxSampleRateHz = 32000;
|
|
constexpr size_t kMaxNumChannels = 2;
|
|
std::array<int16_t, kMaxNumChannels * kMaxSampleRateHz / 100> frame;
|
|
frame.fill(0);
|
|
StreamConfig config(16000, 1);
|
|
// Call with the default parameters; there should be an init.
|
|
EXPECT_CALL(mock, InitializeLocked).Times(0);
|
|
EXPECT_NOERR(mock.ProcessStream(frame.data(), config, config, frame.data()));
|
|
EXPECT_NOERR(
|
|
mock.ProcessReverseStream(frame.data(), config, config, frame.data()));
|
|
|
|
// New sample rate. (Only impacts ProcessStream).
|
|
config = StreamConfig(32000, 1);
|
|
EXPECT_CALL(mock, InitializeLocked).Times(1);
|
|
EXPECT_NOERR(mock.ProcessStream(frame.data(), config, config, frame.data()));
|
|
|
|
// New number of channels.
|
|
config = StreamConfig(32000, 2);
|
|
EXPECT_CALL(mock, InitializeLocked).Times(2);
|
|
EXPECT_NOERR(mock.ProcessStream(frame.data(), config, config, frame.data()));
|
|
EXPECT_NOERR(
|
|
mock.ProcessReverseStream(frame.data(), config, config, frame.data()));
|
|
|
|
// A new sample rate passed to ProcessReverseStream should cause an init.
|
|
config = StreamConfig(16000, 2);
|
|
EXPECT_CALL(mock, InitializeLocked).Times(1);
|
|
EXPECT_NOERR(
|
|
mock.ProcessReverseStream(frame.data(), config, config, frame.data()));
|
|
}
|
|
|
|
TEST(AudioProcessingImplTest, UpdateCapturePreGainRuntimeSetting) {
|
|
rtc::scoped_refptr<AudioProcessing> apm =
|
|
AudioProcessingBuilderForTesting().Create();
|
|
webrtc::AudioProcessing::Config apm_config;
|
|
apm_config.pre_amplifier.enabled = true;
|
|
apm_config.pre_amplifier.fixed_gain_factor = 1.f;
|
|
apm->ApplyConfig(apm_config);
|
|
|
|
constexpr int kSampleRateHz = 48000;
|
|
constexpr int16_t kAudioLevel = 10000;
|
|
constexpr size_t kNumChannels = 2;
|
|
|
|
std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
|
|
StreamConfig config(kSampleRateHz, kNumChannels);
|
|
frame.fill(kAudioLevel);
|
|
apm->ProcessStream(frame.data(), config, config, frame.data());
|
|
EXPECT_EQ(frame[100], kAudioLevel)
|
|
<< "With factor 1, frame shouldn't be modified.";
|
|
|
|
constexpr float kGainFactor = 2.f;
|
|
apm->SetRuntimeSetting(
|
|
AudioProcessing::RuntimeSetting::CreateCapturePreGain(kGainFactor));
|
|
|
|
// Process for two frames to have time to ramp up gain.
|
|
for (int i = 0; i < 2; ++i) {
|
|
frame.fill(kAudioLevel);
|
|
apm->ProcessStream(frame.data(), config, config, frame.data());
|
|
}
|
|
EXPECT_EQ(frame[100], kGainFactor * kAudioLevel)
|
|
<< "Frame should be amplified.";
|
|
}
|
|
|
|
TEST(AudioProcessingImplTest,
|
|
LevelAdjustmentUpdateCapturePreGainRuntimeSetting) {
|
|
rtc::scoped_refptr<AudioProcessing> apm =
|
|
AudioProcessingBuilderForTesting().Create();
|
|
webrtc::AudioProcessing::Config apm_config;
|
|
apm_config.capture_level_adjustment.enabled = true;
|
|
apm_config.capture_level_adjustment.pre_gain_factor = 1.f;
|
|
apm->ApplyConfig(apm_config);
|
|
|
|
constexpr int kSampleRateHz = 48000;
|
|
constexpr int16_t kAudioLevel = 10000;
|
|
constexpr size_t kNumChannels = 2;
|
|
|
|
std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
|
|
StreamConfig config(kSampleRateHz, kNumChannels);
|
|
frame.fill(kAudioLevel);
|
|
apm->ProcessStream(frame.data(), config, config, frame.data());
|
|
EXPECT_EQ(frame[100], kAudioLevel)
|
|
<< "With factor 1, frame shouldn't be modified.";
|
|
|
|
constexpr float kGainFactor = 2.f;
|
|
apm->SetRuntimeSetting(
|
|
AudioProcessing::RuntimeSetting::CreateCapturePreGain(kGainFactor));
|
|
|
|
// Process for two frames to have time to ramp up gain.
|
|
for (int i = 0; i < 2; ++i) {
|
|
frame.fill(kAudioLevel);
|
|
apm->ProcessStream(frame.data(), config, config, frame.data());
|
|
}
|
|
EXPECT_EQ(frame[100], kGainFactor * kAudioLevel)
|
|
<< "Frame should be amplified.";
|
|
}
|
|
|
|
TEST(AudioProcessingImplTest,
|
|
LevelAdjustmentUpdateCapturePostGainRuntimeSetting) {
|
|
rtc::scoped_refptr<AudioProcessing> apm =
|
|
AudioProcessingBuilderForTesting().Create();
|
|
webrtc::AudioProcessing::Config apm_config;
|
|
apm_config.capture_level_adjustment.enabled = true;
|
|
apm_config.capture_level_adjustment.post_gain_factor = 1.f;
|
|
apm->ApplyConfig(apm_config);
|
|
|
|
constexpr int kSampleRateHz = 48000;
|
|
constexpr int16_t kAudioLevel = 10000;
|
|
constexpr size_t kNumChannels = 2;
|
|
|
|
std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
|
|
StreamConfig config(kSampleRateHz, kNumChannels);
|
|
frame.fill(kAudioLevel);
|
|
apm->ProcessStream(frame.data(), config, config, frame.data());
|
|
EXPECT_EQ(frame[100], kAudioLevel)
|
|
<< "With factor 1, frame shouldn't be modified.";
|
|
|
|
constexpr float kGainFactor = 2.f;
|
|
apm->SetRuntimeSetting(
|
|
AudioProcessing::RuntimeSetting::CreateCapturePostGain(kGainFactor));
|
|
|
|
// Process for two frames to have time to ramp up gain.
|
|
for (int i = 0; i < 2; ++i) {
|
|
frame.fill(kAudioLevel);
|
|
apm->ProcessStream(frame.data(), config, config, frame.data());
|
|
}
|
|
EXPECT_EQ(frame[100], kGainFactor * kAudioLevel)
|
|
<< "Frame should be amplified.";
|
|
}
|
|
|
|
TEST(AudioProcessingImplTest, EchoControllerObservesSetCaptureUsageChange) {
|
|
// Tests that the echo controller observes that the capture usage has been
|
|
// updated.
|
|
auto echo_control_factory = std::make_unique<MockEchoControlFactory>();
|
|
const MockEchoControlFactory* echo_control_factory_ptr =
|
|
echo_control_factory.get();
|
|
|
|
rtc::scoped_refptr<AudioProcessing> apm =
|
|
AudioProcessingBuilderForTesting()
|
|
.SetEchoControlFactory(std::move(echo_control_factory))
|
|
.Create();
|
|
|
|
constexpr int16_t kAudioLevel = 10000;
|
|
constexpr int kSampleRateHz = 48000;
|
|
constexpr int kNumChannels = 2;
|
|
std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
|
|
StreamConfig config(kSampleRateHz, kNumChannels);
|
|
frame.fill(kAudioLevel);
|
|
|
|
MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext();
|
|
|
|
// Ensure that SetCaptureOutputUsage is not called when no runtime settings
|
|
// are passed.
|
|
EXPECT_CALL(*echo_control_mock, SetCaptureOutputUsage(testing::_)).Times(0);
|
|
apm->ProcessStream(frame.data(), config, config, frame.data());
|
|
|
|
// Ensure that SetCaptureOutputUsage is called with the right information when
|
|
// a runtime setting is passed.
|
|
EXPECT_CALL(*echo_control_mock,
|
|
SetCaptureOutputUsage(/*capture_output_used=*/false))
|
|
.Times(1);
|
|
EXPECT_TRUE(apm->PostRuntimeSetting(
|
|
AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting(
|
|
/*capture_output_used=*/false)));
|
|
apm->ProcessStream(frame.data(), config, config, frame.data());
|
|
|
|
EXPECT_CALL(*echo_control_mock,
|
|
SetCaptureOutputUsage(/*capture_output_used=*/true))
|
|
.Times(1);
|
|
EXPECT_TRUE(apm->PostRuntimeSetting(
|
|
AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting(
|
|
/*capture_output_used=*/true)));
|
|
apm->ProcessStream(frame.data(), config, config, frame.data());
|
|
|
|
// The number of positions to place items in the queue is equal to the queue
|
|
// size minus 1.
|
|
constexpr int kNumSlotsInQueue = RuntimeSettingQueueSize();
|
|
|
|
// Ensure that SetCaptureOutputUsage is called with the right information when
|
|
// many runtime settings are passed.
|
|
for (int k = 0; k < kNumSlotsInQueue - 1; ++k) {
|
|
EXPECT_TRUE(apm->PostRuntimeSetting(
|
|
AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting(
|
|
/*capture_output_used=*/false)));
|
|
}
|
|
EXPECT_CALL(*echo_control_mock,
|
|
SetCaptureOutputUsage(/*capture_output_used=*/false))
|
|
.Times(kNumSlotsInQueue - 1);
|
|
apm->ProcessStream(frame.data(), config, config, frame.data());
|
|
|
|
// Ensure that SetCaptureOutputUsage is properly called with the fallback
|
|
// value when the runtime settings queue becomes full.
|
|
for (int k = 0; k < kNumSlotsInQueue; ++k) {
|
|
EXPECT_TRUE(apm->PostRuntimeSetting(
|
|
AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting(
|
|
/*capture_output_used=*/false)));
|
|
}
|
|
EXPECT_FALSE(apm->PostRuntimeSetting(
|
|
AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting(
|
|
/*capture_output_used=*/false)));
|
|
EXPECT_FALSE(apm->PostRuntimeSetting(
|
|
AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting(
|
|
/*capture_output_used=*/false)));
|
|
EXPECT_CALL(*echo_control_mock,
|
|
SetCaptureOutputUsage(/*capture_output_used=*/false))
|
|
.Times(kNumSlotsInQueue);
|
|
EXPECT_CALL(*echo_control_mock,
|
|
SetCaptureOutputUsage(/*capture_output_used=*/true))
|
|
.Times(1);
|
|
apm->ProcessStream(frame.data(), config, config, frame.data());
|
|
}
|
|
|
|
TEST(AudioProcessingImplTest,
|
|
EchoControllerObservesPreAmplifierEchoPathGainChange) {
|
|
// Tests that the echo controller observes an echo path gain change when the
|
|
// pre-amplifier submodule changes the gain.
|
|
auto echo_control_factory = std::make_unique<MockEchoControlFactory>();
|
|
const auto* echo_control_factory_ptr = echo_control_factory.get();
|
|
|
|
rtc::scoped_refptr<AudioProcessing> apm =
|
|
AudioProcessingBuilderForTesting()
|
|
.SetEchoControlFactory(std::move(echo_control_factory))
|
|
.Create();
|
|
// Disable AGC.
|
|
webrtc::AudioProcessing::Config apm_config;
|
|
apm_config.gain_controller1.enabled = false;
|
|
apm_config.gain_controller2.enabled = false;
|
|
apm_config.pre_amplifier.enabled = true;
|
|
apm_config.pre_amplifier.fixed_gain_factor = 1.f;
|
|
apm->ApplyConfig(apm_config);
|
|
|
|
constexpr int16_t kAudioLevel = 10000;
|
|
constexpr size_t kSampleRateHz = 48000;
|
|
constexpr size_t kNumChannels = 2;
|
|
std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
|
|
StreamConfig config(kSampleRateHz, kNumChannels);
|
|
frame.fill(kAudioLevel);
|
|
|
|
MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext();
|
|
|
|
EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
|
|
EXPECT_CALL(*echo_control_mock,
|
|
ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/false))
|
|
.Times(1);
|
|
apm->ProcessStream(frame.data(), config, config, frame.data());
|
|
|
|
EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
|
|
EXPECT_CALL(*echo_control_mock,
|
|
ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/true))
|
|
.Times(1);
|
|
apm->SetRuntimeSetting(
|
|
AudioProcessing::RuntimeSetting::CreateCapturePreGain(2.f));
|
|
apm->ProcessStream(frame.data(), config, config, frame.data());
|
|
}
|
|
|
|
TEST(AudioProcessingImplTest,
|
|
EchoControllerObservesLevelAdjustmentPreGainEchoPathGainChange) {
|
|
// Tests that the echo controller observes an echo path gain change when the
|
|
// pre-amplifier submodule changes the gain.
|
|
auto echo_control_factory = std::make_unique<MockEchoControlFactory>();
|
|
const auto* echo_control_factory_ptr = echo_control_factory.get();
|
|
|
|
rtc::scoped_refptr<AudioProcessing> apm =
|
|
AudioProcessingBuilderForTesting()
|
|
.SetEchoControlFactory(std::move(echo_control_factory))
|
|
.Create();
|
|
// Disable AGC.
|
|
webrtc::AudioProcessing::Config apm_config;
|
|
apm_config.gain_controller1.enabled = false;
|
|
apm_config.gain_controller2.enabled = false;
|
|
apm_config.capture_level_adjustment.enabled = true;
|
|
apm_config.capture_level_adjustment.pre_gain_factor = 1.f;
|
|
apm->ApplyConfig(apm_config);
|
|
|
|
constexpr int16_t kAudioLevel = 10000;
|
|
constexpr size_t kSampleRateHz = 48000;
|
|
constexpr size_t kNumChannels = 2;
|
|
std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
|
|
StreamConfig config(kSampleRateHz, kNumChannels);
|
|
frame.fill(kAudioLevel);
|
|
|
|
MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext();
|
|
|
|
EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
|
|
EXPECT_CALL(*echo_control_mock,
|
|
ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/false))
|
|
.Times(1);
|
|
apm->ProcessStream(frame.data(), config, config, frame.data());
|
|
|
|
EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
|
|
EXPECT_CALL(*echo_control_mock,
|
|
ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/true))
|
|
.Times(1);
|
|
apm->SetRuntimeSetting(
|
|
AudioProcessing::RuntimeSetting::CreateCapturePreGain(2.f));
|
|
apm->ProcessStream(frame.data(), config, config, frame.data());
|
|
}
|
|
|
|
TEST(AudioProcessingImplTest,
|
|
EchoControllerObservesAnalogAgc1EchoPathGainChange) {
|
|
// Tests that the echo controller observes an echo path gain change when the
|
|
// AGC1 analog adaptive submodule changes the analog gain.
|
|
auto echo_control_factory = std::make_unique<MockEchoControlFactory>();
|
|
const auto* echo_control_factory_ptr = echo_control_factory.get();
|
|
|
|
rtc::scoped_refptr<AudioProcessing> apm =
|
|
AudioProcessingBuilderForTesting()
|
|
.SetEchoControlFactory(std::move(echo_control_factory))
|
|
.Create();
|
|
webrtc::AudioProcessing::Config apm_config;
|
|
// Enable AGC1.
|
|
apm_config.gain_controller1.enabled = true;
|
|
apm_config.gain_controller1.analog_gain_controller.enabled = true;
|
|
apm_config.gain_controller2.enabled = false;
|
|
apm_config.pre_amplifier.enabled = false;
|
|
apm->ApplyConfig(apm_config);
|
|
|
|
constexpr int16_t kAudioLevel = 1000;
|
|
constexpr size_t kSampleRateHz = 48000;
|
|
constexpr size_t kNumChannels = 2;
|
|
std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
|
|
StreamConfig stream_config(kSampleRateHz, kNumChannels);
|
|
frame.fill(kAudioLevel);
|
|
|
|
MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext();
|
|
|
|
constexpr int kInitialStreamAnalogLevel = 123;
|
|
apm->set_stream_analog_level(kInitialStreamAnalogLevel);
|
|
|
|
// When the first fame is processed, no echo path gain change must be
|
|
// detected.
|
|
EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
|
|
EXPECT_CALL(*echo_control_mock,
|
|
ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/false))
|
|
.Times(1);
|
|
apm->ProcessStream(frame.data(), stream_config, stream_config, frame.data());
|
|
|
|
// Simulate the application of the recommended analog level.
|
|
int recommended_analog_level = apm->recommended_stream_analog_level();
|
|
if (recommended_analog_level == kInitialStreamAnalogLevel) {
|
|
// Force an analog gain change if it did not happen.
|
|
recommended_analog_level++;
|
|
}
|
|
apm->set_stream_analog_level(recommended_analog_level);
|
|
|
|
// After the first fame and with a stream analog level change, the echo path
|
|
// gain change must be detected.
|
|
EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
|
|
EXPECT_CALL(*echo_control_mock,
|
|
ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/true))
|
|
.Times(1);
|
|
apm->ProcessStream(frame.data(), stream_config, stream_config, frame.data());
|
|
}
|
|
|
|
TEST(AudioProcessingImplTest,
|
|
ProcessWithAgc2AndTransientSuppressorVadModeDefault) {
|
|
webrtc::test::ScopedFieldTrials field_trials(
|
|
"WebRTC-Audio-GainController2/Disabled/");
|
|
auto apm = AudioProcessingBuilder()
|
|
.SetConfig({.gain_controller1{.enabled = false}})
|
|
.Create();
|
|
ASSERT_EQ(apm->Initialize(), AudioProcessing::kNoError);
|
|
webrtc::AudioProcessing::Config apm_config;
|
|
apm_config.gain_controller1.enabled = false;
|
|
apm_config.gain_controller2.enabled = true;
|
|
apm_config.gain_controller2.adaptive_digital.enabled = true;
|
|
apm_config.transient_suppression.enabled = true;
|
|
apm->ApplyConfig(apm_config);
|
|
constexpr int kSampleRateHz = 48000;
|
|
constexpr int kNumChannels = 1;
|
|
std::array<float, kSampleRateHz / 100> buffer;
|
|
float* channel_pointers[] = {buffer.data()};
|
|
StreamConfig stream_config(/*sample_rate_hz=*/kSampleRateHz,
|
|
/*num_channels=*/kNumChannels);
|
|
Random random_generator(2341U);
|
|
constexpr int kFramesToProcess = 10;
|
|
for (int i = 0; i < kFramesToProcess; ++i) {
|
|
RandomizeSampleVector(&random_generator, buffer);
|
|
ASSERT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config,
|
|
channel_pointers),
|
|
kNoErr);
|
|
}
|
|
}
|
|
|
|
TEST(AudioProcessingImplTest,
|
|
ProcessWithAgc2AndTransientSuppressorVadModeRnnVad) {
|
|
webrtc::test::ScopedFieldTrials field_trials(
|
|
"WebRTC-Audio-GainController2/Enabled,switch_to_agc2:true/");
|
|
rtc::scoped_refptr<AudioProcessing> apm = AudioProcessingBuilder().Create();
|
|
ASSERT_EQ(apm->Initialize(), AudioProcessing::kNoError);
|
|
webrtc::AudioProcessing::Config apm_config;
|
|
apm_config.gain_controller1.enabled = false;
|
|
apm_config.gain_controller2.enabled = true;
|
|
apm_config.gain_controller2.adaptive_digital.enabled = true;
|
|
apm_config.transient_suppression.enabled = true;
|
|
apm->ApplyConfig(apm_config);
|
|
constexpr int kSampleRateHz = 48000;
|
|
constexpr int kNumChannels = 1;
|
|
std::array<float, kSampleRateHz / 100> buffer;
|
|
float* channel_pointers[] = {buffer.data()};
|
|
StreamConfig stream_config(/*sample_rate_hz=*/kSampleRateHz,
|
|
/*num_channels=*/kNumChannels);
|
|
Random random_generator(2341U);
|
|
constexpr int kFramesToProcess = 10;
|
|
for (int i = 0; i < kFramesToProcess; ++i) {
|
|
RandomizeSampleVector(&random_generator, buffer);
|
|
ASSERT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config,
|
|
channel_pointers),
|
|
kNoErr);
|
|
}
|
|
}
|
|
|
|
TEST(AudioProcessingImplTest, EchoControllerObservesPlayoutVolumeChange) {
|
|
// Tests that the echo controller observes an echo path gain change when a
|
|
// playout volume change is reported.
|
|
auto echo_control_factory = std::make_unique<MockEchoControlFactory>();
|
|
const auto* echo_control_factory_ptr = echo_control_factory.get();
|
|
|
|
rtc::scoped_refptr<AudioProcessing> apm =
|
|
AudioProcessingBuilderForTesting()
|
|
.SetEchoControlFactory(std::move(echo_control_factory))
|
|
.Create();
|
|
// Disable AGC.
|
|
webrtc::AudioProcessing::Config apm_config;
|
|
apm_config.gain_controller1.enabled = false;
|
|
apm_config.gain_controller2.enabled = false;
|
|
apm->ApplyConfig(apm_config);
|
|
|
|
constexpr int16_t kAudioLevel = 10000;
|
|
constexpr size_t kSampleRateHz = 48000;
|
|
constexpr size_t kNumChannels = 2;
|
|
std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
|
|
StreamConfig stream_config(kSampleRateHz, kNumChannels);
|
|
frame.fill(kAudioLevel);
|
|
|
|
MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext();
|
|
|
|
EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
|
|
EXPECT_CALL(*echo_control_mock,
|
|
ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/false))
|
|
.Times(1);
|
|
apm->ProcessStream(frame.data(), stream_config, stream_config, frame.data());
|
|
|
|
EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
|
|
EXPECT_CALL(*echo_control_mock,
|
|
ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/false))
|
|
.Times(1);
|
|
apm->SetRuntimeSetting(
|
|
AudioProcessing::RuntimeSetting::CreatePlayoutVolumeChange(50));
|
|
apm->ProcessStream(frame.data(), stream_config, stream_config, frame.data());
|
|
|
|
EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
|
|
EXPECT_CALL(*echo_control_mock,
|
|
ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/false))
|
|
.Times(1);
|
|
apm->SetRuntimeSetting(
|
|
AudioProcessing::RuntimeSetting::CreatePlayoutVolumeChange(50));
|
|
apm->ProcessStream(frame.data(), stream_config, stream_config, frame.data());
|
|
|
|
EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
|
|
EXPECT_CALL(*echo_control_mock,
|
|
ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/true))
|
|
.Times(1);
|
|
apm->SetRuntimeSetting(
|
|
AudioProcessing::RuntimeSetting::CreatePlayoutVolumeChange(100));
|
|
apm->ProcessStream(frame.data(), stream_config, stream_config, frame.data());
|
|
}
|
|
|
|
TEST(AudioProcessingImplTest, RenderPreProcessorBeforeEchoDetector) {
|
|
// Make sure that signal changes caused by a render pre-processing sub-module
|
|
// take place before any echo detector analysis.
|
|
auto test_echo_detector = rtc::make_ref_counted<TestEchoDetector>();
|
|
std::unique_ptr<CustomProcessing> test_render_pre_processor(
|
|
new TestRenderPreProcessor());
|
|
// Create APM injecting the test echo detector and render pre-processor.
|
|
rtc::scoped_refptr<AudioProcessing> apm =
|
|
AudioProcessingBuilderForTesting()
|
|
.SetEchoDetector(test_echo_detector)
|
|
.SetRenderPreProcessing(std::move(test_render_pre_processor))
|
|
.Create();
|
|
webrtc::AudioProcessing::Config apm_config;
|
|
apm_config.pre_amplifier.enabled = true;
|
|
apm->ApplyConfig(apm_config);
|
|
|
|
constexpr int16_t kAudioLevel = 1000;
|
|
constexpr int kSampleRateHz = 16000;
|
|
constexpr size_t kNumChannels = 1;
|
|
// Explicitly initialize APM to ensure no render frames are discarded.
|
|
const ProcessingConfig processing_config = {{
|
|
{kSampleRateHz, kNumChannels},
|
|
{kSampleRateHz, kNumChannels},
|
|
{kSampleRateHz, kNumChannels},
|
|
{kSampleRateHz, kNumChannels},
|
|
}};
|
|
apm->Initialize(processing_config);
|
|
|
|
std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
|
|
StreamConfig stream_config(kSampleRateHz, kNumChannels);
|
|
|
|
constexpr float kAudioLevelFloat = static_cast<float>(kAudioLevel);
|
|
constexpr float kExpectedPreprocessedAudioLevel =
|
|
TestRenderPreProcessor::ProcessSample(kAudioLevelFloat);
|
|
ASSERT_NE(kAudioLevelFloat, kExpectedPreprocessedAudioLevel);
|
|
|
|
// Analyze a render stream frame.
|
|
frame.fill(kAudioLevel);
|
|
ASSERT_EQ(AudioProcessing::Error::kNoError,
|
|
apm->ProcessReverseStream(frame.data(), stream_config,
|
|
stream_config, frame.data()));
|
|
// Trigger a call to in EchoDetector::AnalyzeRenderAudio() via
|
|
// ProcessStream().
|
|
frame.fill(kAudioLevel);
|
|
ASSERT_EQ(AudioProcessing::Error::kNoError,
|
|
apm->ProcessStream(frame.data(), stream_config, stream_config,
|
|
frame.data()));
|
|
// Regardless of how the call to in EchoDetector::AnalyzeRenderAudio() is
|
|
// triggered, the line below checks that the call has occurred. If not, the
|
|
// APM implementation may have changed and this test might need to be adapted.
|
|
ASSERT_TRUE(test_echo_detector->analyze_render_audio_called());
|
|
// Check that the data read in EchoDetector::AnalyzeRenderAudio() is that
|
|
// produced by the render pre-processor.
|
|
EXPECT_EQ(kExpectedPreprocessedAudioLevel,
|
|
test_echo_detector->last_render_audio_first_sample());
|
|
}
|
|
|
|
// Disabling build-optional submodules and trying to enable them via the APM
|
|
// config should be bit-exact with running APM with said submodules disabled.
|
|
// This mainly tests that SetCreateOptionalSubmodulesForTesting has an effect.
|
|
TEST(ApmWithSubmodulesExcludedTest, BitexactWithDisabledModules) {
|
|
auto apm = rtc::make_ref_counted<AudioProcessingImpl>();
|
|
ASSERT_EQ(apm->Initialize(), AudioProcessing::kNoError);
|
|
|
|
ApmSubmoduleCreationOverrides overrides;
|
|
overrides.transient_suppression = true;
|
|
apm->OverrideSubmoduleCreationForTesting(overrides);
|
|
|
|
AudioProcessing::Config apm_config = apm->GetConfig();
|
|
apm_config.transient_suppression.enabled = true;
|
|
apm->ApplyConfig(apm_config);
|
|
|
|
rtc::scoped_refptr<AudioProcessing> apm_reference =
|
|
AudioProcessingBuilder().Create();
|
|
apm_config = apm_reference->GetConfig();
|
|
apm_config.transient_suppression.enabled = false;
|
|
apm_reference->ApplyConfig(apm_config);
|
|
|
|
constexpr int kSampleRateHz = 16000;
|
|
constexpr int kNumChannels = 1;
|
|
std::array<float, kSampleRateHz / 100> buffer;
|
|
std::array<float, kSampleRateHz / 100> buffer_reference;
|
|
float* channel_pointers[] = {buffer.data()};
|
|
float* channel_pointers_reference[] = {buffer_reference.data()};
|
|
StreamConfig stream_config(/*sample_rate_hz=*/kSampleRateHz,
|
|
/*num_channels=*/kNumChannels);
|
|
Random random_generator(2341U);
|
|
constexpr int kFramesToProcessPerConfiguration = 10;
|
|
|
|
for (int i = 0; i < kFramesToProcessPerConfiguration; ++i) {
|
|
RandomizeSampleVector(&random_generator, buffer);
|
|
std::copy(buffer.begin(), buffer.end(), buffer_reference.begin());
|
|
ASSERT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config,
|
|
channel_pointers),
|
|
kNoErr);
|
|
ASSERT_EQ(
|
|
apm_reference->ProcessStream(channel_pointers_reference, stream_config,
|
|
stream_config, channel_pointers_reference),
|
|
kNoErr);
|
|
for (int j = 0; j < kSampleRateHz / 100; ++j) {
|
|
EXPECT_EQ(buffer[j], buffer_reference[j]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Disable transient suppressor creation and run APM in ways that should trigger
|
|
// calls to the transient suppressor API.
|
|
TEST(ApmWithSubmodulesExcludedTest, ReinitializeTransientSuppressor) {
|
|
auto apm = rtc::make_ref_counted<AudioProcessingImpl>();
|
|
ASSERT_EQ(apm->Initialize(), kNoErr);
|
|
|
|
ApmSubmoduleCreationOverrides overrides;
|
|
overrides.transient_suppression = true;
|
|
apm->OverrideSubmoduleCreationForTesting(overrides);
|
|
|
|
AudioProcessing::Config config = apm->GetConfig();
|
|
config.transient_suppression.enabled = true;
|
|
apm->ApplyConfig(config);
|
|
// 960 samples per frame: 10 ms of <= 48 kHz audio with <= 2 channels.
|
|
float buffer[960];
|
|
float* channel_pointers[] = {&buffer[0], &buffer[480]};
|
|
Random random_generator(2341U);
|
|
constexpr int kFramesToProcessPerConfiguration = 3;
|
|
|
|
StreamConfig initial_stream_config(/*sample_rate_hz=*/16000,
|
|
/*num_channels=*/1);
|
|
for (int i = 0; i < kFramesToProcessPerConfiguration; ++i) {
|
|
RandomizeSampleVector(&random_generator, buffer);
|
|
EXPECT_EQ(apm->ProcessStream(channel_pointers, initial_stream_config,
|
|
initial_stream_config, channel_pointers),
|
|
kNoErr);
|
|
}
|
|
|
|
StreamConfig stereo_stream_config(/*sample_rate_hz=*/16000,
|
|
/*num_channels=*/2);
|
|
for (int i = 0; i < kFramesToProcessPerConfiguration; ++i) {
|
|
RandomizeSampleVector(&random_generator, buffer);
|
|
EXPECT_EQ(apm->ProcessStream(channel_pointers, stereo_stream_config,
|
|
stereo_stream_config, channel_pointers),
|
|
kNoErr);
|
|
}
|
|
|
|
StreamConfig high_sample_rate_stream_config(/*sample_rate_hz=*/48000,
|
|
/*num_channels=*/2);
|
|
for (int i = 0; i < kFramesToProcessPerConfiguration; ++i) {
|
|
RandomizeSampleVector(&random_generator, buffer);
|
|
EXPECT_EQ(
|
|
apm->ProcessStream(channel_pointers, high_sample_rate_stream_config,
|
|
high_sample_rate_stream_config, channel_pointers),
|
|
kNoErr);
|
|
}
|
|
}
|
|
|
|
// Disable transient suppressor creation and run APM in ways that should trigger
|
|
// calls to the transient suppressor API.
|
|
TEST(ApmWithSubmodulesExcludedTest, ToggleTransientSuppressor) {
|
|
auto apm = rtc::make_ref_counted<AudioProcessingImpl>();
|
|
ASSERT_EQ(apm->Initialize(), AudioProcessing::kNoError);
|
|
|
|
ApmSubmoduleCreationOverrides overrides;
|
|
overrides.transient_suppression = true;
|
|
apm->OverrideSubmoduleCreationForTesting(overrides);
|
|
|
|
// 960 samples per frame: 10 ms of <= 48 kHz audio with <= 2 channels.
|
|
float buffer[960];
|
|
float* channel_pointers[] = {&buffer[0], &buffer[480]};
|
|
Random random_generator(2341U);
|
|
constexpr int kFramesToProcessPerConfiguration = 3;
|
|
StreamConfig stream_config(/*sample_rate_hz=*/16000,
|
|
/*num_channels=*/1);
|
|
|
|
AudioProcessing::Config config = apm->GetConfig();
|
|
config.transient_suppression.enabled = true;
|
|
apm->ApplyConfig(config);
|
|
for (int i = 0; i < kFramesToProcessPerConfiguration; ++i) {
|
|
RandomizeSampleVector(&random_generator, buffer);
|
|
EXPECT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config,
|
|
channel_pointers),
|
|
kNoErr);
|
|
}
|
|
|
|
config = apm->GetConfig();
|
|
config.transient_suppression.enabled = false;
|
|
apm->ApplyConfig(config);
|
|
for (int i = 0; i < kFramesToProcessPerConfiguration; ++i) {
|
|
RandomizeSampleVector(&random_generator, buffer);
|
|
EXPECT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config,
|
|
channel_pointers),
|
|
kNoErr);
|
|
}
|
|
|
|
config = apm->GetConfig();
|
|
config.transient_suppression.enabled = true;
|
|
apm->ApplyConfig(config);
|
|
for (int i = 0; i < kFramesToProcessPerConfiguration; ++i) {
|
|
RandomizeSampleVector(&random_generator, buffer);
|
|
EXPECT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config,
|
|
channel_pointers),
|
|
kNoErr);
|
|
}
|
|
}
|
|
|
|
class StartupInputVolumeParameterizedTest
|
|
: public ::testing::TestWithParam<int> {};
|
|
|
|
// Tests that, when no input volume controller is used, the startup input volume
|
|
// is never modified.
|
|
TEST_P(StartupInputVolumeParameterizedTest,
|
|
WithNoInputVolumeControllerStartupVolumeNotModified) {
|
|
webrtc::AudioProcessing::Config config;
|
|
config.gain_controller1.enabled = false;
|
|
config.gain_controller2.enabled = false;
|
|
auto apm = AudioProcessingBuilder().SetConfig(config).Create();
|
|
|
|
int startup_volume = GetParam();
|
|
int recommended_volume = ProcessInputVolume(
|
|
*apm, /*num_frames=*/1, /*initial_volume=*/startup_volume);
|
|
EXPECT_EQ(recommended_volume, startup_volume);
|
|
}
|
|
|
|
INSTANTIATE_TEST_SUITE_P(AudioProcessingImplTest,
|
|
StartupInputVolumeParameterizedTest,
|
|
::testing::Values(0, 5, 15, 50, 100));
|
|
|
|
// Tests that, when no input volume controller is used, the recommended input
|
|
// volume always matches the applied one.
|
|
TEST(AudioProcessingImplTest,
|
|
WithNoInputVolumeControllerAppliedAndRecommendedVolumesMatch) {
|
|
webrtc::AudioProcessing::Config config;
|
|
config.gain_controller1.enabled = false;
|
|
config.gain_controller2.enabled = false;
|
|
auto apm = AudioProcessingBuilder().SetConfig(config).Create();
|
|
|
|
Random rand_gen(42);
|
|
for (int i = 0; i < 32; ++i) {
|
|
SCOPED_TRACE(i);
|
|
int32_t applied_volume = rand_gen.Rand(/*low=*/0, /*high=*/255);
|
|
int recommended_volume =
|
|
ProcessInputVolume(*apm, /*num_frames=*/1, applied_volume);
|
|
EXPECT_EQ(recommended_volume, applied_volume);
|
|
}
|
|
}
|
|
|
|
class ApmInputVolumeControllerParametrizedTest
|
|
: public ::testing::TestWithParam<
|
|
std::tuple<int, int, AudioProcessing::Config>> {
|
|
protected:
|
|
ApmInputVolumeControllerParametrizedTest()
|
|
: sample_rate_hz_(std::get<0>(GetParam())),
|
|
num_channels_(std::get<1>(GetParam())),
|
|
channels_(num_channels_),
|
|
channel_pointers_(num_channels_) {
|
|
const int frame_size = sample_rate_hz_ / 100;
|
|
for (int c = 0; c < num_channels_; ++c) {
|
|
channels_[c].resize(frame_size);
|
|
channel_pointers_[c] = channels_[c].data();
|
|
std::fill(channels_[c].begin(), channels_[c].end(), 0.0f);
|
|
}
|
|
}
|
|
|
|
int sample_rate_hz() const { return sample_rate_hz_; }
|
|
int num_channels() const { return num_channels_; }
|
|
AudioProcessing::Config GetConfig() const { return std::get<2>(GetParam()); }
|
|
|
|
float* const* channel_pointers() { return channel_pointers_.data(); }
|
|
|
|
private:
|
|
const int sample_rate_hz_;
|
|
const int num_channels_;
|
|
std::vector<std::vector<float>> channels_;
|
|
std::vector<float*> channel_pointers_;
|
|
};
|
|
|
|
TEST_P(ApmInputVolumeControllerParametrizedTest,
|
|
EnforceMinInputVolumeAtStartupWithZeroVolume) {
|
|
const StreamConfig stream_config(sample_rate_hz(), num_channels());
|
|
auto apm = AudioProcessingBuilder().SetConfig(GetConfig()).Create();
|
|
|
|
apm->set_stream_analog_level(0);
|
|
apm->ProcessStream(channel_pointers(), stream_config, stream_config,
|
|
channel_pointers());
|
|
EXPECT_GT(apm->recommended_stream_analog_level(), 0);
|
|
}
|
|
|
|
TEST_P(ApmInputVolumeControllerParametrizedTest,
|
|
EnforceMinInputVolumeAtStartupWithNonZeroVolume) {
|
|
const StreamConfig stream_config(sample_rate_hz(), num_channels());
|
|
auto apm = AudioProcessingBuilder().SetConfig(GetConfig()).Create();
|
|
|
|
constexpr int kStartupVolume = 3;
|
|
apm->set_stream_analog_level(kStartupVolume);
|
|
apm->ProcessStream(channel_pointers(), stream_config, stream_config,
|
|
channel_pointers());
|
|
EXPECT_GT(apm->recommended_stream_analog_level(), kStartupVolume);
|
|
}
|
|
|
|
TEST_P(ApmInputVolumeControllerParametrizedTest,
|
|
EnforceMinInputVolumeAfterManualVolumeAdjustment) {
|
|
const auto config = GetConfig();
|
|
if (config.gain_controller1.enabled) {
|
|
// After a downward manual adjustment, AGC1 slowly converges to the minimum
|
|
// input volume.
|
|
GTEST_SKIP() << "Does not apply to AGC1";
|
|
}
|
|
const StreamConfig stream_config(sample_rate_hz(), num_channels());
|
|
auto apm = AudioProcessingBuilder().SetConfig(GetConfig()).Create();
|
|
|
|
apm->set_stream_analog_level(20);
|
|
apm->ProcessStream(channel_pointers(), stream_config, stream_config,
|
|
channel_pointers());
|
|
constexpr int kManuallyAdjustedVolume = 3;
|
|
apm->set_stream_analog_level(kManuallyAdjustedVolume);
|
|
apm->ProcessStream(channel_pointers(), stream_config, stream_config,
|
|
channel_pointers());
|
|
EXPECT_GT(apm->recommended_stream_analog_level(), kManuallyAdjustedVolume);
|
|
}
|
|
|
|
TEST_P(ApmInputVolumeControllerParametrizedTest,
|
|
DoNotEnforceMinInputVolumeAtStartupWithHighVolume) {
|
|
const StreamConfig stream_config(sample_rate_hz(), num_channels());
|
|
auto apm = AudioProcessingBuilder().SetConfig(GetConfig()).Create();
|
|
|
|
constexpr int kStartupVolume = 200;
|
|
apm->set_stream_analog_level(kStartupVolume);
|
|
apm->ProcessStream(channel_pointers(), stream_config, stream_config,
|
|
channel_pointers());
|
|
EXPECT_EQ(apm->recommended_stream_analog_level(), kStartupVolume);
|
|
}
|
|
|
|
TEST_P(ApmInputVolumeControllerParametrizedTest,
|
|
DoNotEnforceMinInputVolumeAfterManualVolumeAdjustmentToZero) {
|
|
const StreamConfig stream_config(sample_rate_hz(), num_channels());
|
|
auto apm = AudioProcessingBuilder().SetConfig(GetConfig()).Create();
|
|
|
|
apm->set_stream_analog_level(100);
|
|
apm->ProcessStream(channel_pointers(), stream_config, stream_config,
|
|
channel_pointers());
|
|
apm->set_stream_analog_level(0);
|
|
apm->ProcessStream(channel_pointers(), stream_config, stream_config,
|
|
channel_pointers());
|
|
EXPECT_EQ(apm->recommended_stream_analog_level(), 0);
|
|
}
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
AudioProcessingImplTest,
|
|
ApmInputVolumeControllerParametrizedTest,
|
|
::testing::Combine(
|
|
::testing::Values(8000, 16000, 32000, 48000), // Sample rates.
|
|
::testing::Values(1, 2), // Number of channels.
|
|
::testing::Values(
|
|
// Full AGC1.
|
|
AudioProcessing::Config{
|
|
.gain_controller1 = {.enabled = true,
|
|
.analog_gain_controller =
|
|
{.enabled = true,
|
|
.enable_digital_adaptive = true}},
|
|
.gain_controller2 = {.enabled = false}},
|
|
// Hybrid AGC.
|
|
AudioProcessing::Config{
|
|
.gain_controller1 = {.enabled = true,
|
|
.analog_gain_controller =
|
|
{.enabled = true,
|
|
.enable_digital_adaptive = false}},
|
|
.gain_controller2 = {.enabled = true,
|
|
.adaptive_digital = {.enabled = true}}})));
|
|
|
|
// When the input volume is not emulated and no input volume controller is
|
|
// active, the recommended volume must always be the applied volume.
|
|
TEST(AudioProcessingImplTest,
|
|
RecommendAppliedInputVolumeWithNoAgcWithNoEmulation) {
|
|
auto apm = AudioProcessingBuilder()
|
|
.SetConfig({.capture_level_adjustment = {.enabled = false},
|
|
.gain_controller1 = {.enabled = false}})
|
|
.Create();
|
|
|
|
constexpr int kOneFrame = 1;
|
|
EXPECT_EQ(ProcessInputVolume(*apm, kOneFrame, /*initial_volume=*/123), 123);
|
|
EXPECT_EQ(ProcessInputVolume(*apm, kOneFrame, /*initial_volume=*/59), 59);
|
|
EXPECT_EQ(ProcessInputVolume(*apm, kOneFrame, /*initial_volume=*/135), 135);
|
|
}
|
|
|
|
// When the input volume is emulated, the recommended volume must always be the
|
|
// applied volume and at any time it must not be that set in the input volume
|
|
// emulator.
|
|
// TODO(bugs.webrtc.org/14581): Enable when APM fixed to let this test pass.
|
|
TEST(AudioProcessingImplTest,
|
|
DISABLED_RecommendAppliedInputVolumeWithNoAgcWithEmulation) {
|
|
auto apm =
|
|
AudioProcessingBuilder()
|
|
.SetConfig({.capture_level_adjustment = {.enabled = true,
|
|
.analog_mic_gain_emulation{
|
|
.enabled = true,
|
|
.initial_level = 255}},
|
|
.gain_controller1 = {.enabled = false}})
|
|
.Create();
|
|
|
|
constexpr int kOneFrame = 1;
|
|
EXPECT_EQ(ProcessInputVolume(*apm, kOneFrame, /*initial_volume=*/123), 123);
|
|
EXPECT_EQ(ProcessInputVolume(*apm, kOneFrame, /*initial_volume=*/59), 59);
|
|
EXPECT_EQ(ProcessInputVolume(*apm, kOneFrame, /*initial_volume=*/135), 135);
|
|
}
|
|
|
|
// Even if there is an enabled input volume controller, when the input volume is
|
|
// emulated, the recommended volume is always the applied volume because the
|
|
// active controller must only adjust the internally emulated volume and leave
|
|
// the externally applied volume unchanged.
|
|
// TODO(bugs.webrtc.org/14581): Enable when APM fixed to let this test pass.
|
|
TEST(AudioProcessingImplTest,
|
|
DISABLED_RecommendAppliedInputVolumeWithAgcWithEmulation) {
|
|
auto apm =
|
|
AudioProcessingBuilder()
|
|
.SetConfig({.capture_level_adjustment = {.enabled = true,
|
|
.analog_mic_gain_emulation{
|
|
.enabled = true}},
|
|
.gain_controller1 = {.enabled = true,
|
|
.analog_gain_controller{
|
|
.enabled = true,
|
|
}}})
|
|
.Create();
|
|
|
|
constexpr int kOneFrame = 1;
|
|
EXPECT_EQ(ProcessInputVolume(*apm, kOneFrame, /*initial_volume=*/123), 123);
|
|
EXPECT_EQ(ProcessInputVolume(*apm, kOneFrame, /*initial_volume=*/59), 59);
|
|
EXPECT_EQ(ProcessInputVolume(*apm, kOneFrame, /*initial_volume=*/135), 135);
|
|
}
|
|
|
|
TEST(AudioProcessingImplTest,
|
|
Agc2FieldTrialDoNotSwitchToFullAgc2WhenNoAgcIsActive) {
|
|
constexpr AudioProcessing::Config kOriginal{
|
|
.gain_controller1{.enabled = false},
|
|
.gain_controller2{.enabled = false},
|
|
};
|
|
webrtc::test::ScopedFieldTrials field_trials(
|
|
"WebRTC-Audio-GainController2/Enabled,switch_to_agc2:true/");
|
|
|
|
// Test config application via `AudioProcessing` ctor.
|
|
auto adjusted =
|
|
AudioProcessingBuilder().SetConfig(kOriginal).Create()->GetConfig();
|
|
EXPECT_EQ(adjusted.gain_controller1, kOriginal.gain_controller1);
|
|
EXPECT_EQ(adjusted.gain_controller2, kOriginal.gain_controller2);
|
|
|
|
// Test config application via `AudioProcessing::ApplyConfig()`.
|
|
auto apm = AudioProcessingBuilder().Create();
|
|
apm->ApplyConfig(kOriginal);
|
|
adjusted = apm->GetConfig();
|
|
EXPECT_EQ(adjusted.gain_controller1, kOriginal.gain_controller1);
|
|
EXPECT_EQ(adjusted.gain_controller2, kOriginal.gain_controller2);
|
|
}
|
|
|
|
TEST(AudioProcessingImplTest,
|
|
Agc2FieldTrialDoNotSwitchToFullAgc2WithAgc1Agc2InputVolumeControllers) {
|
|
constexpr AudioProcessing::Config kOriginal{
|
|
.gain_controller1{.enabled = true,
|
|
.analog_gain_controller{.enabled = true}},
|
|
.gain_controller2{.enabled = true,
|
|
.input_volume_controller{.enabled = true}},
|
|
};
|
|
webrtc::test::ScopedFieldTrials field_trials(
|
|
"WebRTC-Audio-GainController2/Enabled,switch_to_agc2:true/");
|
|
|
|
// Test config application via `AudioProcessing` ctor.
|
|
auto adjusted =
|
|
AudioProcessingBuilder().SetConfig(kOriginal).Create()->GetConfig();
|
|
EXPECT_EQ(adjusted.gain_controller1, kOriginal.gain_controller1);
|
|
EXPECT_EQ(adjusted.gain_controller2, kOriginal.gain_controller2);
|
|
|
|
// Test config application via `AudioProcessing::ApplyConfig()`.
|
|
auto apm = AudioProcessingBuilder().Create();
|
|
apm->ApplyConfig(kOriginal);
|
|
adjusted = apm->GetConfig();
|
|
EXPECT_EQ(adjusted.gain_controller1, kOriginal.gain_controller1);
|
|
EXPECT_EQ(adjusted.gain_controller2, kOriginal.gain_controller2);
|
|
}
|
|
|
|
class Agc2FieldTrialParametrizedTest
|
|
: public ::testing::TestWithParam<AudioProcessing::Config> {};
|
|
|
|
TEST_P(Agc2FieldTrialParametrizedTest, DoNotChangeConfigIfDisabled) {
|
|
const AudioProcessing::Config original = GetParam();
|
|
webrtc::test::ScopedFieldTrials field_trials(
|
|
"WebRTC-Audio-GainController2/Disabled/");
|
|
|
|
// Test config application via `AudioProcessing` ctor.
|
|
auto adjusted =
|
|
AudioProcessingBuilder().SetConfig(original).Create()->GetConfig();
|
|
EXPECT_EQ(adjusted.gain_controller1, original.gain_controller1);
|
|
EXPECT_EQ(adjusted.gain_controller2, original.gain_controller2);
|
|
|
|
// Test config application via `AudioProcessing::ApplyConfig()`.
|
|
auto apm = AudioProcessingBuilder().Create();
|
|
apm->ApplyConfig(original);
|
|
adjusted = apm->GetConfig();
|
|
EXPECT_EQ(adjusted.gain_controller1, original.gain_controller1);
|
|
EXPECT_EQ(adjusted.gain_controller2, original.gain_controller2);
|
|
}
|
|
|
|
TEST_P(Agc2FieldTrialParametrizedTest, DoNotChangeConfigIfNoOverride) {
|
|
const AudioProcessing::Config original = GetParam();
|
|
webrtc::test::ScopedFieldTrials field_trials(
|
|
"WebRTC-Audio-GainController2/Enabled,"
|
|
"switch_to_agc2:false,"
|
|
"disallow_transient_suppressor_usage:false/");
|
|
|
|
// Test config application via `AudioProcessing` ctor.
|
|
auto adjusted =
|
|
AudioProcessingBuilder().SetConfig(original).Create()->GetConfig();
|
|
EXPECT_EQ(adjusted.gain_controller1, original.gain_controller1);
|
|
EXPECT_EQ(adjusted.gain_controller2, original.gain_controller2);
|
|
|
|
// Test config application via `AudioProcessing::ApplyConfig()`.
|
|
auto apm = AudioProcessingBuilder().Create();
|
|
apm->ApplyConfig(original);
|
|
adjusted = apm->GetConfig();
|
|
EXPECT_EQ(adjusted.gain_controller1, original.gain_controller1);
|
|
EXPECT_EQ(adjusted.gain_controller2, original.gain_controller2);
|
|
}
|
|
|
|
TEST_P(Agc2FieldTrialParametrizedTest, DoNotSwitchToFullAgc2) {
|
|
const AudioProcessing::Config original = GetParam();
|
|
webrtc::test::ScopedFieldTrials field_trials(
|
|
"WebRTC-Audio-GainController2/Enabled,switch_to_agc2:false/");
|
|
|
|
// Test config application via `AudioProcessing` ctor.
|
|
auto adjusted =
|
|
AudioProcessingBuilder().SetConfig(original).Create()->GetConfig();
|
|
EXPECT_EQ(adjusted.gain_controller1, original.gain_controller1);
|
|
EXPECT_EQ(adjusted.gain_controller2, original.gain_controller2);
|
|
|
|
// Test config application via `AudioProcessing::ApplyConfig()`.
|
|
auto apm = AudioProcessingBuilder().Create();
|
|
apm->ApplyConfig(original);
|
|
adjusted = apm->GetConfig();
|
|
EXPECT_EQ(adjusted.gain_controller1, original.gain_controller1);
|
|
EXPECT_EQ(adjusted.gain_controller2, original.gain_controller2);
|
|
}
|
|
|
|
TEST_P(Agc2FieldTrialParametrizedTest, SwitchToFullAgc2) {
|
|
const AudioProcessing::Config original = GetParam();
|
|
webrtc::test::ScopedFieldTrials field_trials(
|
|
"WebRTC-Audio-GainController2/Enabled,switch_to_agc2:true/");
|
|
|
|
// Test config application via `AudioProcessing` ctor.
|
|
auto adjusted =
|
|
AudioProcessingBuilder().SetConfig(original).Create()->GetConfig();
|
|
EXPECT_FALSE(adjusted.gain_controller1.enabled);
|
|
EXPECT_TRUE(adjusted.gain_controller2.enabled);
|
|
EXPECT_TRUE(adjusted.gain_controller2.input_volume_controller.enabled);
|
|
EXPECT_TRUE(adjusted.gain_controller2.adaptive_digital.enabled);
|
|
|
|
// Test config application via `AudioProcessing::ApplyConfig()`.
|
|
auto apm = AudioProcessingBuilder().Create();
|
|
apm->ApplyConfig(original);
|
|
adjusted = apm->GetConfig();
|
|
EXPECT_FALSE(adjusted.gain_controller1.enabled);
|
|
EXPECT_TRUE(adjusted.gain_controller2.enabled);
|
|
EXPECT_TRUE(adjusted.gain_controller2.input_volume_controller.enabled);
|
|
EXPECT_TRUE(adjusted.gain_controller2.adaptive_digital.enabled);
|
|
}
|
|
|
|
TEST_P(Agc2FieldTrialParametrizedTest,
|
|
SwitchToFullAgc2AndOverrideInputVolumeControllerParameters) {
|
|
const AudioProcessing::Config original = GetParam();
|
|
webrtc::test::ScopedFieldTrials field_trials(
|
|
"WebRTC-Audio-GainController2/Enabled,switch_to_agc2:true,"
|
|
"min_input_volume:123,"
|
|
"clipped_level_min:20,"
|
|
"clipped_level_step:30,"
|
|
"clipped_ratio_threshold:0.4,"
|
|
"clipped_wait_frames:50,"
|
|
"enable_clipping_predictor:true,"
|
|
"target_range_max_dbfs:-6,"
|
|
"target_range_min_dbfs:-70,"
|
|
"update_input_volume_wait_frames:80,"
|
|
"speech_probability_threshold:0.9,"
|
|
"speech_ratio_threshold:1.0/");
|
|
|
|
// Test config application via `AudioProcessing` ctor.
|
|
auto adjusted =
|
|
AudioProcessingBuilder().SetConfig(original).Create()->GetConfig();
|
|
EXPECT_FALSE(adjusted.gain_controller1.enabled);
|
|
EXPECT_TRUE(adjusted.gain_controller2.enabled);
|
|
EXPECT_TRUE(adjusted.gain_controller2.input_volume_controller.enabled);
|
|
EXPECT_TRUE(adjusted.gain_controller2.adaptive_digital.enabled);
|
|
|
|
// Test config application via `AudioProcessing::ApplyConfig()`.
|
|
auto apm = AudioProcessingBuilder().Create();
|
|
apm->ApplyConfig(original);
|
|
adjusted = apm->GetConfig();
|
|
EXPECT_FALSE(adjusted.gain_controller1.enabled);
|
|
EXPECT_TRUE(adjusted.gain_controller2.enabled);
|
|
EXPECT_TRUE(adjusted.gain_controller2.input_volume_controller.enabled);
|
|
EXPECT_TRUE(adjusted.gain_controller2.adaptive_digital.enabled);
|
|
}
|
|
|
|
TEST_P(Agc2FieldTrialParametrizedTest,
|
|
SwitchToFullAgc2AndOverrideAdaptiveDigitalControllerParameters) {
|
|
const AudioProcessing::Config original = GetParam();
|
|
webrtc::test::ScopedFieldTrials field_trials(
|
|
"WebRTC-Audio-GainController2/Enabled,switch_to_agc2:true,"
|
|
"headroom_db:10,"
|
|
"max_gain_db:20,"
|
|
"initial_gain_db:7,"
|
|
"max_gain_change_db_per_second:5,"
|
|
"max_output_noise_level_dbfs:-40/");
|
|
|
|
// Test config application via `AudioProcessing` ctor.
|
|
auto adjusted =
|
|
AudioProcessingBuilder().SetConfig(original).Create()->GetConfig();
|
|
EXPECT_FALSE(adjusted.gain_controller1.enabled);
|
|
EXPECT_TRUE(adjusted.gain_controller2.enabled);
|
|
EXPECT_TRUE(adjusted.gain_controller2.input_volume_controller.enabled);
|
|
EXPECT_TRUE(adjusted.gain_controller2.adaptive_digital.enabled);
|
|
ASSERT_NE(adjusted.gain_controller2.adaptive_digital,
|
|
original.gain_controller2.adaptive_digital);
|
|
EXPECT_EQ(adjusted.gain_controller2.adaptive_digital.headroom_db, 10);
|
|
EXPECT_EQ(adjusted.gain_controller2.adaptive_digital.max_gain_db, 20);
|
|
EXPECT_EQ(adjusted.gain_controller2.adaptive_digital.initial_gain_db, 7);
|
|
EXPECT_EQ(
|
|
adjusted.gain_controller2.adaptive_digital.max_gain_change_db_per_second,
|
|
5);
|
|
EXPECT_EQ(
|
|
adjusted.gain_controller2.adaptive_digital.max_output_noise_level_dbfs,
|
|
-40);
|
|
|
|
// Test config application via `AudioProcessing::ApplyConfig()`.
|
|
auto apm = AudioProcessingBuilder().Create();
|
|
apm->ApplyConfig(original);
|
|
adjusted = apm->GetConfig();
|
|
EXPECT_FALSE(adjusted.gain_controller1.enabled);
|
|
EXPECT_TRUE(adjusted.gain_controller2.enabled);
|
|
EXPECT_TRUE(adjusted.gain_controller2.input_volume_controller.enabled);
|
|
EXPECT_TRUE(adjusted.gain_controller2.adaptive_digital.enabled);
|
|
ASSERT_NE(adjusted.gain_controller2.adaptive_digital,
|
|
original.gain_controller2.adaptive_digital);
|
|
EXPECT_EQ(adjusted.gain_controller2.adaptive_digital.headroom_db, 10);
|
|
EXPECT_EQ(adjusted.gain_controller2.adaptive_digital.max_gain_db, 20);
|
|
EXPECT_EQ(adjusted.gain_controller2.adaptive_digital.initial_gain_db, 7);
|
|
EXPECT_EQ(
|
|
adjusted.gain_controller2.adaptive_digital.max_gain_change_db_per_second,
|
|
5);
|
|
EXPECT_EQ(
|
|
adjusted.gain_controller2.adaptive_digital.max_output_noise_level_dbfs,
|
|
-40);
|
|
}
|
|
|
|
TEST_P(Agc2FieldTrialParametrizedTest, ProcessSucceedsWithTs) {
|
|
AudioProcessing::Config config = GetParam();
|
|
if (!config.transient_suppression.enabled) {
|
|
GTEST_SKIP() << "TS is disabled, skip.";
|
|
}
|
|
|
|
webrtc::test::ScopedFieldTrials field_trials(
|
|
"WebRTC-Audio-GainController2/Disabled/");
|
|
auto apm = AudioProcessingBuilder().SetConfig(config).Create();
|
|
|
|
constexpr int kSampleRateHz = 48000;
|
|
constexpr int kNumChannels = 1;
|
|
std::array<float, kSampleRateHz / 100> buffer;
|
|
float* channel_pointers[] = {buffer.data()};
|
|
StreamConfig stream_config(kSampleRateHz, kNumChannels);
|
|
Random random_generator(2341U);
|
|
constexpr int kFramesToProcess = 10;
|
|
int volume = 100;
|
|
for (int i = 0; i < kFramesToProcess; ++i) {
|
|
SCOPED_TRACE(i);
|
|
RandomizeSampleVector(&random_generator, buffer);
|
|
apm->set_stream_analog_level(volume);
|
|
ASSERT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config,
|
|
channel_pointers),
|
|
kNoErr);
|
|
volume = apm->recommended_stream_analog_level();
|
|
}
|
|
}
|
|
|
|
TEST_P(Agc2FieldTrialParametrizedTest, ProcessSucceedsWithoutTs) {
|
|
webrtc::test::ScopedFieldTrials field_trials(
|
|
"WebRTC-Audio-GainController2/Enabled,"
|
|
"switch_to_agc2:false,"
|
|
"disallow_transient_suppressor_usage:true/");
|
|
auto apm = AudioProcessingBuilder().SetConfig(GetParam()).Create();
|
|
|
|
constexpr int kSampleRateHz = 48000;
|
|
constexpr int kNumChannels = 1;
|
|
std::array<float, kSampleRateHz / 100> buffer;
|
|
float* channel_pointers[] = {buffer.data()};
|
|
StreamConfig stream_config(kSampleRateHz, kNumChannels);
|
|
Random random_generator(2341U);
|
|
constexpr int kFramesToProcess = 10;
|
|
int volume = 100;
|
|
for (int i = 0; i < kFramesToProcess; ++i) {
|
|
SCOPED_TRACE(i);
|
|
RandomizeSampleVector(&random_generator, buffer);
|
|
apm->set_stream_analog_level(volume);
|
|
ASSERT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config,
|
|
channel_pointers),
|
|
kNoErr);
|
|
volume = apm->recommended_stream_analog_level();
|
|
}
|
|
}
|
|
|
|
TEST_P(Agc2FieldTrialParametrizedTest,
|
|
ProcessSucceedsWhenSwitchToFullAgc2WithTs) {
|
|
AudioProcessing::Config config = GetParam();
|
|
if (!config.transient_suppression.enabled) {
|
|
GTEST_SKIP() << "TS is disabled, skip.";
|
|
}
|
|
|
|
webrtc::test::ScopedFieldTrials field_trials(
|
|
"WebRTC-Audio-GainController2/Enabled,"
|
|
"switch_to_agc2:true,"
|
|
"disallow_transient_suppressor_usage:false/");
|
|
auto apm = AudioProcessingBuilder().SetConfig(config).Create();
|
|
|
|
constexpr int kSampleRateHz = 48000;
|
|
constexpr int kNumChannels = 1;
|
|
std::array<float, kSampleRateHz / 100> buffer;
|
|
float* channel_pointers[] = {buffer.data()};
|
|
StreamConfig stream_config(kSampleRateHz, kNumChannels);
|
|
Random random_generator(2341U);
|
|
constexpr int kFramesToProcess = 10;
|
|
int volume = 100;
|
|
for (int i = 0; i < kFramesToProcess; ++i) {
|
|
SCOPED_TRACE(i);
|
|
RandomizeSampleVector(&random_generator, buffer);
|
|
apm->set_stream_analog_level(volume);
|
|
ASSERT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config,
|
|
channel_pointers),
|
|
kNoErr);
|
|
volume = apm->recommended_stream_analog_level();
|
|
}
|
|
}
|
|
|
|
TEST_P(Agc2FieldTrialParametrizedTest,
|
|
ProcessSucceedsWhenSwitchToFullAgc2WithoutTs) {
|
|
webrtc::test::ScopedFieldTrials field_trials(
|
|
"WebRTC-Audio-GainController2/Enabled,"
|
|
"switch_to_agc2:true,"
|
|
"disallow_transient_suppressor_usage:true/");
|
|
auto apm = AudioProcessingBuilder().SetConfig(GetParam()).Create();
|
|
|
|
constexpr int kSampleRateHz = 48000;
|
|
constexpr int kNumChannels = 1;
|
|
std::array<float, kSampleRateHz / 100> buffer;
|
|
float* channel_pointers[] = {buffer.data()};
|
|
StreamConfig stream_config(kSampleRateHz, kNumChannels);
|
|
Random random_generator(2341U);
|
|
constexpr int kFramesToProcess = 10;
|
|
int volume = 100;
|
|
for (int i = 0; i < kFramesToProcess; ++i) {
|
|
SCOPED_TRACE(i);
|
|
RandomizeSampleVector(&random_generator, buffer);
|
|
apm->set_stream_analog_level(volume);
|
|
ASSERT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config,
|
|
channel_pointers),
|
|
kNoErr);
|
|
volume = apm->recommended_stream_analog_level();
|
|
}
|
|
}
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
AudioProcessingImplTest,
|
|
Agc2FieldTrialParametrizedTest,
|
|
::testing::Values(
|
|
// Full AGC1, TS disabled.
|
|
AudioProcessing::Config{
|
|
.transient_suppression = {.enabled = false},
|
|
.gain_controller1 =
|
|
{.enabled = true,
|
|
.analog_gain_controller = {.enabled = true,
|
|
.enable_digital_adaptive = true}},
|
|
.gain_controller2 = {.enabled = false}},
|
|
// Full AGC1, TS enabled.
|
|
AudioProcessing::Config{
|
|
.transient_suppression = {.enabled = true},
|
|
.gain_controller1 =
|
|
{.enabled = true,
|
|
.analog_gain_controller = {.enabled = true,
|
|
.enable_digital_adaptive = true}},
|
|
.gain_controller2 = {.enabled = false}},
|
|
// Hybrid AGC, TS disabled.
|
|
AudioProcessing::Config{
|
|
.transient_suppression = {.enabled = false},
|
|
.gain_controller1 =
|
|
{.enabled = true,
|
|
.analog_gain_controller = {.enabled = true,
|
|
.enable_digital_adaptive = false}},
|
|
.gain_controller2 = {.enabled = true,
|
|
.adaptive_digital = {.enabled = true}}},
|
|
// Hybrid AGC, TS enabled.
|
|
AudioProcessing::Config{
|
|
.transient_suppression = {.enabled = true},
|
|
.gain_controller1 =
|
|
{.enabled = true,
|
|
.analog_gain_controller = {.enabled = true,
|
|
.enable_digital_adaptive = false}},
|
|
.gain_controller2 = {.enabled = true,
|
|
.adaptive_digital = {.enabled = true}}}));
|
|
|
|
TEST(AudioProcessingImplTest, CanDisableTransientSuppressor) {
|
|
constexpr AudioProcessing::Config kOriginal = {
|
|
.transient_suppression = {.enabled = false}};
|
|
|
|
// Test config application via `AudioProcessing` ctor.
|
|
auto adjusted =
|
|
AudioProcessingBuilder().SetConfig(kOriginal).Create()->GetConfig();
|
|
EXPECT_FALSE(adjusted.transient_suppression.enabled);
|
|
|
|
// Test config application via `AudioProcessing::ApplyConfig()`.
|
|
auto apm = AudioProcessingBuilder().Create();
|
|
apm->ApplyConfig(kOriginal);
|
|
adjusted = apm->GetConfig();
|
|
EXPECT_FALSE(apm->GetConfig().transient_suppression.enabled);
|
|
}
|
|
|
|
TEST(AudioProcessingImplTest, CanEnableTs) {
|
|
constexpr AudioProcessing::Config kOriginal = {
|
|
.transient_suppression = {.enabled = true}};
|
|
|
|
// Test config application via `AudioProcessing` ctor.
|
|
auto adjusted =
|
|
AudioProcessingBuilder().SetConfig(kOriginal).Create()->GetConfig();
|
|
EXPECT_TRUE(adjusted.transient_suppression.enabled);
|
|
|
|
// Test config application via `AudioProcessing::ApplyConfig()`.
|
|
auto apm = AudioProcessingBuilder().Create();
|
|
apm->ApplyConfig(kOriginal);
|
|
adjusted = apm->GetConfig();
|
|
EXPECT_TRUE(adjusted.transient_suppression.enabled);
|
|
}
|
|
|
|
TEST(AudioProcessingImplTest, CanDisableTsWithAgc2FieldTrialDisabled) {
|
|
constexpr AudioProcessing::Config kOriginal = {
|
|
.transient_suppression = {.enabled = false}};
|
|
webrtc::test::ScopedFieldTrials field_trials(
|
|
"WebRTC-Audio-GainController2/Disabled/");
|
|
|
|
// Test config application via `AudioProcessing` ctor.
|
|
auto adjusted =
|
|
AudioProcessingBuilder().SetConfig(kOriginal).Create()->GetConfig();
|
|
EXPECT_FALSE(adjusted.transient_suppression.enabled);
|
|
|
|
// Test config application via `AudioProcessing::ApplyConfig()`.
|
|
auto apm = AudioProcessingBuilder().Create();
|
|
apm->ApplyConfig(kOriginal);
|
|
adjusted = apm->GetConfig();
|
|
EXPECT_FALSE(apm->GetConfig().transient_suppression.enabled);
|
|
}
|
|
|
|
TEST(AudioProcessingImplTest, CanEnableTsWithAgc2FieldTrialDisabled) {
|
|
constexpr AudioProcessing::Config kOriginal = {
|
|
.transient_suppression = {.enabled = true}};
|
|
webrtc::test::ScopedFieldTrials field_trials(
|
|
"WebRTC-Audio-GainController2/Disabled/");
|
|
|
|
// Test config application via `AudioProcessing` ctor.
|
|
auto adjusted =
|
|
AudioProcessingBuilder().SetConfig(kOriginal).Create()->GetConfig();
|
|
EXPECT_TRUE(adjusted.transient_suppression.enabled);
|
|
|
|
// Test config application via `AudioProcessing::ApplyConfig()`.
|
|
auto apm = AudioProcessingBuilder().Create();
|
|
apm->ApplyConfig(kOriginal);
|
|
adjusted = apm->GetConfig();
|
|
EXPECT_TRUE(adjusted.transient_suppression.enabled);
|
|
}
|
|
|
|
TEST(AudioProcessingImplTest,
|
|
CanDisableTsWithAgc2FieldTrialEnabledAndUsageAllowed) {
|
|
constexpr AudioProcessing::Config kOriginal = {
|
|
.transient_suppression = {.enabled = false}};
|
|
webrtc::test::ScopedFieldTrials field_trials(
|
|
"WebRTC-Audio-GainController2/Enabled,"
|
|
"disallow_transient_suppressor_usage:false/");
|
|
|
|
// Test config application via `AudioProcessing` ctor.
|
|
auto adjusted =
|
|
AudioProcessingBuilder().SetConfig(kOriginal).Create()->GetConfig();
|
|
EXPECT_FALSE(adjusted.transient_suppression.enabled);
|
|
|
|
// Test config application via `AudioProcessing::ApplyConfig()`.
|
|
auto apm = AudioProcessingBuilder().Create();
|
|
apm->ApplyConfig(kOriginal);
|
|
adjusted = apm->GetConfig();
|
|
EXPECT_FALSE(adjusted.transient_suppression.enabled);
|
|
}
|
|
|
|
TEST(AudioProcessingImplTest,
|
|
CanEnableTsWithAgc2FieldTrialEnabledAndUsageAllowed) {
|
|
constexpr AudioProcessing::Config kOriginal = {
|
|
.transient_suppression = {.enabled = true}};
|
|
webrtc::test::ScopedFieldTrials field_trials(
|
|
"WebRTC-Audio-GainController2/Enabled,"
|
|
"disallow_transient_suppressor_usage:false/");
|
|
|
|
// Test config application via `AudioProcessing` ctor.
|
|
auto adjusted =
|
|
AudioProcessingBuilder().SetConfig(kOriginal).Create()->GetConfig();
|
|
EXPECT_TRUE(adjusted.transient_suppression.enabled);
|
|
|
|
// Test config application via `AudioProcessing::ApplyConfig()`.
|
|
auto apm = AudioProcessingBuilder().Create();
|
|
apm->ApplyConfig(kOriginal);
|
|
adjusted = apm->GetConfig();
|
|
EXPECT_TRUE(adjusted.transient_suppression.enabled);
|
|
}
|
|
|
|
TEST(AudioProcessingImplTest,
|
|
CannotEnableTsWithAgc2FieldTrialEnabledAndUsageDisallowed) {
|
|
constexpr AudioProcessing::Config kOriginal = {
|
|
.transient_suppression = {.enabled = true}};
|
|
webrtc::test::ScopedFieldTrials field_trials(
|
|
"WebRTC-Audio-GainController2/Enabled,"
|
|
"disallow_transient_suppressor_usage:true/");
|
|
|
|
// Test config application via `AudioProcessing` ctor.
|
|
auto adjusted =
|
|
AudioProcessingBuilder().SetConfig(kOriginal).Create()->GetConfig();
|
|
EXPECT_FALSE(adjusted.transient_suppression.enabled);
|
|
|
|
// Test config application via `AudioProcessing::ApplyConfig()`.
|
|
auto apm = AudioProcessingBuilder().Create();
|
|
apm->ApplyConfig(kOriginal);
|
|
adjusted = apm->GetConfig();
|
|
EXPECT_FALSE(apm->GetConfig().transient_suppression.enabled);
|
|
}
|
|
|
|
} // namespace webrtc
|