Changed the digital AGC1 gain to properly support multichannel
Beyond making the digital AGC1 code properly support multichannel, this CL also -Removes deprecated debug logging code. -Converts the gain application to be fully in floating point which --Is less computationally complex. --Does not quantize the samples to 16 bit before applying the gains. Bug: webrtc:10859 Change-Id: I6020ba8ae7e311dfc93a72783a2bb68d935f90c5 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/159861 Commit-Queue: Per Åhgren <peah@webrtc.org> Reviewed-by: Gustaf Ullberg <gustaf@webrtc.org> Cr-Commit-Position: refs/heads/master@{#29886}
This commit is contained in:
@ -18,8 +18,8 @@
|
||||
#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/constructor_magic.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "system_wrappers/include/field_trial.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
@ -39,59 +39,65 @@ int16_t MapSetting(GainControl::Mode mode) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
class GainControlImpl::GainController {
|
||||
public:
|
||||
explicit GainController() {
|
||||
state_ = WebRtcAgc_Create();
|
||||
RTC_CHECK(state_);
|
||||
struct GainControlImpl::MonoAgcState {
|
||||
MonoAgcState() {
|
||||
state = WebRtcAgc_Create();
|
||||
RTC_CHECK(state);
|
||||
}
|
||||
|
||||
~GainController() {
|
||||
RTC_DCHECK(state_);
|
||||
WebRtcAgc_Free(state_);
|
||||
~MonoAgcState() {
|
||||
RTC_DCHECK(state);
|
||||
WebRtcAgc_Free(state);
|
||||
}
|
||||
|
||||
Handle* state() {
|
||||
RTC_DCHECK(state_);
|
||||
return state_;
|
||||
}
|
||||
|
||||
void Initialize(int minimum_capture_level,
|
||||
int maximum_capture_level,
|
||||
Mode mode,
|
||||
int sample_rate_hz,
|
||||
int capture_level) {
|
||||
RTC_DCHECK(state_);
|
||||
int error =
|
||||
WebRtcAgc_Init(state_, minimum_capture_level, maximum_capture_level,
|
||||
MapSetting(mode), sample_rate_hz);
|
||||
RTC_DCHECK_EQ(0, error);
|
||||
|
||||
set_capture_level(capture_level);
|
||||
}
|
||||
|
||||
void set_capture_level(int capture_level) { capture_level_ = capture_level; }
|
||||
|
||||
int get_capture_level() {
|
||||
RTC_DCHECK(capture_level_);
|
||||
return *capture_level_;
|
||||
}
|
||||
|
||||
private:
|
||||
Handle* state_;
|
||||
// TODO(peah): Remove the optional once the initialization is moved into the
|
||||
// ctor.
|
||||
absl::optional<int> capture_level_;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(GainController);
|
||||
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),
|
||||
@ -102,7 +108,7 @@ GainControlImpl::GainControlImpl()
|
||||
was_analog_level_set_(false),
|
||||
stream_is_saturated_(false) {}
|
||||
|
||||
GainControlImpl::~GainControlImpl() {}
|
||||
GainControlImpl::~GainControlImpl() = default;
|
||||
|
||||
void GainControlImpl::ProcessRenderAudio(
|
||||
rtc::ArrayView<const int16_t> packed_render_audio) {
|
||||
@ -110,8 +116,8 @@ void GainControlImpl::ProcessRenderAudio(
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& gain_controller : gain_controllers_) {
|
||||
WebRtcAgc_AddFarend(gain_controller->state(), packed_render_audio.data(),
|
||||
for (size_t ch = 0; ch < mono_agcs_.size(); ++ch) {
|
||||
WebRtcAgc_AddFarend(mono_agcs_[ch]->state, packed_render_audio.data(),
|
||||
packed_render_audio.size());
|
||||
}
|
||||
}
|
||||
@ -120,27 +126,28 @@ 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_low_pass_data;
|
||||
rtc::ArrayView<const int16_t> mixed_low_pass(mixed_low_pass_data.data(),
|
||||
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_low_pass_data.data());
|
||||
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 value =
|
||||
FloatS16ToS16(audio.split_channels_const(kBand0To8kHz)[0][i]);
|
||||
for (int j = 1; j < num_channels; ++j) {
|
||||
value += FloatS16ToS16(audio.split_channels_const(kBand0To8kHz)[j][i]);
|
||||
int32_t sum = 0;
|
||||
for (int ch = 0; ch < num_channels; ++ch) {
|
||||
sum += FloatS16ToS16(audio.split_channels_const(kBand0To8kHz)[ch][i]);
|
||||
}
|
||||
mixed_low_pass_data[i] = value / num_channels;
|
||||
mixed_16_kHz_render_data[i] = sum / num_channels;
|
||||
}
|
||||
}
|
||||
|
||||
packed_buffer->clear();
|
||||
packed_buffer->insert(packed_buffer->end(), mixed_low_pass.data(),
|
||||
(mixed_low_pass.data() + audio.num_frames_per_band()));
|
||||
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) {
|
||||
@ -151,7 +158,7 @@ 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_, gain_controllers_.size());
|
||||
RTC_DCHECK_LE(*num_proc_channels_, mono_agcs_.size());
|
||||
|
||||
int16_t split_band_data[AudioBuffer::kMaxNumBands]
|
||||
[AudioBuffer::kMaxSplitFrameLength];
|
||||
@ -159,39 +166,35 @@ int GainControlImpl::AnalyzeCaptureAudio(const AudioBuffer& audio) {
|
||||
split_band_data[0], split_band_data[1], split_band_data[2]};
|
||||
|
||||
if (mode_ == kAdaptiveAnalog) {
|
||||
int capture_channel = 0;
|
||||
for (auto& gain_controller : gain_controllers_) {
|
||||
gain_controller->set_capture_level(analog_capture_level_);
|
||||
for (size_t ch = 0; ch < mono_agcs_.size(); ++ch) {
|
||||
capture_levels_[ch] = analog_capture_level_;
|
||||
|
||||
audio.ExportSplitChannelData(capture_channel, split_bands);
|
||||
audio.ExportSplitChannelData(ch, split_bands);
|
||||
|
||||
int err =
|
||||
WebRtcAgc_AddMic(gain_controller->state(), split_bands,
|
||||
WebRtcAgc_AddMic(mono_agcs_[ch]->state, split_bands,
|
||||
audio.num_bands(), audio.num_frames_per_band());
|
||||
|
||||
if (err != AudioProcessing::kNoError) {
|
||||
return AudioProcessing::kUnspecifiedError;
|
||||
}
|
||||
++capture_channel;
|
||||
}
|
||||
} else if (mode_ == kAdaptiveDigital) {
|
||||
int capture_channel = 0;
|
||||
for (auto& gain_controller : gain_controllers_) {
|
||||
for (size_t ch = 0; ch < mono_agcs_.size(); ++ch) {
|
||||
int32_t capture_level_out = 0;
|
||||
|
||||
audio.ExportSplitChannelData(capture_channel, split_bands);
|
||||
audio.ExportSplitChannelData(ch, split_bands);
|
||||
|
||||
int err =
|
||||
WebRtcAgc_VirtualMic(gain_controller->state(), split_bands,
|
||||
WebRtcAgc_VirtualMic(mono_agcs_[ch]->state, split_bands,
|
||||
audio.num_bands(), audio.num_frames_per_band(),
|
||||
analog_capture_level_, &capture_level_out);
|
||||
|
||||
gain_controller->set_capture_level(capture_level_out);
|
||||
capture_levels_[ch] = capture_level_out;
|
||||
|
||||
if (err != AudioProcessing::kNoError) {
|
||||
return AudioProcessing::kUnspecifiedError;
|
||||
}
|
||||
++capture_channel;
|
||||
}
|
||||
}
|
||||
|
||||
@ -214,57 +217,78 @@ int GainControlImpl::ProcessCaptureAudio(AudioBuffer* audio,
|
||||
RTC_DCHECK_EQ(audio->num_channels(), *num_proc_channels_);
|
||||
|
||||
stream_is_saturated_ = false;
|
||||
int capture_channel = 0;
|
||||
for (auto& gain_controller : gain_controllers_) {
|
||||
int32_t capture_level_out = 0;
|
||||
uint8_t saturation_warning = 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(capture_channel, split_bands);
|
||||
audio->ExportSplitChannelData(ch, split_bands);
|
||||
|
||||
// The call to stream_has_echo() is ok from a deadlock perspective
|
||||
// as the capture lock is allready held.
|
||||
int err = WebRtcAgc_Process(
|
||||
gain_controller->state(), split_bands, audio->num_bands(),
|
||||
audio->num_frames_per_band(), split_bands,
|
||||
gain_controller->get_capture_level(), &capture_level_out,
|
||||
stream_has_echo, &saturation_warning);
|
||||
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;
|
||||
|
||||
audio->ImportSplitChannelData(capture_channel, split_bands);
|
||||
error_reported = error_reported || err_analyze != AudioProcessing::kNoError;
|
||||
|
||||
if (err != AudioProcessing::kNoError) {
|
||||
return AudioProcessing::kUnspecifiedError;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
gain_controller->set_capture_level(capture_level_out);
|
||||
if (saturation_warning == 1) {
|
||||
stream_is_saturated_ = true;
|
||||
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);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
++capture_channel;
|
||||
}
|
||||
|
||||
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 (auto& gain_controller : gain_controllers_) {
|
||||
analog_capture_level_ += gain_controller->get_capture_level();
|
||||
// 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_proc_channels_);
|
||||
if (error_reported) {
|
||||
return AudioProcessing::kUnspecifiedError;
|
||||
}
|
||||
|
||||
was_analog_level_set_ = false;
|
||||
|
||||
return AudioProcessing::kNoError;
|
||||
}
|
||||
|
||||
int GainControlImpl::compression_gain_db() const {
|
||||
return compression_gain_db_;
|
||||
}
|
||||
|
||||
// TODO(ajm): ensure this is called under kAdaptiveAnalog.
|
||||
int GainControlImpl::set_stream_analog_level(int level) {
|
||||
@ -282,9 +306,6 @@ int GainControlImpl::set_stream_analog_level(int level) {
|
||||
int GainControlImpl::stream_analog_level() const {
|
||||
data_dumper_->DumpRaw("gain_control_stream_analog_level", 1,
|
||||
&analog_capture_level_);
|
||||
// TODO(ajm): enable this assertion?
|
||||
// RTC_DCHECK_EQ(kAdaptiveAnalog, mode_);
|
||||
|
||||
return analog_capture_level_;
|
||||
}
|
||||
|
||||
@ -301,10 +322,6 @@ int GainControlImpl::Enable(bool enable) {
|
||||
return AudioProcessing::kNoError;
|
||||
}
|
||||
|
||||
bool GainControlImpl::is_enabled() const {
|
||||
return enabled_;
|
||||
}
|
||||
|
||||
int GainControlImpl::set_mode(Mode mode) {
|
||||
if (MapSetting(mode) == -1) {
|
||||
return AudioProcessing::kBadParameterError;
|
||||
@ -317,49 +334,21 @@ int GainControlImpl::set_mode(Mode mode) {
|
||||
return AudioProcessing::kNoError;
|
||||
}
|
||||
|
||||
GainControl::Mode GainControlImpl::mode() const {
|
||||
return mode_;
|
||||
}
|
||||
|
||||
int GainControlImpl::set_analog_level_limits(int minimum, int maximum) {
|
||||
if (minimum < 0) {
|
||||
if (minimum < 0 || maximum > 65535 || maximum < minimum) {
|
||||
return AudioProcessing::kBadParameterError;
|
||||
}
|
||||
|
||||
if (maximum > 65535) {
|
||||
return AudioProcessing::kBadParameterError;
|
||||
}
|
||||
minimum_capture_level_ = minimum;
|
||||
maximum_capture_level_ = maximum;
|
||||
|
||||
if (maximum < minimum) {
|
||||
return AudioProcessing::kBadParameterError;
|
||||
}
|
||||
|
||||
size_t num_proc_channels_local = 0u;
|
||||
int sample_rate_hz_local = 0;
|
||||
{
|
||||
minimum_capture_level_ = minimum;
|
||||
maximum_capture_level_ = maximum;
|
||||
|
||||
RTC_DCHECK(num_proc_channels_);
|
||||
RTC_DCHECK(sample_rate_hz_);
|
||||
num_proc_channels_local = *num_proc_channels_;
|
||||
sample_rate_hz_local = *sample_rate_hz_;
|
||||
}
|
||||
Initialize(num_proc_channels_local, sample_rate_hz_local);
|
||||
RTC_DCHECK(num_proc_channels_);
|
||||
RTC_DCHECK(sample_rate_hz_);
|
||||
Initialize(*num_proc_channels_, *sample_rate_hz_);
|
||||
return AudioProcessing::kNoError;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (level > 31 || level < 0) {
|
||||
@ -369,10 +358,6 @@ int GainControlImpl::set_target_level_dbfs(int level) {
|
||||
return Configure();
|
||||
}
|
||||
|
||||
int GainControlImpl::target_level_dbfs() const {
|
||||
return target_level_dbfs_;
|
||||
}
|
||||
|
||||
int GainControlImpl::set_compression_gain_db(int gain) {
|
||||
if (gain < 0 || gain > 90) {
|
||||
RTC_LOG(LS_ERROR) << "set_compression_gain_db(" << gain << ") failed.";
|
||||
@ -387,10 +372,6 @@ int GainControlImpl::enable_limiter(bool 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();
|
||||
|
||||
@ -401,13 +382,18 @@ void GainControlImpl::Initialize(size_t num_proc_channels, int sample_rate_hz) {
|
||||
return;
|
||||
}
|
||||
|
||||
gain_controllers_.resize(*num_proc_channels_);
|
||||
for (auto& gain_controller : gain_controllers_) {
|
||||
if (!gain_controller) {
|
||||
gain_controller.reset(new GainController());
|
||||
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());
|
||||
}
|
||||
gain_controller->Initialize(minimum_capture_level_, maximum_capture_level_,
|
||||
mode_, *sample_rate_hz_, analog_capture_level_);
|
||||
|
||||
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();
|
||||
@ -424,11 +410,10 @@ int GainControlImpl::Configure() {
|
||||
config.limiterEnable = limiter_enabled_;
|
||||
|
||||
int error = AudioProcessing::kNoError;
|
||||
for (auto& gain_controller : gain_controllers_) {
|
||||
const int handle_error =
|
||||
WebRtcAgc_set_config(gain_controller->state(), config);
|
||||
if (handle_error != AudioProcessing::kNoError) {
|
||||
error = handle_error;
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user