Chromium Code Reviews| Index: webrtc/modules/audio_processing/level_controller/level_controller.cc |
| diff --git a/webrtc/modules/audio_processing/level_controller/level_controller.cc b/webrtc/modules/audio_processing/level_controller/level_controller.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..22114a912b349b97439e303171273b6609304fc0 |
| --- /dev/null |
| +++ b/webrtc/modules/audio_processing/level_controller/level_controller.cc |
| @@ -0,0 +1,151 @@ |
| +/* |
| + * Copyright (c) 2016 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/level_controller/level_controller.h" |
| + |
| +#include <algorithm> |
| +#include <numeric> |
| + |
| +#include "webrtc/base/array_view.h" |
| +#include "webrtc/base/checks.h" |
| +#include "webrtc/modules/audio_processing/audio_buffer.h" |
| +#include "webrtc/modules/audio_processing/level_controller/gain_applier.h" |
| +#include "webrtc/modules/audio_processing/level_controller/gain_selector.h" |
| +#include "webrtc/modules/audio_processing/level_controller/noise_level_estimator.h" |
| +#include "webrtc/modules/audio_processing/level_controller/peak_level_estimator.h" |
| +#include "webrtc/modules/audio_processing/level_controller/saturating_gain_estimator.h" |
| +#include "webrtc/modules/audio_processing/level_controller/signal_classifier.h" |
| +#include "webrtc/modules/audio_processing/logging/apm_data_dumper.h" |
| + |
| +namespace webrtc { |
| +namespace { |
| + |
| +void UpdateAndRemoveDcLevel(float* dc_level, rtc::ArrayView<float> x) { |
| + RTC_DCHECK_LT(0u, x.size()); |
|
hlundin-webrtc
2016/06/27 11:21:15
I find it easier to read RTC_DCHECK(!x.empty());
peah-webrtc
2016/06/27 22:51:48
Done.
|
| + float mean = 0; |
| + for (float v : x) { |
| + mean += v; |
| + } |
| + mean /= x.size(); |
| + *dc_level += 0.01f * (mean - *dc_level); |
|
hlundin-webrtc
2016/06/27 11:21:15
Is the size of x always constant in milliseconds?
peah-webrtc
2016/06/27 22:51:48
Good point!
Done.
|
| + |
| + for (float& v : x) { |
| + v -= *dc_level; |
| + } |
| +} |
| + |
| +float FrameEnergy(const AudioBuffer& audio) { |
| + auto sample_power = [](float a, float b) { return a + b * b; }; |
|
hlundin-webrtc
2016/06/27 11:21:15
Nit: "Specify the return type of the lambda explic
peah-webrtc
2016/06/27 22:51:48
Good point!
Done.
|
| + |
| + float energy = 0.f; |
| + for (size_t k = 0; k < audio.num_channels(); ++k) { |
| + float channel_energy = 0; |
|
hlundin-webrtc
2016/06/27 11:21:15
No need to initialize this to zero. Just add float
peah-webrtc
2016/06/27 22:51:48
Done.
|
| + channel_energy = std::accumulate( |
| + audio.channels_const_f()[k], |
| + audio.channels_const_f()[k] + audio.num_frames(), 0, sample_power); |
| + energy = std::max(channel_energy, energy); |
| + } |
| + return energy; |
| +} |
| + |
| +float PeakLevel(const AudioBuffer& audio) { |
| + float peak_level = 0.f; |
| + auto compare_abs = [](float a, float b) { return std::abs(a) < std::abs(b); }; |
|
hlundin-webrtc
2016/06/27 11:21:15
Move this lambda to in-line below.
peah-webrtc
2016/06/27 22:51:48
Done.
|
| + |
| + for (size_t k = 0; k < audio.num_channels(); ++k) { |
| + auto channel_peak_level = std::max_element( |
| + audio.channels_const_f()[k], |
| + audio.channels_const_f()[k] + audio.num_frames(), compare_abs); |
| + peak_level = std::max(*channel_peak_level, peak_level); |
| + } |
| + return peak_level; |
| +} |
| + |
| +} // namespace |
| + |
| +int LevelController::instance_count_ = 0; |
| + |
| +LevelController::LevelController() { |
| + ++instance_count_; |
| + data_dumper_.reset(new ApmDataDumper(instance_count_)); |
| + gain_selector_.reset(new GainSelector()); |
| + gain_applier_.reset(new GainApplier(data_dumper_.get())); |
| + signal_classifier_.reset(new SignalClassifier(data_dumper_.get())); |
| + noise_level_estimator_.reset(new NoiseLevelEstimator()); |
| + peak_level_estimator_.reset(new PeakLevelEstimator()); |
| + saturating_gain_estimator_.reset(new SaturatingGainEstimator()); |
| + Initialize(AudioProcessing::kSampleRate48kHz, 1u); |
| +} |
| + |
| +// TODO(peah): See if the destructors can be removed. |
| +LevelController::~LevelController() {} |
| + |
| +void LevelController::Initialize(int sample_rate_hz, size_t num_channels_) { |
| + data_dumper_->InitiateNewSetOfRecordings(); |
| + gain_selector_->Initialize(sample_rate_hz); |
| + gain_applier_->Initialize(sample_rate_hz); |
| + signal_classifier_->Initialize(sample_rate_hz); |
| + noise_level_estimator_->Initialize(sample_rate_hz); |
| + peak_level_estimator_->Initialize(); |
| + saturating_gain_estimator_->Initialize(); |
| + |
| + sample_rate_hz_ = sample_rate_hz; |
| +} |
| + |
| +void LevelController::Process(AudioBuffer* audio) { |
| + RTC_DCHECK_LT(0u, audio->num_channels()); |
| + RTC_DCHECK_GE(2u, audio->num_channels()); |
| + data_dumper_->DumpWav("lc_input", audio->num_frames(), |
| + audio->channels_const_f()[0], sample_rate_hz_, 1); |
| + |
| + // Remove DC level. |
| + for (size_t k = 0; k < audio->num_channels(); ++k) { |
| + UpdateAndRemoveDcLevel( |
| + &dc_level_[k], |
| + rtc::ArrayView<float>(audio->channels_f()[k], audio->num_frames())); |
| + } |
| + |
| + SignalClassifier::SignalType signal_type; |
| + signal_classifier_->Analyze(*audio, &signal_type); |
| + int tmp = static_cast<int>(signal_type); |
| + data_dumper_->DumpRaw("lc_signal_type", 1, &tmp); |
| + |
| + // Estimate the noise energy. |
| + float frame_energy = FrameEnergy(*audio); |
| + float noise_energy = |
| + noise_level_estimator_->Analyze(signal_type, frame_energy); |
|
hlundin-webrtc
2016/06/27 11:21:15
You can just as well call FrameEnergy(*audio) dire
peah-webrtc
2016/06/27 22:51:48
Done.
|
| + |
| + // Estimate the overall signal peak level. |
| + float frame_peak_level = PeakLevel(*audio); |
| + float peak_level = |
| + peak_level_estimator_->Analyze(signal_type, frame_peak_level); |
|
hlundin-webrtc
2016/06/27 11:21:15
Same here. Call PeakLevel(*audio) directly as a pa
peah-webrtc
2016/06/27 22:51:48
Done.
|
| + |
| + float saturating_gain = saturating_gain_estimator_->GetGain(); |
| + |
| + // Compute the new gain to apply. |
| + float new_gain = |
| + gain_selector_->GetNewGain(peak_level, noise_energy, saturating_gain); |
| + |
| + // Apply the gain to the signal. |
| + int num_saturations = gain_applier_->Process(new_gain, audio); |
| + |
| + // Estimate the gain that saturates the overall signal. |
| + saturating_gain_estimator_->Update(new_gain, num_saturations); |
| + |
| + data_dumper_->DumpRaw("lc_selected_gain", 1, &new_gain); |
| + data_dumper_->DumpRaw("lc_noise_energy", 1, &noise_energy); |
| + data_dumper_->DumpRaw("lc_peak_level", 1, &peak_level); |
| + data_dumper_->DumpRaw("lc_saturating_gain", 1, &saturating_gain); |
| + |
| + data_dumper_->DumpWav("lc_output", audio->num_frames(), |
| + audio->channels_f()[0], sample_rate_hz_, 1); |
| +} |
| + |
| +} // namespace webrtc |