1 /*
2  *  Copyright (c) 2018 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/aec3/reverb_frequency_response.h"
12 
13 #include <stddef.h>
14 
15 #include <algorithm>
16 #include <array>
17 #include <numeric>
18 
19 #include "api/array_view.h"
20 #include "modules/audio_processing/aec3/aec3_common.h"
21 #include "rtc_base/checks.h"
22 
23 namespace webrtc {
24 
25 namespace {
26 
27 // Computes the ratio of the energies between the direct path and the tail. The
28 // energy is computed in the power spectrum domain discarding the DC
29 // contributions.
AverageDecayWithinFilter(rtc::ArrayView<const float> freq_resp_direct_path,rtc::ArrayView<const float> freq_resp_tail)30 float AverageDecayWithinFilter(
31     rtc::ArrayView<const float> freq_resp_direct_path,
32     rtc::ArrayView<const float> freq_resp_tail) {
33   // Skipping the DC for the ratio computation
34   constexpr size_t kSkipBins = 1;
35   RTC_CHECK_EQ(freq_resp_direct_path.size(), freq_resp_tail.size());
36 
37   float direct_path_energy =
38       std::accumulate(freq_resp_direct_path.begin() + kSkipBins,
39                       freq_resp_direct_path.end(), 0.f);
40 
41   if (direct_path_energy == 0.f) {
42     return 0.f;
43   }
44 
45   float tail_energy = std::accumulate(freq_resp_tail.begin() + kSkipBins,
46                                       freq_resp_tail.end(), 0.f);
47   return tail_energy / direct_path_energy;
48 }
49 
50 }  // namespace
51 
ReverbFrequencyResponse()52 ReverbFrequencyResponse::ReverbFrequencyResponse() {
53   tail_response_.fill(0.f);
54 }
55 ReverbFrequencyResponse::~ReverbFrequencyResponse() = default;
56 
Update(const std::vector<std::array<float,kFftLengthBy2Plus1>> & frequency_response,int filter_delay_blocks,const absl::optional<float> & linear_filter_quality,bool stationary_block)57 void ReverbFrequencyResponse::Update(
58     const std::vector<std::array<float, kFftLengthBy2Plus1>>&
59         frequency_response,
60     int filter_delay_blocks,
61     const absl::optional<float>& linear_filter_quality,
62     bool stationary_block) {
63   if (stationary_block || !linear_filter_quality) {
64     return;
65   }
66 
67   Update(frequency_response, filter_delay_blocks, *linear_filter_quality);
68 }
69 
Update(const std::vector<std::array<float,kFftLengthBy2Plus1>> & frequency_response,int filter_delay_blocks,float linear_filter_quality)70 void ReverbFrequencyResponse::Update(
71     const std::vector<std::array<float, kFftLengthBy2Plus1>>&
72         frequency_response,
73     int filter_delay_blocks,
74     float linear_filter_quality) {
75   rtc::ArrayView<const float> freq_resp_tail(
76       frequency_response[frequency_response.size() - 1]);
77 
78   rtc::ArrayView<const float> freq_resp_direct_path(
79       frequency_response[filter_delay_blocks]);
80 
81   float average_decay =
82       AverageDecayWithinFilter(freq_resp_direct_path, freq_resp_tail);
83 
84   const float smoothing = 0.2f * linear_filter_quality;
85   average_decay_ += smoothing * (average_decay - average_decay_);
86 
87   for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) {
88     tail_response_[k] = freq_resp_direct_path[k] * average_decay_;
89   }
90 
91   for (size_t k = 1; k < kFftLengthBy2; ++k) {
92     const float avg_neighbour =
93         0.5f * (tail_response_[k - 1] + tail_response_[k + 1]);
94     tail_response_[k] = std::max(tail_response_[k], avg_neighbour);
95   }
96 }
97 
98 }  // namespace webrtc
99