From 4d4cce8b3f02bd350f209e0a77b6bba13494ac62 Mon Sep 17 00:00:00 2001 From: Alessio Bazzica Date: Wed, 10 Apr 2019 11:11:09 +0200 Subject: [PATCH] RNN VAD: Switch to PFFFT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This CL replaces KissFFT with PFFFT for the spectral features computation. Remarks: - Extra FFT output vector copy eliminated - Scaling and windowing merged into a single vector for efficiency - Nyquist frequency hack to keep the iteration in BandFeaturesExtractor::ComputeSpectralCrossCorrelation simple Bug: webrtc:9577, webrtc:10480 Change-Id: I436563bd257f66a243f5402be270ffcf859bd184 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/130221 Commit-Queue: Alessio Bazzica Reviewed-by: Per Ã…hgren Cr-Commit-Position: refs/heads/master@{#27536} --- .../audio_processing/agc2/rnn_vad/BUILD.gn | 4 +- .../audio_processing/agc2/rnn_vad/common.h | 1 - .../audio_processing/agc2/rnn_vad/fft_util.cc | 63 ------------------ .../audio_processing/agc2/rnn_vad/fft_util.h | 55 ---------------- .../agc2/rnn_vad/fft_util_unittest.cc | 64 ------------------- .../agc2/rnn_vad/rnn_vad_tool.cc | 4 ++ .../agc2/rnn_vad/spectral_features.cc | 63 ++++++++++++++---- .../agc2/rnn_vad/spectral_features.h | 12 ++-- .../rnn_vad/spectral_features_internal.cc | 15 +++-- .../agc2/rnn_vad/spectral_features_internal.h | 15 +++-- .../spectral_features_internal_unittest.cc | 10 ++- 11 files changed, 91 insertions(+), 215 deletions(-) delete mode 100644 modules/audio_processing/agc2/rnn_vad/fft_util.cc delete mode 100644 modules/audio_processing/agc2/rnn_vad/fft_util.h delete mode 100644 modules/audio_processing/agc2/rnn_vad/fft_util_unittest.cc diff --git a/modules/audio_processing/agc2/rnn_vad/BUILD.gn b/modules/audio_processing/agc2/rnn_vad/BUILD.gn index cd9a7a24a2..902082ef7f 100644 --- a/modules/audio_processing/agc2/rnn_vad/BUILD.gn +++ b/modules/audio_processing/agc2/rnn_vad/BUILD.gn @@ -16,8 +16,6 @@ rtc_source_set("rnn_vad") { "common.h", "features_extraction.cc", "features_extraction.h", - "fft_util.cc", - "fft_util.h", "lp_residual.cc", "lp_residual.h", "pitch_info.h", @@ -90,7 +88,6 @@ if (rtc_include_tests) { sources = [ "auto_correlation_unittest.cc", "features_extraction_unittest.cc", - "fft_util_unittest.cc", "lp_residual_unittest.cc", "pitch_search_internal_unittest.cc", "pitch_search_unittest.cc", @@ -111,6 +108,7 @@ if (rtc_include_tests) { "../../../../rtc_base:checks", "../../../../rtc_base:logging", "../../../../test:test_support", + "../../utility:pffft_wrapper", "//third_party/rnnoise:rnn_vad", ] data = unittest_resources diff --git a/modules/audio_processing/agc2/rnn_vad/common.h b/modules/audio_processing/agc2/rnn_vad/common.h index 4fef3ab56c..6b434d2171 100644 --- a/modules/audio_processing/agc2/rnn_vad/common.h +++ b/modules/audio_processing/agc2/rnn_vad/common.h @@ -53,7 +53,6 @@ constexpr size_t kMinPitch48kHz = kMinPitch24kHz * 2; constexpr size_t kMaxPitch48kHz = kMaxPitch24kHz * 2; // Spectral features. -constexpr size_t kFftSizeBy2Plus1 = kFrameSize20ms24kHz / 2 + 1; constexpr size_t kNumBands = 22; constexpr size_t kNumLowerBands = 6; static_assert((0 < kNumLowerBands) && (kNumLowerBands < kNumBands), ""); diff --git a/modules/audio_processing/agc2/rnn_vad/fft_util.cc b/modules/audio_processing/agc2/rnn_vad/fft_util.cc deleted file mode 100644 index 4cc3ed9545..0000000000 --- a/modules/audio_processing/agc2/rnn_vad/fft_util.cc +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2018 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. - */ - -#include "modules/audio_processing/agc2/rnn_vad/fft_util.h" - -#include -#include -#include - -#include "rtc_base/checks.h" - -namespace webrtc { -namespace rnn_vad { -namespace { - -constexpr size_t kHalfFrameSize = kFrameSize20ms24kHz / 2; - -// Computes the first half of the Vorbis window. -std::array ComputeHalfVorbisWindow() { - std::array half_window{}; - for (size_t i = 0; i < kHalfFrameSize; ++i) { - half_window[i] = - std::sin(0.5 * kPi * std::sin(0.5 * kPi * (i + 0.5) / kHalfFrameSize) * - std::sin(0.5 * kPi * (i + 0.5) / kHalfFrameSize)); - } - return half_window; -} - -} // namespace - -FftUtil::FftUtil() - : half_window_(ComputeHalfVorbisWindow()), - fft_(static_cast(input_buf_.size())) {} - -FftUtil::~FftUtil() = default; - -void FftUtil::WindowedFft(rtc::ArrayView samples, - rtc::ArrayView> dst) { - RTC_DCHECK_EQ(samples.size(), kFrameSize20ms24kHz); - RTC_DCHECK_EQ(dst.size(), kFftSizeBy2Plus1); - // Apply windowing. - RTC_DCHECK_EQ(input_buf_.size(), 2 * half_window_.size()); - for (size_t i = 0; i < input_buf_.size() / 2; ++i) { - input_buf_[i].real(samples[i] * half_window_[i]); - size_t j = kFrameSize20ms24kHz - i - 1; - input_buf_[j].real(samples[j] * half_window_[i]); - } - fft_.ForwardFft(kFrameSize20ms24kHz, input_buf_.data(), kFrameSize20ms24kHz, - output_buf_.data()); - // Copy the first symmetric conjugate part. - RTC_DCHECK_LT(dst.size(), output_buf_.size()); - std::copy(output_buf_.begin(), output_buf_.begin() + dst.size(), dst.begin()); -} - -} // namespace rnn_vad -} // namespace webrtc diff --git a/modules/audio_processing/agc2/rnn_vad/fft_util.h b/modules/audio_processing/agc2/rnn_vad/fft_util.h deleted file mode 100644 index e38b0fffae..0000000000 --- a/modules/audio_processing/agc2/rnn_vad/fft_util.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2018 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. - */ - -#ifndef MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_FFT_UTIL_H_ -#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_FFT_UTIL_H_ - -#include -#include - -#include "api/array_view.h" -#include "modules/audio_processing/agc2/rnn_vad/common.h" -#include "third_party/rnnoise/src/kiss_fft.h" - -namespace webrtc { -namespace rnn_vad { - -// TODO(alessiob): Switch to PFFFT and remove this class. -// FFT implementation wrapper for the band-wise analysis step in which 20 ms -// frames at 24 kHz are analyzed in the frequency domain. The goal of this class -// are (i) making easy to switch to another FFT implementation, (ii) own the -// input buffer for the FFT and (iii) apply a windowing function before -// computing the FFT. -class FftUtil { - public: - FftUtil(); - FftUtil(const FftUtil&) = delete; - FftUtil& operator=(const FftUtil&) = delete; - ~FftUtil(); - // Applies a windowing function to |samples|, computes the real forward FFT - // and writes the result in |dst|. - // The size of |samples| must be 480 (20 ms at 24 kHz). - // The size of |dst| must be 241 since the complex conjugate is not written. - void WindowedFft(rtc::ArrayView samples, - rtc::ArrayView> dst); - - private: - static_assert((kFrameSize20ms24kHz & 1) == 0, - "kFrameSize20ms24kHz must be even."); - const std::array half_window_; - std::array, kFrameSize20ms24kHz> input_buf_; - std::array, kFrameSize20ms24kHz> output_buf_; - rnnoise::KissFft fft_; -}; - -} // namespace rnn_vad -} // namespace webrtc - -#endif // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_FFT_UTIL_H_ diff --git a/modules/audio_processing/agc2/rnn_vad/fft_util_unittest.cc b/modules/audio_processing/agc2/rnn_vad/fft_util_unittest.cc deleted file mode 100644 index a7efa1eb41..0000000000 --- a/modules/audio_processing/agc2/rnn_vad/fft_util_unittest.cc +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2018 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. - */ - -#include -#include -#include - -#include "modules/audio_processing/agc2/rnn_vad/common.h" -#include "modules/audio_processing/agc2/rnn_vad/fft_util.h" -#include "rtc_base/checks.h" -// TODO(bugs.webrtc.org/8948): Add when the issue is fixed. -// #include "test/fpe_observer.h" -#include "test/gtest.h" - -namespace webrtc { -namespace rnn_vad { -namespace test { -namespace { - -std::vector CreateSine(float amplitude, - float frequency_hz, - float duration_s, - int sample_rate_hz) { - size_t num_samples = static_cast(duration_s * sample_rate_hz); - std::vector signal(num_samples); - for (size_t i = 0; i < num_samples; ++i) { - signal[i] = - amplitude * std::sin(i * 2.0 * kPi * frequency_hz / sample_rate_hz); - } - return signal; -} - -} // namespace - -TEST(RnnVadTest, FftUtilTest) { - for (float frequency_hz : {200.f, 450.f, 1500.f}) { - SCOPED_TRACE(frequency_hz); - auto x = CreateSine( - /*amplitude=*/1000.f, frequency_hz, - /*duration_s=*/0.02f, - /*sample_rate_hz=*/kSampleRate24kHz); - FftUtil analyzer; - std::vector> x_fft(x.size() / 2 + 1); - analyzer.WindowedFft(x, x_fft); - int peak_fft_bin_index = std::distance( - x_fft.begin(), - std::max_element(x_fft.begin(), x_fft.end(), - [](std::complex a, std::complex b) { - return std::abs(a) < std::abs(b); - })); - EXPECT_EQ(frequency_hz, kSampleRate24kHz * peak_fft_bin_index / x.size()); - } -} - -} // namespace test -} // namespace rnn_vad -} // namespace webrtc diff --git a/modules/audio_processing/agc2/rnn_vad/rnn_vad_tool.cc b/modules/audio_processing/agc2/rnn_vad/rnn_vad_tool.cc index b66dfd684d..8d61bd8d4b 100644 --- a/modules/audio_processing/agc2/rnn_vad/rnn_vad_tool.cc +++ b/modules/audio_processing/agc2/rnn_vad/rnn_vad_tool.cc @@ -108,6 +108,10 @@ int main(int argc, char* argv[]) { if (features_file) { const float float_is_silence = is_silence ? 1.f : 0.f; fwrite(&float_is_silence, sizeof(float), 1, features_file); + if (is_silence) { + // Do not write uninitialized values. + feature_vector.fill(0.f); + } fwrite(feature_vector.data(), sizeof(float), kFeatureVectorSize, features_file); } diff --git a/modules/audio_processing/agc2/rnn_vad/spectral_features.cc b/modules/audio_processing/agc2/rnn_vad/spectral_features.cc index 82355798a0..81e3339d70 100644 --- a/modules/audio_processing/agc2/rnn_vad/spectral_features.cc +++ b/modules/audio_processing/agc2/rnn_vad/spectral_features.cc @@ -45,12 +45,52 @@ void UpdateCepstralDifferenceStats( sym_matrix_buf->Push(distances); } +// Computes the first half of the Vorbis window. +std::array ComputeScaledHalfVorbisWindow( + float scaling = 1.f) { + constexpr size_t kHalfSize = kFrameSize20ms24kHz / 2; + std::array half_window{}; + for (size_t i = 0; i < kHalfSize; ++i) { + half_window[i] = + scaling * + std::sin(0.5 * kPi * std::sin(0.5 * kPi * (i + 0.5) / kHalfSize) * + std::sin(0.5 * kPi * (i + 0.5) / kHalfSize)); + } + return half_window; +} + +// Computes the forward FFT on a 20 ms frame to which a given window function is +// applied. The Fourier coefficient corresponding to the Nyquist frequency is +// set to zero (it is never used and this allows to simplify the code). +void ComputeWindowedForwardFft( + rtc::ArrayView frame, + const std::array& half_window, + Pffft::FloatBuffer* fft_input_buffer, + Pffft::FloatBuffer* fft_output_buffer, + Pffft* fft) { + RTC_DCHECK_EQ(frame.size(), 2 * half_window.size()); + // Apply windowing. + auto in = fft_input_buffer->GetView(); + for (size_t i = 0, j = kFrameSize20ms24kHz - 1; i < half_window.size(); + ++i, --j) { + in[i] = frame[i] * half_window[i]; + in[j] = frame[j] * half_window[i]; + } + fft->ForwardTransform(*fft_input_buffer, fft_output_buffer, /*ordered=*/true); + // Set the Nyquist frequency coefficient to zero. + auto out = fft_output_buffer->GetView(); + out[1] = 0.f; +} + } // namespace SpectralFeaturesExtractor::SpectralFeaturesExtractor() - : fft_(), - reference_frame_fft_(kFftSizeBy2Plus1), - lagged_frame_fft_(kFftSizeBy2Plus1), + : half_window_(ComputeScaledHalfVorbisWindow( + 1.f / static_cast(kFrameSize20ms24kHz))), + fft_(kFrameSize20ms24kHz, Pffft::FftType::kReal), + fft_buffer_(fft_.CreateBuffer()), + reference_frame_fft_(fft_.CreateBuffer()), + lagged_frame_fft_(fft_.CreateBuffer()), dct_table_(ComputeDctTable()) {} SpectralFeaturesExtractor::~SpectralFeaturesExtractor() = default; @@ -70,10 +110,10 @@ bool SpectralFeaturesExtractor::CheckSilenceComputeFeatures( rtc::ArrayView bands_cross_corr, float* variability) { // Compute the Opus band energies for the reference frame. - fft_.WindowedFft(reference_frame, reference_frame_fft_); + ComputeWindowedForwardFft(reference_frame, half_window_, fft_buffer_.get(), + reference_frame_fft_.get(), &fft_); spectral_correlator_.ComputeAutoCorrelation( - {reference_frame_fft_.data(), kFftSizeBy2Plus1}, - reference_frame_bands_energy_); + reference_frame_fft_->GetConstView(), reference_frame_bands_energy_); // Check if the reference frame has silence. const float tot_energy = std::accumulate(reference_frame_bands_energy_.begin(), @@ -82,9 +122,10 @@ bool SpectralFeaturesExtractor::CheckSilenceComputeFeatures( return true; } // Compute the Opus band energies for the lagged frame. - fft_.WindowedFft(lagged_frame, lagged_frame_fft_); - spectral_correlator_.ComputeAutoCorrelation( - {lagged_frame_fft_.data(), kFftSizeBy2Plus1}, lagged_frame_bands_energy_); + ComputeWindowedForwardFft(lagged_frame, half_window_, fft_buffer_.get(), + lagged_frame_fft_.get(), &fft_); + spectral_correlator_.ComputeAutoCorrelation(lagged_frame_fft_->GetConstView(), + lagged_frame_bands_energy_); // Log of the band energies for the reference frame. std::array log_bands_energy; ComputeSmoothedLogMagnitudeSpectrum(reference_frame_bands_energy_, @@ -134,8 +175,8 @@ void SpectralFeaturesExtractor::ComputeAvgAndDerivatives( void SpectralFeaturesExtractor::ComputeNormalizedCepstralCorrelation( rtc::ArrayView bands_cross_corr) { spectral_correlator_.ComputeCrossCorrelation( - {reference_frame_fft_.data(), kFftSizeBy2Plus1}, - {lagged_frame_fft_.data(), kFftSizeBy2Plus1}, bands_cross_corr_); + reference_frame_fft_->GetConstView(), lagged_frame_fft_->GetConstView(), + bands_cross_corr_); // Normalize. for (size_t i = 0; i < bands_cross_corr_.size(); ++i) { bands_cross_corr_[i] = diff --git a/modules/audio_processing/agc2/rnn_vad/spectral_features.h b/modules/audio_processing/agc2/rnn_vad/spectral_features.h index 047af247ce..d327ef8e01 100644 --- a/modules/audio_processing/agc2/rnn_vad/spectral_features.h +++ b/modules/audio_processing/agc2/rnn_vad/spectral_features.h @@ -12,16 +12,16 @@ #define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_SPECTRAL_FEATURES_H_ #include -#include #include +#include #include #include "api/array_view.h" #include "modules/audio_processing/agc2/rnn_vad/common.h" -#include "modules/audio_processing/agc2/rnn_vad/fft_util.h" #include "modules/audio_processing/agc2/rnn_vad/ring_buffer.h" #include "modules/audio_processing/agc2/rnn_vad/spectral_features_internal.h" #include "modules/audio_processing/agc2/rnn_vad/symmetric_matrix_buffer.h" +#include "modules/audio_processing/utility/pffft_wrapper.h" namespace webrtc { namespace rnn_vad { @@ -58,9 +58,11 @@ class SpectralFeaturesExtractor { rtc::ArrayView bands_cross_corr); float ComputeVariability() const; - FftUtil fft_; - std::vector> reference_frame_fft_; - std::vector> lagged_frame_fft_; + const std::array half_window_; + Pffft fft_; + std::unique_ptr fft_buffer_; + std::unique_ptr reference_frame_fft_; + std::unique_ptr lagged_frame_fft_; SpectralCorrelator spectral_correlator_; std::array reference_frame_bands_energy_; std::array lagged_frame_bands_energy_; diff --git a/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.cc b/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.cc index 8135e3c333..29192a08f6 100644 --- a/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.cc +++ b/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.cc @@ -91,22 +91,26 @@ SpectralCorrelator::SpectralCorrelator() SpectralCorrelator::~SpectralCorrelator() = default; void SpectralCorrelator::ComputeAutoCorrelation( - rtc::ArrayView, kFftSizeBy2Plus1> x, + rtc::ArrayView x, rtc::ArrayView auto_corr) const { ComputeCrossCorrelation(x, x, auto_corr); } void SpectralCorrelator::ComputeCrossCorrelation( - rtc::ArrayView, kFftSizeBy2Plus1> x, - rtc::ArrayView, kFftSizeBy2Plus1> y, + rtc::ArrayView x, + rtc::ArrayView y, rtc::ArrayView cross_corr) const { + RTC_DCHECK_EQ(x.size(), kFrameSize20ms24kHz); + RTC_DCHECK_EQ(x.size(), y.size()); + RTC_DCHECK_EQ(x[1], 0.f) << "The Nyquist coefficient must be zeroed."; + RTC_DCHECK_EQ(y[1], 0.f) << "The Nyquist coefficient must be zeroed."; constexpr auto kOpusScaleNumBins24kHz20ms = GetOpusScaleNumBins24kHz20ms(); size_t k = 0; // Next Fourier coefficient index. cross_corr[0] = 0.f; for (size_t i = 0; i < kOpusBands24kHz - 1; ++i) { cross_corr[i + 1] = 0.f; for (int j = 0; j < kOpusScaleNumBins24kHz20ms[i]; ++j) { // Band size. - const float v = x[k].real() * y[k].real() + x[k].imag() * y[k].imag(); + const float v = x[2 * k] * y[2 * k] + x[2 * k + 1] * y[2 * k + 1]; const float tmp = weights_[k] * v; cross_corr[i] += v - tmp; cross_corr[i + 1] += tmp; @@ -114,8 +118,7 @@ void SpectralCorrelator::ComputeCrossCorrelation( } } cross_corr[0] *= 2.f; // The first band only gets half contribution. - // The Nyquist coefficient is never used. - RTC_DCHECK_EQ(k, kFftSizeBy2Plus1 - 1); + RTC_DCHECK_EQ(k, kFrameSize20ms24kHz / 2); // Nyquist coefficient never used. } void ComputeSmoothedLogMagnitudeSpectrum( diff --git a/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.h b/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.h index 0ec9652370..24b0219c8f 100644 --- a/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.h +++ b/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.h @@ -13,7 +13,6 @@ #include #include -#include #include #include "api/array_view.h" @@ -50,14 +49,22 @@ class SpectralCorrelator { ~SpectralCorrelator(); // Computes the band-wise spectral auto-correlations. + // |x| must: + // - have size equal to |kFrameSize20ms24kHz|; + // - be encoded as vectors of interleaved real-complex FFT coefficients + // where x[1] = y[1] = 0 (the Nyquist frequency coefficient is omitted). void ComputeAutoCorrelation( - rtc::ArrayView, kFftSizeBy2Plus1> x, + rtc::ArrayView x, rtc::ArrayView auto_corr) const; // Computes the band-wise spectral cross-correlations. + // |x| and |y| must: + // - have size equal to |kFrameSize20ms24kHz|; + // - be encoded as vectors of interleaved real-complex FFT coefficients where + // x[1] = y[1] = 0 (the Nyquist frequency coefficient is omitted). void ComputeCrossCorrelation( - rtc::ArrayView, kFftSizeBy2Plus1> x, - rtc::ArrayView, kFftSizeBy2Plus1> y, + rtc::ArrayView x, + rtc::ArrayView y, rtc::ArrayView cross_corr) const; private: diff --git a/modules/audio_processing/agc2/rnn_vad/spectral_features_internal_unittest.cc b/modules/audio_processing/agc2/rnn_vad/spectral_features_internal_unittest.cc index 4ff711883f..d112eb713f 100644 --- a/modules/audio_processing/agc2/rnn_vad/spectral_features_internal_unittest.cc +++ b/modules/audio_processing/agc2/rnn_vad/spectral_features_internal_unittest.cc @@ -18,6 +18,7 @@ #include "api/array_view.h" #include "modules/audio_processing/agc2/rnn_vad/test_utils.h" +#include "modules/audio_processing/utility/pffft_wrapper.h" // TODO(bugs.webrtc.org/8948): Add when the issue is fixed. // #include "test/fpe_observer.h" #include "test/gtest.h" @@ -86,10 +87,13 @@ TEST(RnnVadTest, DISABLED_TestOpusScaleWeights) { TEST(RnnVadTest, SpectralCorrelatorValidOutput) { SpectralCorrelator e; - std::array, kFftSizeBy2Plus1> in; + Pffft fft(kFrameSize20ms24kHz, Pffft::FftType::kReal); + auto in = fft.CreateBuffer(); std::array out; - in.fill({1.f, 1.f}); - e.ComputeAutoCorrelation(in, out); + auto in_view = in->GetView(); + std::fill(in_view.begin(), in_view.end(), 1.f); + in_view[1] = 0.f; // Nyquist frequency. + e.ComputeAutoCorrelation(in_view, out); for (size_t i = 0; i < kOpusBands24kHz; ++i) { SCOPED_TRACE(i); EXPECT_GT(out[i], 0.f);