diff options
Diffstat (limited to 'webrtc/modules/audio_processing/gain_control_impl.cc')
-rw-r--r-- | webrtc/modules/audio_processing/gain_control_impl.cc | 462 |
1 files changed, 257 insertions, 205 deletions
diff --git a/webrtc/modules/audio_processing/gain_control_impl.cc b/webrtc/modules/audio_processing/gain_control_impl.cc index 3b1537e..b5454c0 100644 --- a/webrtc/modules/audio_processing/gain_control_impl.cc +++ b/webrtc/modules/audio_processing/gain_control_impl.cc @@ -8,13 +8,18 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/audio_processing/gain_control_impl.h" +#include "modules/audio_processing/gain_control_impl.h" -#include <assert.h> +#include <cstdint> -#include "webrtc/modules/audio_processing/audio_buffer.h" -#include "webrtc/modules/audio_processing/agc/legacy/gain_control.h" -#include "webrtc/system_wrappers/include/critical_section_wrapper.h" +#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 { @@ -30,314 +35,361 @@ int16_t MapSetting(GainControl::Mode mode) { case GainControl::kFixedDigital: return kAgcModeFixedDigital; } - assert(false); + RTC_NOTREACHED(); 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; +// Checks whether the legacy digital gain application should be used. +bool UseLegacyDigitalGainApplier() { + return field_trial::IsEnabled("WebRTC-UseLegacyDigitalGainApplier"); +} + +// Floating point variant of WebRtcAgc_Process. +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; } - assert(audio->num_frames_per_band() <= 160); + 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; + } + } + } +} - for (int i = 0; i < num_handles(); i++) { - Handle* my_handle = static_cast<Handle*>(handle(i)); - int err = WebRtcAgc_AddFarend( - my_handle, - audio->mixed_low_pass_data(), - audio->num_frames_per_band()); +} // namespace - if (err != apm_->kNoError) { - return GetHandleError(my_handle); - } +struct GainControlImpl::MonoAgcState { + MonoAgcState() { + state = WebRtcAgc_Create(); + RTC_CHECK(state); } - return apm_->kNoError; + ~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_)), + use_legacy_gain_applier_(UseLegacyDigitalGainApplier()), + 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()); + } } -int GainControlImpl::AnalyzeCaptureAudio(AudioBuffer* audio) { - if (!is_component_enabled()) { - return apm_->kNoError; +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; + } } - assert(audio->num_frames_per_band() <= 160); - assert(audio->num_channels() == num_handles()); + 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()); - int err = apm_->kNoError; + 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) { - capture_levels_.assign(num_handles(), analog_capture_level_); - for (int i = 0; i < num_handles(); i++) { - Handle* my_handle = static_cast<Handle*>(handle(i)); - err = WebRtcAgc_AddMic( - my_handle, - audio->split_bands(i), - audio->num_bands(), - audio->num_frames_per_band()); - - if (err != apm_->kNoError) { - return GetHandleError(my_handle); + 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 (int i = 0; i < num_handles(); i++) { - Handle* my_handle = static_cast<Handle*>(handle(i)); + for (size_t ch = 0; ch < mono_agcs_.size(); ++ch) { int32_t capture_level_out = 0; - err = WebRtcAgc_VirtualMic( - my_handle, - audio->split_bands(i), - audio->num_bands(), - audio->num_frames_per_band(), - analog_capture_level_, - &capture_level_out); + audio.ExportSplitChannelData(ch, split_bands); - capture_levels_[i] = capture_level_out; + int err = + WebRtcAgc_VirtualMic(mono_agcs_[ch]->state, split_bands, + audio.num_bands(), audio.num_frames_per_band(), + analog_capture_level_, &capture_level_out); - if (err != apm_->kNoError) { - return GetHandleError(my_handle); - } + capture_levels_[ch] = capture_level_out; + if (err != AudioProcessing::kNoError) { + return AudioProcessing::kUnspecifiedError; + } } } - return apm_->kNoError; + return AudioProcessing::kNoError; } -int GainControlImpl::ProcessCaptureAudio(AudioBuffer* audio) { - if (!is_component_enabled()) { - return apm_->kNoError; - } - +int GainControlImpl::ProcessCaptureAudio(AudioBuffer* audio, + bool stream_has_echo) { if (mode_ == kAdaptiveAnalog && !was_analog_level_set_) { - return apm_->kStreamParameterNotSetError; + return AudioProcessing::kStreamParameterNotSetError; } - assert(audio->num_frames_per_band() <= 160); - assert(audio->num_channels() == num_handles()); + 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; - for (int i = 0; i < num_handles(); i++) { - Handle* my_handle = static_cast<Handle*>(handle(i)); - int32_t capture_level_out = 0; + 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; + } - int err = WebRtcAgc_Process( - my_handle, - audio->split_bands_const(i), - audio->num_bands(), - audio->num_frames_per_band(), - audio->split_bands(i), - capture_levels_[i], - &capture_level_out, - apm_->echo_cancellation()->stream_has_echo(), - &saturation_warning); - - if (err != apm_->kNoError) { - return GetHandleError(my_handle); + // 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; } + } + + if (use_legacy_gain_applier_) { + 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); - capture_levels_[i] = capture_level_out; - if (saturation_warning == 1) { - stream_is_saturated_ = true; + int err_process = WebRtcAgc_Process( + mono_agcs_[ch]->state, mono_agcs_[index_to_apply]->gains, split_bands, + audio->num_bands(), split_bands); + RTC_DCHECK_EQ(err_process, 0); + + audio->ImportSplitChannelData(ch, split_bands); + } + } else { + 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 average across the handles. - analog_capture_level_ = 0; - for (int i = 0; i < num_handles(); i++) { - analog_capture_level_ += capture_levels_[i]; + // 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]); } + } - analog_capture_level_ /= num_handles(); + if (error_reported) { + return AudioProcessing::kUnspecifiedError; } was_analog_level_set_ = false; - return apm_->kNoError; + + return AudioProcessing::kNoError; } + // TODO(ajm): ensure this is called under kAdaptiveAnalog. int GainControlImpl::set_stream_analog_level(int level) { - CriticalSectionScoped crit_scoped(crit_); + 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 apm_->kBadParameterError; + return AudioProcessing::kBadParameterError; } analog_capture_level_ = level; - return apm_->kNoError; + return AudioProcessing::kNoError; } -int GainControlImpl::stream_analog_level() { - // TODO(ajm): enable this assertion? - //assert(mode_ == kAdaptiveAnalog); - +int GainControlImpl::stream_analog_level() const { + data_dumper_->DumpRaw("gain_control_stream_analog_level", 1, + &analog_capture_level_); 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; + return AudioProcessing::kBadParameterError; } mode_ = mode; - return Initialize(); + RTC_DCHECK(num_proc_channels_); + RTC_DCHECK(sample_rate_hz_); + Initialize(*num_proc_channels_, *sample_rate_hz_); + return AudioProcessing::kNoError; } -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; +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; - return Initialize(); -} - -int GainControlImpl::analog_level_minimum() const { - return minimum_capture_level_; -} - -int GainControlImpl::analog_level_maximum() const { - return maximum_capture_level_; + RTC_DCHECK(num_proc_channels_); + RTC_DCHECK(sample_rate_hz_); + Initialize(*num_proc_channels_, *sample_rate_hz_); + return AudioProcessing::kNoError; } -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; + return AudioProcessing::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; + RTC_LOG(LS_ERROR) << "set_compression_gain_db(" << gain << ") failed."; + return AudioProcessing::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_; -} +void GainControlImpl::Initialize(size_t num_proc_channels, int sample_rate_hz) { + data_dumper_->InitiateNewSetOfRecordings(); -int GainControlImpl::Initialize() { - int err = ProcessingComponent::Initialize(); - if (err != apm_->kNoError || !is_component_enabled()) { - return err; - } + RTC_DCHECK(sample_rate_hz == 16000 || sample_rate_hz == 32000 || + sample_rate_hz == 48000); - capture_levels_.assign(num_handles(), analog_capture_level_); - return apm_->kNoError; -} + num_proc_channels_ = num_proc_channels; + sample_rate_hz_ = sample_rate_hz; -void* GainControlImpl::CreateHandle() const { - return WebRtcAgc_Create(); -} + 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()); + } -void GainControlImpl::DestroyHandle(void* handle) const { - WebRtcAgc_Free(static_cast<Handle*>(handle)); -} + 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_; + } -int GainControlImpl::InitializeHandle(void* handle) const { - return WebRtcAgc_Init(static_cast<Handle*>(handle), - minimum_capture_level_, - maximum_capture_level_, - MapSetting(mode_), - apm_->proc_sample_rate_hz()); + Configure(); } -int GainControlImpl::ConfigureHandle(void* handle) const { +int GainControlImpl::Configure() { WebRtcAgcConfig 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<int16_t>(-target_level_dbfs_); + // 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.compressionGaindB = static_cast<int16_t>(compression_gain_db_); config.limiterEnable = limiter_enabled_; - return WebRtcAgc_set_config(static_cast<Handle*>(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; + 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 |