diff --git a/webrtc/modules/audio_processing/agc/legacy/analog_agc.c b/webrtc/modules/audio_processing/agc/legacy/analog_agc.c index be644d9701..3a1dc9d5ce 100644 --- a/webrtc/modules/audio_processing/agc/legacy/analog_agc.c +++ b/webrtc/modules/audio_processing/agc/legacy/analog_agc.c @@ -250,34 +250,35 @@ int WebRtcAgc_AddMic(void *state, int16_t* const* in_mic, size_t num_bands, return 0; } -int WebRtcAgc_AddFarend(void *state, const int16_t *in_far, size_t samples) -{ +int WebRtcAgc_AddFarend(void *state, const int16_t *in_far, size_t samples) { + LegacyAgc* stt = (LegacyAgc*)state; + + int err = WebRtcAgc_GetAddFarendError(state, samples); + + if (err != 0) + return err; + + return WebRtcAgc_AddFarendToDigital(&stt->digitalAgc, in_far, samples); +} + +int WebRtcAgc_GetAddFarendError(void *state, size_t samples) { LegacyAgc* stt; stt = (LegacyAgc*)state; - if (stt == NULL) - { - return -1; - } + if (stt == NULL) + return -1; - if (stt->fs == 8000) - { - if (samples != 80) - { - return -1; - } - } else if (stt->fs == 16000 || stt->fs == 32000 || stt->fs == 48000) - { - if (samples != 160) - { - return -1; - } - } else - { - return -1; - } + if (stt->fs == 8000) { + if (samples != 80) + return -1; + } else if (stt->fs == 16000 || stt->fs == 32000 || stt->fs == 48000) { + if (samples != 160) + return -1; + } else { + return -1; + } - return WebRtcAgc_AddFarendToDigital(&stt->digitalAgc, in_far, samples); + return 0; } int WebRtcAgc_VirtualMic(void *agcInst, int16_t* const* in_near, diff --git a/webrtc/modules/audio_processing/agc/legacy/gain_control.h b/webrtc/modules/audio_processing/agc/legacy/gain_control.h index 08c1988f01..db942fe5ec 100644 --- a/webrtc/modules/audio_processing/agc/legacy/gain_control.h +++ b/webrtc/modules/audio_processing/agc/legacy/gain_control.h @@ -49,6 +49,20 @@ extern "C" { #endif +/* + * This function analyses the number of samples passed to + * farend and produces any error code that could arise. + * + * Input: + * - agcInst : AGC instance. + * - samples : Number of samples in input vector. + * + * Return value: + * : 0 - Normal operation. + * : -1 - Error. + */ +int WebRtcAgc_GetAddFarendError(void* state, size_t samples); + /* * This function processes a 10 ms frame of far-end speech to determine * if there is active speech. The length of the input speech vector must be diff --git a/webrtc/modules/audio_processing/audio_processing_impl.cc b/webrtc/modules/audio_processing/audio_processing_impl.cc index 0daaf1f449..3105224656 100644 --- a/webrtc/modules/audio_processing/audio_processing_impl.cc +++ b/webrtc/modules/audio_processing/audio_processing_impl.cc @@ -532,6 +532,7 @@ int AudioProcessingImpl::ProcessStream(const float* const* src, echo_cancellation_->ReadQueuedRenderData(); echo_control_mobile_->ReadQueuedRenderData(); + gain_control_->ReadQueuedRenderData(); ProcessingConfig processing_config = api_format_; processing_config.input_stream() = input_config; @@ -576,6 +577,7 @@ int AudioProcessingImpl::ProcessStream(AudioFrame* frame) { CriticalSectionScoped crit_scoped(crit_); echo_cancellation_->ReadQueuedRenderData(); echo_control_mobile_->ReadQueuedRenderData(); + gain_control_->ReadQueuedRenderData(); if (!frame) { return kNullPointerError; diff --git a/webrtc/modules/audio_processing/gain_control_impl.cc b/webrtc/modules/audio_processing/gain_control_impl.cc index 595596b559..4d84b2416a 100644 --- a/webrtc/modules/audio_processing/gain_control_impl.cc +++ b/webrtc/modules/audio_processing/gain_control_impl.cc @@ -35,20 +35,26 @@ int16_t MapSetting(GainControl::Mode mode) { } } // namespace +const size_t GainControlImpl::kAllowedValuesOfSamplesPerFrame1; +const size_t GainControlImpl::kAllowedValuesOfSamplesPerFrame2; + 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) {} + : 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), + render_queue_element_max_size_(0) { + AllocateRenderQueue(); +} GainControlImpl::~GainControlImpl() {} @@ -59,21 +65,53 @@ int GainControlImpl::ProcessRenderAudio(AudioBuffer* audio) { assert(audio->num_frames_per_band() <= 160); + render_queue_buffer_.resize(0); for (int i = 0; i < num_handles(); i++) { Handle* my_handle = static_cast(handle(i)); - int err = WebRtcAgc_AddFarend( - my_handle, - audio->mixed_low_pass_data(), - audio->num_frames_per_band()); + int err = + WebRtcAgc_GetAddFarendError(my_handle, audio->num_frames_per_band()); - if (err != apm_->kNoError) { + if (err != apm_->kNoError) return GetHandleError(my_handle); - } + + // Buffer the samples in the render queue. + render_queue_buffer_.insert( + render_queue_buffer_.end(), audio->mixed_low_pass_data(), + (audio->mixed_low_pass_data() + audio->num_frames_per_band())); + } + + // Insert the samples into the queue. + if (!render_signal_queue_->Insert(&render_queue_buffer_)) { + ReadQueuedRenderData(); + + // Retry the insert (should always work). + RTC_DCHECK_EQ(render_signal_queue_->Insert(&render_queue_buffer_), true); } return apm_->kNoError; } +// Read chunks of data that were received and queued on the render side from +// a queue. All the data chunks are buffered into the farend signal of the AGC. +void GainControlImpl::ReadQueuedRenderData() { + if (!is_component_enabled()) { + return; + } + + while (render_signal_queue_->Remove(&capture_queue_buffer_)) { + int buffer_index = 0; + const int num_frames_per_band = + capture_queue_buffer_.size() / num_handles(); + for (int i = 0; i < num_handles(); i++) { + Handle* my_handle = static_cast(handle(i)); + WebRtcAgc_AddFarend(my_handle, &capture_queue_buffer_[buffer_index], + num_frames_per_band); + + buffer_index += num_frames_per_band; + } + } +} + int GainControlImpl::AnalyzeCaptureAudio(AudioBuffer* audio) { if (!is_component_enabled()) { return apm_->kNoError; @@ -179,6 +217,12 @@ int GainControlImpl::ProcessCaptureAudio(AudioBuffer* audio) { // TODO(ajm): ensure this is called under kAdaptiveAnalog. int GainControlImpl::set_stream_analog_level(int level) { + // TODO(peah): Verify that this is really needed to do the reading + // here as well as in ProcessStream. It works since these functions + // are called from the same thread, but it is not nice to do it in two + // places if not needed. + ReadQueuedRenderData(); + CriticalSectionScoped crit_scoped(crit_); was_analog_level_set_ = true; if (level < minimum_capture_level_ || level > maximum_capture_level_) { @@ -296,12 +340,36 @@ int GainControlImpl::Initialize() { return err; } + AllocateRenderQueue(); + const int n = num_handles(); RTC_CHECK_GE(n, 0) << "Bad number of handles: " << n; capture_levels_.assign(n, analog_capture_level_); return apm_->kNoError; } +void GainControlImpl::AllocateRenderQueue() { + const size_t max_frame_size = std::max( + kAllowedValuesOfSamplesPerFrame1, kAllowedValuesOfSamplesPerFrame2); + + const size_t new_render_queue_element_max_size = std::max( + static_cast(1), (max_frame_size * num_handles())); + + if (new_render_queue_element_max_size > render_queue_element_max_size_) { + std::vector template_queue_element(render_queue_element_max_size_); + + render_signal_queue_.reset( + new SwapQueue, RenderQueueItemVerifier>( + kMaxNumFramesToBuffer, template_queue_element, + RenderQueueItemVerifier(render_queue_element_max_size_))); + } else { + render_signal_queue_->Clear(); + } + + render_queue_buffer_.resize(new_render_queue_element_max_size); + capture_queue_buffer_.resize(new_render_queue_element_max_size); +} + void* GainControlImpl::CreateHandle() const { return WebRtcAgc_Create(); } diff --git a/webrtc/modules/audio_processing/gain_control_impl.h b/webrtc/modules/audio_processing/gain_control_impl.h index f24d200cf2..b766ca3714 100644 --- a/webrtc/modules/audio_processing/gain_control_impl.h +++ b/webrtc/modules/audio_processing/gain_control_impl.h @@ -13,6 +13,8 @@ #include +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/common_audio/swap_queue.h" #include "webrtc/modules/audio_processing/include/audio_processing.h" #include "webrtc/modules/audio_processing/processing_component.h" @@ -41,7 +43,16 @@ class GainControlImpl : public GainControl, bool is_limiter_enabled() const override; Mode mode() const override; + // Reads render side data that has been queued on the render call. + void ReadQueuedRenderData(); + private: + static const size_t kAllowedValuesOfSamplesPerFrame1 = 80; + static const size_t kAllowedValuesOfSamplesPerFrame2 = 160; + // TODO(peah): Decrease this once we properly handle hugely unbalanced + // reverse and forward call numbers. + static const size_t kMaxNumFramesToBuffer = 100; + // GainControl implementation. int Enable(bool enable) override; int set_stream_analog_level(int level) override; @@ -64,6 +75,8 @@ class GainControlImpl : public GainControl, int num_handles_required() const override; int GetHandleError(void* handle) const override; + void AllocateRenderQueue(); + const AudioProcessing* apm_; CriticalSectionWrapper* crit_; Mode mode_; @@ -76,6 +89,13 @@ class GainControlImpl : public GainControl, int analog_capture_level_; bool was_analog_level_set_; bool stream_is_saturated_; + + size_t render_queue_element_max_size_; + std::vector render_queue_buffer_; + std::vector capture_queue_buffer_; + rtc::scoped_ptr< + SwapQueue, RenderQueueItemVerifier>> + render_signal_queue_; }; } // namespace webrtc