tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

alignment_mixer.cc (5787B)


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