mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-12 21:30:45 +01:00
Add refined handling of the internal scaling of the audio in APM
This CL adds functionality that allows adjusting the audio levels internally in APM. The main purpose of the functionality is to allow APM to optionally be moved to an integration that does not provide an analog gain to control, and the implementation of this has been tailored specifically to meet the requirements for that. More specifically, this CL does -Add a new variant of the pre-amplifier gain that is intended to replace the pre-amplifier gain (but at the moment can coexist with that). The main differences with the pre-amplifier gain is that an attenuating gain is allowed, the gain is applied jointly with any emulated analog gain, and that its packaging fits better with the post gain. -Add an emulation of an analog microphone gain. The emulation is designed to match the analog mic gain functionality in Chrome OS (which is digital) but should be usable also on other platforms. -Add a post-gain which is applied after all processing has been applied. The purpose of this gain is for it to work well with the integration in ChromeOS, and be used to compensate for the offset that there is applied on some USB audio devices. Bug: b/177830918 Change-Id: I0f312996e4088c9bd242a713a703eaaeb17f188a Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/209707 Commit-Queue: Per Åhgren <peah@webrtc.org> Reviewed-by: Gustaf Ullberg <gustaf@webrtc.org> Reviewed-by: Alessio Bazzica <alessiob@webrtc.org> Cr-Commit-Position: refs/heads/master@{#33466}
This commit is contained in:
parent
b3159517c3
commit
db5d728878
22 changed files with 1307 additions and 77 deletions
|
@ -195,6 +195,7 @@ rtc_library("audio_processing") {
|
|||
"agc2:adaptive_digital",
|
||||
"agc2:fixed_digital",
|
||||
"agc2:gain_applier",
|
||||
"capture_levels_adjuster",
|
||||
"ns",
|
||||
"transient:transient_suppressor_api",
|
||||
"vad",
|
||||
|
@ -385,6 +386,8 @@ if (rtc_include_tests) {
|
|||
"agc2:rnn_vad_with_level_unittests",
|
||||
"agc2:test_utils",
|
||||
"agc2/rnn_vad:unittests",
|
||||
"capture_levels_adjuster",
|
||||
"capture_levels_adjuster:capture_levels_adjuster_unittests",
|
||||
"test/conversational_speech:unittest",
|
||||
"transient:transient_suppression_unittests",
|
||||
"utility:legacy_delay_estimator_unittest",
|
||||
|
|
|
@ -186,6 +186,12 @@ void AecDumpImpl::WriteRuntimeSetting(
|
|||
setting->set_capture_pre_gain(x);
|
||||
break;
|
||||
}
|
||||
case AudioProcessing::RuntimeSetting::Type::kCapturePostGain: {
|
||||
float x;
|
||||
runtime_setting.GetFloat(&x);
|
||||
setting->set_capture_post_gain(x);
|
||||
break;
|
||||
}
|
||||
case AudioProcessing::RuntimeSetting::Type::
|
||||
kCustomRenderProcessingRuntimeSetting: {
|
||||
float x;
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
#include "common_audio/audio_converter.h"
|
||||
#include "common_audio/include/audio_util.h"
|
||||
#include "modules/audio_processing/aec_dump/aec_dump_factory.h"
|
||||
#include "modules/audio_processing/agc2/gain_applier.h"
|
||||
#include "modules/audio_processing/audio_buffer.h"
|
||||
#include "modules/audio_processing/common.h"
|
||||
#include "modules/audio_processing/include/audio_frame_view.h"
|
||||
|
@ -145,7 +144,7 @@ bool AudioProcessingImpl::SubmoduleStates::Update(
|
|||
bool noise_suppressor_enabled,
|
||||
bool adaptive_gain_controller_enabled,
|
||||
bool gain_controller2_enabled,
|
||||
bool pre_amplifier_enabled,
|
||||
bool gain_adjustment_enabled,
|
||||
bool echo_controller_enabled,
|
||||
bool voice_detector_enabled,
|
||||
bool transient_suppressor_enabled) {
|
||||
|
@ -159,7 +158,7 @@ bool AudioProcessingImpl::SubmoduleStates::Update(
|
|||
changed |=
|
||||
(adaptive_gain_controller_enabled != adaptive_gain_controller_enabled_);
|
||||
changed |= (gain_controller2_enabled != gain_controller2_enabled_);
|
||||
changed |= (pre_amplifier_enabled_ != pre_amplifier_enabled);
|
||||
changed |= (gain_adjustment_enabled != gain_adjustment_enabled_);
|
||||
changed |= (echo_controller_enabled != echo_controller_enabled_);
|
||||
changed |= (voice_detector_enabled != voice_detector_enabled_);
|
||||
changed |= (transient_suppressor_enabled != transient_suppressor_enabled_);
|
||||
|
@ -170,7 +169,7 @@ bool AudioProcessingImpl::SubmoduleStates::Update(
|
|||
noise_suppressor_enabled_ = noise_suppressor_enabled;
|
||||
adaptive_gain_controller_enabled_ = adaptive_gain_controller_enabled;
|
||||
gain_controller2_enabled_ = gain_controller2_enabled;
|
||||
pre_amplifier_enabled_ = pre_amplifier_enabled;
|
||||
gain_adjustment_enabled_ = gain_adjustment_enabled;
|
||||
echo_controller_enabled_ = echo_controller_enabled;
|
||||
voice_detector_enabled_ = voice_detector_enabled;
|
||||
transient_suppressor_enabled_ = transient_suppressor_enabled;
|
||||
|
@ -202,7 +201,7 @@ bool AudioProcessingImpl::SubmoduleStates::CaptureMultiBandProcessingActive(
|
|||
bool AudioProcessingImpl::SubmoduleStates::CaptureFullBandProcessingActive()
|
||||
const {
|
||||
return gain_controller2_enabled_ || capture_post_processor_enabled_ ||
|
||||
pre_amplifier_enabled_;
|
||||
gain_adjustment_enabled_;
|
||||
}
|
||||
|
||||
bool AudioProcessingImpl::SubmoduleStates::CaptureAnalyzerActive() const {
|
||||
|
@ -422,6 +421,7 @@ void AudioProcessingImpl::InitializeLocked() {
|
|||
InitializeAnalyzer();
|
||||
InitializePostProcessor();
|
||||
InitializePreProcessor();
|
||||
InitializeCaptureLevelsAdjuster();
|
||||
|
||||
if (aec_dump_) {
|
||||
aec_dump_->WriteInitMessage(formats_.api_format, rtc::TimeUTCMillis());
|
||||
|
@ -563,6 +563,9 @@ void AudioProcessingImpl::ApplyConfig(const AudioProcessing::Config& config) {
|
|||
config_.pre_amplifier.fixed_gain_factor !=
|
||||
config.pre_amplifier.fixed_gain_factor;
|
||||
|
||||
const bool gain_adjustment_config_changed =
|
||||
config_.capture_level_adjustment != config.capture_level_adjustment;
|
||||
|
||||
config_ = config;
|
||||
|
||||
if (aec_config_changed) {
|
||||
|
@ -594,8 +597,8 @@ void AudioProcessingImpl::ApplyConfig(const AudioProcessing::Config& config) {
|
|||
InitializeGainController2();
|
||||
}
|
||||
|
||||
if (pre_amplifier_config_changed) {
|
||||
InitializePreAmplifier();
|
||||
if (pre_amplifier_config_changed || gain_adjustment_config_changed) {
|
||||
InitializeCaptureLevelsAdjuster();
|
||||
}
|
||||
|
||||
if (config_.level_estimation.enabled && !submodules_.output_level_estimator) {
|
||||
|
@ -688,6 +691,7 @@ bool AudioProcessingImpl::PostRuntimeSetting(RuntimeSetting setting) {
|
|||
case RuntimeSetting::Type::kPlayoutAudioDeviceChange:
|
||||
return render_runtime_settings_enqueuer_.Enqueue(setting);
|
||||
case RuntimeSetting::Type::kCapturePreGain:
|
||||
case RuntimeSetting::Type::kCapturePostGain:
|
||||
case RuntimeSetting::Type::kCaptureCompressionGain:
|
||||
case RuntimeSetting::Type::kCaptureFixedPostGain:
|
||||
case RuntimeSetting::Type::kCaptureOutputUsed:
|
||||
|
@ -809,11 +813,41 @@ void AudioProcessingImpl::HandleCaptureRuntimeSettings() {
|
|||
}
|
||||
switch (setting.type()) {
|
||||
case RuntimeSetting::Type::kCapturePreGain:
|
||||
if (config_.pre_amplifier.enabled) {
|
||||
if (config_.pre_amplifier.enabled ||
|
||||
config_.capture_level_adjustment.enabled) {
|
||||
float value;
|
||||
setting.GetFloat(&value);
|
||||
config_.pre_amplifier.fixed_gain_factor = value;
|
||||
submodules_.pre_amplifier->SetGainFactor(value);
|
||||
// If the pre-amplifier is used, apply the new gain to the
|
||||
// pre-amplifier regardless if the capture level adjustment is
|
||||
// activated. This approach allows both functionalities to coexist
|
||||
// until they have been properly merged.
|
||||
if (config_.pre_amplifier.enabled) {
|
||||
config_.pre_amplifier.fixed_gain_factor = value;
|
||||
} else {
|
||||
config_.capture_level_adjustment.pre_gain_factor = value;
|
||||
}
|
||||
|
||||
// Use both the pre-amplifier and the capture level adjustment gains
|
||||
// as pre-gains.
|
||||
float gain = 1.f;
|
||||
if (config_.pre_amplifier.enabled) {
|
||||
gain *= config_.pre_amplifier.fixed_gain_factor;
|
||||
}
|
||||
if (config_.capture_level_adjustment.enabled) {
|
||||
gain *= config_.capture_level_adjustment.pre_gain_factor;
|
||||
}
|
||||
|
||||
submodules_.capture_levels_adjuster->SetPreGain(gain);
|
||||
}
|
||||
// TODO(bugs.chromium.org/9138): Log setting handling by Aec Dump.
|
||||
break;
|
||||
case RuntimeSetting::Type::kCapturePostGain:
|
||||
if (config_.capture_level_adjustment.enabled) {
|
||||
float value;
|
||||
setting.GetFloat(&value);
|
||||
config_.capture_level_adjustment.post_gain_factor = value;
|
||||
submodules_.capture_levels_adjuster->SetPostGain(
|
||||
config_.capture_level_adjustment.post_gain_factor);
|
||||
}
|
||||
// TODO(bugs.chromium.org/9138): Log setting handling by Aec Dump.
|
||||
break;
|
||||
|
@ -896,6 +930,7 @@ void AudioProcessingImpl::HandleRenderRuntimeSettings() {
|
|||
}
|
||||
break;
|
||||
case RuntimeSetting::Type::kCapturePreGain: // fall-through
|
||||
case RuntimeSetting::Type::kCapturePostGain: // fall-through
|
||||
case RuntimeSetting::Type::kCaptureCompressionGain: // fall-through
|
||||
case RuntimeSetting::Type::kCaptureFixedPostGain: // fall-through
|
||||
case RuntimeSetting::Type::kCaptureOutputUsed: // fall-through
|
||||
|
@ -1083,10 +1118,21 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() {
|
|||
/*use_split_band_data=*/false);
|
||||
}
|
||||
|
||||
if (submodules_.pre_amplifier) {
|
||||
submodules_.pre_amplifier->ApplyGain(AudioFrameView<float>(
|
||||
capture_buffer->channels(), capture_buffer->num_channels(),
|
||||
capture_buffer->num_frames()));
|
||||
if (submodules_.capture_levels_adjuster) {
|
||||
// If the analog mic gain emulation is active, get the emulated analog mic
|
||||
// gain and pass it to the analog gain control functionality.
|
||||
if (config_.capture_level_adjustment.analog_mic_gain_emulation.enabled) {
|
||||
int level = submodules_.capture_levels_adjuster->GetAnalogMicGainLevel();
|
||||
if (submodules_.agc_manager) {
|
||||
submodules_.agc_manager->set_stream_analog_level(level);
|
||||
} else if (submodules_.gain_control) {
|
||||
int error = submodules_.gain_control->set_stream_analog_level(level);
|
||||
RTC_DCHECK_EQ(kNoError, error);
|
||||
}
|
||||
}
|
||||
|
||||
submodules_.capture_levels_adjuster->ApplyPreLevelAdjustment(
|
||||
*capture_buffer);
|
||||
}
|
||||
|
||||
capture_input_rms_.Analyze(rtc::ArrayView<const float>(
|
||||
|
@ -1110,14 +1156,15 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() {
|
|||
capture_.prev_analog_mic_level != -1;
|
||||
capture_.prev_analog_mic_level = analog_mic_level;
|
||||
|
||||
// Detect and flag any change in the pre-amplifier gain.
|
||||
if (submodules_.pre_amplifier) {
|
||||
float pre_amp_gain = submodules_.pre_amplifier->GetGainFactor();
|
||||
// Detect and flag any change in the capture level adjustment pre-gain.
|
||||
if (submodules_.capture_levels_adjuster) {
|
||||
float pre_adjustment_gain =
|
||||
submodules_.capture_levels_adjuster->GetPreAdjustmentGain();
|
||||
capture_.echo_path_gain_change =
|
||||
capture_.echo_path_gain_change ||
|
||||
(capture_.prev_pre_amp_gain != pre_amp_gain &&
|
||||
capture_.prev_pre_amp_gain >= 0.f);
|
||||
capture_.prev_pre_amp_gain = pre_amp_gain;
|
||||
(capture_.prev_pre_adjustment_gain != pre_adjustment_gain &&
|
||||
capture_.prev_pre_adjustment_gain >= 0.f);
|
||||
capture_.prev_pre_adjustment_gain = pre_adjustment_gain;
|
||||
}
|
||||
|
||||
// Detect volume change.
|
||||
|
@ -1325,6 +1372,23 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() {
|
|||
// Pass stats for reporting.
|
||||
stats_reporter_.UpdateStatistics(capture_.stats);
|
||||
|
||||
if (submodules_.capture_levels_adjuster) {
|
||||
submodules_.capture_levels_adjuster->ApplyPostLevelAdjustment(
|
||||
*capture_buffer);
|
||||
|
||||
// If the analog mic gain emulation is active, retrieve the level from the
|
||||
// analog gain control and set it to mic gain emulator.
|
||||
if (config_.capture_level_adjustment.analog_mic_gain_emulation.enabled) {
|
||||
if (submodules_.agc_manager) {
|
||||
submodules_.capture_levels_adjuster->SetAnalogMicGainLevel(
|
||||
submodules_.agc_manager->stream_analog_level());
|
||||
} else if (submodules_.gain_control) {
|
||||
submodules_.capture_levels_adjuster->SetAnalogMicGainLevel(
|
||||
submodules_.gain_control->stream_analog_level());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Temporarily set the output to zero after the stream has been unmuted
|
||||
// (capture output is again used). The purpose of this is to avoid clicks and
|
||||
// artefacts in the audio that results when the processing again is
|
||||
|
@ -1541,16 +1605,29 @@ void AudioProcessingImpl::set_stream_key_pressed(bool key_pressed) {
|
|||
void AudioProcessingImpl::set_stream_analog_level(int level) {
|
||||
MutexLock lock_capture(&mutex_capture_);
|
||||
|
||||
if (config_.capture_level_adjustment.analog_mic_gain_emulation.enabled) {
|
||||
// If the analog mic gain is emulated internally, simply cache the level for
|
||||
// later reporting back as the recommended stream analog level to use.
|
||||
capture_.cached_stream_analog_level_ = level;
|
||||
return;
|
||||
}
|
||||
|
||||
if (submodules_.agc_manager) {
|
||||
submodules_.agc_manager->set_stream_analog_level(level);
|
||||
data_dumper_->DumpRaw("experimental_gain_control_set_stream_analog_level",
|
||||
1, &level);
|
||||
} else if (submodules_.gain_control) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (submodules_.gain_control) {
|
||||
int error = submodules_.gain_control->set_stream_analog_level(level);
|
||||
RTC_DCHECK_EQ(kNoError, error);
|
||||
} else {
|
||||
capture_.cached_stream_analog_level_ = level;
|
||||
return;
|
||||
}
|
||||
|
||||
// If no analog mic gain control functionality is in place, cache the level
|
||||
// for later reporting back as the recommended stream analog level to use.
|
||||
capture_.cached_stream_analog_level_ = level;
|
||||
}
|
||||
|
||||
int AudioProcessingImpl::recommended_stream_analog_level() const {
|
||||
|
@ -1559,13 +1636,19 @@ int AudioProcessingImpl::recommended_stream_analog_level() const {
|
|||
}
|
||||
|
||||
int AudioProcessingImpl::recommended_stream_analog_level_locked() const {
|
||||
if (submodules_.agc_manager) {
|
||||
return submodules_.agc_manager->stream_analog_level();
|
||||
} else if (submodules_.gain_control) {
|
||||
return submodules_.gain_control->stream_analog_level();
|
||||
} else {
|
||||
if (config_.capture_level_adjustment.analog_mic_gain_emulation.enabled) {
|
||||
return capture_.cached_stream_analog_level_;
|
||||
}
|
||||
|
||||
if (submodules_.agc_manager) {
|
||||
return submodules_.agc_manager->stream_analog_level();
|
||||
}
|
||||
|
||||
if (submodules_.gain_control) {
|
||||
return submodules_.gain_control->stream_analog_level();
|
||||
}
|
||||
|
||||
return capture_.cached_stream_analog_level_;
|
||||
}
|
||||
|
||||
bool AudioProcessingImpl::CreateAndAttachAecDump(const std::string& file_name,
|
||||
|
@ -1629,7 +1712,8 @@ bool AudioProcessingImpl::UpdateActiveSubmoduleStates() {
|
|||
config_.high_pass_filter.enabled, !!submodules_.echo_control_mobile,
|
||||
config_.residual_echo_detector.enabled, !!submodules_.noise_suppressor,
|
||||
!!submodules_.gain_control, !!submodules_.gain_controller2,
|
||||
config_.pre_amplifier.enabled, capture_nonlocked_.echo_controller_enabled,
|
||||
config_.pre_amplifier.enabled || config_.capture_level_adjustment.enabled,
|
||||
capture_nonlocked_.echo_controller_enabled,
|
||||
config_.voice_detection.enabled, !!submodules_.transient_suppressor);
|
||||
}
|
||||
|
||||
|
@ -1873,12 +1957,27 @@ void AudioProcessingImpl::InitializeNoiseSuppressor() {
|
|||
}
|
||||
}
|
||||
|
||||
void AudioProcessingImpl::InitializePreAmplifier() {
|
||||
if (config_.pre_amplifier.enabled) {
|
||||
submodules_.pre_amplifier.reset(
|
||||
new GainApplier(true, config_.pre_amplifier.fixed_gain_factor));
|
||||
void AudioProcessingImpl::InitializeCaptureLevelsAdjuster() {
|
||||
if (config_.pre_amplifier.enabled ||
|
||||
config_.capture_level_adjustment.enabled) {
|
||||
// Use both the pre-amplifier and the capture level adjustment gains as
|
||||
// pre-gains.
|
||||
float pre_gain = 1.f;
|
||||
if (config_.pre_amplifier.enabled) {
|
||||
pre_gain *= config_.pre_amplifier.fixed_gain_factor;
|
||||
}
|
||||
if (config_.capture_level_adjustment.enabled) {
|
||||
pre_gain *= config_.capture_level_adjustment.pre_gain_factor;
|
||||
}
|
||||
|
||||
submodules_.capture_levels_adjuster =
|
||||
std::make_unique<CaptureLevelsAdjuster>(
|
||||
config_.capture_level_adjustment.analog_mic_gain_emulation.enabled,
|
||||
config_.capture_level_adjustment.analog_mic_gain_emulation
|
||||
.initial_level,
|
||||
pre_gain, config_.capture_level_adjustment.post_gain_factor);
|
||||
} else {
|
||||
submodules_.pre_amplifier.reset();
|
||||
submodules_.capture_levels_adjuster.reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2045,7 +2144,7 @@ AudioProcessingImpl::ApmCaptureState::ApmCaptureState()
|
|||
split_rate(kSampleRate16kHz),
|
||||
echo_path_gain_change(false),
|
||||
prev_analog_mic_level(-1),
|
||||
prev_pre_amp_gain(-1.f),
|
||||
prev_pre_adjustment_gain(-1.f),
|
||||
playout_volume(-1),
|
||||
prev_playout_volume(-1) {}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "modules/audio_processing/agc/agc_manager_direct.h"
|
||||
#include "modules/audio_processing/agc/gain_control.h"
|
||||
#include "modules/audio_processing/audio_buffer.h"
|
||||
#include "modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.h"
|
||||
#include "modules/audio_processing/echo_control_mobile_impl.h"
|
||||
#include "modules/audio_processing/gain_control_impl.h"
|
||||
#include "modules/audio_processing/gain_controller2.h"
|
||||
|
@ -202,7 +203,7 @@ class AudioProcessingImpl : public AudioProcessing {
|
|||
bool noise_suppressor_enabled,
|
||||
bool adaptive_gain_controller_enabled,
|
||||
bool gain_controller2_enabled,
|
||||
bool pre_amplifier_enabled,
|
||||
bool gain_adjustment_enabled,
|
||||
bool echo_controller_enabled,
|
||||
bool voice_detector_enabled,
|
||||
bool transient_suppressor_enabled);
|
||||
|
@ -226,7 +227,7 @@ class AudioProcessingImpl : public AudioProcessing {
|
|||
bool noise_suppressor_enabled_ = false;
|
||||
bool adaptive_gain_controller_enabled_ = false;
|
||||
bool gain_controller2_enabled_ = false;
|
||||
bool pre_amplifier_enabled_ = false;
|
||||
bool gain_adjustment_enabled_ = false;
|
||||
bool echo_controller_enabled_ = false;
|
||||
bool voice_detector_enabled_ = false;
|
||||
bool transient_suppressor_enabled_ = false;
|
||||
|
@ -270,7 +271,8 @@ class AudioProcessingImpl : public AudioProcessing {
|
|||
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_);
|
||||
void InitializeGainController2() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_);
|
||||
void InitializeNoiseSuppressor() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_);
|
||||
void InitializePreAmplifier() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_);
|
||||
void InitializeCaptureLevelsAdjuster()
|
||||
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_);
|
||||
void InitializePostProcessor() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_);
|
||||
void InitializeAnalyzer() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_);
|
||||
|
||||
|
@ -392,10 +394,10 @@ class AudioProcessingImpl : public AudioProcessing {
|
|||
std::unique_ptr<TransientSuppressor> transient_suppressor;
|
||||
std::unique_ptr<CustomProcessing> capture_post_processor;
|
||||
std::unique_ptr<CustomProcessing> render_pre_processor;
|
||||
std::unique_ptr<GainApplier> pre_amplifier;
|
||||
std::unique_ptr<CustomAudioAnalyzer> capture_analyzer;
|
||||
std::unique_ptr<LevelEstimator> output_level_estimator;
|
||||
std::unique_ptr<VoiceDetection> voice_detector;
|
||||
std::unique_ptr<CaptureLevelsAdjuster> capture_levels_adjuster;
|
||||
} submodules_;
|
||||
|
||||
// State that is written to while holding both the render and capture locks
|
||||
|
@ -445,7 +447,7 @@ class AudioProcessingImpl : public AudioProcessing {
|
|||
int split_rate;
|
||||
bool echo_path_gain_change;
|
||||
int prev_analog_mic_level;
|
||||
float prev_pre_amp_gain;
|
||||
float prev_pre_adjustment_gain;
|
||||
int playout_volume;
|
||||
int prev_playout_volume;
|
||||
AudioProcessingStats stats;
|
||||
|
|
|
@ -203,6 +203,72 @@ TEST(AudioProcessingImplTest, UpdateCapturePreGainRuntimeSetting) {
|
|||
<< "Frame should be amplified.";
|
||||
}
|
||||
|
||||
TEST(AudioProcessingImplTest,
|
||||
LevelAdjustmentUpdateCapturePreGainRuntimeSetting) {
|
||||
std::unique_ptr<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, /*has_keyboard=*/false);
|
||||
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) {
|
||||
std::unique_ptr<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, /*has_keyboard=*/false);
|
||||
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.
|
||||
|
@ -328,6 +394,49 @@ TEST(AudioProcessingImplTest,
|
|||
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();
|
||||
|
||||
std::unique_ptr<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, /*has_keyboard=*/false);
|
||||
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
|
||||
|
|
|
@ -913,6 +913,131 @@ TEST_F(ApmTest, PreAmplifier) {
|
|||
EXPECT_EQ(config.pre_amplifier.fixed_gain_factor, 1.5f);
|
||||
}
|
||||
|
||||
// This test a simple test that ensures that the emulated analog mic gain
|
||||
// functionality runs without crashing.
|
||||
TEST_F(ApmTest, AnalogMicGainEmulation) {
|
||||
// Fill the audio frame with a sawtooth pattern.
|
||||
rtc::ArrayView<int16_t> frame_data = GetMutableFrameData(&frame_);
|
||||
const size_t samples_per_channel = frame_.samples_per_channel;
|
||||
for (size_t i = 0; i < samples_per_channel; i++) {
|
||||
for (size_t ch = 0; ch < frame_.num_channels; ++ch) {
|
||||
frame_data[i + ch * samples_per_channel] = 100 * ((i % 3) - 1);
|
||||
}
|
||||
}
|
||||
// Cache the frame in tmp_frame.
|
||||
Int16FrameData tmp_frame;
|
||||
tmp_frame.CopyFrom(frame_);
|
||||
|
||||
// Enable the analog gain emulation.
|
||||
AudioProcessing::Config config = apm_->GetConfig();
|
||||
config.capture_level_adjustment.enabled = true;
|
||||
config.capture_level_adjustment.analog_mic_gain_emulation.enabled = true;
|
||||
config.capture_level_adjustment.analog_mic_gain_emulation.initial_level = 21;
|
||||
config.gain_controller1.enabled = true;
|
||||
config.gain_controller1.mode =
|
||||
AudioProcessing::Config::GainController1::Mode::kAdaptiveAnalog;
|
||||
config.gain_controller1.analog_gain_controller.enabled = true;
|
||||
apm_->ApplyConfig(config);
|
||||
|
||||
// Process a number of frames to ensure that the code runs without crashes.
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
frame_.CopyFrom(tmp_frame);
|
||||
EXPECT_EQ(apm_->kNoError, ProcessStreamChooser(kIntFormat));
|
||||
}
|
||||
}
|
||||
|
||||
// This test repeatedly reconfigures the capture level adjustment functionality
|
||||
// in APM, processes a number of frames, and checks that output signal has the
|
||||
// right level.
|
||||
TEST_F(ApmTest, CaptureLevelAdjustment) {
|
||||
// Fill the audio frame with a sawtooth pattern.
|
||||
rtc::ArrayView<int16_t> frame_data = GetMutableFrameData(&frame_);
|
||||
const size_t samples_per_channel = frame_.samples_per_channel;
|
||||
for (size_t i = 0; i < samples_per_channel; i++) {
|
||||
for (size_t ch = 0; ch < frame_.num_channels; ++ch) {
|
||||
frame_data[i + ch * samples_per_channel] = 100 * ((i % 3) - 1);
|
||||
}
|
||||
}
|
||||
// Cache the frame in tmp_frame.
|
||||
Int16FrameData tmp_frame;
|
||||
tmp_frame.CopyFrom(frame_);
|
||||
|
||||
auto compute_power = [](const Int16FrameData& frame) {
|
||||
rtc::ArrayView<const int16_t> data = GetFrameData(frame);
|
||||
return std::accumulate(data.begin(), data.end(), 0.0f,
|
||||
[](float a, float b) { return a + b * b; }) /
|
||||
data.size() / 32768 / 32768;
|
||||
};
|
||||
|
||||
const float input_power = compute_power(tmp_frame);
|
||||
// Double-check that the input data is large compared to the error kEpsilon.
|
||||
constexpr float kEpsilon = 1e-20f;
|
||||
RTC_DCHECK_GE(input_power, 10 * kEpsilon);
|
||||
|
||||
// 1. Enable pre-amp with 0 dB gain.
|
||||
AudioProcessing::Config config = apm_->GetConfig();
|
||||
config.capture_level_adjustment.enabled = true;
|
||||
config.capture_level_adjustment.pre_gain_factor = 0.5f;
|
||||
config.capture_level_adjustment.post_gain_factor = 4.f;
|
||||
const float expected_output_power1 =
|
||||
config.capture_level_adjustment.pre_gain_factor *
|
||||
config.capture_level_adjustment.pre_gain_factor *
|
||||
config.capture_level_adjustment.post_gain_factor *
|
||||
config.capture_level_adjustment.post_gain_factor * input_power;
|
||||
apm_->ApplyConfig(config);
|
||||
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
frame_.CopyFrom(tmp_frame);
|
||||
EXPECT_EQ(apm_->kNoError, ProcessStreamChooser(kIntFormat));
|
||||
}
|
||||
float output_power = compute_power(frame_);
|
||||
EXPECT_NEAR(output_power, expected_output_power1, kEpsilon);
|
||||
config = apm_->GetConfig();
|
||||
EXPECT_EQ(config.capture_level_adjustment.pre_gain_factor, 0.5f);
|
||||
EXPECT_EQ(config.capture_level_adjustment.post_gain_factor, 4.f);
|
||||
|
||||
// 2. Change pre-amp gain via ApplyConfig.
|
||||
config.capture_level_adjustment.pre_gain_factor = 1.0f;
|
||||
config.capture_level_adjustment.post_gain_factor = 2.f;
|
||||
const float expected_output_power2 =
|
||||
config.capture_level_adjustment.pre_gain_factor *
|
||||
config.capture_level_adjustment.pre_gain_factor *
|
||||
config.capture_level_adjustment.post_gain_factor *
|
||||
config.capture_level_adjustment.post_gain_factor * input_power;
|
||||
apm_->ApplyConfig(config);
|
||||
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
frame_.CopyFrom(tmp_frame);
|
||||
EXPECT_EQ(apm_->kNoError, ProcessStreamChooser(kIntFormat));
|
||||
}
|
||||
output_power = compute_power(frame_);
|
||||
EXPECT_NEAR(output_power, expected_output_power2, kEpsilon);
|
||||
config = apm_->GetConfig();
|
||||
EXPECT_EQ(config.capture_level_adjustment.pre_gain_factor, 1.0f);
|
||||
EXPECT_EQ(config.capture_level_adjustment.post_gain_factor, 2.f);
|
||||
|
||||
// 3. Change pre-amp gain via a RuntimeSetting.
|
||||
constexpr float kPreGain3 = 0.5f;
|
||||
constexpr float kPostGain3 = 3.f;
|
||||
const float expected_output_power3 =
|
||||
kPreGain3 * kPreGain3 * kPostGain3 * kPostGain3 * input_power;
|
||||
|
||||
apm_->SetRuntimeSetting(
|
||||
AudioProcessing::RuntimeSetting::CreateCapturePreGain(kPreGain3));
|
||||
apm_->SetRuntimeSetting(
|
||||
AudioProcessing::RuntimeSetting::CreateCapturePostGain(kPostGain3));
|
||||
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
frame_.CopyFrom(tmp_frame);
|
||||
EXPECT_EQ(apm_->kNoError, ProcessStreamChooser(kIntFormat));
|
||||
}
|
||||
output_power = compute_power(frame_);
|
||||
EXPECT_NEAR(output_power, expected_output_power3, kEpsilon);
|
||||
config = apm_->GetConfig();
|
||||
EXPECT_EQ(config.capture_level_adjustment.pre_gain_factor, 0.5f);
|
||||
EXPECT_EQ(config.capture_level_adjustment.post_gain_factor, 3.f);
|
||||
}
|
||||
|
||||
TEST_F(ApmTest, GainControl) {
|
||||
AudioProcessing::Config config = apm_->GetConfig();
|
||||
config.gain_controller1.enabled = false;
|
||||
|
@ -2428,36 +2553,6 @@ TEST(RuntimeSettingTest, TestDefaultCtor) {
|
|||
EXPECT_EQ(AudioProcessing::RuntimeSetting::Type::kNotSpecified, s.type());
|
||||
}
|
||||
|
||||
TEST(RuntimeSettingDeathTest, TestCapturePreGain) {
|
||||
using Type = AudioProcessing::RuntimeSetting::Type;
|
||||
{
|
||||
auto s = AudioProcessing::RuntimeSetting::CreateCapturePreGain(1.25f);
|
||||
EXPECT_EQ(Type::kCapturePreGain, s.type());
|
||||
float v;
|
||||
s.GetFloat(&v);
|
||||
EXPECT_EQ(1.25f, v);
|
||||
}
|
||||
|
||||
#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
|
||||
EXPECT_DEATH(AudioProcessing::RuntimeSetting::CreateCapturePreGain(0.1f), "");
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(RuntimeSettingDeathTest, TestCaptureFixedPostGain) {
|
||||
using Type = AudioProcessing::RuntimeSetting::Type;
|
||||
{
|
||||
auto s = AudioProcessing::RuntimeSetting::CreateCaptureFixedPostGain(1.25f);
|
||||
EXPECT_EQ(Type::kCaptureFixedPostGain, s.type());
|
||||
float v;
|
||||
s.GetFloat(&v);
|
||||
EXPECT_EQ(1.25f, v);
|
||||
}
|
||||
|
||||
#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
|
||||
EXPECT_DEATH(AudioProcessing::RuntimeSetting::CreateCapturePreGain(0.1f), "");
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(RuntimeSettingTest, TestUsageWithSwapQueue) {
|
||||
SwapQueue<AudioProcessing::RuntimeSetting> q(1);
|
||||
auto s = AudioProcessing::RuntimeSetting();
|
||||
|
|
45
modules/audio_processing/capture_levels_adjuster/BUILD.gn
Normal file
45
modules/audio_processing/capture_levels_adjuster/BUILD.gn
Normal file
|
@ -0,0 +1,45 @@
|
|||
# Copyright (c) 2021 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.
|
||||
|
||||
import("../../../webrtc.gni")
|
||||
|
||||
rtc_library("capture_levels_adjuster") {
|
||||
visibility = [ "*" ]
|
||||
|
||||
sources = [
|
||||
"audio_samples_scaler.cc",
|
||||
"audio_samples_scaler.h",
|
||||
"capture_levels_adjuster.cc",
|
||||
"capture_levels_adjuster.h",
|
||||
]
|
||||
|
||||
defines = []
|
||||
|
||||
deps = [
|
||||
"..:audio_buffer",
|
||||
"../../../api:array_view",
|
||||
"../../../rtc_base:checks",
|
||||
"../../../rtc_base:safe_minmax",
|
||||
]
|
||||
}
|
||||
|
||||
rtc_library("capture_levels_adjuster_unittests") {
|
||||
testonly = true
|
||||
|
||||
sources = [
|
||||
"audio_samples_scaler_unittest.cc",
|
||||
"capture_levels_adjuster_unittest.cc",
|
||||
]
|
||||
deps = [
|
||||
":capture_levels_adjuster",
|
||||
"..:audioproc_test_utils",
|
||||
"../../../rtc_base:gunit_helpers",
|
||||
"../../../rtc_base:stringutils",
|
||||
"../../../test:test_support",
|
||||
]
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright (c) 2021 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/capture_levels_adjuster/audio_samples_scaler.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "api/array_view.h"
|
||||
#include "modules/audio_processing/audio_buffer.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/numerics/safe_minmax.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
AudioSamplesScaler::AudioSamplesScaler(float initial_gain)
|
||||
: previous_gain_(initial_gain), target_gain_(initial_gain) {}
|
||||
|
||||
void AudioSamplesScaler::Process(AudioBuffer& audio_buffer) {
|
||||
if (static_cast<int>(audio_buffer.num_frames()) != samples_per_channel_) {
|
||||
// Update the members depending on audio-buffer length if needed.
|
||||
RTC_DCHECK_GT(audio_buffer.num_frames(), 0);
|
||||
samples_per_channel_ = static_cast<int>(audio_buffer.num_frames());
|
||||
one_by_samples_per_channel_ = 1.f / samples_per_channel_;
|
||||
}
|
||||
|
||||
if (target_gain_ == 1.f && previous_gain_ == target_gain_) {
|
||||
// If only a gain of 1 is to be applied, do an early return without applying
|
||||
// any gain.
|
||||
return;
|
||||
}
|
||||
|
||||
float gain = previous_gain_;
|
||||
if (previous_gain_ == target_gain_) {
|
||||
// Apply a non-changing gain.
|
||||
for (size_t channel = 0; channel < audio_buffer.num_channels(); ++channel) {
|
||||
rtc::ArrayView<float> channel_view(audio_buffer.channels()[channel],
|
||||
samples_per_channel_);
|
||||
for (float& sample : channel_view) {
|
||||
sample *= gain;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const float increment =
|
||||
(target_gain_ - previous_gain_) * one_by_samples_per_channel_;
|
||||
|
||||
if (increment > 0.f) {
|
||||
// Apply an increasing gain.
|
||||
for (size_t channel = 0; channel < audio_buffer.num_channels();
|
||||
++channel) {
|
||||
gain = previous_gain_;
|
||||
rtc::ArrayView<float> channel_view(audio_buffer.channels()[channel],
|
||||
samples_per_channel_);
|
||||
for (float& sample : channel_view) {
|
||||
gain = std::min(gain + increment, target_gain_);
|
||||
sample *= gain;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Apply a decreasing gain.
|
||||
for (size_t channel = 0; channel < audio_buffer.num_channels();
|
||||
++channel) {
|
||||
gain = previous_gain_;
|
||||
rtc::ArrayView<float> channel_view(audio_buffer.channels()[channel],
|
||||
samples_per_channel_);
|
||||
for (float& sample : channel_view) {
|
||||
gain = std::max(gain + increment, target_gain_);
|
||||
sample *= gain;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
previous_gain_ = target_gain_;
|
||||
|
||||
// Saturate the samples to be in the S16 range.
|
||||
for (size_t channel = 0; channel < audio_buffer.num_channels(); ++channel) {
|
||||
rtc::ArrayView<float> channel_view(audio_buffer.channels()[channel],
|
||||
samples_per_channel_);
|
||||
for (float& sample : channel_view) {
|
||||
constexpr float kMinFloatS16Value = -32768.f;
|
||||
constexpr float kMaxFloatS16Value = 32767.f;
|
||||
sample = rtc::SafeClamp(sample, kMinFloatS16Value, kMaxFloatS16Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef MODULES_AUDIO_PROCESSING_CAPTURE_LEVELS_ADJUSTER_AUDIO_SAMPLES_SCALER_H_
|
||||
#define MODULES_AUDIO_PROCESSING_CAPTURE_LEVELS_ADJUSTER_AUDIO_SAMPLES_SCALER_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "modules/audio_processing/audio_buffer.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Handles and applies a gain to the samples in an audio buffer.
|
||||
// The gain is applied for each sample and any changes in the gain take effect
|
||||
// gradually (in a linear manner) over one frame.
|
||||
class AudioSamplesScaler {
|
||||
public:
|
||||
// C-tor. The supplied `initial_gain` is used immediately at the first call to
|
||||
// Process(), i.e., in contrast to the gain supplied by SetGain(...) there is
|
||||
// no gradual change to the `initial_gain`.
|
||||
explicit AudioSamplesScaler(float initial_gain);
|
||||
AudioSamplesScaler(const AudioSamplesScaler&) = delete;
|
||||
AudioSamplesScaler& operator=(const AudioSamplesScaler&) = delete;
|
||||
|
||||
// Applies the specified gain to the audio in `audio_buffer`.
|
||||
void Process(AudioBuffer& audio_buffer);
|
||||
|
||||
// Sets the gain to apply to each sample.
|
||||
void SetGain(float gain) { target_gain_ = gain; }
|
||||
|
||||
private:
|
||||
float previous_gain_ = 1.f;
|
||||
float target_gain_ = 1.f;
|
||||
int samples_per_channel_ = -1;
|
||||
float one_by_samples_per_channel_ = -1.f;
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_AUDIO_PROCESSING_CAPTURE_LEVELS_ADJUSTER_AUDIO_SAMPLES_SCALER_H_
|
|
@ -0,0 +1,204 @@
|
|||
/*
|
||||
* Copyright (c) 2021 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/capture_levels_adjuster/audio_samples_scaler.h"
|
||||
|
||||
#include <tuple>
|
||||
|
||||
#include "modules/audio_processing/test/audio_buffer_tools.h"
|
||||
#include "rtc_base/strings/string_builder.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
|
||||
float SampleValueForChannel(int channel) {
|
||||
constexpr float kSampleBaseValue = 100.f;
|
||||
constexpr float kSampleChannelOffset = 1.f;
|
||||
return kSampleBaseValue + channel * kSampleChannelOffset;
|
||||
}
|
||||
|
||||
void PopulateBuffer(AudioBuffer& audio_buffer) {
|
||||
for (size_t ch = 0; ch < audio_buffer.num_channels(); ++ch) {
|
||||
test::FillBufferChannel(SampleValueForChannel(ch), ch, audio_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr int kNumFramesToProcess = 10;
|
||||
|
||||
class AudioSamplesScalerTest
|
||||
: public ::testing::Test,
|
||||
public ::testing::WithParamInterface<std::tuple<int, int, float>> {
|
||||
protected:
|
||||
int sample_rate_hz() const { return std::get<0>(GetParam()); }
|
||||
int num_channels() const { return std::get<1>(GetParam()); }
|
||||
float initial_gain() const { return std::get<2>(GetParam()); }
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
AudioSamplesScalerTestSuite,
|
||||
AudioSamplesScalerTest,
|
||||
::testing::Combine(::testing::Values(16000, 32000, 48000),
|
||||
::testing::Values(1, 2, 4),
|
||||
::testing::Values(0.1f, 1.f, 2.f, 4.f)));
|
||||
|
||||
TEST_P(AudioSamplesScalerTest, InitialGainIsRespected) {
|
||||
AudioSamplesScaler scaler(initial_gain());
|
||||
|
||||
AudioBuffer audio_buffer(sample_rate_hz(), num_channels(), sample_rate_hz(),
|
||||
num_channels(), sample_rate_hz(), num_channels());
|
||||
|
||||
for (int frame = 0; frame < kNumFramesToProcess; ++frame) {
|
||||
PopulateBuffer(audio_buffer);
|
||||
scaler.Process(audio_buffer);
|
||||
for (int ch = 0; ch < num_channels(); ++ch) {
|
||||
for (size_t i = 0; i < audio_buffer.num_frames(); ++i) {
|
||||
EXPECT_FLOAT_EQ(audio_buffer.channels_const()[ch][i],
|
||||
initial_gain() * SampleValueForChannel(ch));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(AudioSamplesScalerTest, VerifyGainAdjustment) {
|
||||
const float higher_gain = initial_gain();
|
||||
const float lower_gain = higher_gain / 2.f;
|
||||
|
||||
AudioSamplesScaler scaler(lower_gain);
|
||||
|
||||
AudioBuffer audio_buffer(sample_rate_hz(), num_channels(), sample_rate_hz(),
|
||||
num_channels(), sample_rate_hz(), num_channels());
|
||||
|
||||
// Allow the intial, lower, gain to take effect.
|
||||
PopulateBuffer(audio_buffer);
|
||||
|
||||
scaler.Process(audio_buffer);
|
||||
|
||||
// Set the new, higher, gain.
|
||||
scaler.SetGain(higher_gain);
|
||||
|
||||
// Ensure that the new, higher, gain is achieved gradually over one frame.
|
||||
PopulateBuffer(audio_buffer);
|
||||
|
||||
scaler.Process(audio_buffer);
|
||||
for (int ch = 0; ch < num_channels(); ++ch) {
|
||||
for (size_t i = 0; i < audio_buffer.num_frames() - 1; ++i) {
|
||||
EXPECT_LT(audio_buffer.channels_const()[ch][i],
|
||||
higher_gain * SampleValueForChannel(ch));
|
||||
EXPECT_LE(audio_buffer.channels_const()[ch][i],
|
||||
audio_buffer.channels_const()[ch][i + 1]);
|
||||
}
|
||||
EXPECT_LE(audio_buffer.channels_const()[ch][audio_buffer.num_frames() - 1],
|
||||
higher_gain * SampleValueForChannel(ch));
|
||||
}
|
||||
|
||||
// Ensure that the new, higher, gain is achieved and stay unchanged.
|
||||
for (int frame = 0; frame < kNumFramesToProcess; ++frame) {
|
||||
PopulateBuffer(audio_buffer);
|
||||
scaler.Process(audio_buffer);
|
||||
|
||||
for (int ch = 0; ch < num_channels(); ++ch) {
|
||||
for (size_t i = 0; i < audio_buffer.num_frames(); ++i) {
|
||||
EXPECT_FLOAT_EQ(audio_buffer.channels_const()[ch][i],
|
||||
higher_gain * SampleValueForChannel(ch));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the new, lower, gain.
|
||||
scaler.SetGain(lower_gain);
|
||||
|
||||
// Ensure that the new, lower, gain is achieved gradually over one frame.
|
||||
PopulateBuffer(audio_buffer);
|
||||
scaler.Process(audio_buffer);
|
||||
for (int ch = 0; ch < num_channels(); ++ch) {
|
||||
for (size_t i = 0; i < audio_buffer.num_frames() - 1; ++i) {
|
||||
EXPECT_GT(audio_buffer.channels_const()[ch][i],
|
||||
lower_gain * SampleValueForChannel(ch));
|
||||
EXPECT_GE(audio_buffer.channels_const()[ch][i],
|
||||
audio_buffer.channels_const()[ch][i + 1]);
|
||||
}
|
||||
EXPECT_GE(audio_buffer.channels_const()[ch][audio_buffer.num_frames() - 1],
|
||||
lower_gain * SampleValueForChannel(ch));
|
||||
}
|
||||
|
||||
// Ensure that the new, lower, gain is achieved and stay unchanged.
|
||||
for (int frame = 0; frame < kNumFramesToProcess; ++frame) {
|
||||
PopulateBuffer(audio_buffer);
|
||||
scaler.Process(audio_buffer);
|
||||
|
||||
for (int ch = 0; ch < num_channels(); ++ch) {
|
||||
for (size_t i = 0; i < audio_buffer.num_frames(); ++i) {
|
||||
EXPECT_FLOAT_EQ(audio_buffer.channels_const()[ch][i],
|
||||
lower_gain * SampleValueForChannel(ch));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(AudioSamplesScaler, UpwardsClamping) {
|
||||
constexpr int kSampleRateHz = 48000;
|
||||
constexpr int kNumChannels = 1;
|
||||
constexpr float kGain = 10.f;
|
||||
constexpr float kMaxClampedSampleValue = 32767.f;
|
||||
static_assert(kGain > 1.f, "");
|
||||
|
||||
AudioSamplesScaler scaler(kGain);
|
||||
|
||||
AudioBuffer audio_buffer(kSampleRateHz, kNumChannels, kSampleRateHz,
|
||||
kNumChannels, kSampleRateHz, kNumChannels);
|
||||
|
||||
for (int frame = 0; frame < kNumFramesToProcess; ++frame) {
|
||||
for (size_t ch = 0; ch < audio_buffer.num_channels(); ++ch) {
|
||||
test::FillBufferChannel(
|
||||
kMaxClampedSampleValue - audio_buffer.num_channels() + 1.f + ch, ch,
|
||||
audio_buffer);
|
||||
}
|
||||
|
||||
scaler.Process(audio_buffer);
|
||||
for (int ch = 0; ch < kNumChannels; ++ch) {
|
||||
for (size_t i = 0; i < audio_buffer.num_frames(); ++i) {
|
||||
EXPECT_FLOAT_EQ(audio_buffer.channels_const()[ch][i],
|
||||
kMaxClampedSampleValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(AudioSamplesScaler, DownwardsClamping) {
|
||||
constexpr int kSampleRateHz = 48000;
|
||||
constexpr int kNumChannels = 1;
|
||||
constexpr float kGain = 10.f;
|
||||
constexpr float kMinClampedSampleValue = -32768.f;
|
||||
static_assert(kGain > 1.f, "");
|
||||
|
||||
AudioSamplesScaler scaler(kGain);
|
||||
|
||||
AudioBuffer audio_buffer(kSampleRateHz, kNumChannels, kSampleRateHz,
|
||||
kNumChannels, kSampleRateHz, kNumChannels);
|
||||
|
||||
for (int frame = 0; frame < kNumFramesToProcess; ++frame) {
|
||||
for (size_t ch = 0; ch < audio_buffer.num_channels(); ++ch) {
|
||||
test::FillBufferChannel(
|
||||
kMinClampedSampleValue + audio_buffer.num_channels() - 1.f + ch, ch,
|
||||
audio_buffer);
|
||||
}
|
||||
|
||||
scaler.Process(audio_buffer);
|
||||
for (int ch = 0; ch < kNumChannels; ++ch) {
|
||||
for (size_t i = 0; i < audio_buffer.num_frames(); ++i) {
|
||||
EXPECT_FLOAT_EQ(audio_buffer.channels_const()[ch][i],
|
||||
kMinClampedSampleValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace webrtc
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright (c) 2021 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/capture_levels_adjuster/capture_levels_adjuster.h"
|
||||
|
||||
#include "modules/audio_processing/audio_buffer.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/numerics/safe_minmax.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int kMinAnalogMicGainLevel = 0;
|
||||
constexpr int kMaxAnalogMicGainLevel = 255;
|
||||
|
||||
float ComputeLevelBasedGain(int emulated_analog_mic_gain_level) {
|
||||
static_assert(
|
||||
kMinAnalogMicGainLevel == 0,
|
||||
"The minimum gain level must be 0 for the maths below to work.");
|
||||
static_assert(kMaxAnalogMicGainLevel > 0,
|
||||
"The minimum gain level must be larger than 0 for the maths "
|
||||
"below to work.");
|
||||
constexpr float kGainToLevelMultiplier = 1.f / kMaxAnalogMicGainLevel;
|
||||
|
||||
RTC_DCHECK_GE(emulated_analog_mic_gain_level, kMinAnalogMicGainLevel);
|
||||
RTC_DCHECK_LE(emulated_analog_mic_gain_level, kMaxAnalogMicGainLevel);
|
||||
return kGainToLevelMultiplier * emulated_analog_mic_gain_level;
|
||||
}
|
||||
|
||||
float ComputePreGain(float pre_gain,
|
||||
int emulated_analog_mic_gain_level,
|
||||
bool emulated_analog_mic_gain_enabled) {
|
||||
return emulated_analog_mic_gain_enabled
|
||||
? pre_gain * ComputeLevelBasedGain(emulated_analog_mic_gain_level)
|
||||
: pre_gain;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
CaptureLevelsAdjuster::CaptureLevelsAdjuster(
|
||||
bool emulated_analog_mic_gain_enabled,
|
||||
int emulated_analog_mic_gain_level,
|
||||
float pre_gain,
|
||||
float post_gain)
|
||||
: emulated_analog_mic_gain_enabled_(emulated_analog_mic_gain_enabled),
|
||||
emulated_analog_mic_gain_level_(emulated_analog_mic_gain_level),
|
||||
pre_gain_(pre_gain),
|
||||
pre_adjustment_gain_(ComputePreGain(pre_gain_,
|
||||
emulated_analog_mic_gain_level_,
|
||||
emulated_analog_mic_gain_enabled_)),
|
||||
pre_scaler_(pre_adjustment_gain_),
|
||||
post_scaler_(post_gain) {}
|
||||
|
||||
void CaptureLevelsAdjuster::ApplyPreLevelAdjustment(AudioBuffer& audio_buffer) {
|
||||
pre_scaler_.Process(audio_buffer);
|
||||
}
|
||||
|
||||
void CaptureLevelsAdjuster::ApplyPostLevelAdjustment(
|
||||
AudioBuffer& audio_buffer) {
|
||||
post_scaler_.Process(audio_buffer);
|
||||
}
|
||||
|
||||
void CaptureLevelsAdjuster::SetPreGain(float pre_gain) {
|
||||
pre_gain_ = pre_gain;
|
||||
UpdatePreAdjustmentGain();
|
||||
}
|
||||
|
||||
void CaptureLevelsAdjuster::SetPostGain(float post_gain) {
|
||||
post_scaler_.SetGain(post_gain);
|
||||
}
|
||||
|
||||
void CaptureLevelsAdjuster::SetAnalogMicGainLevel(int level) {
|
||||
RTC_DCHECK_GE(level, kMinAnalogMicGainLevel);
|
||||
RTC_DCHECK_LE(level, kMaxAnalogMicGainLevel);
|
||||
int clamped_level =
|
||||
rtc::SafeClamp(level, kMinAnalogMicGainLevel, kMaxAnalogMicGainLevel);
|
||||
|
||||
emulated_analog_mic_gain_level_ = clamped_level;
|
||||
UpdatePreAdjustmentGain();
|
||||
}
|
||||
|
||||
void CaptureLevelsAdjuster::UpdatePreAdjustmentGain() {
|
||||
pre_adjustment_gain_ =
|
||||
ComputePreGain(pre_gain_, emulated_analog_mic_gain_level_,
|
||||
emulated_analog_mic_gain_enabled_);
|
||||
pre_scaler_.SetGain(pre_adjustment_gain_);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
#ifndef MODULES_AUDIO_PROCESSING_CAPTURE_LEVELS_ADJUSTER_CAPTURE_LEVELS_ADJUSTER_H_
|
||||
#define MODULES_AUDIO_PROCESSING_CAPTURE_LEVELS_ADJUSTER_CAPTURE_LEVELS_ADJUSTER_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "modules/audio_processing/audio_buffer.h"
|
||||
#include "modules/audio_processing/capture_levels_adjuster/audio_samples_scaler.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Adjusts the level of the capture signal before and after all capture-side
|
||||
// processing is done using a combination of explicitly specified gains
|
||||
// and an emulated analog gain functionality where a specified analog level
|
||||
// results in an additional gain. The pre-adjustment is achieved by combining
|
||||
// the gain value `pre_gain` and the level `emulated_analog_mic_gain_level` to
|
||||
// form a combined gain of `pre_gain`*`emulated_analog_mic_gain_level`/255 which
|
||||
// is multiplied to each sample. The intention of the
|
||||
// `emulated_analog_mic_gain_level` is to be controlled by the analog AGC
|
||||
// functionality and to produce an emulated analog mic gain equal to
|
||||
// `emulated_analog_mic_gain_level`/255. The post level adjustment is achieved
|
||||
// by multiplying each sample with the value of `post_gain`. Any changes in the
|
||||
// gains take are done smoothly over one frame and the scaled samples are
|
||||
// clamped to fit into the allowed S16 sample range.
|
||||
class CaptureLevelsAdjuster {
|
||||
public:
|
||||
// C-tor. The values for the level and the gains must fulfill
|
||||
// 0 <= emulated_analog_mic_gain_level <= 255.
|
||||
// 0.f <= pre_gain.
|
||||
// 0.f <= post_gain.
|
||||
CaptureLevelsAdjuster(bool emulated_analog_mic_gain_enabled,
|
||||
int emulated_analog_mic_gain_level,
|
||||
float pre_gain,
|
||||
float post_gain);
|
||||
CaptureLevelsAdjuster(const CaptureLevelsAdjuster&) = delete;
|
||||
CaptureLevelsAdjuster& operator=(const CaptureLevelsAdjuster&) = delete;
|
||||
|
||||
// Adjusts the level of the signal. This should be called before any of the
|
||||
// other processing is performed.
|
||||
void ApplyPreLevelAdjustment(AudioBuffer& audio_buffer);
|
||||
|
||||
// Adjusts the level of the signal. This should be called after all of the
|
||||
// other processing have been performed.
|
||||
void ApplyPostLevelAdjustment(AudioBuffer& audio_buffer);
|
||||
|
||||
// Sets the gain to apply to each sample before any of the other processing is
|
||||
// performed.
|
||||
void SetPreGain(float pre_gain);
|
||||
|
||||
// Returns the total pre-adjustment gain applied, comprising both the pre_gain
|
||||
// as well as the gain from the emulated analog mic, to each sample before any
|
||||
// of the other processing is performed.
|
||||
float GetPreAdjustmentGain() const { return pre_adjustment_gain_; }
|
||||
|
||||
// Sets the gain to apply to each sample after all of the other processing
|
||||
// have been performed.
|
||||
void SetPostGain(float post_gain);
|
||||
|
||||
// Sets the analog gain level to use for the emulated analog gain.
|
||||
// `level` must be in the range [0...255].
|
||||
void SetAnalogMicGainLevel(int level);
|
||||
|
||||
// Returns the current analog gain level used for the emulated analog gain.
|
||||
int GetAnalogMicGainLevel() const { return emulated_analog_mic_gain_level_; }
|
||||
|
||||
private:
|
||||
// Updates the value of `pre_adjustment_gain_` based on the supplied values
|
||||
// for `pre_gain` and `emulated_analog_mic_gain_level_`.
|
||||
void UpdatePreAdjustmentGain();
|
||||
|
||||
const bool emulated_analog_mic_gain_enabled_;
|
||||
int emulated_analog_mic_gain_level_;
|
||||
float pre_gain_;
|
||||
float pre_adjustment_gain_;
|
||||
AudioSamplesScaler pre_scaler_;
|
||||
AudioSamplesScaler post_scaler_;
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_AUDIO_PROCESSING_CAPTURE_LEVELS_ADJUSTER_CAPTURE_LEVELS_ADJUSTER_H_
|
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
* Copyright (c) 2021 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/capture_levels_adjuster/capture_levels_adjuster.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <tuple>
|
||||
|
||||
#include "modules/audio_processing/test/audio_buffer_tools.h"
|
||||
#include "rtc_base/strings/string_builder.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
|
||||
float SampleValueForChannel(int channel) {
|
||||
constexpr float kSampleBaseValue = 100.f;
|
||||
constexpr float kSampleChannelOffset = 1.f;
|
||||
return kSampleBaseValue + channel * kSampleChannelOffset;
|
||||
}
|
||||
|
||||
void PopulateBuffer(AudioBuffer& audio_buffer) {
|
||||
for (size_t ch = 0; ch < audio_buffer.num_channels(); ++ch) {
|
||||
test::FillBufferChannel(SampleValueForChannel(ch), ch, audio_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
float ComputeExpectedSignalGainAfterApplyPreLevelAdjustment(
|
||||
bool emulated_analog_mic_gain_enabled,
|
||||
int emulated_analog_mic_gain_level,
|
||||
float pre_gain) {
|
||||
if (!emulated_analog_mic_gain_enabled) {
|
||||
return pre_gain;
|
||||
}
|
||||
return pre_gain * std::min(emulated_analog_mic_gain_level, 255) / 255.f;
|
||||
}
|
||||
|
||||
float ComputeExpectedSignalGainAfterApplyPostLevelAdjustment(
|
||||
bool emulated_analog_mic_gain_enabled,
|
||||
int emulated_analog_mic_gain_level,
|
||||
float pre_gain,
|
||||
float post_gain) {
|
||||
return post_gain * ComputeExpectedSignalGainAfterApplyPreLevelAdjustment(
|
||||
emulated_analog_mic_gain_enabled,
|
||||
emulated_analog_mic_gain_level, pre_gain);
|
||||
}
|
||||
|
||||
constexpr int kNumFramesToProcess = 10;
|
||||
|
||||
class CaptureLevelsAdjusterTest
|
||||
: public ::testing::Test,
|
||||
public ::testing::WithParamInterface<
|
||||
std::tuple<int, int, bool, int, float, float>> {
|
||||
protected:
|
||||
int sample_rate_hz() const { return std::get<0>(GetParam()); }
|
||||
int num_channels() const { return std::get<1>(GetParam()); }
|
||||
bool emulated_analog_mic_gain_enabled() const {
|
||||
return std::get<2>(GetParam());
|
||||
}
|
||||
int emulated_analog_mic_gain_level() const { return std::get<3>(GetParam()); }
|
||||
float pre_gain() const { return std::get<4>(GetParam()); }
|
||||
float post_gain() const { return std::get<5>(GetParam()); }
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
CaptureLevelsAdjusterTestSuite,
|
||||
CaptureLevelsAdjusterTest,
|
||||
::testing::Combine(::testing::Values(16000, 32000, 48000),
|
||||
::testing::Values(1, 2, 4),
|
||||
::testing::Values(false, true),
|
||||
::testing::Values(21, 255),
|
||||
::testing::Values(0.1f, 1.f, 4.f),
|
||||
::testing::Values(0.1f, 1.f, 4.f)));
|
||||
|
||||
TEST_P(CaptureLevelsAdjusterTest, InitialGainIsInstantlyAchieved) {
|
||||
CaptureLevelsAdjuster adjuster(emulated_analog_mic_gain_enabled(),
|
||||
emulated_analog_mic_gain_level(), pre_gain(),
|
||||
post_gain());
|
||||
|
||||
AudioBuffer audio_buffer(sample_rate_hz(), num_channels(), sample_rate_hz(),
|
||||
num_channels(), sample_rate_hz(), num_channels());
|
||||
|
||||
const float expected_signal_gain_after_pre_gain =
|
||||
ComputeExpectedSignalGainAfterApplyPreLevelAdjustment(
|
||||
emulated_analog_mic_gain_enabled(), emulated_analog_mic_gain_level(),
|
||||
pre_gain());
|
||||
const float expected_signal_gain_after_post_level_adjustment =
|
||||
ComputeExpectedSignalGainAfterApplyPostLevelAdjustment(
|
||||
emulated_analog_mic_gain_enabled(), emulated_analog_mic_gain_level(),
|
||||
pre_gain(), post_gain());
|
||||
|
||||
for (int frame = 0; frame < kNumFramesToProcess; ++frame) {
|
||||
PopulateBuffer(audio_buffer);
|
||||
adjuster.ApplyPreLevelAdjustment(audio_buffer);
|
||||
EXPECT_FLOAT_EQ(adjuster.GetPreAdjustmentGain(),
|
||||
expected_signal_gain_after_pre_gain);
|
||||
|
||||
for (int ch = 0; ch < num_channels(); ++ch) {
|
||||
for (size_t i = 0; i < audio_buffer.num_frames(); ++i) {
|
||||
EXPECT_FLOAT_EQ(
|
||||
audio_buffer.channels_const()[ch][i],
|
||||
expected_signal_gain_after_pre_gain * SampleValueForChannel(ch));
|
||||
}
|
||||
}
|
||||
adjuster.ApplyPostLevelAdjustment(audio_buffer);
|
||||
for (int ch = 0; ch < num_channels(); ++ch) {
|
||||
for (size_t i = 0; i < audio_buffer.num_frames(); ++i) {
|
||||
EXPECT_FLOAT_EQ(audio_buffer.channels_const()[ch][i],
|
||||
expected_signal_gain_after_post_level_adjustment *
|
||||
SampleValueForChannel(ch));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(CaptureLevelsAdjusterTest, NewGainsAreAchieved) {
|
||||
const int lower_emulated_analog_mic_gain_level =
|
||||
emulated_analog_mic_gain_level();
|
||||
const float lower_pre_gain = pre_gain();
|
||||
const float lower_post_gain = post_gain();
|
||||
const int higher_emulated_analog_mic_gain_level =
|
||||
std::min(lower_emulated_analog_mic_gain_level * 2, 255);
|
||||
const float higher_pre_gain = lower_pre_gain * 2.f;
|
||||
const float higher_post_gain = lower_post_gain * 2.f;
|
||||
|
||||
CaptureLevelsAdjuster adjuster(emulated_analog_mic_gain_enabled(),
|
||||
lower_emulated_analog_mic_gain_level,
|
||||
lower_pre_gain, lower_post_gain);
|
||||
|
||||
AudioBuffer audio_buffer(sample_rate_hz(), num_channels(), sample_rate_hz(),
|
||||
num_channels(), sample_rate_hz(), num_channels());
|
||||
|
||||
const float expected_signal_gain_after_pre_gain =
|
||||
ComputeExpectedSignalGainAfterApplyPreLevelAdjustment(
|
||||
emulated_analog_mic_gain_enabled(),
|
||||
higher_emulated_analog_mic_gain_level, higher_pre_gain);
|
||||
const float expected_signal_gain_after_post_level_adjustment =
|
||||
ComputeExpectedSignalGainAfterApplyPostLevelAdjustment(
|
||||
emulated_analog_mic_gain_enabled(),
|
||||
higher_emulated_analog_mic_gain_level, higher_pre_gain,
|
||||
higher_post_gain);
|
||||
|
||||
adjuster.SetPreGain(higher_pre_gain);
|
||||
adjuster.SetPostGain(higher_post_gain);
|
||||
adjuster.SetAnalogMicGainLevel(higher_emulated_analog_mic_gain_level);
|
||||
|
||||
PopulateBuffer(audio_buffer);
|
||||
adjuster.ApplyPreLevelAdjustment(audio_buffer);
|
||||
adjuster.ApplyPostLevelAdjustment(audio_buffer);
|
||||
EXPECT_EQ(adjuster.GetAnalogMicGainLevel(),
|
||||
higher_emulated_analog_mic_gain_level);
|
||||
|
||||
for (int frame = 1; frame < kNumFramesToProcess; ++frame) {
|
||||
PopulateBuffer(audio_buffer);
|
||||
adjuster.ApplyPreLevelAdjustment(audio_buffer);
|
||||
EXPECT_FLOAT_EQ(adjuster.GetPreAdjustmentGain(),
|
||||
expected_signal_gain_after_pre_gain);
|
||||
for (int ch = 0; ch < num_channels(); ++ch) {
|
||||
for (size_t i = 0; i < audio_buffer.num_frames(); ++i) {
|
||||
EXPECT_FLOAT_EQ(
|
||||
audio_buffer.channels_const()[ch][i],
|
||||
expected_signal_gain_after_pre_gain * SampleValueForChannel(ch));
|
||||
}
|
||||
}
|
||||
|
||||
adjuster.ApplyPostLevelAdjustment(audio_buffer);
|
||||
for (int ch = 0; ch < num_channels(); ++ch) {
|
||||
for (size_t i = 0; i < audio_buffer.num_frames(); ++i) {
|
||||
EXPECT_FLOAT_EQ(audio_buffer.channels_const()[ch][i],
|
||||
expected_signal_gain_after_post_level_adjustment *
|
||||
SampleValueForChannel(ch));
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_EQ(adjuster.GetAnalogMicGainLevel(),
|
||||
higher_emulated_analog_mic_gain_level);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace webrtc
|
|
@ -92,6 +92,7 @@ message RuntimeSetting {
|
|||
optional int32 playout_volume_change = 4;
|
||||
optional PlayoutAudioDeviceInfo playout_audio_device_change = 5;
|
||||
optional bool capture_output_used = 6;
|
||||
optional float capture_post_gain = 7;
|
||||
}
|
||||
|
||||
message Event {
|
||||
|
|
|
@ -106,6 +106,19 @@ bool Agc2Config::operator==(const Agc2Config& rhs) const {
|
|||
adaptive_rhs.max_output_noise_level_dbfs;
|
||||
}
|
||||
|
||||
bool AudioProcessing::Config::CaptureLevelAdjustment::operator==(
|
||||
const AudioProcessing::Config::CaptureLevelAdjustment& rhs) const {
|
||||
return enabled == rhs.enabled && pre_gain_factor == rhs.pre_gain_factor &&
|
||||
post_gain_factor && rhs.post_gain_factor &&
|
||||
analog_mic_gain_emulation == rhs.analog_mic_gain_emulation;
|
||||
}
|
||||
|
||||
bool AudioProcessing::Config::CaptureLevelAdjustment::AnalogMicGainEmulation::
|
||||
operator==(const AudioProcessing::Config::CaptureLevelAdjustment::
|
||||
AnalogMicGainEmulation& rhs) const {
|
||||
return enabled == rhs.enabled && initial_level == rhs.initial_level;
|
||||
}
|
||||
|
||||
std::string AudioProcessing::Config::ToString() const {
|
||||
char buf[2048];
|
||||
rtc::SimpleStringBuilder builder(buf);
|
||||
|
@ -118,7 +131,15 @@ std::string AudioProcessing::Config::ToString() const {
|
|||
<< ", multi_channel_capture: " << pipeline.multi_channel_capture
|
||||
<< " }, pre_amplifier: { enabled: " << pre_amplifier.enabled
|
||||
<< ", fixed_gain_factor: " << pre_amplifier.fixed_gain_factor
|
||||
<< " }, high_pass_filter: { enabled: " << high_pass_filter.enabled
|
||||
<< " },capture_level_adjustment: { enabled: "
|
||||
<< capture_level_adjustment.enabled
|
||||
<< ", pre_gain_factor: " << capture_level_adjustment.pre_gain_factor
|
||||
<< ", post_gain_factor: " << capture_level_adjustment.post_gain_factor
|
||||
<< ", analog_mic_gain_emulation: { enabled: "
|
||||
<< capture_level_adjustment.analog_mic_gain_emulation.enabled
|
||||
<< ", initial_level: "
|
||||
<< capture_level_adjustment.analog_mic_gain_emulation.initial_level
|
||||
<< " }}, high_pass_filter: { enabled: " << high_pass_filter.enabled
|
||||
<< " }, echo_canceller: { enabled: " << echo_canceller.enabled
|
||||
<< ", mobile_mode: " << echo_canceller.mobile_mode
|
||||
<< ", enforce_high_pass_filtering: "
|
||||
|
|
|
@ -206,11 +206,37 @@ class RTC_EXPORT AudioProcessing : public rtc::RefCountInterface {
|
|||
|
||||
// Enabled the pre-amplifier. It amplifies the capture signal
|
||||
// before any other processing is done.
|
||||
// TODO(webrtc:5298): Deprecate and use the pre-gain functionality in
|
||||
// capture_level_adjustment instead.
|
||||
struct PreAmplifier {
|
||||
bool enabled = false;
|
||||
float fixed_gain_factor = 1.f;
|
||||
} pre_amplifier;
|
||||
|
||||
// Functionality for general level adjustment in the capture pipeline. This
|
||||
// should not be used together with the legacy PreAmplifier functionality.
|
||||
struct CaptureLevelAdjustment {
|
||||
bool operator==(const CaptureLevelAdjustment& rhs) const;
|
||||
bool operator!=(const CaptureLevelAdjustment& rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
bool enabled = false;
|
||||
// The `pre_gain_factor` scales the signal before any processing is done.
|
||||
float pre_gain_factor = 1.f;
|
||||
// The `post_gain_factor` scales the signal after all processing is done.
|
||||
float post_gain_factor = 1.f;
|
||||
struct AnalogMicGainEmulation {
|
||||
bool operator==(const AnalogMicGainEmulation& rhs) const;
|
||||
bool operator!=(const AnalogMicGainEmulation& rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
bool enabled = false;
|
||||
// Initial analog gain level to use for the emulated analog gain. Must
|
||||
// be in the range [0...255].
|
||||
int initial_level = 255;
|
||||
} analog_mic_gain_emulation;
|
||||
} capture_level_adjustment;
|
||||
|
||||
struct HighPassFilter {
|
||||
bool enabled = false;
|
||||
bool apply_in_full_band = true;
|
||||
|
@ -381,6 +407,7 @@ class RTC_EXPORT AudioProcessing : public rtc::RefCountInterface {
|
|||
kPlayoutVolumeChange,
|
||||
kCustomRenderProcessingRuntimeSetting,
|
||||
kPlayoutAudioDeviceChange,
|
||||
kCapturePostGain,
|
||||
kCaptureOutputUsed
|
||||
};
|
||||
|
||||
|
@ -394,10 +421,13 @@ class RTC_EXPORT AudioProcessing : public rtc::RefCountInterface {
|
|||
~RuntimeSetting() = default;
|
||||
|
||||
static RuntimeSetting CreateCapturePreGain(float gain) {
|
||||
RTC_DCHECK_GE(gain, 1.f) << "Attenuation is not allowed.";
|
||||
return {Type::kCapturePreGain, gain};
|
||||
}
|
||||
|
||||
static RuntimeSetting CreateCapturePostGain(float gain) {
|
||||
return {Type::kCapturePostGain, gain};
|
||||
}
|
||||
|
||||
// Corresponds to Config::GainController1::compression_gain_db, but for
|
||||
// runtime configuration.
|
||||
static RuntimeSetting CreateCompressionGainDb(int gain_db) {
|
||||
|
|
|
@ -599,8 +599,23 @@ void AecDumpBasedSimulator::HandleMessage(
|
|||
RTC_CHECK(ap_.get());
|
||||
if (msg.has_capture_pre_gain()) {
|
||||
// Handle capture pre-gain runtime setting only if not overridden.
|
||||
if ((!settings_.use_pre_amplifier || *settings_.use_pre_amplifier) &&
|
||||
!settings_.pre_amplifier_gain_factor) {
|
||||
const bool pre_amplifier_overridden =
|
||||
(!settings_.use_pre_amplifier || *settings_.use_pre_amplifier) &&
|
||||
!settings_.pre_amplifier_gain_factor;
|
||||
const bool capture_level_adjustment_overridden =
|
||||
(!settings_.use_capture_level_adjustment ||
|
||||
*settings_.use_capture_level_adjustment) &&
|
||||
!settings_.pre_gain_factor;
|
||||
if (pre_amplifier_overridden || capture_level_adjustment_overridden) {
|
||||
ap_->SetRuntimeSetting(
|
||||
AudioProcessing::RuntimeSetting::CreateCapturePreGain(
|
||||
msg.capture_pre_gain()));
|
||||
}
|
||||
} else if (msg.has_capture_post_gain()) {
|
||||
// Handle capture post-gain runtime setting only if not overridden.
|
||||
if ((!settings_.use_capture_level_adjustment ||
|
||||
*settings_.use_capture_level_adjustment) &&
|
||||
!settings_.post_gain_factor) {
|
||||
ap_->SetRuntimeSetting(
|
||||
AudioProcessing::RuntimeSetting::CreateCapturePreGain(
|
||||
msg.capture_pre_gain()));
|
||||
|
|
|
@ -51,5 +51,18 @@ void ExtractVectorFromAudioBuffer(const StreamConfig& stream_config,
|
|||
source->CopyTo(stream_config, &output[0]);
|
||||
}
|
||||
|
||||
void FillBuffer(float value, AudioBuffer& audio_buffer) {
|
||||
for (size_t ch = 0; ch < audio_buffer.num_channels(); ++ch) {
|
||||
FillBufferChannel(value, ch, audio_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void FillBufferChannel(float value, int channel, AudioBuffer& audio_buffer) {
|
||||
RTC_CHECK_LT(channel, audio_buffer.num_channels());
|
||||
for (size_t i = 0; i < audio_buffer.num_frames(); ++i) {
|
||||
audio_buffer.channels()[channel][i] = value;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
||||
|
|
|
@ -30,6 +30,12 @@ void ExtractVectorFromAudioBuffer(const StreamConfig& stream_config,
|
|||
AudioBuffer* source,
|
||||
std::vector<float>* destination);
|
||||
|
||||
// Sets all values in `audio_buffer` to `value`.
|
||||
void FillBuffer(float value, AudioBuffer& audio_buffer);
|
||||
|
||||
// Sets all values channel `channel` for `audio_buffer` to `value`.
|
||||
void FillBufferChannel(float value, int channel, AudioBuffer& audio_buffer);
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
||||
|
||||
|
|
|
@ -499,6 +499,34 @@ void AudioProcessingSimulator::ConfigureAudioProcessor() {
|
|||
}
|
||||
}
|
||||
|
||||
if (settings_.use_analog_mic_gain_emulation) {
|
||||
if (*settings_.use_analog_mic_gain_emulation) {
|
||||
apm_config.capture_level_adjustment.enabled = true;
|
||||
apm_config.capture_level_adjustment.analog_mic_gain_emulation.enabled =
|
||||
true;
|
||||
} else {
|
||||
apm_config.capture_level_adjustment.analog_mic_gain_emulation.enabled =
|
||||
false;
|
||||
}
|
||||
}
|
||||
if (settings_.analog_mic_gain_emulation_initial_level) {
|
||||
apm_config.capture_level_adjustment.analog_mic_gain_emulation
|
||||
.initial_level = *settings_.analog_mic_gain_emulation_initial_level;
|
||||
}
|
||||
|
||||
if (settings_.use_capture_level_adjustment) {
|
||||
apm_config.capture_level_adjustment.enabled =
|
||||
*settings_.use_capture_level_adjustment;
|
||||
}
|
||||
if (settings_.pre_gain_factor) {
|
||||
apm_config.capture_level_adjustment.pre_gain_factor =
|
||||
*settings_.pre_gain_factor;
|
||||
}
|
||||
if (settings_.post_gain_factor) {
|
||||
apm_config.capture_level_adjustment.post_gain_factor =
|
||||
*settings_.post_gain_factor;
|
||||
}
|
||||
|
||||
const bool use_aec = settings_.use_aec && *settings_.use_aec;
|
||||
const bool use_aecm = settings_.use_aecm && *settings_.use_aecm;
|
||||
if (use_aec || use_aecm) {
|
||||
|
|
|
@ -99,6 +99,8 @@ struct SimulationSettings {
|
|||
absl::optional<bool> use_agc;
|
||||
absl::optional<bool> use_agc2;
|
||||
absl::optional<bool> use_pre_amplifier;
|
||||
absl::optional<bool> use_capture_level_adjustment;
|
||||
absl::optional<bool> use_analog_mic_gain_emulation;
|
||||
absl::optional<bool> use_hpf;
|
||||
absl::optional<bool> use_ns;
|
||||
absl::optional<int> use_ts;
|
||||
|
@ -116,6 +118,9 @@ struct SimulationSettings {
|
|||
AudioProcessing::Config::GainController2::LevelEstimator
|
||||
agc2_adaptive_level_estimator;
|
||||
absl::optional<float> pre_amplifier_gain_factor;
|
||||
absl::optional<float> pre_gain_factor;
|
||||
absl::optional<float> post_gain_factor;
|
||||
absl::optional<float> analog_mic_gain_emulation_initial_level;
|
||||
absl::optional<int> ns_level;
|
||||
absl::optional<bool> ns_analysis_on_linear_aec_output;
|
||||
absl::optional<int> maximum_internal_processing_rate;
|
||||
|
|
|
@ -89,7 +89,17 @@ ABSL_FLAG(int,
|
|||
ABSL_FLAG(int,
|
||||
pre_amplifier,
|
||||
kParameterNotSpecifiedValue,
|
||||
"Activate (1) or deactivate (0) the pre amplifier");
|
||||
"Activate (1) or deactivate(0) the pre amplifier");
|
||||
ABSL_FLAG(
|
||||
int,
|
||||
capture_level_adjustment,
|
||||
kParameterNotSpecifiedValue,
|
||||
"Activate (1) or deactivate(0) the capture level adjustment functionality");
|
||||
ABSL_FLAG(int,
|
||||
analog_mic_gain_emulation,
|
||||
kParameterNotSpecifiedValue,
|
||||
"Activate (1) or deactivate(0) the analog mic gain emulation in the "
|
||||
"production (non-test) code.");
|
||||
ABSL_FLAG(int,
|
||||
hpf,
|
||||
kParameterNotSpecifiedValue,
|
||||
|
@ -157,6 +167,19 @@ ABSL_FLAG(float,
|
|||
pre_amplifier_gain_factor,
|
||||
kParameterNotSpecifiedValue,
|
||||
"Pre-amplifier gain factor (linear) to apply");
|
||||
ABSL_FLAG(float,
|
||||
pre_gain_factor,
|
||||
kParameterNotSpecifiedValue,
|
||||
"Pre-gain factor (linear) to apply in the capture level adjustment");
|
||||
ABSL_FLAG(float,
|
||||
post_gain_factor,
|
||||
kParameterNotSpecifiedValue,
|
||||
"Post-gain factor (linear) to apply in the capture level adjustment");
|
||||
ABSL_FLAG(float,
|
||||
analog_mic_gain_emulation_initial_level,
|
||||
kParameterNotSpecifiedValue,
|
||||
"Emulated analog mic level to apply initially in the production "
|
||||
"(non-test) code.");
|
||||
ABSL_FLAG(int,
|
||||
ns_level,
|
||||
kParameterNotSpecifiedValue,
|
||||
|
@ -183,11 +206,16 @@ ABSL_FLAG(int,
|
|||
stream_drift_samples,
|
||||
kParameterNotSpecifiedValue,
|
||||
"Specify the number of stream drift samples to use");
|
||||
ABSL_FLAG(int, initial_mic_level, 100, "Initial mic level (0-255)");
|
||||
ABSL_FLAG(int,
|
||||
initial_mic_level,
|
||||
100,
|
||||
"Initial mic level (0-255) for the analog mic gain simulation in the "
|
||||
"test code");
|
||||
ABSL_FLAG(int,
|
||||
simulate_mic_gain,
|
||||
0,
|
||||
"Activate (1) or deactivate (0) the analog mic gain simulation");
|
||||
"Activate (1) or deactivate(0) the analog mic gain simulation in the "
|
||||
"test code");
|
||||
ABSL_FLAG(int,
|
||||
multi_channel_render,
|
||||
kParameterNotSpecifiedValue,
|
||||
|
@ -414,6 +442,10 @@ SimulationSettings CreateSettings() {
|
|||
SetSettingIfFlagSet(absl::GetFlag(FLAGS_agc2), &settings.use_agc2);
|
||||
SetSettingIfFlagSet(absl::GetFlag(FLAGS_pre_amplifier),
|
||||
&settings.use_pre_amplifier);
|
||||
SetSettingIfFlagSet(absl::GetFlag(FLAGS_capture_level_adjustment),
|
||||
&settings.use_capture_level_adjustment);
|
||||
SetSettingIfFlagSet(absl::GetFlag(FLAGS_analog_mic_gain_emulation),
|
||||
&settings.use_analog_mic_gain_emulation);
|
||||
SetSettingIfFlagSet(absl::GetFlag(FLAGS_hpf), &settings.use_hpf);
|
||||
SetSettingIfFlagSet(absl::GetFlag(FLAGS_ns), &settings.use_ns);
|
||||
SetSettingIfSpecified(absl::GetFlag(FLAGS_ts), &settings.use_ts);
|
||||
|
@ -439,6 +471,13 @@ SimulationSettings CreateSettings() {
|
|||
absl::GetFlag(FLAGS_agc2_adaptive_level_estimator));
|
||||
SetSettingIfSpecified(absl::GetFlag(FLAGS_pre_amplifier_gain_factor),
|
||||
&settings.pre_amplifier_gain_factor);
|
||||
SetSettingIfSpecified(absl::GetFlag(FLAGS_pre_gain_factor),
|
||||
&settings.pre_gain_factor);
|
||||
SetSettingIfSpecified(absl::GetFlag(FLAGS_post_gain_factor),
|
||||
&settings.post_gain_factor);
|
||||
SetSettingIfSpecified(
|
||||
absl::GetFlag(FLAGS_analog_mic_gain_emulation_initial_level),
|
||||
&settings.analog_mic_gain_emulation_initial_level);
|
||||
SetSettingIfSpecified(absl::GetFlag(FLAGS_ns_level), &settings.ns_level);
|
||||
SetSettingIfFlagSet(absl::GetFlag(FLAGS_ns_analysis_on_linear_aec_output),
|
||||
&settings.ns_analysis_on_linear_aec_output);
|
||||
|
|
Loading…
Reference in a new issue