1 /*
2 * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include "modules/audio_processing/ns/signal_model_estimator.h"
12
13 #include "modules/audio_processing/ns/fast_math.h"
14
15 namespace webrtc {
16
17 namespace {
18
19 constexpr float kOneByFftSizeBy2Plus1 = 1.f / kFftSizeBy2Plus1;
20
21 // Computes the difference measure between input spectrum and a template/learned
22 // noise spectrum.
ComputeSpectralDiff(rtc::ArrayView<const float,kFftSizeBy2Plus1> conservative_noise_spectrum,rtc::ArrayView<const float,kFftSizeBy2Plus1> signal_spectrum,float signal_spectral_sum,float diff_normalization)23 float ComputeSpectralDiff(
24 rtc::ArrayView<const float, kFftSizeBy2Plus1> conservative_noise_spectrum,
25 rtc::ArrayView<const float, kFftSizeBy2Plus1> signal_spectrum,
26 float signal_spectral_sum,
27 float diff_normalization) {
28 // spectral_diff = var(signal_spectrum) - cov(signal_spectrum, magnAvgPause)^2
29 // / var(magnAvgPause)
30
31 // Compute average quantities.
32 float noise_average = 0.f;
33 for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) {
34 // Conservative smooth noise spectrum from pause frames.
35 noise_average += conservative_noise_spectrum[i];
36 }
37 noise_average = noise_average * kOneByFftSizeBy2Plus1;
38 float signal_average = signal_spectral_sum * kOneByFftSizeBy2Plus1;
39
40 // Compute variance and covariance quantities.
41 float covariance = 0.f;
42 float noise_variance = 0.f;
43 float signal_variance = 0.f;
44 for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) {
45 float signal_diff = signal_spectrum[i] - signal_average;
46 float noise_diff = conservative_noise_spectrum[i] - noise_average;
47 covariance += signal_diff * noise_diff;
48 noise_variance += noise_diff * noise_diff;
49 signal_variance += signal_diff * signal_diff;
50 }
51 covariance *= kOneByFftSizeBy2Plus1;
52 noise_variance *= kOneByFftSizeBy2Plus1;
53 signal_variance *= kOneByFftSizeBy2Plus1;
54
55 // Update of average magnitude spectrum.
56 float spectral_diff =
57 signal_variance - (covariance * covariance) / (noise_variance + 0.0001f);
58 // Normalize.
59 return spectral_diff / (diff_normalization + 0.0001f);
60 }
61
62 // Updates the spectral flatness based on the input spectrum.
UpdateSpectralFlatness(rtc::ArrayView<const float,kFftSizeBy2Plus1> signal_spectrum,float signal_spectral_sum,float * spectral_flatness)63 void UpdateSpectralFlatness(
64 rtc::ArrayView<const float, kFftSizeBy2Plus1> signal_spectrum,
65 float signal_spectral_sum,
66 float* spectral_flatness) {
67 RTC_DCHECK(spectral_flatness);
68
69 // Compute log of ratio of the geometric to arithmetic mean (handle the log(0)
70 // separately).
71 constexpr float kAveraging = 0.3f;
72 float avg_spect_flatness_num = 0.f;
73 for (size_t i = 1; i < kFftSizeBy2Plus1; ++i) {
74 if (signal_spectrum[i] == 0.f) {
75 *spectral_flatness -= kAveraging * (*spectral_flatness);
76 return;
77 }
78 }
79
80 for (size_t i = 1; i < kFftSizeBy2Plus1; ++i) {
81 avg_spect_flatness_num += LogApproximation(signal_spectrum[i]);
82 }
83
84 float avg_spect_flatness_denom = signal_spectral_sum - signal_spectrum[0];
85
86 avg_spect_flatness_denom = avg_spect_flatness_denom * kOneByFftSizeBy2Plus1;
87 avg_spect_flatness_num = avg_spect_flatness_num * kOneByFftSizeBy2Plus1;
88
89 float spectral_tmp =
90 ExpApproximation(avg_spect_flatness_num) / avg_spect_flatness_denom;
91
92 // Time-avg update of spectral flatness feature.
93 *spectral_flatness += kAveraging * (spectral_tmp - *spectral_flatness);
94 }
95
96 // Updates the log LRT measures.
UpdateSpectralLrt(rtc::ArrayView<const float,kFftSizeBy2Plus1> prior_snr,rtc::ArrayView<const float,kFftSizeBy2Plus1> post_snr,rtc::ArrayView<float,kFftSizeBy2Plus1> avg_log_lrt,float * lrt)97 void UpdateSpectralLrt(rtc::ArrayView<const float, kFftSizeBy2Plus1> prior_snr,
98 rtc::ArrayView<const float, kFftSizeBy2Plus1> post_snr,
99 rtc::ArrayView<float, kFftSizeBy2Plus1> avg_log_lrt,
100 float* lrt) {
101 RTC_DCHECK(lrt);
102
103 for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) {
104 float tmp1 = 1.f + 2.f * prior_snr[i];
105 float tmp2 = 2.f * prior_snr[i] / (tmp1 + 0.0001f);
106 float bessel_tmp = (post_snr[i] + 1.f) * tmp2;
107 avg_log_lrt[i] +=
108 .5f * (bessel_tmp - LogApproximation(tmp1) - avg_log_lrt[i]);
109 }
110
111 float log_lrt_time_avg_k_sum = 0.f;
112 for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) {
113 log_lrt_time_avg_k_sum += avg_log_lrt[i];
114 }
115 *lrt = log_lrt_time_avg_k_sum * kOneByFftSizeBy2Plus1;
116 }
117
118 } // namespace
119
SignalModelEstimator()120 SignalModelEstimator::SignalModelEstimator()
121 : prior_model_estimator_(kLtrFeatureThr) {}
122
AdjustNormalization(int32_t num_analyzed_frames,float signal_energy)123 void SignalModelEstimator::AdjustNormalization(int32_t num_analyzed_frames,
124 float signal_energy) {
125 diff_normalization_ *= num_analyzed_frames;
126 diff_normalization_ += signal_energy;
127 diff_normalization_ /= (num_analyzed_frames + 1);
128 }
129
130 // Update the noise features.
Update(rtc::ArrayView<const float,kFftSizeBy2Plus1> prior_snr,rtc::ArrayView<const float,kFftSizeBy2Plus1> post_snr,rtc::ArrayView<const float,kFftSizeBy2Plus1> conservative_noise_spectrum,rtc::ArrayView<const float,kFftSizeBy2Plus1> signal_spectrum,float signal_spectral_sum,float signal_energy)131 void SignalModelEstimator::Update(
132 rtc::ArrayView<const float, kFftSizeBy2Plus1> prior_snr,
133 rtc::ArrayView<const float, kFftSizeBy2Plus1> post_snr,
134 rtc::ArrayView<const float, kFftSizeBy2Plus1> conservative_noise_spectrum,
135 rtc::ArrayView<const float, kFftSizeBy2Plus1> signal_spectrum,
136 float signal_spectral_sum,
137 float signal_energy) {
138 // Compute spectral flatness on input spectrum.
139 UpdateSpectralFlatness(signal_spectrum, signal_spectral_sum,
140 &features_.spectral_flatness);
141
142 // Compute difference of input spectrum with learned/estimated noise spectrum.
143 float spectral_diff =
144 ComputeSpectralDiff(conservative_noise_spectrum, signal_spectrum,
145 signal_spectral_sum, diff_normalization_);
146 // Compute time-avg update of difference feature.
147 features_.spectral_diff += 0.3f * (spectral_diff - features_.spectral_diff);
148
149 signal_energy_sum_ += signal_energy;
150
151 // Compute histograms for parameter decisions (thresholds and weights for
152 // features). Parameters are extracted periodically.
153 if (--histogram_analysis_counter_ > 0) {
154 histograms_.Update(features_);
155 } else {
156 // Compute model parameters.
157 prior_model_estimator_.Update(histograms_);
158
159 // Clear histograms for next update.
160 histograms_.Clear();
161
162 histogram_analysis_counter_ = kFeatureUpdateWindowSize;
163
164 // Update every window:
165 // Compute normalization for the spectral difference for next estimation.
166 signal_energy_sum_ = signal_energy_sum_ / kFeatureUpdateWindowSize;
167 diff_normalization_ = 0.5f * (signal_energy_sum_ + diff_normalization_);
168 signal_energy_sum_ = 0.f;
169 }
170
171 // Compute the LRT.
172 UpdateSpectralLrt(prior_snr, post_snr, features_.avg_log_lrt, &features_.lrt);
173 }
174
175 } // namespace webrtc
176