• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "modules/audio_processing/aec3/alignment_mixer.h"
11 
12 #include <algorithm>
13 
14 #include "rtc_base/checks.h"
15 
16 namespace webrtc {
17 namespace {
18 
ChooseMixingVariant(bool downmix,bool adaptive_selection,int num_channels)19 AlignmentMixer::MixingVariant ChooseMixingVariant(bool downmix,
20                                                   bool adaptive_selection,
21                                                   int num_channels) {
22   RTC_DCHECK(!(adaptive_selection && downmix));
23   RTC_DCHECK_LT(0, num_channels);
24 
25   if (num_channels == 1) {
26     return AlignmentMixer::MixingVariant::kFixed;
27   }
28   if (downmix) {
29     return AlignmentMixer::MixingVariant::kDownmix;
30   }
31   if (adaptive_selection) {
32     return AlignmentMixer::MixingVariant::kAdaptive;
33   }
34   return AlignmentMixer::MixingVariant::kFixed;
35 }
36 
37 }  // namespace
38 
AlignmentMixer(size_t num_channels,const EchoCanceller3Config::Delay::AlignmentMixing & config)39 AlignmentMixer::AlignmentMixer(
40     size_t num_channels,
41     const EchoCanceller3Config::Delay::AlignmentMixing& config)
42     : AlignmentMixer(num_channels,
43                      config.downmix,
44                      config.adaptive_selection,
45                      config.activity_power_threshold,
46                      config.prefer_first_two_channels) {}
47 
AlignmentMixer(size_t num_channels,bool downmix,bool adaptive_selection,float activity_power_threshold,bool prefer_first_two_channels)48 AlignmentMixer::AlignmentMixer(size_t num_channels,
49                                bool downmix,
50                                bool adaptive_selection,
51                                float activity_power_threshold,
52                                bool prefer_first_two_channels)
53     : num_channels_(num_channels),
54       one_by_num_channels_(1.f / num_channels_),
55       excitation_energy_threshold_(kBlockSize * activity_power_threshold),
56       prefer_first_two_channels_(prefer_first_two_channels),
57       selection_variant_(
58           ChooseMixingVariant(downmix, adaptive_selection, num_channels_)) {
59   if (selection_variant_ == MixingVariant::kAdaptive) {
60     std::fill(strong_block_counters_.begin(), strong_block_counters_.end(), 0);
61     cumulative_energies_.resize(num_channels_);
62     std::fill(cumulative_energies_.begin(), cumulative_energies_.end(), 0.f);
63   }
64 }
65 
ProduceOutput(rtc::ArrayView<const std::vector<float>> x,rtc::ArrayView<float,kBlockSize> y)66 void AlignmentMixer::ProduceOutput(rtc::ArrayView<const std::vector<float>> x,
67                                    rtc::ArrayView<float, kBlockSize> y) {
68   RTC_DCHECK_EQ(x.size(), num_channels_);
69   if (selection_variant_ == MixingVariant::kDownmix) {
70     Downmix(x, y);
71     return;
72   }
73 
74   int ch = selection_variant_ == MixingVariant::kFixed ? 0 : SelectChannel(x);
75 
76   RTC_DCHECK_GE(x.size(), ch);
77   std::copy(x[ch].begin(), x[ch].end(), y.begin());
78 }
79 
Downmix(rtc::ArrayView<const std::vector<float>> x,rtc::ArrayView<float,kBlockSize> y) const80 void AlignmentMixer::Downmix(rtc::ArrayView<const std::vector<float>> x,
81                              rtc::ArrayView<float, kBlockSize> y) const {
82   RTC_DCHECK_EQ(x.size(), num_channels_);
83   RTC_DCHECK_GE(num_channels_, 2);
84   std::copy(x[0].begin(), x[0].end(), y.begin());
85   for (size_t ch = 1; ch < num_channels_; ++ch) {
86     for (size_t i = 0; i < kBlockSize; ++i) {
87       y[i] += x[ch][i];
88     }
89   }
90 
91   for (size_t i = 0; i < kBlockSize; ++i) {
92     y[i] *= one_by_num_channels_;
93   }
94 }
95 
SelectChannel(rtc::ArrayView<const std::vector<float>> x)96 int AlignmentMixer::SelectChannel(rtc::ArrayView<const std::vector<float>> x) {
97   RTC_DCHECK_EQ(x.size(), num_channels_);
98   RTC_DCHECK_GE(num_channels_, 2);
99   RTC_DCHECK_EQ(cumulative_energies_.size(), num_channels_);
100 
101   constexpr size_t kBlocksToChooseLeftOrRight =
102       static_cast<size_t>(0.5f * kNumBlocksPerSecond);
103   const bool good_signal_in_left_or_right =
104       prefer_first_two_channels_ &&
105       (strong_block_counters_[0] > kBlocksToChooseLeftOrRight ||
106        strong_block_counters_[1] > kBlocksToChooseLeftOrRight);
107 
108   const int num_ch_to_analyze =
109       good_signal_in_left_or_right ? 2 : num_channels_;
110 
111   constexpr int kNumBlocksBeforeEnergySmoothing = 60 * kNumBlocksPerSecond;
112   ++block_counter_;
113 
114   for (int ch = 0; ch < num_ch_to_analyze; ++ch) {
115     RTC_DCHECK_EQ(x[ch].size(), kBlockSize);
116     float x2_sum = 0.f;
117     for (size_t i = 0; i < kBlockSize; ++i) {
118       x2_sum += x[ch][i] * x[ch][i];
119     }
120 
121     if (ch < 2 && x2_sum > excitation_energy_threshold_) {
122       ++strong_block_counters_[ch];
123     }
124 
125     if (block_counter_ <= kNumBlocksBeforeEnergySmoothing) {
126       cumulative_energies_[ch] += x2_sum;
127     } else {
128       constexpr float kSmoothing = 1.f / (10 * kNumBlocksPerSecond);
129       cumulative_energies_[ch] +=
130           kSmoothing * (x2_sum - cumulative_energies_[ch]);
131     }
132   }
133 
134   // Normalize the energies to allow the energy computations to from now be
135   // based on smoothing.
136   if (block_counter_ == kNumBlocksBeforeEnergySmoothing) {
137     constexpr float kOneByNumBlocksBeforeEnergySmoothing =
138         1.f / kNumBlocksBeforeEnergySmoothing;
139     for (int ch = 0; ch < num_ch_to_analyze; ++ch) {
140       cumulative_energies_[ch] *= kOneByNumBlocksBeforeEnergySmoothing;
141     }
142   }
143 
144   int strongest_ch = 0;
145   for (int ch = 0; ch < num_ch_to_analyze; ++ch) {
146     if (cumulative_energies_[ch] > cumulative_energies_[strongest_ch]) {
147       strongest_ch = ch;
148     }
149   }
150 
151   if ((good_signal_in_left_or_right && selected_channel_ > 1) ||
152       cumulative_energies_[strongest_ch] >
153           2.f * cumulative_energies_[selected_channel_]) {
154     selected_channel_ = strongest_ch;
155   }
156 
157   return selected_channel_;
158 }
159 
160 }  // namespace webrtc
161