summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster')
-rw-r--r--third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/BUILD.gn45
-rw-r--r--third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/audio_samples_scaler.cc92
-rw-r--r--third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/audio_samples_scaler.h46
-rw-r--r--third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/audio_samples_scaler_unittest.cc204
-rw-r--r--third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.cc96
-rw-r--r--third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.h88
-rw-r--r--third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster_gn/moz.build237
-rw-r--r--third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster_unittest.cc187
8 files changed, 995 insertions, 0 deletions
diff --git a/third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/BUILD.gn b/third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/BUILD.gn
new file mode 100644
index 0000000000..e7ff8482f6
--- /dev/null
+++ b/third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/BUILD.gn
@@ -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",
+ ]
+}
diff --git a/third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/audio_samples_scaler.cc b/third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/audio_samples_scaler.cc
new file mode 100644
index 0000000000..cb2336b87d
--- /dev/null
+++ b/third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/audio_samples_scaler.cc
@@ -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
diff --git a/third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/audio_samples_scaler.h b/third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/audio_samples_scaler.h
new file mode 100644
index 0000000000..2ae8533940
--- /dev/null
+++ b/third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/audio_samples_scaler.h
@@ -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_
diff --git a/third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/audio_samples_scaler_unittest.cc b/third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/audio_samples_scaler_unittest.cc
new file mode 100644
index 0000000000..6e5fc2cbe3
--- /dev/null
+++ b/third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/audio_samples_scaler_unittest.cc
@@ -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
diff --git a/third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.cc b/third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.cc
new file mode 100644
index 0000000000..dfda582915
--- /dev/null
+++ b/third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.cc
@@ -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
diff --git a/third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.h b/third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.h
new file mode 100644
index 0000000000..38b68ad06c
--- /dev/null
+++ b/third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.h
@@ -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_
diff --git a/third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster_gn/moz.build b/third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster_gn/moz.build
new file mode 100644
index 0000000000..d80a3bb1c6
--- /dev/null
+++ b/third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster_gn/moz.build
@@ -0,0 +1,237 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+ ### This moz.build was AUTOMATICALLY GENERATED from a GN config, ###
+ ### DO NOT edit it by hand. ###
+
+COMPILE_FLAGS["OS_INCLUDES"] = []
+AllowCompilerWarnings()
+
+DEFINES["ABSL_ALLOCATOR_NOTHROW"] = "1"
+DEFINES["RTC_DAV1D_IN_INTERNAL_DECODER_FACTORY"] = True
+DEFINES["RTC_ENABLE_VP9"] = True
+DEFINES["WEBRTC_ENABLE_PROTOBUF"] = "0"
+DEFINES["WEBRTC_LIBRARY_IMPL"] = True
+DEFINES["WEBRTC_MOZILLA_BUILD"] = True
+DEFINES["WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS"] = "0"
+DEFINES["WEBRTC_STRICT_FIELD_TRIALS"] = "0"
+
+FINAL_LIBRARY = "webrtc"
+
+
+LOCAL_INCLUDES += [
+ "!/ipc/ipdl/_ipdlheaders",
+ "!/third_party/libwebrtc/gen",
+ "/ipc/chromium/src",
+ "/third_party/libwebrtc/",
+ "/third_party/libwebrtc/third_party/abseil-cpp/",
+ "/tools/profiler/public"
+]
+
+UNIFIED_SOURCES += [
+ "/third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/audio_samples_scaler.cc",
+ "/third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.cc"
+]
+
+if not CONFIG["MOZ_DEBUG"]:
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "0"
+ DEFINES["NDEBUG"] = True
+ DEFINES["NVALGRIND"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1":
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "1"
+
+if CONFIG["OS_TARGET"] == "Android":
+
+ DEFINES["ANDROID"] = True
+ DEFINES["ANDROID_NDK_VERSION_ROLL"] = "r22_1"
+ DEFINES["HAVE_SYS_UIO_H"] = True
+ DEFINES["WEBRTC_ANDROID"] = True
+ DEFINES["WEBRTC_ANDROID_OPENSLES"] = True
+ DEFINES["WEBRTC_ENABLE_LIBEVENT"] = True
+ DEFINES["WEBRTC_LINUX"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_GNU_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+ OS_LIBS += [
+ "log"
+ ]
+
+if CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["WEBRTC_MAC"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_LIBCPP_HAS_NO_ALIGNED_ALLOCATION"] = True
+ DEFINES["__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES"] = "0"
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_NSS_CERTS"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_UDEV"] = True
+ DEFINES["WEBRTC_ENABLE_LIBEVENT"] = True
+ DEFINES["WEBRTC_LINUX"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+ OS_LIBS += [
+ "rt"
+ ]
+
+if CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_X11"] = "1"
+ DEFINES["WEBRTC_BSD"] = True
+ DEFINES["WEBRTC_ENABLE_LIBEVENT"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["CERT_CHAIN_PARA_HAS_EXTRA_FIELDS"] = True
+ DEFINES["NOMINMAX"] = True
+ DEFINES["NTDDI_VERSION"] = "0x0A000000"
+ DEFINES["PSAPI_VERSION"] = "2"
+ DEFINES["RTC_ENABLE_WIN_WGC"] = True
+ DEFINES["UNICODE"] = True
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["WEBRTC_WIN"] = True
+ DEFINES["WIN32"] = True
+ DEFINES["WIN32_LEAN_AND_MEAN"] = True
+ DEFINES["WINAPI_FAMILY"] = "WINAPI_FAMILY_DESKTOP_APP"
+ DEFINES["WINVER"] = "0x0A00"
+ DEFINES["_ATL_NO_OPENGL"] = True
+ DEFINES["_CRT_RAND_S"] = True
+ DEFINES["_CRT_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_ENABLE_EXTENDED_ALIGNED_STORAGE"] = True
+ DEFINES["_HAS_EXCEPTIONS"] = "0"
+ DEFINES["_HAS_NODISCARD"] = True
+ DEFINES["_SCL_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_SECURE_ATL"] = True
+ DEFINES["_UNICODE"] = True
+ DEFINES["_WIN32_WINNT"] = "0x0A00"
+ DEFINES["_WINDOWS"] = True
+ DEFINES["__STD_C"] = True
+
+ OS_LIBS += [
+ "crypt32",
+ "iphlpapi",
+ "secur32",
+ "winmm"
+ ]
+
+if CONFIG["TARGET_CPU"] == "aarch64":
+
+ DEFINES["WEBRTC_ARCH_ARM64"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+
+if CONFIG["TARGET_CPU"] == "arm":
+
+ CXXFLAGS += [
+ "-mfpu=neon"
+ ]
+
+ DEFINES["WEBRTC_ARCH_ARM"] = True
+ DEFINES["WEBRTC_ARCH_ARM_V7"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+
+if CONFIG["TARGET_CPU"] == "mips32":
+
+ DEFINES["MIPS32_LE"] = True
+ DEFINES["MIPS_FPU_LE"] = True
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["TARGET_CPU"] == "mips64":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["TARGET_CPU"] == "x86":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+
+if CONFIG["TARGET_CPU"] == "x86_64":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Android":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["_HAS_ITERATOR_DEBUGGING"] = "0"
+
+if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_X11"] = "1"
+
+if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm":
+
+ OS_LIBS += [
+ "android_support",
+ "unwind"
+ ]
+
+if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86":
+
+ CXXFLAGS += [
+ "-msse2"
+ ]
+
+ OS_LIBS += [
+ "android_support"
+ ]
+
+if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "arm":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "x86":
+
+ CXXFLAGS += [
+ "-msse2"
+ ]
+
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "x86_64":
+
+ DEFINES["_GNU_SOURCE"] = True
+
+Library("capture_levels_adjuster_gn")
diff --git a/third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster_unittest.cc b/third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster_unittest.cc
new file mode 100644
index 0000000000..1183441a14
--- /dev/null
+++ b/third_party/libwebrtc/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster_unittest.cc
@@ -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