blob: b6cd6f9d9b6b15ff69b8e8a97ac363e5d03b1608 [file] [log] [blame]
/*
* 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.
*/
#include <memory>
#include "common_audio/sparse_fir_filter.h"
#include "common_audio/fir_filter.h"
#include "common_audio/fir_filter_factory.h"
#include "rtc_base/arraysize.h"
#include "test/gtest.h"
namespace webrtc {
namespace {
static const float kCoeffs[] = {0.2f, 0.3f, 0.5f, 0.7f, 0.11f};
static const float kInput[] = {1.f, 2.f, 3.f, 4.f, 5.f,
6.f, 7.f, 8.f, 9.f, 10.f};
template <size_t N>
void VerifyOutput(const float (&expected_output)[N], const float (&output)[N]) {
EXPECT_EQ(0, memcmp(expected_output, output, sizeof(output)));
}
} // namespace
TEST(SparseFIRFilterTest, FilterAsIdentity) {
const float kCoeff = 1.f;
const size_t kNumCoeff = 1;
const size_t kSparsity = 3;
const size_t kOffset = 0;
float output[arraysize(kInput)];
SparseFIRFilter filter(&kCoeff, kNumCoeff, kSparsity, kOffset);
filter.Filter(kInput, arraysize(kInput), output);
VerifyOutput(kInput, output);
}
TEST(SparseFIRFilterTest, SameOutputForScalarCoefficientAndDifferentSparsity) {
const float kCoeff = 2.f;
const size_t kNumCoeff = 1;
const size_t kLowSparsity = 1;
const size_t kHighSparsity = 7;
const size_t kOffset = 0;
float low_sparsity_output[arraysize(kInput)];
float high_sparsity_output[arraysize(kInput)];
SparseFIRFilter low_sparsity_filter(&kCoeff, kNumCoeff, kLowSparsity,
kOffset);
SparseFIRFilter high_sparsity_filter(&kCoeff, kNumCoeff, kHighSparsity,
kOffset);
low_sparsity_filter.Filter(kInput, arraysize(kInput), low_sparsity_output);
high_sparsity_filter.Filter(kInput, arraysize(kInput), high_sparsity_output);
VerifyOutput(low_sparsity_output, high_sparsity_output);
}
TEST(SparseFIRFilterTest, FilterUsedAsScalarMultiplication) {
const float kCoeff = 5.f;
const size_t kNumCoeff = 1;
const size_t kSparsity = 5;
const size_t kOffset = 0;
float output[arraysize(kInput)];
SparseFIRFilter filter(&kCoeff, kNumCoeff, kSparsity, kOffset);
filter.Filter(kInput, arraysize(kInput), output);
EXPECT_FLOAT_EQ(5.f, output[0]);
EXPECT_FLOAT_EQ(20.f, output[3]);
EXPECT_FLOAT_EQ(25.f, output[4]);
EXPECT_FLOAT_EQ(50.f, output[arraysize(kInput) - 1]);
}
TEST(SparseFIRFilterTest, FilterUsedAsInputShifting) {
const float kCoeff = 1.f;
const size_t kNumCoeff = 1;
const size_t kSparsity = 1;
const size_t kOffset = 4;
float output[arraysize(kInput)];
SparseFIRFilter filter(&kCoeff, kNumCoeff, kSparsity, kOffset);
filter.Filter(kInput, arraysize(kInput), output);
EXPECT_FLOAT_EQ(0.f, output[0]);
EXPECT_FLOAT_EQ(0.f, output[3]);
EXPECT_FLOAT_EQ(1.f, output[4]);
EXPECT_FLOAT_EQ(2.f, output[5]);
EXPECT_FLOAT_EQ(6.f, output[arraysize(kInput) - 1]);
}
TEST(SparseFIRFilterTest, FilterUsedAsArbitraryWeighting) {
const size_t kSparsity = 2;
const size_t kOffset = 1;
float output[arraysize(kInput)];
SparseFIRFilter filter(kCoeffs, arraysize(kCoeffs), kSparsity, kOffset);
filter.Filter(kInput, arraysize(kInput), output);
EXPECT_FLOAT_EQ(0.f, output[0]);
EXPECT_FLOAT_EQ(0.9f, output[3]);
EXPECT_FLOAT_EQ(1.4f, output[4]);
EXPECT_FLOAT_EQ(2.4f, output[5]);
EXPECT_FLOAT_EQ(8.61f, output[arraysize(kInput) - 1]);
}
TEST(SparseFIRFilterTest, FilterInLengthLesserOrEqualToCoefficientsLength) {
const size_t kSparsity = 1;
const size_t kOffset = 0;
float output[arraysize(kInput)];
SparseFIRFilter filter(kCoeffs, arraysize(kCoeffs), kSparsity, kOffset);
filter.Filter(kInput, 2, output);
EXPECT_FLOAT_EQ(0.2f, output[0]);
EXPECT_FLOAT_EQ(0.7f, output[1]);
}
TEST(SparseFIRFilterTest, MultipleFilterCalls) {
const size_t kSparsity = 1;
const size_t kOffset = 0;
float output[arraysize(kInput)];
SparseFIRFilter filter(kCoeffs, arraysize(kCoeffs), kSparsity, kOffset);
filter.Filter(kInput, 2, output);
EXPECT_FLOAT_EQ(0.2f, output[0]);
EXPECT_FLOAT_EQ(0.7f, output[1]);
filter.Filter(kInput, 2, output);
EXPECT_FLOAT_EQ(1.3f, output[0]);
EXPECT_FLOAT_EQ(2.4f, output[1]);
filter.Filter(kInput, 2, output);
EXPECT_FLOAT_EQ(2.81f, output[0]);
EXPECT_FLOAT_EQ(2.62f, output[1]);
filter.Filter(kInput, 2, output);
EXPECT_FLOAT_EQ(2.81f, output[0]);
EXPECT_FLOAT_EQ(2.62f, output[1]);
filter.Filter(&kInput[3], 3, output);
EXPECT_FLOAT_EQ(3.41f, output[0]);
EXPECT_FLOAT_EQ(4.12f, output[1]);
EXPECT_FLOAT_EQ(6.21f, output[2]);
filter.Filter(&kInput[3], 3, output);
EXPECT_FLOAT_EQ(8.12f, output[0]);
EXPECT_FLOAT_EQ(9.14f, output[1]);
EXPECT_FLOAT_EQ(9.45f, output[2]);
}
TEST(SparseFIRFilterTest, VerifySampleBasedVsBlockBasedFiltering) {
const size_t kSparsity = 3;
const size_t kOffset = 1;
float output_block_based[arraysize(kInput)];
SparseFIRFilter filter_block(kCoeffs, arraysize(kCoeffs), kSparsity, kOffset);
filter_block.Filter(kInput, arraysize(kInput), output_block_based);
float output_sample_based[arraysize(kInput)];
SparseFIRFilter filter_sample(kCoeffs, arraysize(kCoeffs), kSparsity,
kOffset);
for (size_t i = 0; i < arraysize(kInput); ++i)
filter_sample.Filter(&kInput[i], 1, &output_sample_based[i]);
VerifyOutput(output_block_based, output_sample_based);
}
TEST(SparseFIRFilterTest, SimpleHighPassFilter) {
const size_t kSparsity = 2;
const size_t kOffset = 2;
const float kHPCoeffs[] = {1.f, -1.f};
const float kConstantInput[] = {1.f, 1.f, 1.f, 1.f, 1.f,
1.f, 1.f, 1.f, 1.f, 1.f};
float output[arraysize(kConstantInput)];
SparseFIRFilter filter(kHPCoeffs, arraysize(kHPCoeffs), kSparsity, kOffset);
filter.Filter(kConstantInput, arraysize(kConstantInput), output);
EXPECT_FLOAT_EQ(0.f, output[0]);
EXPECT_FLOAT_EQ(0.f, output[1]);
EXPECT_FLOAT_EQ(1.f, output[2]);
EXPECT_FLOAT_EQ(1.f, output[3]);
for (size_t i = kSparsity + kOffset; i < arraysize(kConstantInput); ++i)
EXPECT_FLOAT_EQ(0.f, output[i]);
}
TEST(SparseFIRFilterTest, SimpleLowPassFilter) {
const size_t kSparsity = 2;
const size_t kOffset = 2;
const float kLPCoeffs[] = {1.f, 1.f};
const float kHighFrequencyInput[] = {1.f, 1.f, -1.f, -1.f, 1.f,
1.f, -1.f, -1.f, 1.f, 1.f};
float output[arraysize(kHighFrequencyInput)];
SparseFIRFilter filter(kLPCoeffs, arraysize(kLPCoeffs), kSparsity, kOffset);
filter.Filter(kHighFrequencyInput, arraysize(kHighFrequencyInput), output);
EXPECT_FLOAT_EQ(0.f, output[0]);
EXPECT_FLOAT_EQ(0.f, output[1]);
EXPECT_FLOAT_EQ(1.f, output[2]);
EXPECT_FLOAT_EQ(1.f, output[3]);
for (size_t i = kSparsity + kOffset; i < arraysize(kHighFrequencyInput); ++i)
EXPECT_FLOAT_EQ(0.f, output[i]);
}
TEST(SparseFIRFilterTest, SameOutputWhenSwappedCoefficientsAndInput) {
const size_t kSparsity = 1;
const size_t kOffset = 0;
float output[arraysize(kCoeffs)];
float output_swapped[arraysize(kCoeffs)];
SparseFIRFilter filter(kCoeffs, arraysize(kCoeffs), kSparsity, kOffset);
// Use arraysize(kCoeffs) for in_length to get same-length outputs.
filter.Filter(kInput, arraysize(kCoeffs), output);
SparseFIRFilter filter_swapped(kInput, arraysize(kCoeffs), kSparsity,
kOffset);
filter_swapped.Filter(kCoeffs, arraysize(kCoeffs), output_swapped);
VerifyOutput(output, output_swapped);
}
TEST(SparseFIRFilterTest, SameOutputAsFIRFilterWhenSparsityOneAndOffsetZero) {
const size_t kSparsity = 1;
const size_t kOffset = 0;
float output[arraysize(kInput)];
float sparse_output[arraysize(kInput)];
std::unique_ptr<FIRFilter> filter(
CreateFirFilter(kCoeffs, arraysize(kCoeffs), arraysize(kInput)));
SparseFIRFilter sparse_filter(kCoeffs, arraysize(kCoeffs), kSparsity,
kOffset);
filter->Filter(kInput, arraysize(kInput), output);
sparse_filter.Filter(kInput, arraysize(kInput), sparse_output);
for (size_t i = 0; i < arraysize(kInput); ++i) {
EXPECT_FLOAT_EQ(output[i], sparse_output[i]);
}
}
} // namespace webrtc