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 |