/* * 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 "webrtc/modules/audio_processing/gain_control_impl.h" #include #include "webrtc/modules/audio_processing/audio_buffer.h" #include "webrtc/modules/audio_processing/agc/include/gain_control.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.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; } assert(false); return -1; } } // namespace GainControlImpl::GainControlImpl(const AudioProcessing* apm, CriticalSectionWrapper* crit) : ProcessingComponent(), apm_(apm), crit_(crit), 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() {} int GainControlImpl::ProcessRenderAudio(AudioBuffer* audio) { if (!is_component_enabled()) { return apm_->kNoError; } assert(audio->samples_per_split_channel() <= 160); const int16_t* mixed_data = audio->low_pass_split_data(0); if (audio->num_channels() > 1) { audio->CopyAndMixLowPass(1); mixed_data = audio->mixed_low_pass_data(0); } for (int i = 0; i < num_handles(); i++) { Handle* my_handle = static_cast(handle(i)); int err = WebRtcAgc_AddFarend( my_handle, mixed_data, static_cast(audio->samples_per_split_channel())); if (err != apm_->kNoError) { return GetHandleError(my_handle); } } return apm_->kNoError; } int GainControlImpl::AnalyzeCaptureAudio(AudioBuffer* audio) { if (!is_component_enabled()) { return apm_->kNoError; } assert(audio->samples_per_split_channel() <= 160); assert(audio->num_channels() == num_handles()); int err = apm_->kNoError; if (mode_ == kAdaptiveAnalog) { capture_levels_.assign(num_handles(), analog_capture_level_); for (int i = 0; i < num_handles(); i++) { Handle* my_handle = static_cast(handle(i)); err = WebRtcAgc_AddMic( my_handle, audio->low_pass_split_data(i), audio->high_pass_split_data(i), static_cast(audio->samples_per_split_channel())); if (err != apm_->kNoError) { return GetHandleError(my_handle); } } } else if (mode_ == kAdaptiveDigital) { for (int i = 0; i < num_handles(); i++) { Handle* my_handle = static_cast(handle(i)); int32_t capture_level_out = 0; err = WebRtcAgc_VirtualMic( my_handle, audio->low_pass_split_data(i), audio->high_pass_split_data(i), static_cast(audio->samples_per_split_channel()), analog_capture_level_, &capture_level_out); capture_levels_[i] = capture_level_out; if (err != apm_->kNoError) { return GetHandleError(my_handle); } } } return apm_->kNoError; } int GainControlImpl::ProcessCaptureAudio(AudioBuffer* audio) { if (!is_component_enabled()) { return apm_->kNoError; } if (mode_ == kAdaptiveAnalog && !was_analog_level_set_) { return apm_->kStreamParameterNotSetError; } assert(audio->samples_per_split_channel() <= 160); assert(audio->num_channels() == num_handles()); stream_is_saturated_ = false; for (int i = 0; i < num_handles(); i++) { Handle* my_handle = static_cast(handle(i)); int32_t capture_level_out = 0; uint8_t saturation_warning = 0; int err = WebRtcAgc_Process( my_handle, audio->low_pass_split_data(i), audio->high_pass_split_data(i), static_cast(audio->samples_per_split_channel()), audio->low_pass_split_data(i), audio->high_pass_split_data(i), capture_levels_[i], &capture_level_out, apm_->echo_cancellation()->stream_has_echo(), &saturation_warning); if (err != apm_->kNoError) { return GetHandleError(my_handle); } capture_levels_[i] = capture_level_out; if (saturation_warning == 1) { stream_is_saturated_ = true; } } if (mode_ == kAdaptiveAnalog) { // Take the analog level to be the average across the handles. analog_capture_level_ = 0; for (int i = 0; i < num_handles(); i++) { analog_capture_level_ += capture_levels_[i]; } analog_capture_level_ /= num_handles(); } was_analog_level_set_ = false; return apm_->kNoError; } // TODO(ajm): ensure this is called under kAdaptiveAnalog. int GainControlImpl::set_stream_analog_level(int level) { was_analog_level_set_ = true; if (level < minimum_capture_level_ || level > maximum_capture_level_) { return apm_->kBadParameterError; } analog_capture_level_ = level; return apm_->kNoError; } int GainControlImpl::stream_analog_level() { // TODO(ajm): enable this assertion? //assert(mode_ == kAdaptiveAnalog); return analog_capture_level_; } int GainControlImpl::Enable(bool enable) { CriticalSectionScoped crit_scoped(crit_); return EnableComponent(enable); } bool GainControlImpl::is_enabled() const { return is_component_enabled(); } int GainControlImpl::set_mode(Mode mode) { CriticalSectionScoped crit_scoped(crit_); if (MapSetting(mode) == -1) { return apm_->kBadParameterError; } mode_ = mode; return Initialize(); } GainControl::Mode GainControlImpl::mode() const { return mode_; } int GainControlImpl::set_analog_level_limits(int minimum, int maximum) { CriticalSectionScoped crit_scoped(crit_); if (minimum < 0) { return apm_->kBadParameterError; } if (maximum > 65535) { return apm_->kBadParameterError; } if (maximum < minimum) { return apm_->kBadParameterError; } minimum_capture_level_ = minimum; maximum_capture_level_ = maximum; return Initialize(); } int GainControlImpl::analog_level_minimum() const { return minimum_capture_level_; } int GainControlImpl::analog_level_maximum() const { return maximum_capture_level_; } bool GainControlImpl::stream_is_saturated() const { return stream_is_saturated_; } int GainControlImpl::set_target_level_dbfs(int level) { CriticalSectionScoped crit_scoped(crit_); if (level > 31 || level < 0) { return apm_->kBadParameterError; } target_level_dbfs_ = level; return Configure(); } int GainControlImpl::target_level_dbfs() const { return target_level_dbfs_; } int GainControlImpl::set_compression_gain_db(int gain) { CriticalSectionScoped crit_scoped(crit_); if (gain < 0 || gain > 90) { return apm_->kBadParameterError; } compression_gain_db_ = gain; return Configure(); } int GainControlImpl::compression_gain_db() const { return compression_gain_db_; } int GainControlImpl::enable_limiter(bool enable) { CriticalSectionScoped crit_scoped(crit_); limiter_enabled_ = enable; return Configure(); } bool GainControlImpl::is_limiter_enabled() const { return limiter_enabled_; } int GainControlImpl::Initialize() { int err = ProcessingComponent::Initialize(); if (err != apm_->kNoError || !is_component_enabled()) { return err; } capture_levels_.assign(num_handles(), analog_capture_level_); return apm_->kNoError; } void* GainControlImpl::CreateHandle() const { Handle* handle = NULL; if (WebRtcAgc_Create(&handle) != apm_->kNoError) { handle = NULL; } else { assert(handle != NULL); } return handle; } void GainControlImpl::DestroyHandle(void* handle) const { WebRtcAgc_Free(static_cast(handle)); } int GainControlImpl::InitializeHandle(void* handle) const { return WebRtcAgc_Init(static_cast(handle), minimum_capture_level_, maximum_capture_level_, MapSetting(mode_), apm_->proc_sample_rate_hz()); } int GainControlImpl::ConfigureHandle(void* handle) const { WebRtcAgc_config_t config; // TODO(ajm): Flip the sign here (since AGC expects a positive value) if we // change the interface. //assert(target_level_dbfs_ <= 0); //config.targetLevelDbfs = static_cast(-target_level_dbfs_); config.targetLevelDbfs = static_cast(target_level_dbfs_); config.compressionGaindB = static_cast(compression_gain_db_); config.limiterEnable = limiter_enabled_; return WebRtcAgc_set_config(static_cast(handle), config); } int GainControlImpl::num_handles_required() const { return apm_->num_output_channels(); } int GainControlImpl::GetHandleError(void* handle) const { // The AGC has no get_error() function. // (Despite listing errors in its interface...) assert(handle != NULL); return apm_->kUnspecifiedError; } } // namespace webrtc