| /* |
| * Copyright (c) 2019 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 "modules/audio_processing/ns/prior_signal_model_estimator.h" |
| |
| #include <math.h> |
| #include <algorithm> |
| |
| #include "modules/audio_processing/ns/fast_math.h" |
| #include "rtc_base/checks.h" |
| |
| namespace webrtc { |
| |
| namespace { |
| |
| // Identifies the first of the two largest peaks in the histogram. |
| void FindFirstOfTwoLargestPeaks( |
| float bin_size, |
| rtc::ArrayView<const int, kHistogramSize> spectral_flatness, |
| float* peak_position, |
| int* peak_weight) { |
| RTC_DCHECK(peak_position); |
| RTC_DCHECK(peak_weight); |
| |
| int peak_value = 0; |
| int secondary_peak_value = 0; |
| *peak_position = 0.f; |
| float secondary_peak_position = 0.f; |
| *peak_weight = 0; |
| int secondary_peak_weight = 0; |
| |
| // Identify the two largest peaks. |
| for (int i = 0; i < kHistogramSize; ++i) { |
| const float bin_mid = (i + 0.5f) * bin_size; |
| if (spectral_flatness[i] > peak_value) { |
| // Found new "first" peak candidate. |
| secondary_peak_value = peak_value; |
| secondary_peak_weight = *peak_weight; |
| secondary_peak_position = *peak_position; |
| |
| peak_value = spectral_flatness[i]; |
| *peak_weight = spectral_flatness[i]; |
| *peak_position = bin_mid; |
| } else if (spectral_flatness[i] > secondary_peak_value) { |
| // Found new "second" peak candidate. |
| secondary_peak_value = spectral_flatness[i]; |
| secondary_peak_weight = spectral_flatness[i]; |
| secondary_peak_position = bin_mid; |
| } |
| } |
| |
| // Merge the peaks if they are close. |
| if ((fabs(secondary_peak_position - *peak_position) < 2 * bin_size) && |
| (secondary_peak_weight > 0.5f * (*peak_weight))) { |
| *peak_weight += secondary_peak_weight; |
| *peak_position = 0.5f * (*peak_position + secondary_peak_position); |
| } |
| } |
| |
| void UpdateLrt(rtc::ArrayView<const int, kHistogramSize> lrt_histogram, |
| float* prior_model_lrt, |
| bool* low_lrt_fluctuations) { |
| RTC_DCHECK(prior_model_lrt); |
| RTC_DCHECK(low_lrt_fluctuations); |
| |
| float average = 0.f; |
| float average_compl = 0.f; |
| float average_squared = 0.f; |
| int count = 0; |
| |
| for (int i = 0; i < 10; ++i) { |
| float bin_mid = (i + 0.5f) * kBinSizeLrt; |
| average += lrt_histogram[i] * bin_mid; |
| count += lrt_histogram[i]; |
| } |
| if (count > 0) { |
| average = average / count; |
| } |
| |
| for (int i = 0; i < kHistogramSize; ++i) { |
| float bin_mid = (i + 0.5f) * kBinSizeLrt; |
| average_squared += lrt_histogram[i] * bin_mid * bin_mid; |
| average_compl += lrt_histogram[i] * bin_mid; |
| } |
| constexpr float kOneFeatureUpdateWindowSize = 1.f / kFeatureUpdateWindowSize; |
| average_squared = average_squared * kOneFeatureUpdateWindowSize; |
| average_compl = average_compl * kOneFeatureUpdateWindowSize; |
| |
| // Fluctuation limit of LRT feature. |
| *low_lrt_fluctuations = average_squared - average * average_compl < 0.05f; |
| |
| // Get threshold for LRT feature. |
| constexpr float kMaxLrt = 1.f; |
| constexpr float kMinLrt = .2f; |
| if (*low_lrt_fluctuations) { |
| // Very low fluctuation, so likely noise. |
| *prior_model_lrt = kMaxLrt; |
| } else { |
| *prior_model_lrt = std::min(kMaxLrt, std::max(kMinLrt, 1.2f * average)); |
| } |
| } |
| |
| } // namespace |
| |
| PriorSignalModelEstimator::PriorSignalModelEstimator(float lrt_initial_value) |
| : prior_model_(lrt_initial_value) {} |
| |
| // Extract thresholds for feature parameters and computes the threshold/weights. |
| void PriorSignalModelEstimator::Update(const Histograms& histograms) { |
| bool low_lrt_fluctuations; |
| UpdateLrt(histograms.get_lrt(), &prior_model_.lrt, &low_lrt_fluctuations); |
| |
| // For spectral flatness and spectral difference: compute the main peaks of |
| // the histograms. |
| float spectral_flatness_peak_position; |
| int spectral_flatness_peak_weight; |
| FindFirstOfTwoLargestPeaks( |
| kBinSizeSpecFlat, histograms.get_spectral_flatness(), |
| &spectral_flatness_peak_position, &spectral_flatness_peak_weight); |
| |
| float spectral_diff_peak_position = 0.f; |
| int spectral_diff_peak_weight = 0; |
| FindFirstOfTwoLargestPeaks(kBinSizeSpecDiff, histograms.get_spectral_diff(), |
| &spectral_diff_peak_position, |
| &spectral_diff_peak_weight); |
| |
| // Reject if weight of peaks is not large enough, or peak value too small. |
| // Peak limit for spectral flatness (varies between 0 and 1). |
| const int use_spec_flat = spectral_flatness_peak_weight < 0.3f * 500 || |
| spectral_flatness_peak_position < 0.6f |
| ? 0 |
| : 1; |
| |
| // Reject if weight of peaks is not large enough or if fluctuation of the LRT |
| // feature are very low, indicating a noise state. |
| const int use_spec_diff = |
| spectral_diff_peak_weight < 0.3f * 500 || low_lrt_fluctuations ? 0 : 1; |
| |
| // Update the model. |
| prior_model_.template_diff_threshold = 1.2f * spectral_diff_peak_position; |
| prior_model_.template_diff_threshold = |
| std::min(1.f, std::max(0.16f, prior_model_.template_diff_threshold)); |
| |
| float one_by_feature_sum = 1.f / (1.f + use_spec_flat + use_spec_diff); |
| prior_model_.lrt_weighting = one_by_feature_sum; |
| |
| if (use_spec_flat == 1) { |
| prior_model_.flatness_threshold = 0.9f * spectral_flatness_peak_position; |
| prior_model_.flatness_threshold = |
| std::min(.95f, std::max(0.1f, prior_model_.flatness_threshold)); |
| prior_model_.flatness_weighting = one_by_feature_sum; |
| } else { |
| prior_model_.flatness_weighting = 0.f; |
| } |
| |
| if (use_spec_diff == 1) { |
| prior_model_.difference_weighting = one_by_feature_sum; |
| } else { |
| prior_model_.difference_weighting = 0.f; |
| } |
| } |
| |
| } // namespace webrtc |