From 5c71e74331bd57dd94be6d66d7490c1a95154e48 Mon Sep 17 00:00:00 2001 From: Alex Loiko Date: Mon, 2 Jul 2018 11:37:47 +0200 Subject: [PATCH] Add AGC1-compliant fake recording device. The AGC submodule of APM changes analog gain. These gain changes are typically ignored by the test tool audioproc_f. There is an option of the test tool to take action on the gain changes. It's the '--simulate_mic_gain' option. The option converts the analog gain to a digital gain. The digital gain is applied to the capture stream. This change adds a new simulated microphone kind. The new microphone has a gain curve defined by modules/audio_processing/agc/gain_map_internal.h. That gain curve defines how AGC1 expects a microphone to behave. Bug: webrtc:7494 Change-Id: Ifb3f54a8c6f8c001a711fa977f39f32413069780 Reviewed-on: https://webrtc-review.googlesource.com/86128 Commit-Queue: Alex Loiko Reviewed-by: Sam Zackrisson Cr-Commit-Position: refs/heads/master@{#23801} --- modules/audio_processing/BUILD.gn | 10 +++- .../test/fake_recording_device.cc | 58 +++++++++++++++---- .../test/fake_recording_device_unittest.cc | 2 +- 3 files changed, 58 insertions(+), 12 deletions(-) diff --git a/modules/audio_processing/BUILD.gn b/modules/audio_processing/BUILD.gn index 89493ec712..bbe9980cde 100644 --- a/modules/audio_processing/BUILD.gn +++ b/modules/audio_processing/BUILD.gn @@ -39,7 +39,6 @@ rtc_static_library("audio_processing") { "agc/agc.h", "agc/agc_manager_direct.cc", "agc/agc_manager_direct.h", - "agc/gain_map_internal.h", "agc/loudness_histogram.cc", "agc/loudness_histogram.h", "agc/utility.cc", @@ -110,6 +109,7 @@ rtc_static_library("audio_processing") { defines = [] deps = [ ":aec_core", + ":agc_gain_map_internal", ":apm_logging", ":audio_frame_view", ":audio_generator_interface", @@ -413,6 +413,12 @@ rtc_source_set("aec_core") { } } +rtc_source_set("agc_gain_map_internal") { + sources = [ + "agc/gain_map_internal.h", + ] +} + if (rtc_include_tests) { rtc_source_set("mocks") { testonly = true @@ -629,11 +635,13 @@ if (rtc_include_tests) { "test/fake_recording_device.h", ] deps = [ + ":agc_gain_map_internal", "../../api:array_view", "../../api/audio:audio_frame_api", "../../common_audio:common_audio", "../../rtc_base:checks", "../../rtc_base:rtc_base_approved", + "../../rtc_base:safe_minmax", "//third_party/abseil-cpp/absl/types:optional", ] } diff --git a/modules/audio_processing/test/fake_recording_device.cc b/modules/audio_processing/test/fake_recording_device.cc index 9c6ea51086..214cf91465 100644 --- a/modules/audio_processing/test/fake_recording_device.cc +++ b/modules/audio_processing/test/fake_recording_device.cc @@ -13,7 +13,10 @@ #include #include "absl/types/optional.h" +#include "modules/audio_processing/agc/gain_map_internal.h" #include "rtc_base/logging.h" +#include "rtc_base/numerics/safe_conversions.h" +#include "rtc_base/numerics/safe_minmax.h" #include "rtc_base/ptr_util.h" namespace webrtc { @@ -21,8 +24,6 @@ namespace test { namespace { -constexpr int16_t kInt16SampleMin = -32768; -constexpr int16_t kInt16SampleMax = 32767; constexpr float kFloatSampleMin = -32768.f; constexpr float kFloatSampleMax = 32767.0f; @@ -76,11 +77,7 @@ class FakeRecordingDeviceLinear final : public FakeRecordingDeviceWorker { const float divisor = (undo_mic_level_ && *undo_mic_level_ > 0) ? *undo_mic_level_ : 255.f; for (size_t i = 0; i < number_of_samples; ++i) { - data[i] = - std::max(kInt16SampleMin, - std::min(kInt16SampleMax, - static_cast(static_cast(data[i]) * - mic_level_ / divisor))); + data[i] = rtc::saturated_cast(data[i] * mic_level_ / divisor); } } void ModifyBufferFloat(ChannelBuffer* buffer) override { @@ -91,9 +88,47 @@ class FakeRecordingDeviceLinear final : public FakeRecordingDeviceWorker { for (size_t c = 0; c < buffer->num_channels(); ++c) { for (size_t i = 0; i < buffer->num_frames(); ++i) { buffer->channels()[c][i] = - std::max(kFloatSampleMin, - std::min(kFloatSampleMax, - buffer->channels()[c][i] * mic_level_ / divisor)); + rtc::SafeClamp(buffer->channels()[c][i] * mic_level_ / divisor, + kFloatSampleMin, kFloatSampleMax); + } + } + } +}; + +float ComputeAgc1LinearFactor(const absl::optional& undo_mic_level, + int mic_level) { + // If an undo level is specified, virtually restore the unmodified + // microphone level; otherwise simulate the mic gain only. + const int undo_level = + (undo_mic_level && *undo_mic_level > 0) ? *undo_mic_level : 100; + return DbToRatio(kGainMap[mic_level] - kGainMap[undo_level]); +} + +// Roughly dB-scale fake recording device. Valid levels are [0, 255]. The mic +// applies a gain from kGainMap in agc/gain_map_internal.h. +class FakeRecordingDeviceAgc1 final : public FakeRecordingDeviceWorker { + public: + explicit FakeRecordingDeviceAgc1(const int initial_mic_level) + : FakeRecordingDeviceWorker(initial_mic_level) {} + ~FakeRecordingDeviceAgc1() override = default; + void ModifyBufferInt16(AudioFrame* buffer) override { + const float scaling_factor = + ComputeAgc1LinearFactor(undo_mic_level_, mic_level_); + const size_t number_of_samples = + buffer->samples_per_channel_ * buffer->num_channels_; + int16_t* data = buffer->mutable_data(); + for (size_t i = 0; i < number_of_samples; ++i) { + data[i] = rtc::saturated_cast(data[i] * scaling_factor); + } + } + void ModifyBufferFloat(ChannelBuffer* buffer) override { + const float scaling_factor = + ComputeAgc1LinearFactor(undo_mic_level_, mic_level_); + for (size_t c = 0; c < buffer->num_channels(); ++c) { + for (size_t i = 0; i < buffer->num_frames(); ++i) { + buffer->channels()[c][i] = + rtc::SafeClamp(buffer->channels()[c][i] * scaling_factor, + kFloatSampleMin, kFloatSampleMax); } } } @@ -110,6 +145,9 @@ FakeRecordingDevice::FakeRecordingDevice(int initial_mic_level, case 1: worker_ = rtc::MakeUnique(initial_mic_level); break; + case 2: + worker_ = rtc::MakeUnique(initial_mic_level); + break; default: RTC_NOTREACHED(); break; diff --git a/modules/audio_processing/test/fake_recording_device_unittest.cc b/modules/audio_processing/test/fake_recording_device_unittest.cc index 504459a3c6..95a4f805ed 100644 --- a/modules/audio_processing/test/fake_recording_device_unittest.cc +++ b/modules/audio_processing/test/fake_recording_device_unittest.cc @@ -27,7 +27,7 @@ constexpr int kInitialMicLevel = 100; // TODO(alessiob): Add new fake recording device kind values here as they are // added in FakeRecordingDevice::FakeRecordingDevice. -const std::vector kFakeRecDeviceKinds = {0, 1}; +const std::vector kFakeRecDeviceKinds = {0, 1, 2}; const std::vector> kTestMultiChannelSamples{ std::vector{-10.f, -1.f, -0.1f, 0.f, 0.1f, 1.f, 10.f}};