/* * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ // An implementation of a 3-band FIR filter-bank with DCT modulation, similar to // the proposed in "Multirate Signal Processing for Communication Systems" by // Fredric J Harris. // // The idea is to take a heterodyne system and change the order of the // components to get something which is efficient to implement digitally. // // It is possible to separate the filter using the noble identity as follows: // // H(z) = H0(z^3) + z^-1 * H1(z^3) + z^-2 * H2(z^3) // // This is used in the analysis stage to first downsample serial to parallel // and then filter each branch with one of these polyphase decompositions of the // lowpass prototype. Because each filter is only a modulation of the prototype, // it is enough to multiply each coefficient by the respective cosine value to // shift it to the desired band. But because the cosine period is 12 samples, // it requires separating the prototype even further using the noble identity. // After filtering and modulating for each band, the output of all filters is // accumulated to get the downsampled bands. // // A similar logic can be applied to the synthesis stage. #include "modules/audio_processing/three_band_filter_bank.h" #include <array> #include "rtc_base/checks.h" namespace webrtc { namespace { // Factors to take into account when choosing `kFilterSize`: // 1. Higher `kFilterSize`, means faster transition, which ensures less // aliasing. This is especially important when there is non-linear // processing between the splitting and merging. // 2. The delay that this filter bank introduces is // `kNumBands` * `kSparsity` * `kFilterSize` / 2, so it increases linearly // with `kFilterSize`. // 3. The computation complexity also increases linearly with `kFilterSize`. // The Matlab code to generate these `kFilterCoeffs` is: // // N = kNumBands * kSparsity * kFilterSize - 1; // h = fir1(N, 1 / (2 * kNumBands), kaiser(N + 1, 3.5)); // reshape(h, kNumBands * kSparsity, kFilterSize); // // The code below uses the values of kFilterSize, kNumBands and kSparsity // specified in the header. // Because the total bandwidth of the lower and higher band is double the middle // one (because of the spectrum parity), the low-pass prototype is half the // bandwidth of 1 / (2 * `kNumBands`) and is then shifted with cosine modulation // to the right places. // A Kaiser window is used because of its flexibility and the alpha is set to // 3.5, since that sets a stop band attenuation of 40dB ensuring a fast // transition. constexpr int kSubSampling = …; constexpr int kDctSize = …; static_assert …; const float kFilterCoeffs[ThreeBandFilterBank::kNumNonZeroFilters][kFilterSize] = …; constexpr int kZeroFilterIndex1 = …; constexpr int kZeroFilterIndex2 = …; const float kDctModulation[ThreeBandFilterBank::kNumNonZeroFilters][kDctSize] = …; // Filters the input signal `in` with the filter `filter` using a shift by // `in_shift`, taking into account the previous state. void FilterCore( rtc::ArrayView<const float, kFilterSize> filter, rtc::ArrayView<const float, ThreeBandFilterBank::kSplitBandSize> in, const int in_shift, rtc::ArrayView<float, ThreeBandFilterBank::kSplitBandSize> out, rtc::ArrayView<float, kMemorySize> state) { … } } // namespace // Because the low-pass filter prototype has half bandwidth it is possible to // use a DCT to shift it in both directions at the same time, to the center // frequencies [1 / 12, 3 / 12, 5 / 12]. ThreeBandFilterBank::ThreeBandFilterBank() { … } ThreeBandFilterBank::~ThreeBandFilterBank() = default; // The analysis can be separated in these steps: // 1. Serial to parallel downsampling by a factor of `kNumBands`. // 2. Filtering of `kSparsity` different delayed signals with polyphase // decomposition of the low-pass prototype filter and upsampled by a factor // of `kSparsity`. // 3. Modulating with cosines and accumulating to get the desired band. void ThreeBandFilterBank::Analysis( rtc::ArrayView<const float, kFullBandSize> in, rtc::ArrayView<const rtc::ArrayView<float>, ThreeBandFilterBank::kNumBands> out) { … } // The synthesis can be separated in these steps: // 1. Modulating with cosines. // 2. Filtering each one with a polyphase decomposition of the low-pass // prototype filter upsampled by a factor of `kSparsity` and accumulating // `kSparsity` signals with different delays. // 3. Parallel to serial upsampling by a factor of `kNumBands`. void ThreeBandFilterBank::Synthesis( rtc::ArrayView<const rtc::ArrayView<float>, ThreeBandFilterBank::kNumBands> in, rtc::ArrayView<float, kFullBandSize> out) { … } } // namespace webrtc