446 lines
11 KiB
C++
446 lines
11 KiB
C++
/*
|
|
* Copyright (c) 2011 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 "gain_control_impl.h"
|
|
|
|
#include <cassert>
|
|
|
|
#include "critical_section_wrapper.h"
|
|
#include "gain_control.h"
|
|
|
|
#include "audio_processing_impl.h"
|
|
#include "audio_buffer.h"
|
|
|
|
namespace webrtc {
|
|
|
|
typedef void Handle;
|
|
|
|
/*template <class T>
|
|
class GainControlHandle : public ComponentHandle<T> {
|
|
public:
|
|
GainControlHandle();
|
|
virtual ~GainControlHandle();
|
|
|
|
virtual int Create();
|
|
virtual T* ptr() const;
|
|
|
|
private:
|
|
T* handle;
|
|
};*/
|
|
|
|
namespace {
|
|
WebRtc_Word16 MapSetting(GainControl::Mode mode) {
|
|
switch (mode) {
|
|
case GainControl::kAdaptiveAnalog:
|
|
return kAgcModeAdaptiveAnalog;
|
|
break;
|
|
case GainControl::kAdaptiveDigital:
|
|
return kAgcModeAdaptiveDigital;
|
|
break;
|
|
case GainControl::kFixedDigital:
|
|
return kAgcModeFixedDigital;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
return -1;
|
|
}
|
|
}
|
|
} // namespace
|
|
|
|
GainControlImpl::GainControlImpl(const AudioProcessingImpl* apm)
|
|
: ProcessingComponent(apm),
|
|
apm_(apm),
|
|
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);
|
|
|
|
WebRtc_Word16* 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++) {
|
|
int err = WebRtcAgc_AddFarend(
|
|
static_cast<Handle*>(handle(i)),
|
|
mixed_data,
|
|
static_cast<WebRtc_Word16>(audio->samples_per_split_channel()));
|
|
|
|
if (err != apm_->kNoError) {
|
|
return TranslateError(err);
|
|
}
|
|
}
|
|
|
|
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) {
|
|
for (int i = 0; i < num_handles(); i++) {
|
|
err = WebRtcAgc_AddMic(
|
|
static_cast<Handle*>(handle(i)),
|
|
audio->low_pass_split_data(i),
|
|
audio->high_pass_split_data(i),
|
|
static_cast<WebRtc_Word16>(audio->samples_per_split_channel()));
|
|
|
|
if (err != apm_->kNoError) {
|
|
return TranslateError(err);
|
|
}
|
|
}
|
|
} else if (mode_ == kAdaptiveDigital) {
|
|
|
|
for (int i = 0; i < num_handles(); i++) {
|
|
WebRtc_Word32 capture_level_out = 0;
|
|
err = WebRtcAgc_VirtualMic(
|
|
static_cast<Handle*>(handle(i)),
|
|
audio->low_pass_split_data(i),
|
|
audio->high_pass_split_data(i),
|
|
static_cast<WebRtc_Word16>(audio->samples_per_split_channel()),
|
|
//capture_levels_[i],
|
|
analog_capture_level_,
|
|
&capture_level_out);
|
|
capture_levels_[i] = capture_level_out;
|
|
|
|
if (err != apm_->kNoError) {
|
|
return TranslateError(err);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
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++) {
|
|
WebRtc_Word32 capture_level_out = 0;
|
|
WebRtc_UWord8 saturation_warning = 0;
|
|
|
|
int err = WebRtcAgc_Process(
|
|
static_cast<Handle*>(handle(i)),
|
|
audio->low_pass_split_data(i),
|
|
audio->high_pass_split_data(i),
|
|
static_cast<WebRtc_Word16>(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 TranslateError(err);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
if (mode_ == kAdaptiveAnalog) {
|
|
if (level != analog_capture_level_) {
|
|
// The analog level has been changed; update our internal levels.
|
|
capture_levels_.assign(num_handles(), level);
|
|
}
|
|
}
|
|
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(*apm_->crit());
|
|
return EnableComponent(enable);
|
|
}
|
|
|
|
bool GainControlImpl::is_enabled() const {
|
|
return is_component_enabled();
|
|
}
|
|
|
|
int GainControlImpl::set_mode(Mode mode) {
|
|
CriticalSectionScoped crit_scoped(*apm_->crit());
|
|
if (mode != kAdaptiveAnalog &&
|
|
mode != kAdaptiveDigital &&
|
|
mode != kFixedDigital) {
|
|
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(*apm_->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(*apm_->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(*apm_->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(*apm_->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;
|
|
}
|
|
|
|
analog_capture_level_ =
|
|
(maximum_capture_level_ - minimum_capture_level_) >> 1;
|
|
capture_levels_.assign(num_handles(), analog_capture_level_);
|
|
was_analog_level_set_ = false;
|
|
|
|
return apm_->kNoError;
|
|
}
|
|
|
|
int GainControlImpl::get_version(char* version, int version_len_bytes) const {
|
|
if (WebRtcAgc_Version(version, version_len_bytes) != 0) {
|
|
return apm_->kBadParameterError;
|
|
}
|
|
|
|
return apm_->kNoError;
|
|
}
|
|
|
|
void* GainControlImpl::CreateHandle() const {
|
|
Handle* handle = NULL;
|
|
if (WebRtcAgc_Create(&handle) != apm_->kNoError) {
|
|
handle = NULL;
|
|
} else {
|
|
assert(handle != NULL);
|
|
}
|
|
|
|
return handle;
|
|
}
|
|
|
|
int GainControlImpl::DestroyHandle(void* handle) const {
|
|
return WebRtcAgc_Free(static_cast<Handle*>(handle));
|
|
}
|
|
|
|
int GainControlImpl::InitializeHandle(void* handle) const {
|
|
return WebRtcAgc_Init(static_cast<Handle*>(handle),
|
|
minimum_capture_level_,
|
|
maximum_capture_level_,
|
|
MapSetting(mode_),
|
|
apm_->sample_rate_hz());
|
|
}
|
|
|
|
/*int GainControlImpl::InitializeHandles(const vector<void*>& handles)
|
|
const {
|
|
int err = apm_->kNoError;
|
|
|
|
err = WebRtcAgc_Init(static_cast<Handle*>(handles[0]),
|
|
minimum_capture_level_,
|
|
maximum_capture_level_,
|
|
mode_,
|
|
apm_->SampleRateHz());
|
|
if (err != apm_->kNoError) {
|
|
return TranslateError(err);
|
|
}
|
|
|
|
for (size_t i = 1; i < num_handles(); i++) {
|
|
WebRtcAgc_Init(static_cast<Handle*>(handles[i]),
|
|
minimum_capture_level_,
|
|
maximum_capture_level_,
|
|
kFixedDigital,
|
|
apm_->SampleRateHz());
|
|
if (err != apm_->kNoError) {
|
|
return TranslateError(err);
|
|
}
|
|
}
|
|
|
|
return apm_->kNoError;
|
|
}*/
|
|
|
|
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<WebRtc_Word16>(-target_level_dbfs_);
|
|
config.targetLevelDbfs = static_cast<WebRtc_Word16>(target_level_dbfs_);
|
|
config.compressionGaindB =
|
|
static_cast<WebRtc_Word16>(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::GetConfiguration() {
|
|
if (!initialized_) {
|
|
return apm_->kNoError;
|
|
}
|
|
|
|
WebRtcAgc_config_t config;
|
|
int err = WebRtcAgc_get_config(handle_, &config);
|
|
if (err != apm_->kNoError) {
|
|
return TranslateError(err);
|
|
}
|
|
|
|
if (config.targetLevelDbfs < 0) {
|
|
return apm_->kUnspecifiedError;
|
|
}
|
|
target_level_dbfs_ = config.targetLevelDbfs;
|
|
|
|
if (config.compressionGaindB < 0) {
|
|
return apm_->kUnspecifiedError;
|
|
}
|
|
compression_gain_db_ = config.compressionGaindB;
|
|
|
|
if (config.limiterEnable == 0) {
|
|
limiter_enabled_ = false;
|
|
} else if (config.limiterEnable == 1) {
|
|
limiter_enabled_ = true;
|
|
} else {
|
|
return apm_->kUnspecifiedError;
|
|
}
|
|
|
|
return apm_->kNoError;
|
|
}*/
|
|
|
|
// TODO(ajm): implement
|
|
int GainControlImpl::TranslateError(int /*err*/) const {
|
|
return -1;
|
|
}
|
|
} // namespace webrtc
|