summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/modules/audio_processing/gain_control_impl.cc
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/gain_control_impl.cc
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/gain_control_impl.cc')
-rw-r--r--third_party/libwebrtc/modules/audio_processing/gain_control_impl.cc370
1 files changed, 370 insertions, 0 deletions
diff --git a/third_party/libwebrtc/modules/audio_processing/gain_control_impl.cc b/third_party/libwebrtc/modules/audio_processing/gain_control_impl.cc
new file mode 100644
index 0000000000..5f2b4872b9
--- /dev/null
+++ b/third_party/libwebrtc/modules/audio_processing/gain_control_impl.cc
@@ -0,0 +1,370 @@
+/*
+ * Copyright (c) 2012 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/gain_control_impl.h"
+
+#include <cstdint>
+
+#include "absl/types/optional.h"
+#include "modules/audio_processing/agc/legacy/gain_control.h"
+#include "modules/audio_processing/audio_buffer.h"
+#include "modules/audio_processing/include/audio_processing.h"
+#include "modules/audio_processing/logging/apm_data_dumper.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "system_wrappers/include/field_trial.h"
+
+namespace webrtc {
+
+typedef void Handle;
+
+namespace {
+int16_t MapSetting(GainControl::Mode mode) {
+ switch (mode) {
+ case GainControl::kAdaptiveAnalog:
+ return kAgcModeAdaptiveAnalog;
+ case GainControl::kAdaptiveDigital:
+ return kAgcModeAdaptiveDigital;
+ case GainControl::kFixedDigital:
+ return kAgcModeFixedDigital;
+ }
+ RTC_DCHECK_NOTREACHED();
+ return -1;
+}
+
+// Applies the sub-frame `gains` to all the bands in `out` and clamps the output
+// in the signed 16 bit range.
+void ApplyDigitalGain(const int32_t gains[11],
+ size_t num_bands,
+ float* const* out) {
+ constexpr float kScaling = 1.f / 65536.f;
+ constexpr int kNumSubSections = 16;
+ constexpr float kOneByNumSubSections = 1.f / kNumSubSections;
+
+ float gains_scaled[11];
+ for (int k = 0; k < 11; ++k) {
+ gains_scaled[k] = gains[k] * kScaling;
+ }
+
+ for (size_t b = 0; b < num_bands; ++b) {
+ float* out_band = out[b];
+ for (int k = 0, sample = 0; k < 10; ++k) {
+ const float delta =
+ (gains_scaled[k + 1] - gains_scaled[k]) * kOneByNumSubSections;
+ float gain = gains_scaled[k];
+ for (int n = 0; n < kNumSubSections; ++n, ++sample) {
+ RTC_DCHECK_EQ(k * kNumSubSections + n, sample);
+ out_band[sample] *= gain;
+ out_band[sample] =
+ std::min(32767.f, std::max(-32768.f, out_band[sample]));
+ gain += delta;
+ }
+ }
+ }
+}
+
+} // namespace
+
+struct GainControlImpl::MonoAgcState {
+ MonoAgcState() {
+ state = WebRtcAgc_Create();
+ RTC_CHECK(state);
+ }
+
+ ~MonoAgcState() {
+ RTC_DCHECK(state);
+ WebRtcAgc_Free(state);
+ }
+
+ MonoAgcState(const MonoAgcState&) = delete;
+ MonoAgcState& operator=(const MonoAgcState&) = delete;
+ int32_t gains[11];
+ Handle* state;
+};
+
+int GainControlImpl::instance_counter_ = 0;
+
+GainControlImpl::GainControlImpl()
+ : data_dumper_(new ApmDataDumper(instance_counter_)),
+ mode_(kAdaptiveAnalog),
+ minimum_capture_level_(0),
+ maximum_capture_level_(255),
+ limiter_enabled_(true),
+ target_level_dbfs_(3),
+ compression_gain_db_(9),
+ analog_capture_level_(0),
+ was_analog_level_set_(false),
+ stream_is_saturated_(false) {}
+
+GainControlImpl::~GainControlImpl() = default;
+
+void GainControlImpl::ProcessRenderAudio(
+ rtc::ArrayView<const int16_t> packed_render_audio) {
+ for (size_t ch = 0; ch < mono_agcs_.size(); ++ch) {
+ WebRtcAgc_AddFarend(mono_agcs_[ch]->state, packed_render_audio.data(),
+ packed_render_audio.size());
+ }
+}
+
+void GainControlImpl::PackRenderAudioBuffer(
+ const AudioBuffer& audio,
+ std::vector<int16_t>* packed_buffer) {
+ RTC_DCHECK_GE(AudioBuffer::kMaxSplitFrameLength, audio.num_frames_per_band());
+ std::array<int16_t, AudioBuffer::kMaxSplitFrameLength>
+ mixed_16_kHz_render_data;
+ rtc::ArrayView<const int16_t> mixed_16_kHz_render(
+ mixed_16_kHz_render_data.data(), audio.num_frames_per_band());
+ if (audio.num_channels() == 1) {
+ FloatS16ToS16(audio.split_bands_const(0)[kBand0To8kHz],
+ audio.num_frames_per_band(), mixed_16_kHz_render_data.data());
+ } else {
+ const int num_channels = static_cast<int>(audio.num_channels());
+ for (size_t i = 0; i < audio.num_frames_per_band(); ++i) {
+ int32_t sum = 0;
+ for (int ch = 0; ch < num_channels; ++ch) {
+ sum += FloatS16ToS16(audio.split_channels_const(kBand0To8kHz)[ch][i]);
+ }
+ mixed_16_kHz_render_data[i] = sum / num_channels;
+ }
+ }
+
+ packed_buffer->clear();
+ packed_buffer->insert(
+ packed_buffer->end(), mixed_16_kHz_render.data(),
+ (mixed_16_kHz_render.data() + audio.num_frames_per_band()));
+}
+
+int GainControlImpl::AnalyzeCaptureAudio(const AudioBuffer& audio) {
+ RTC_DCHECK(num_proc_channels_);
+ RTC_DCHECK_GE(AudioBuffer::kMaxSplitFrameLength, audio.num_frames_per_band());
+ RTC_DCHECK_EQ(audio.num_channels(), *num_proc_channels_);
+ RTC_DCHECK_LE(*num_proc_channels_, mono_agcs_.size());
+
+ int16_t split_band_data[AudioBuffer::kMaxNumBands]
+ [AudioBuffer::kMaxSplitFrameLength];
+ int16_t* split_bands[AudioBuffer::kMaxNumBands] = {
+ split_band_data[0], split_band_data[1], split_band_data[2]};
+
+ if (mode_ == kAdaptiveAnalog) {
+ for (size_t ch = 0; ch < mono_agcs_.size(); ++ch) {
+ capture_levels_[ch] = analog_capture_level_;
+
+ audio.ExportSplitChannelData(ch, split_bands);
+
+ int err =
+ WebRtcAgc_AddMic(mono_agcs_[ch]->state, split_bands,
+ audio.num_bands(), audio.num_frames_per_band());
+
+ if (err != AudioProcessing::kNoError) {
+ return AudioProcessing::kUnspecifiedError;
+ }
+ }
+ } else if (mode_ == kAdaptiveDigital) {
+ for (size_t ch = 0; ch < mono_agcs_.size(); ++ch) {
+ int32_t capture_level_out = 0;
+
+ audio.ExportSplitChannelData(ch, split_bands);
+
+ int err =
+ WebRtcAgc_VirtualMic(mono_agcs_[ch]->state, split_bands,
+ audio.num_bands(), audio.num_frames_per_band(),
+ analog_capture_level_, &capture_level_out);
+
+ capture_levels_[ch] = capture_level_out;
+
+ if (err != AudioProcessing::kNoError) {
+ return AudioProcessing::kUnspecifiedError;
+ }
+ }
+ }
+
+ return AudioProcessing::kNoError;
+}
+
+int GainControlImpl::ProcessCaptureAudio(AudioBuffer* audio,
+ bool stream_has_echo) {
+ if (mode_ == kAdaptiveAnalog && !was_analog_level_set_) {
+ return AudioProcessing::kStreamParameterNotSetError;
+ }
+
+ RTC_DCHECK(num_proc_channels_);
+ RTC_DCHECK_GE(AudioBuffer::kMaxSplitFrameLength,
+ audio->num_frames_per_band());
+ RTC_DCHECK_EQ(audio->num_channels(), *num_proc_channels_);
+
+ stream_is_saturated_ = false;
+ bool error_reported = false;
+ for (size_t ch = 0; ch < mono_agcs_.size(); ++ch) {
+ int16_t split_band_data[AudioBuffer::kMaxNumBands]
+ [AudioBuffer::kMaxSplitFrameLength];
+ int16_t* split_bands[AudioBuffer::kMaxNumBands] = {
+ split_band_data[0], split_band_data[1], split_band_data[2]};
+ audio->ExportSplitChannelData(ch, split_bands);
+
+ // The call to stream_has_echo() is ok from a deadlock perspective
+ // as the capture lock is allready held.
+ int32_t new_capture_level = 0;
+ uint8_t saturation_warning = 0;
+ int err_analyze = WebRtcAgc_Analyze(
+ mono_agcs_[ch]->state, split_bands, audio->num_bands(),
+ audio->num_frames_per_band(), capture_levels_[ch], &new_capture_level,
+ stream_has_echo, &saturation_warning, mono_agcs_[ch]->gains);
+ capture_levels_[ch] = new_capture_level;
+
+ error_reported = error_reported || err_analyze != AudioProcessing::kNoError;
+
+ stream_is_saturated_ = stream_is_saturated_ || saturation_warning == 1;
+ }
+
+ // Choose the minimun gain for application
+ size_t index_to_apply = 0;
+ for (size_t ch = 1; ch < mono_agcs_.size(); ++ch) {
+ if (mono_agcs_[index_to_apply]->gains[10] < mono_agcs_[ch]->gains[10]) {
+ index_to_apply = ch;
+ }
+ }
+
+ for (size_t ch = 0; ch < mono_agcs_.size(); ++ch) {
+ ApplyDigitalGain(mono_agcs_[index_to_apply]->gains, audio->num_bands(),
+ audio->split_bands(ch));
+ }
+
+ RTC_DCHECK_LT(0ul, *num_proc_channels_);
+ if (mode_ == kAdaptiveAnalog) {
+ // Take the analog level to be the minimum accross all channels.
+ analog_capture_level_ = capture_levels_[0];
+ for (size_t ch = 1; ch < mono_agcs_.size(); ++ch) {
+ analog_capture_level_ =
+ std::min(analog_capture_level_, capture_levels_[ch]);
+ }
+ }
+
+ if (error_reported) {
+ return AudioProcessing::kUnspecifiedError;
+ }
+
+ was_analog_level_set_ = false;
+
+ return AudioProcessing::kNoError;
+}
+
+// TODO(ajm): ensure this is called under kAdaptiveAnalog.
+int GainControlImpl::set_stream_analog_level(int level) {
+ data_dumper_->DumpRaw("gain_control_set_stream_analog_level", 1, &level);
+
+ was_analog_level_set_ = true;
+ if (level < minimum_capture_level_ || level > maximum_capture_level_) {
+ return AudioProcessing::kBadParameterError;
+ }
+ analog_capture_level_ = level;
+
+ return AudioProcessing::kNoError;
+}
+
+int GainControlImpl::stream_analog_level() const {
+ data_dumper_->DumpRaw("gain_control_stream_analog_level", 1,
+ &analog_capture_level_);
+ return analog_capture_level_;
+}
+
+int GainControlImpl::set_mode(Mode mode) {
+ if (MapSetting(mode) == -1) {
+ return AudioProcessing::kBadParameterError;
+ }
+
+ mode_ = mode;
+ RTC_DCHECK(num_proc_channels_);
+ RTC_DCHECK(sample_rate_hz_);
+ Initialize(*num_proc_channels_, *sample_rate_hz_);
+ return AudioProcessing::kNoError;
+}
+
+int GainControlImpl::set_analog_level_limits(int minimum, int maximum) {
+ if (minimum < 0 || maximum > 65535 || maximum < minimum) {
+ return AudioProcessing::kBadParameterError;
+ }
+
+ minimum_capture_level_ = minimum;
+ maximum_capture_level_ = maximum;
+
+ RTC_DCHECK(num_proc_channels_);
+ RTC_DCHECK(sample_rate_hz_);
+ Initialize(*num_proc_channels_, *sample_rate_hz_);
+ return AudioProcessing::kNoError;
+}
+
+int GainControlImpl::set_target_level_dbfs(int level) {
+ if (level > 31 || level < 0) {
+ return AudioProcessing::kBadParameterError;
+ }
+ target_level_dbfs_ = level;
+ return Configure();
+}
+
+int GainControlImpl::set_compression_gain_db(int gain) {
+ if (gain < 0 || gain > 90) {
+ RTC_LOG(LS_ERROR) << "set_compression_gain_db(" << gain << ") failed.";
+ return AudioProcessing::kBadParameterError;
+ }
+ compression_gain_db_ = gain;
+ return Configure();
+}
+
+int GainControlImpl::enable_limiter(bool enable) {
+ limiter_enabled_ = enable;
+ return Configure();
+}
+
+void GainControlImpl::Initialize(size_t num_proc_channels, int sample_rate_hz) {
+ data_dumper_->InitiateNewSetOfRecordings();
+
+ RTC_DCHECK(sample_rate_hz == 16000 || sample_rate_hz == 32000 ||
+ sample_rate_hz == 48000);
+
+ num_proc_channels_ = num_proc_channels;
+ sample_rate_hz_ = sample_rate_hz;
+
+ mono_agcs_.resize(*num_proc_channels_);
+ capture_levels_.resize(*num_proc_channels_);
+ for (size_t ch = 0; ch < mono_agcs_.size(); ++ch) {
+ if (!mono_agcs_[ch]) {
+ mono_agcs_[ch].reset(new MonoAgcState());
+ }
+
+ int error = WebRtcAgc_Init(mono_agcs_[ch]->state, minimum_capture_level_,
+ maximum_capture_level_, MapSetting(mode_),
+ *sample_rate_hz_);
+ RTC_DCHECK_EQ(error, 0);
+ capture_levels_[ch] = analog_capture_level_;
+ }
+
+ Configure();
+}
+
+int GainControlImpl::Configure() {
+ WebRtcAgcConfig config;
+ // TODO(ajm): Flip the sign here (since AGC expects a positive value) if we
+ // change the interface.
+ // RTC_DCHECK_LE(target_level_dbfs_, 0);
+ // config.targetLevelDbfs = static_cast<int16_t>(-target_level_dbfs_);
+ config.targetLevelDbfs = static_cast<int16_t>(target_level_dbfs_);
+ config.compressionGaindB = static_cast<int16_t>(compression_gain_db_);
+ config.limiterEnable = limiter_enabled_;
+
+ int error = AudioProcessing::kNoError;
+ for (size_t ch = 0; ch < mono_agcs_.size(); ++ch) {
+ int error_ch = WebRtcAgc_set_config(mono_agcs_[ch]->state, config);
+ if (error_ch != AudioProcessing::kNoError) {
+ error = error_ch;
+ }
+ }
+ return error;
+}
+} // namespace webrtc