blob: 87488d267418e5b9cabf86a66bacf5a46a3870c7 [file] [log] [blame]
Per Ã…hgren6a05bb12019-12-03 11:24:59 +01001/*
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
16namespace webrtc {
17namespace {
18
19AlignmentMixer::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
39AlignmentMixer::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
48AlignmentMixer::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
66void 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
80void 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
96int 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