Loading...
Searching...
No Matches
juce_FIRFilter_test.cpp
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce::dsp
{
class FIRFilterTest final : public UnitTest
{
template <typename Type>
struct Helpers
{
static void fillRandom (Random& random, Type* buffer, size_t n)
{
for (size_t i = 0; i < n; ++i)
buffer[i] = (2.0f * random.nextFloat()) - 1.0f;
}
static bool checkArrayIsSimilar (Type* a, Type* b, size_t n) noexcept
{
for (size_t i = 0; i < n; ++i)
if (std::abs (a[i] - b[i]) > 1e-6f)
return false;
return true;
}
};
#if JUCE_USE_SIMD
template <typename Type>
struct Helpers<SIMDRegister<Type>>
{
static void fillRandom (Random& random, SIMDRegister<Type>* buffer, size_t n)
{
Helpers<Type>::fillRandom (random, reinterpret_cast<Type*> (buffer), n * SIMDRegister<Type>::size());
}
static bool checkArrayIsSimilar (SIMDRegister<Type>* a, SIMDRegister<Type>* b, size_t n) noexcept
{
return Helpers<Type>::checkArrayIsSimilar (reinterpret_cast<Type*> (a),
reinterpret_cast<Type*> (b),
n * SIMDRegister<Type>::size());
}
};
#endif
template <typename Type>
static void fillRandom (Random& random, Type* buffer, size_t n) { Helpers<Type>::fillRandom (random, buffer, n); }
template <typename Type>
static bool checkArrayIsSimilar (Type* a, Type* b, size_t n) noexcept { return Helpers<Type>::checkArrayIsSimilar (a, b, n); }
//==============================================================================
// reference implementation of an FIR
template <typename SampleType, typename NumericType>
static void reference (const NumericType* firCoefficients, size_t numCoefficients,
const SampleType* input, SampleType* output, size_t n) noexcept
{
if (numCoefficients == 0)
{
zeromem (output, sizeof (SampleType) * n);
return;
}
HeapBlock<SampleType> scratchBuffer (numCoefficients
#if JUCE_USE_SIMD
+ (SIMDRegister<NumericType>::SIMDRegisterSize / sizeof (SampleType))
#endif
);
#if JUCE_USE_SIMD
SampleType* buffer = reinterpret_cast<SampleType*> (SIMDRegister<NumericType>::getNextSIMDAlignedPtr (reinterpret_cast<NumericType*> (scratchBuffer.getData())));
#else
SampleType* buffer = scratchBuffer.getData();
#endif
zeromem (buffer, sizeof (SampleType) * numCoefficients);
for (size_t i = 0; i < n; ++i)
{
for (size_t j = (numCoefficients - 1); j >= 1; --j)
buffer[j] = buffer[j-1];
buffer[0] = input[i];
SampleType sum (0);
for (size_t j = 0; j < numCoefficients; ++j)
sum += buffer[j] * firCoefficients[j];
output[i] = sum;
}
}
//==============================================================================
struct LargeBlockTest
{
template <typename FloatType>
static void run (FIR::Filter<FloatType>& filter, FloatType* src, FloatType* dst, size_t n)
{
AudioBlock<const FloatType> input (&src, 1, n);
AudioBlock<FloatType> output (&dst, 1, n);
ProcessContextNonReplacing<FloatType> context (input, output);
filter.process (context);
}
};
struct SampleBySampleTest
{
template <typename FloatType>
static void run (FIR::Filter<FloatType>& filter, FloatType* src, FloatType* dst, size_t n)
{
for (size_t i = 0; i < n; ++i)
dst[i] = filter.processSample (src[i]);
}
};
struct SplitBlockTest
{
template <typename FloatType>
static void run (FIR::Filter<FloatType>& filter, FloatType* input, FloatType* output, size_t n)
{
size_t len = 0;
for (size_t i = 0; i < n; i += len)
{
len = jmin (n - i, n / 3);
auto* src = input + i;
auto* dst = output + i;
AudioBlock<const FloatType> inBlock (&src, 1, len);
AudioBlock<FloatType> outBlock (&dst, 1, len);
ProcessContextNonReplacing<FloatType> context (inBlock, outBlock);
filter.process (context);
}
}
};
//==============================================================================
template <typename TheTest, typename SampleType, typename NumericType>
void runTestForType()
{
Random random (8392829);
for (auto size : {1, 2, 4, 8, 12, 13, 25})
{
constexpr size_t n = 813;
HeapBlock<char> inputBuffer, outputBuffer, refBuffer;
AudioBlock<SampleType> input (inputBuffer, 1, n), output (outputBuffer, 1, n), ref (refBuffer, 1, n);
fillRandom (random, input.getChannelPointer (0), n);
HeapBlock<char> firBlock;
AudioBlock<NumericType> fir (firBlock, 1, static_cast<size_t> (size));
fillRandom (random, fir.getChannelPointer (0), static_cast<size_t> (size));
FIR::Filter<SampleType> filter (*new FIR::Coefficients<NumericType> (fir.getChannelPointer (0), static_cast<size_t> (size)));
ProcessSpec spec {0.0, n, 1};
filter.prepare (spec);
reference<SampleType, NumericType> (fir.getChannelPointer (0), static_cast<size_t> (size),
input.getChannelPointer (0), ref.getChannelPointer (0), n);
TheTest::template run<SampleType> (filter, input.getChannelPointer (0), output.getChannelPointer (0), n);
expect (checkArrayIsSimilar (output.getChannelPointer (0), ref.getChannelPointer (0), n));
}
}
template <typename TheTest>
void runTestForAllTypes (const char* unitTestName)
{
beginTest (unitTestName);
runTestForType<TheTest, float, float>();
runTestForType<TheTest, double, double>();
#if JUCE_USE_SIMD
runTestForType<TheTest, SIMDRegister<float>, float>();
runTestForType<TheTest, SIMDRegister<double>, double>();
#endif
}
public:
FIRFilterTest()
: UnitTest ("FIR Filter", UnitTestCategories::dsp)
{}
void runTest() override
{
runTestForAllTypes<LargeBlockTest> ("Large Blocks");
runTestForAllTypes<SampleBySampleTest> ("Sample by Sample");
runTestForAllTypes<SplitBlockTest> ("Split Block");
}
};
static FIRFilterTest firFilterUnitTest;
} // namespace juce::dsp
juce::UnitTest::UnitTest
UnitTest(const String &name, const String &category=String())
Definition juce_UnitTest.cpp:26
juce::UnitTest::beginTest
void beginTest(const String &testName)
Definition juce_UnitTest.cpp:89
juce::UnitTest::expect
void expect(bool testResult, const String &failureMessage=String())
Definition juce_UnitTest.cpp:97
juce::UnitTest::runTest
virtual void runTest()=0
juce::dsp::SIMDRegister::getNextSIMDAlignedPtr
static ElementType * getNextSIMDAlignedPtr(ElementType *ptr) noexcept
Definition juce_SIMDRegister.h:350
juce::dsp::SIMDRegister::SIMDRegisterSize
static constexpr size_t SIMDRegisterSize
Definition juce_SIMDRegister.h:101
juce::dsp::SIMDRegister::size
static constexpr size_t size() noexcept
Definition juce_SIMDRegister.h:119