diff --git a/webrtc/common_audio/common_audio.gyp b/webrtc/common_audio/common_audio.gyp index fa0135eff7..c3f141b444 100644 --- a/webrtc/common_audio/common_audio.gyp +++ b/webrtc/common_audio/common_audio.gyp @@ -30,6 +30,10 @@ }, 'sources': [ 'audio_util.cc', + 'fir_filter.cc', + 'fir_filter.h', + 'fir_filter_neon.h', + 'fir_filter_sse.h', 'include/audio_util.h', 'resampler/include/push_resampler.h', 'resampler/include/resampler.h', @@ -152,6 +156,7 @@ 'target_name': 'common_audio_sse2', 'type': 'static_library', 'sources': [ + 'fir_filter_sse.cc', 'resampler/sinc_resampler_sse.cc', ], 'cflags': ['-msse2',], @@ -168,6 +173,7 @@ 'type': 'static_library', 'includes': ['../build/arm_neon.gypi',], 'sources': [ + 'fir_filter_neon.cc', 'resampler/sinc_resampler_neon.cc', 'signal_processing/cross_correlation_neon.S', 'signal_processing/downsample_fast_neon.S', @@ -190,6 +196,7 @@ ], 'sources': [ 'audio_util_unittest.cc', + 'fir_filter_unittest.cc', 'resampler/resampler_unittest.cc', 'resampler/push_resampler_unittest.cc', 'resampler/push_sinc_resampler_unittest.cc', diff --git a/webrtc/common_audio/fir_filter.cc b/webrtc/common_audio/fir_filter.cc new file mode 100644 index 0000000000..a5411b888b --- /dev/null +++ b/webrtc/common_audio/fir_filter.cc @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2014 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 "webrtc/common_audio/fir_filter.h" + +#include +#include + +#include "webrtc/common_audio/fir_filter_neon.h" +#include "webrtc/common_audio/fir_filter_sse.h" +#include "webrtc/system_wrappers/interface/cpu_features_wrapper.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { + +class FIRFilterC : public FIRFilter { + public: + FIRFilterC(const float* coefficients, + size_t coefficients_length); + + virtual void Filter(const float* in, size_t length, float* out) OVERRIDE; + + private: + size_t coefficients_length_; + size_t state_length_; + scoped_ptr coefficients_; + scoped_ptr state_; +}; + +FIRFilter* FIRFilter::Create(const float* coefficients, + size_t coefficients_length, + size_t max_input_length) { + if (!coefficients || coefficients_length <= 0 || max_input_length <= 0) { + assert(false); + return NULL; + } + + FIRFilter* filter = NULL; +// If we know the minimum architecture at compile time, avoid CPU detection. +#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(__SSE2__) + filter = + new FIRFilterSSE2(coefficients, coefficients_length, max_input_length); +#else + // x86 CPU detection required. + if (WebRtc_GetCPUInfo(kSSE2)) { + filter = + new FIRFilterSSE2(coefficients, coefficients_length, max_input_length); + } else { + filter = new FIRFilterC(coefficients, coefficients_length); + } +#endif +#elif defined(WEBRTC_ARCH_ARM_V7) +#if defined(WEBRTC_ARCH_ARM_NEON) + filter = + new FIRFilterNEON(coefficients, coefficients_length, max_input_length); +#else + // ARM CPU detection required. + if (WebRtc_GetCPUFeaturesARM() & kCPUFeatureNEON) { + filter = + new FIRFilterNEON(coefficients, coefficients_length, max_input_length); + } else { + filter = new FIRFilterC(coefficients, coefficients_length); + } +#endif +#else + filter = new FIRFilterC(coefficients, coefficients_length); +#endif + + return filter; +} + +FIRFilterC::FIRFilterC(const float* coefficients, size_t coefficients_length) + : coefficients_length_(coefficients_length), + state_length_(coefficients_length - 1), + coefficients_(new float[coefficients_length_]), + state_(new float[state_length_]) { + for (size_t i = 0; i < coefficients_length_; ++i) { + coefficients_[i] = coefficients[coefficients_length_ - i - 1]; + } + memset(state_.get(), 0.f, state_length_ * sizeof(state_[0])); +} + +void FIRFilterC::Filter(const float* in, size_t length, float* out) { + assert(length > 0); + + // Convolves the input signal |in| with the filter kernel |coefficients_| + // taking into account the previous state. + for (size_t i = 0; i < length; ++i) { + out[i] = 0.f; + size_t j; + for (j = 0; state_length_ > i && j < state_length_ - i; ++j) { + out[i] += state_[i + j] * coefficients_[j]; + } + for (; j < coefficients_length_; ++j) { + out[i] += in[j + i - state_length_] * coefficients_[j]; + } + } + + // Update current state. + if (length >= state_length_) { + memcpy( + state_.get(), &in[length - state_length_], state_length_ * sizeof(*in)); + } else { + memmove(state_.get(), + &state_[length], + (state_length_ - length) * sizeof(state_[0])); + memcpy(&state_[state_length_ - length], in, length * sizeof(*in)); + } +} + +} // namespace webrtc diff --git a/webrtc/common_audio/fir_filter.h b/webrtc/common_audio/fir_filter.h new file mode 100644 index 0000000000..a5dc6eced1 --- /dev/null +++ b/webrtc/common_audio/fir_filter.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014 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 WEBRTC_COMMON_AUDIO_FIR_FILTER_H_ +#define WEBRTC_COMMON_AUDIO_FIR_FILTER_H_ + +#include + +namespace webrtc { + +// Finite Impulse Response filter using floating-point arithmetic. +class FIRFilter { + public: + // Creates a filter with the given coefficients. All initial state values will + // be zeros. + // The length of the chunks fed to the filter should never be greater than + // |max_input_length|. This is needed because, when vectorizing it is + // necessary to concatenate the input after the state, and resizing this array + // dynamically is expensive. + static FIRFilter* Create(const float* coefficients, + size_t coefficients_length, + size_t max_input_length); + + virtual ~FIRFilter() {} + + // Filters the |in| data supplied. + // |out| must be previously allocated and it must be at least of |length|. + virtual void Filter(const float* in, size_t length, float* out) = 0; +}; + +} // namespace webrtc + +#endif // WEBRTC_COMMON_AUDIO_FIR_FILTER_H_ diff --git a/webrtc/common_audio/fir_filter_neon.cc b/webrtc/common_audio/fir_filter_neon.cc new file mode 100644 index 0000000000..97a75db0f2 --- /dev/null +++ b/webrtc/common_audio/fir_filter_neon.cc @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2014 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 "webrtc/common_audio/fir_filter_neon.h" + +#include +#include +#include + +#include "webrtc/system_wrappers/interface/aligned_malloc.h" + +namespace webrtc { + +FIRFilterNEON::FIRFilterNEON(const float* coefficients, + size_t coefficients_length, + size_t max_input_length) + : // Closest higher multiple of four. + coefficients_length_((coefficients_length + 3) & ~0x03), + state_length_(coefficients_length_ - 1), + coefficients_(static_cast( + AlignedMalloc(sizeof(float) * coefficients_length_, 16))), + state_(static_cast( + AlignedMalloc(sizeof(float) * (max_input_length + state_length_), + 16))) { + // Add zeros at the end of the coefficients. + size_t padding = coefficients_length_ - coefficients_length; + memset(coefficients_.get(), 0.f, padding * sizeof(coefficients_[0])); + // The coefficients are reversed to compensate for the order in which the + // input samples are acquired (most recent last). + for (size_t i = 0; i < coefficients_length; ++i) { + coefficients_[i + padding] = coefficients[coefficients_length - i - 1]; + } + memset(state_.get(), + 0.f, + (max_input_length + state_length_) * sizeof(state_[0])); +} + +void FIRFilterNEON::Filter(const float* in, size_t length, float* out) { + assert(length > 0); + + memcpy(&state_[state_length_], in, length * sizeof(*in)); + + // Convolves the input signal |in| with the filter kernel |coefficients_| + // taking into account the previous state. + for (size_t i = 0; i < length; ++i) { + float* in_ptr = &state_[i]; + float* coef_ptr = coefficients_.get(); + + float32x4_t m_sum = vmovq_n_f32(0); + float32x4_t m_in; + + for (size_t j = 0; j < coefficients_length_; j += 4) { + m_in = vld1q_f32(in_ptr + j); + m_sum = vmlaq_f32(m_sum, m_in, vld1q_f32(coef_ptr + j)); + } + + float32x2_t m_half = vadd_f32(vget_high_f32(m_sum), vget_low_f32(m_sum)); + out[i] = vget_lane_f32(vpadd_f32(m_half, m_half), 0); + } + + // Update current state. + memmove(state_.get(), &state_[length], state_length_ * sizeof(state_[0])); +} + +} // namespace webrtc diff --git a/webrtc/common_audio/fir_filter_neon.h b/webrtc/common_audio/fir_filter_neon.h new file mode 100644 index 0000000000..df41c9256c --- /dev/null +++ b/webrtc/common_audio/fir_filter_neon.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2014 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 WEBRTC_COMMON_AUDIO_FIR_FILTER_NEON_H_ +#define WEBRTC_COMMON_AUDIO_FIR_FILTER_NEON_H_ + +#include "webrtc/common_audio/fir_filter.h" +#include "webrtc/system_wrappers/interface/aligned_malloc.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { + +class FIRFilterNEON : public FIRFilter { + public: + FIRFilterNEON(const float* coefficients, + size_t coefficients_length, + size_t max_input_length); + + virtual void Filter(const float* in, size_t length, float* out) OVERRIDE; + + private: + size_t coefficients_length_; + size_t state_length_; + scoped_ptr coefficients_; + scoped_ptr state_; +}; + +} // namespace webrtc + +#endif // WEBRTC_COMMON_AUDIO_FIR_FILTER_NEON_H_ diff --git a/webrtc/common_audio/fir_filter_sse.cc b/webrtc/common_audio/fir_filter_sse.cc new file mode 100644 index 0000000000..7d873a735c --- /dev/null +++ b/webrtc/common_audio/fir_filter_sse.cc @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2014 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 "webrtc/common_audio/fir_filter_sse.h" + +#include +#include +#include + +#include "webrtc/system_wrappers/interface/aligned_malloc.h" + +namespace webrtc { + +FIRFilterSSE2::FIRFilterSSE2(const float* coefficients, + size_t coefficients_length, + size_t max_input_length) + : // Closest higher multiple of four. + coefficients_length_((coefficients_length + 3) & ~0x03), + state_length_(coefficients_length_ - 1), + coefficients_(static_cast( + AlignedMalloc(sizeof(float) * coefficients_length_, 16))), + state_(static_cast( + AlignedMalloc(sizeof(float) * (max_input_length + state_length_), + 16))) { + // Add zeros at the end of the coefficients. + size_t padding = coefficients_length_ - coefficients_length; + memset(coefficients_.get(), 0.f, padding * sizeof(coefficients_[0])); + // The coefficients are reversed to compensate for the order in which the + // input samples are acquired (most recent last). + for (size_t i = 0; i < coefficients_length; ++i) { + coefficients_[i + padding] = coefficients[coefficients_length - i - 1]; + } + memset(state_.get(), + 0.f, + (max_input_length + state_length_) * sizeof(state_[0])); +} + +void FIRFilterSSE2::Filter(const float* in, size_t length, float* out) { + assert(length > 0); + + memcpy(&state_[state_length_], in, length * sizeof(*in)); + + // Convolves the input signal |in| with the filter kernel |coefficients_| + // taking into account the previous state. + for (size_t i = 0; i < length; ++i) { + float* in_ptr = &state_[i]; + float* coef_ptr = coefficients_.get(); + + __m128 m_sum = _mm_setzero_ps(); + __m128 m_in; + + // Depending on if the pointer is aligned with 16 bytes or not it is loaded + // differently. + if (reinterpret_cast(in_ptr) & 0x0F) { + for (size_t j = 0; j < coefficients_length_; j += 4) { + m_in = _mm_loadu_ps(in_ptr + j); + m_sum = _mm_add_ps(m_sum, _mm_mul_ps(m_in, _mm_load_ps(coef_ptr + j))); + } + } else { + for (size_t j = 0; j < coefficients_length_; j += 4) { + m_in = _mm_load_ps(in_ptr + j); + m_sum = _mm_add_ps(m_sum, _mm_mul_ps(m_in, _mm_load_ps(coef_ptr + j))); + } + } + m_sum = _mm_add_ps(_mm_movehl_ps(m_sum, m_sum), m_sum); + _mm_store_ss(out + i, _mm_add_ss(m_sum, _mm_shuffle_ps(m_sum, m_sum, 1))); + } + + // Update current state. + memmove(state_.get(), &state_[length], state_length_ * sizeof(state_[0])); +} + +} // namespace webrtc diff --git a/webrtc/common_audio/fir_filter_sse.h b/webrtc/common_audio/fir_filter_sse.h new file mode 100644 index 0000000000..a0b9164ef9 --- /dev/null +++ b/webrtc/common_audio/fir_filter_sse.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2014 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 WEBRTC_COMMON_AUDIO_FIR_FILTER_SSE_H_ +#define WEBRTC_COMMON_AUDIO_FIR_FILTER_SSE_H_ + +#include "webrtc/common_audio/fir_filter.h" +#include "webrtc/system_wrappers/interface/aligned_malloc.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { + +class FIRFilterSSE2 : public FIRFilter { + public: + FIRFilterSSE2(const float* coefficients, + size_t coefficients_length, + size_t max_input_length); + + virtual void Filter(const float* in, size_t length, float* out) OVERRIDE; + + private: + size_t coefficients_length_; + size_t state_length_; + scoped_ptr coefficients_; + scoped_ptr state_; +}; + +} // namespace webrtc + +#endif // WEBRTC_COMMON_AUDIO_FIR_FILTER_SSE_H_ diff --git a/webrtc/common_audio/fir_filter_unittest.cc b/webrtc/common_audio/fir_filter_unittest.cc new file mode 100644 index 0000000000..01c716197d --- /dev/null +++ b/webrtc/common_audio/fir_filter_unittest.cc @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2014 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 "webrtc/common_audio/fir_filter.h" + +#include + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { + +static const float kCoefficients[] = {0.2f, 0.3f, 0.5f, 0.7f, 0.11f}; +static const size_t kCoefficientsLength = sizeof(kCoefficients) / + sizeof(kCoefficients[0]); + +static const float kInput[] = {1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, + 8.f, 9.f, 10.f}; +static const size_t kInputLength = sizeof(kInput) / + sizeof(kInput[0]); + +void VerifyOutput(const float* expected_output, + const float* output, + size_t length) { + EXPECT_EQ(0, memcmp(expected_output, + output, + length * sizeof(expected_output[0]))); +} + +TEST(FIRFilterTest, FilterAsIdentity) { + const float kCoefficients[] = {1.f, 0.f, 0.f, 0.f, 0.f}; + float output[kInputLength]; + scoped_ptr filter(FIRFilter::Create( + kCoefficients, kCoefficientsLength, kInputLength)); + filter->Filter(kInput, kInputLength, output); + + VerifyOutput(kInput, output, kInputLength); +} + +TEST(FIRFilterTest, FilterUsedAsScalarMultiplication) { + const float kCoefficients[] = {5.f, 0.f, 0.f, 0.f, 0.f}; + float output[kInputLength]; + scoped_ptr filter(FIRFilter::Create( + kCoefficients, kCoefficientsLength, kInputLength)); + filter->Filter(kInput, kInputLength, 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[kInputLength - 1]); +} + +TEST(FIRFilterTest, FilterUsedAsInputShifting) { + const float kCoefficients[] = {0.f, 0.f, 0.f, 0.f, 1.f}; + float output[kInputLength]; + scoped_ptr filter(FIRFilter::Create( + kCoefficients, kCoefficientsLength, kInputLength)); + filter->Filter(kInput, kInputLength, 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[kInputLength - 1]); +} + +TEST(FIRFilterTest, FilterUsedAsArbitraryWeighting) { + float output[kInputLength]; + scoped_ptr filter(FIRFilter::Create( + kCoefficients, kCoefficientsLength, kInputLength)); + filter->Filter(kInput, kInputLength, output); + + EXPECT_FLOAT_EQ(0.2f, output[0]); + EXPECT_FLOAT_EQ(3.4f, output[3]); + EXPECT_FLOAT_EQ(5.21f, output[4]); + EXPECT_FLOAT_EQ(7.02f, output[5]); + EXPECT_FLOAT_EQ(14.26f, output[kInputLength - 1]); +} + +TEST(FIRFilterTest, FilterInLengthLesserOrEqualToCoefficientsLength) { + float output[kInputLength]; + scoped_ptr filter( + FIRFilter::Create(kCoefficients, kCoefficientsLength, 2)); + filter->Filter(kInput, 2, output); + + EXPECT_FLOAT_EQ(0.2f, output[0]); + EXPECT_FLOAT_EQ(0.7f, output[1]); + filter.reset(FIRFilter::Create( + kCoefficients, kCoefficientsLength, kCoefficientsLength)); + filter->Filter(kInput, kCoefficientsLength, output); + + EXPECT_FLOAT_EQ(0.2f, output[0]); + EXPECT_FLOAT_EQ(3.4f, output[3]); + EXPECT_FLOAT_EQ(5.21f, output[4]); +} + +TEST(FIRFilterTest, MultipleFilterCalls) { + float output[kInputLength]; + scoped_ptr filter( + FIRFilter::Create(kCoefficients, kCoefficientsLength, 3)); + 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(FIRFilterTest, VerifySampleBasedVsBlockBasedFiltering) { + float output_block_based[kInputLength]; + scoped_ptr filter(FIRFilter::Create( + kCoefficients, kCoefficientsLength, kInputLength)); + filter->Filter(kInput, kInputLength, output_block_based); + + float output_sample_based[kInputLength]; + filter.reset(FIRFilter::Create(kCoefficients, kCoefficientsLength, 1)); + for (size_t i = 0; i < kInputLength; ++i) { + filter->Filter(&kInput[i], 1, &output_sample_based[i]); + } + + EXPECT_EQ(0, memcmp(output_sample_based, + output_block_based, + kInputLength)); +} + +TEST(FIRFilterTest, SimplestHighPassFilter) { + const float kCoefficients[] = {1.f, -1.f}; + const size_t kCoefficientsLength = sizeof(kCoefficients) / + sizeof(kCoefficients[0]); + + float kConstantInput[] = {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}; + const size_t kConstantInputLength = sizeof(kConstantInput) / + sizeof(kConstantInput[0]); + + float output[kConstantInputLength]; + scoped_ptr filter(FIRFilter::Create( + kCoefficients, kCoefficientsLength, kConstantInputLength)); + filter->Filter(kConstantInput, kConstantInputLength, output); + EXPECT_FLOAT_EQ(1.f, output[0]); + for (size_t i = kCoefficientsLength - 1; i < kConstantInputLength; ++i) { + EXPECT_FLOAT_EQ(0.f, output[i]); + } +} + +TEST(FIRFilterTest, SimplestLowPassFilter) { + const float kCoefficients[] = {1.f, 1.f}; + const size_t kCoefficientsLength = sizeof(kCoefficients) / + sizeof(kCoefficients[0]); + + float kHighFrequencyInput[] = {-1.f, 1.f, -1.f, 1.f, -1.f, 1.f, -1.f, 1.f}; + const size_t kHighFrequencyInputLength = sizeof(kHighFrequencyInput) / + sizeof(kHighFrequencyInput[0]); + + float output[kHighFrequencyInputLength]; + scoped_ptr filter(FIRFilter::Create( + kCoefficients, kCoefficientsLength, kHighFrequencyInputLength)); + filter->Filter(kHighFrequencyInput, kHighFrequencyInputLength, output); + EXPECT_FLOAT_EQ(-1.f, output[0]); + for (size_t i = kCoefficientsLength - 1; i < kHighFrequencyInputLength; ++i) { + EXPECT_FLOAT_EQ(0.f, output[i]); + } +} + +TEST(FIRFilterTest, SameOutputWhenSwapedCoefficientsAndInput) { + float output[kCoefficientsLength]; + float output_swaped[kCoefficientsLength]; + scoped_ptr filter(FIRFilter::Create( + kCoefficients, kCoefficientsLength, kCoefficientsLength)); + // Use kCoefficientsLength for in_length to get same-length outputs. + filter->Filter(kInput, kCoefficientsLength, output); + + filter.reset(FIRFilter::Create( + kInput, kCoefficientsLength, kCoefficientsLength)); + filter->Filter(kCoefficients, kCoefficientsLength, output_swaped); + + for (size_t i = 0 ; i < kCoefficientsLength; ++i) { + EXPECT_FLOAT_EQ(output[i], output_swaped[i]); + } +} + +} // namespace webrtc