From 8af7b7bfa7d24f4330b168304074f014b7b2d9ff Mon Sep 17 00:00:00 2001 From: paul Date: Fri, 9 Aug 2019 18:24:27 +0200 Subject: [PATCH] Updates --- .gitignore | 4 +- .gitmodules | 6 + CMakeLists.txt | 42 +++-- benchmarks/OPF_high_vs_low.cpp | 168 ++++++++++++++++++ benchmarks/Spans.cpp | 111 ++++++++++++ external/cnpy | 1 + external/gsl-lite | 1 + old/StateVariableFilter.cpp | 35 ++++ old/StateVariableFilter.h | 27 +++ old/StateVariableFilterSSE.cpp | 36 ++++ sources/FilePool.h | 2 +- sources/OnePoleFilter.h | 94 ++++++++++ tests/OnePoleFilterT.cpp | 102 +++++++++++ .../OnePoleFilter/OPF_input_gain_0.1.npy | Bin 0 -> 640 bytes .../OnePoleFilter/OPF_input_gain_0.3.npy | Bin 0 -> 640 bytes .../OnePoleFilter/OPF_input_gain_0.5.npy | Bin 0 -> 640 bytes .../OnePoleFilter/OPF_input_gain_0.7.npy | Bin 0 -> 640 bytes .../OnePoleFilter/OPF_input_gain_0.9.npy | Bin 0 -> 640 bytes .../OnePoleFilter/OPF_output_gain_0.1.npy | Bin 0 -> 640 bytes .../OnePoleFilter/OPF_output_gain_0.3.npy | Bin 0 -> 640 bytes .../OnePoleFilter/OPF_output_gain_0.5.npy | Bin 0 -> 640 bytes .../OnePoleFilter/OPF_output_gain_0.7.npy | Bin 0 -> 640 bytes .../OnePoleFilter/OPF_output_gain_0.9.npy | Bin 0 -> 640 bytes 23 files changed, 611 insertions(+), 18 deletions(-) create mode 100644 benchmarks/OPF_high_vs_low.cpp create mode 100644 benchmarks/Spans.cpp create mode 160000 external/cnpy create mode 160000 external/gsl-lite create mode 100644 old/StateVariableFilter.cpp create mode 100644 old/StateVariableFilter.h create mode 100644 old/StateVariableFilterSSE.cpp create mode 100644 sources/OnePoleFilter.h create mode 100644 tests/OnePoleFilterT.cpp create mode 100644 tests/TestFiles/OnePoleFilter/OPF_input_gain_0.1.npy create mode 100644 tests/TestFiles/OnePoleFilter/OPF_input_gain_0.3.npy create mode 100644 tests/TestFiles/OnePoleFilter/OPF_input_gain_0.5.npy create mode 100644 tests/TestFiles/OnePoleFilter/OPF_input_gain_0.7.npy create mode 100644 tests/TestFiles/OnePoleFilter/OPF_input_gain_0.9.npy create mode 100644 tests/TestFiles/OnePoleFilter/OPF_output_gain_0.1.npy create mode 100644 tests/TestFiles/OnePoleFilter/OPF_output_gain_0.3.npy create mode 100644 tests/TestFiles/OnePoleFilter/OPF_output_gain_0.5.npy create mode 100644 tests/TestFiles/OnePoleFilter/OPF_output_gain_0.7.npy create mode 100644 tests/TestFiles/OnePoleFilter/OPF_output_gain_0.9.npy diff --git a/.gitignore b/.gitignore index b7539ce9..3c66e5f9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ build build-arm build-subl -.vscode \ No newline at end of file +.vscode +perf.data +perf.data.old \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 4ee0bf4e..b52b5486 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,3 +16,9 @@ [submodule "external/readerwriterqueue"] path = external/readerwriterqueue url = https://github.com/cameron314/readerwriterqueue.git +[submodule "external/gsl-lite"] + path = external/gsl-lite + url = https://github.com/martinmoene/gsl-lite.git +[submodule "external/cnpy"] + path = external/cnpy + url = https://github.com/rogersce/cnpy.git diff --git a/CMakeLists.txt b/CMakeLists.txt index f8edc1f2..eeb23cba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,6 @@ if (UNIX) add_compile_options(-Wextra) # add_compile_options(-fno-exceptions) add_compile_options(-ffast-math) - add_compile_options(-fno-rtti) # add_compile_options(-fno-omit-frame-pointer) endif() @@ -47,7 +46,9 @@ add_subdirectory(external/abseil-cpp) add_subdirectory(external/spdlog) add_subdirectory(external/Catch2) add_subdirectory(external/cxxopts) +add_subdirectory(external/gsl-lite) add_subdirectory(external/benchmark) +add_subdirectory(external/cnpy) # Download libsndfile if (WIN32) @@ -106,6 +107,7 @@ set(TEST_SOURCES tests/BufferT.cpp tests/StereoBufferT.cpp tests/FilesT.cpp + tests/OnePoleFilterT.cpp tests/MainT.cpp ) @@ -121,8 +123,12 @@ target_link_libraries(sfzprint absl::strings cxxopts) ############################### # Basic command line program add_executable(sfizz sources/Main.cpp ${COMMON_SOURCES}) +if (HAVE_INTRIN_H OR HAVE_X86INTRIN_H) + target_sources(sfizz PRIVATE ${SSE_SOURCES}) +endif() # Per OS properties if(UNIX) + target_compile_options(sfizz PRIVATE -fno-rtti) target_link_libraries(sfizz stdc++fs) endif(UNIX) target_link_libraries(sfizz absl::strings cxxopts sndfile absl::flat_hash_map readerwriterqueue) @@ -131,33 +137,37 @@ target_link_libraries(sfizz absl::strings cxxopts sndfile absl::flat_hash_map re # Test application add_executable(sfizz_tests ${TEST_SOURCES} ${COMMON_SOURCES}) # Per OS properties +if (HAVE_INTRIN_H OR HAVE_X86INTRIN_H) + target_sources(sfizz_tests PRIVATE ${SSE_SOURCES}) +endif() if(UNIX) -target_link_libraries(sfizz_tests stdc++fs) + target_link_libraries(sfizz_tests stdc++fs) endif(UNIX) -target_link_libraries(sfizz_tests Catch2::Catch2 absl::strings absl::flat_hash_map sndfile readerwriterqueue) +target_link_libraries(sfizz_tests Catch2::Catch2 absl::strings absl::flat_hash_map sndfile readerwriterqueue cnpy-static gsl::gsl-lite) target_include_directories(sfizz_tests SYSTEM PRIVATE sources) file(COPY "tests" DESTINATION ${CMAKE_BINARY_DIR}) ############################### -add_executable(bench_fill benchmarks/Stereo_Fill.cpp ${COMMON_SOURCES}) +add_executable(bench_fill benchmarks/Stereo_Fill.cpp sources/StereoBufferSSE.cpp) # Per OS properties -if(UNIX) - target_link_libraries(bench_fill stdc++fs) -endif(UNIX) -target_link_libraries(bench_fill absl::strings benchmark) +target_link_libraries(bench_fill benchmark) ############################### add_executable(bench_read benchmarks/Stereo_Read_Interleaved.cpp sources/StereoBufferSSE.cpp) # Per OS properties -if(UNIX) - target_link_libraries(bench_read stdc++fs) -endif(UNIX) -target_link_libraries(bench_read absl::strings benchmark) +target_link_libraries(bench_read benchmark) ############################### add_executable(bench_write benchmarks/Stereo_Write_Interleaved.cpp sources/StereoBufferSSE.cpp) # Per OS properties -if(UNIX) - target_link_libraries(bench_write stdc++fs) -endif(UNIX) -target_link_libraries(bench_write absl::strings benchmark) +target_link_libraries(bench_write benchmark) + +############################### +add_executable(bench_span benchmarks/Spans.cpp) +# Per OS properties +target_link_libraries(bench_span benchmark gsl::gsl-lite absl::span) + +############################### +add_executable(bench_opf_high_vs_low benchmarks/OPF_high_vs_low.cpp) +# Per OS properties +target_link_libraries(bench_opf_high_vs_low benchmark gsl::gsl-lite) diff --git a/benchmarks/OPF_high_vs_low.cpp b/benchmarks/OPF_high_vs_low.cpp new file mode 100644 index 00000000..f8e0fa27 --- /dev/null +++ b/benchmarks/OPF_high_vs_low.cpp @@ -0,0 +1,168 @@ +#include +#include "gsl/gsl-lite.hpp" +#include +#include +#include "../sources/Intrinsics.h" + +constexpr float filterGain { 0.25f }; + +void lowpass(gsl::span input, gsl::span lowpass, float gain) +{ + float state = 0.0f; + float intermediate; + const auto G = gain / (1 - gain); + for (auto [in, out] = std::make_pair(input.begin(), lowpass.begin()); + in < input.end() && out < lowpass.end(); + in++, out++) + { + intermediate = G * (*in - state); + *out = intermediate + state; + state = *out + intermediate; + } +} + +void highpass(gsl::span input, gsl::span highpass, float gain) +{ + float state = 0.0f; + float intermediate; + const auto G = gain / (1 - gain); + for (auto [in, out] = std::make_pair(input.begin(), highpass.begin()); + in < input.end() && out < highpass.end(); + in++, out++) + { + intermediate = G * (*in - state); + *out = *in - intermediate - state; + state += 2*intermediate; + } +} + +void highpass_foreach(gsl::span input, gsl::span highpass, float gain) +{ + lowpass(input, highpass, gain); + for (auto [in, out] = std::make_pair(input.begin(), highpass.begin()); + in < input.end() && out < highpass.end(); + in++, out++) + *out = *in - *out; +} + +void highpass_raw(gsl::span input, gsl::span highpass, float gain) +{ + lowpass(input, highpass, gain); + auto in = input.data(); + auto out = highpass.data(); + while (in < input.end() && out < highpass.end()) + { + *out = *in - *out; + out++; + in++; + } +} + +void highpass_sse(gsl::span input, gsl::span highpass, float gain) +{ + lowpass(input, highpass, gain); + auto in = input.data(); + auto out = highpass.data(); + constexpr int FloatAlignment { 4 }; + const auto inputAlignedEnd = input.data() + (input.size() - (input.size() & (FloatAlignment - 1))); + const auto outputAlignedEnd = highpass.data() + (highpass.size() - (highpass.size() & (FloatAlignment - 1))); + while (in < inputAlignedEnd && out < outputAlignedEnd) + { + const auto inputRegister = _mm_loadu_ps(in); + auto outputRegister = _mm_loadu_ps(out); + outputRegister = _mm_sub_ps(inputRegister, outputRegister); + _mm_storeu_ps(out, outputRegister); + in += 4; + out += 4; + } + while (in < input.end() && out < highpass.end()) + { + *out = *in - *out; + out++; + in++; + } +} + + +static void Low(benchmark::State& state) { + std::vector input(state.range(0)); + std::vector output(state.range(0)); + std::random_device rd { }; + std::mt19937 gen { rd() }; + + std::normal_distribution dist { }; + std::generate(input.begin(), input.end(), [&]() { + return dist(gen); + }); + + for (auto _ : state) + lowpass(input, output, filterGain); +} + +static void High(benchmark::State& state) { + std::vector input(state.range(0)); + std::vector output(state.range(0)); + std::random_device rd { }; + std::mt19937 gen { rd() }; + + std::normal_distribution dist { }; + std::generate(input.begin(), input.end(), [&]() { + return dist(gen); + }); + + for (auto _ : state) + highpass(input, output, filterGain); +} + +static void High_ForEach(benchmark::State& state) { + std::vector input(state.range(0)); + std::vector output(state.range(0)); + std::random_device rd { }; + std::mt19937 gen { rd() }; + + std::normal_distribution dist { }; + std::generate(input.begin(), input.end(), [&]() { + return dist(gen); + }); + + for (auto _ : state) + highpass_foreach(input, output, filterGain); +} + +static void High_Raw(benchmark::State& state) { + std::vector input(state.range(0)); + std::vector output(state.range(0)); + std::random_device rd { }; + std::mt19937 gen { rd() }; + + std::normal_distribution dist { }; + std::generate(input.begin(), input.end(), [&]() { + return dist(gen); + }); + + for (auto _ : state) + highpass_raw(input, output, filterGain); +} + +static void High_SSE(benchmark::State& state) { + std::vector input(state.range(0)); + std::vector output(state.range(0)); + std::random_device rd { }; + std::mt19937 gen { rd() }; + + std::normal_distribution dist { }; + std::generate(input.begin(), input.end(), [&]() { + return dist(gen); + }); + + for (auto _ : state) + highpass_sse(input, output, filterGain); +} + + +BENCHMARK(Low)->RangeMultiplier(2)->Range((2<<5), (2<<10)); +BENCHMARK(High)->RangeMultiplier(2)->Range((2<<5), (2<<10)); +BENCHMARK(High_ForEach)->RangeMultiplier(2)->Range((2<<5), (2<<10)); +BENCHMARK(High_Raw)->RangeMultiplier(2)->Range((2<<5), (2<<10)); +BENCHMARK(High_SSE)->RangeMultiplier(2)->Range((2<<5), (2<<10)); +BENCHMARK_MAIN(); \ No newline at end of file diff --git a/benchmarks/Spans.cpp b/benchmarks/Spans.cpp new file mode 100644 index 00000000..f537462c --- /dev/null +++ b/benchmarks/Spans.cpp @@ -0,0 +1,111 @@ +#include +#include "gsl/gsl-lite.hpp" +#include "absl/types/span.h" +#include +#include + +constexpr float filterGain { 0.25f }; + +void processRaw(float* input, float* lowpass, float gain, int numSamples) +{ + const auto end = input + numSamples; + float state = 0.0f; + float intermediate; + const auto G = gain / (1 - gain); + while (input < end) + { + intermediate = G * (*input++ - state); + *lowpass = intermediate + state; + state = *lowpass++ + intermediate; + } +} + +void processGSLSpan(gsl::span input, gsl::span lowpass, float gain) +{ + + float state = 0.0f; + float intermediate; + const auto G = gain / (1 - gain); + for (auto [in, out] = std::make_pair(input.begin(), lowpass.begin()); + in < input.end() && out < lowpass.end(); + in++, out++) + { + intermediate = G * (*in - state); + *out = intermediate + state; + state = *out + intermediate; + } +} + +void processABSLSpan(absl::Span input, absl::Span lowpass, float gain) +{ + + float state = 0.0f; + float intermediate; + const auto G = gain / (1 - gain); + for (auto [in, out] = std::make_pair(input.begin(), lowpass.begin()); + in < input.end() && out < lowpass.end(); + in++, out++) + { + intermediate = G * (*in - state); + *out = intermediate + state; + state = *out + intermediate; + } +} + +static void Raw(benchmark::State& state) { + std::vector input(state.range(0)); + std::vector output(state.range(0)); + std::random_device rd { }; + std::mt19937 gen { rd() }; + + std::normal_distribution dist { }; + std::generate(input.begin(), input.end(), [&]() { + return dist(gen); + }); + + for (auto _ : state) + { + processRaw(input.data(), output.data(), filterGain, state.range(0)); + } +} + +static void GSLSpan(benchmark::State& state) { + std::vector input(state.range(0)); + std::vector output(state.range(0)); + std::random_device rd { }; + std::mt19937 gen { rd() }; + + std::normal_distribution dist { }; + std::generate(input.begin(), input.end(), [&]() { + return dist(gen); + }); + + for (auto _ : state) + { + processGSLSpan(input, output, filterGain); + } +} + +static void ABSLSpan(benchmark::State& state) { + std::vector input(state.range(0)); + std::vector output(state.range(0)); + std::random_device rd { }; + std::mt19937 gen { rd() }; + + std::normal_distribution dist { }; + std::generate(input.begin(), input.end(), [&]() { + return dist(gen); + }); + + for (auto _ : state) + { + processABSLSpan(input, absl::MakeSpan(output), filterGain); + } +} + +// Register the function as a benchmark + +BENCHMARK(Raw)->RangeMultiplier(2)->Range((2<<6), (2<<12)); +BENCHMARK(GSLSpan)->RangeMultiplier(2)->Range((2<<6), (2<<12)); +BENCHMARK(ABSLSpan)->RangeMultiplier(2)->Range((2<<6), (2<<12)); +BENCHMARK_MAIN(); \ No newline at end of file diff --git a/external/cnpy b/external/cnpy new file mode 160000 index 00000000..4e8810b1 --- /dev/null +++ b/external/cnpy @@ -0,0 +1 @@ +Subproject commit 4e8810b1a8637695171ed346ce68f6984e585ef4 diff --git a/external/gsl-lite b/external/gsl-lite new file mode 160000 index 00000000..df36f637 --- /dev/null +++ b/external/gsl-lite @@ -0,0 +1 @@ +Subproject commit df36f6378336280043d7e06d8013e6fa4af6abd7 diff --git a/old/StateVariableFilter.cpp b/old/StateVariableFilter.cpp new file mode 100644 index 00000000..11e1311f --- /dev/null +++ b/old/StateVariableFilter.cpp @@ -0,0 +1,35 @@ +#include "StateVariableFilter.h" +#include + +// template<> +// template<> +// auto StateVariableFilter::process(const float& input [[maybe_unused]]) +// { +// return 0.0; +// } + +// template<> +// template<> +// auto StateVariableFilter::process(const float& input [[maybe_unused]]) +// { +// return std::make_pair(0.0, 0.0); +// } + +// template<> +// template<> +// auto StateVariableFilter::process(const float& input [[maybe_unused]]) +// { +// return std::make_tuple(0.0, 0.0, 0.0); +// } + +template<> +auto StateVariableFilter::process(const float& input [[maybe_unused]], float& bandpass [[maybe_unused]]) +{ + +} + +template<> +auto StateVariableFilter::process(const float& input [[maybe_unused]], float& bandpass [[maybe_unused]], float& lowpass [[maybe_unused]]) +{ + +} \ No newline at end of file diff --git a/old/StateVariableFilter.h b/old/StateVariableFilter.h new file mode 100644 index 00000000..ceb3620b --- /dev/null +++ b/old/StateVariableFilter.h @@ -0,0 +1,27 @@ +#pragma once +#include "Globals.h" +#include +#include + +// enum class SVFReturn { BP_LP_HP, BP_LP, BP}; + +// template +class StateVariableFilter +{ +public: + StateVariableFilter() = default; + StateVariableFilter(float R, float gain) + : R(R), gain(gain) + { + + } + + // Only use specializations + template + auto process(const InputType& input, OutputTypes... outputs) = delete; + +private: + std::array state; + float R { 0.0 }; + float gain { 1.0 }; +}; \ No newline at end of file diff --git a/old/StateVariableFilterSSE.cpp b/old/StateVariableFilterSSE.cpp new file mode 100644 index 00000000..b8a51306 --- /dev/null +++ b/old/StateVariableFilterSSE.cpp @@ -0,0 +1,36 @@ +#include "StateVariableFilter.h" +#include +#include "Intrinsics.h" + +// template<> +// template<> +// auto StateVariableFilter::process<__m128>(const __m128& input [[maybe_unused]]) +// { +// return 0.0; +// } + +// template<> +// template<> +// auto StateVariableFilter::process<__m128>(const __m128& input [[maybe_unused]]) +// { +// return std::make_pair(0.0, 0.0); +// } + +// template<> +// template<> +// auto StateVariableFilter::process<__m128>(const __m128& input [[maybe_unused]]) +// { +// return std::make_tuple(0.0, 0.0, 0.0); +// } + +template<> +auto StateVariableFilter::process(const __m128& input [[maybe_unused]], __m128& bandpass [[maybe_unused]]) +{ + +} + +template<> +auto StateVariableFilter::process(const __m128& input [[maybe_unused]], __m128& bandpass [[maybe_unused]], __m128& lowpass [[maybe_unused]]) +{ + +} \ No newline at end of file diff --git a/sources/FilePool.h b/sources/FilePool.h index 424d2b4a..2fd826db 100644 --- a/sources/FilePool.h +++ b/sources/FilePool.h @@ -8,7 +8,7 @@ #include #include #include -#include +#include "readerwriterqueue.h" #include namespace sfz diff --git a/sources/OnePoleFilter.h b/sources/OnePoleFilter.h new file mode 100644 index 00000000..d8a64383 --- /dev/null +++ b/sources/OnePoleFilter.h @@ -0,0 +1,94 @@ +#include "Globals.h" +#include +#include "gsl/gsl-lite.hpp" + +template +class OnePoleFilter +{ +public: + OnePoleFilter() = default; + // Normalized cutoff with respect to the sampling rate + template + static Type normalizedGain(Type cutoff, C sampleRate) + { + return std::tan( cutoff / static_cast(sampleRate) * M_PIf32 ); + } + + OnePoleFilter(Type gain) + { + setGain(gain); + } + + void setGain(Type gain) + { + this->gain = gain; + G = gain / ( 1 + gain); + } + + Type getGain() const { return gain; } + + int processLowpass(gsl::span input, gsl::span lowpass) + { + for (auto [in, out] = std::pair(input.begin(), lowpass.begin()); + in < input.end() && out < lowpass.end(); in++, out++) + { + oneLowpass(in, out); + } + return std::min(input.size(), lowpass.size()); + } + + int processHighpass(gsl::span input, gsl::span highpass) + { + for (auto [in, out] = std::pair(input.begin(), highpass.begin()); + in < input.end() && out < highpass.end(); in++, out++) + { + oneHighpass(in, out); + } + return std::min(input.size(), highpass.size()); + } + + int processLowpassVariableGain(gsl::span input, gsl::span lowpass, gsl::span gain) + { + for (auto [in, out, g] = std::tuple(input.begin(), lowpass.begin(), gain.begin()); + in < input.end() && out < lowpass.end() && g < gain.end(); in++, out++, g++) + { + setGain(*g); + oneLowpass(in, out); + } + + return std::min({ input.size(), lowpass.size(), gain.size() }); + } + + int processHighpassVariableGain(gsl::span input, gsl::span highpass, gsl::span gain) + { + for (auto [in, out, g] = std::tuple(input.begin(), highpass.begin(), gain.begin()); + in < input.end() && out < highpass.end() && g < gain.end(); in++, out++, g++) + { + setGain(*g); + oneHighpass(in, out); + } + + return std::min({ input.size(), highpass.size(), gain.size() }); + } + + void reset() { state = 0.0; } +private: + Type state { 0.0 }; + Type gain { 0.25 }; + Type intermediate { 0.0 }; + Type G { gain / (1 + gain) }; + + inline void oneLowpass(const Type* in, Type* out) + { + intermediate = G * (*in - state); + *out = intermediate + state; + state = *out + intermediate; + } + + inline void oneHighpass(const Type* in, Type* out) + { + intermediate = G * (*in - state); + *out = *in - intermediate - state; + state += 2*intermediate; + } +}; \ No newline at end of file diff --git a/tests/OnePoleFilterT.cpp b/tests/OnePoleFilterT.cpp new file mode 100644 index 00000000..1d29efdb --- /dev/null +++ b/tests/OnePoleFilterT.cpp @@ -0,0 +1,102 @@ +#include "../sources/OnePoleFilter.h" +#include "catch2/catch.hpp" +#include "cnpy.h" +#include "gsl/gsl-lite.hpp" +#include +#include +using namespace Catch::literals; + +template +void testInputOutput(const std::filesystem::path& inputNumpyFile, const std::filesystem::path& outputNumpyFile, Type gain) +{ + const auto input = cnpy::npy_load(inputNumpyFile.string()); + REQUIRE( input.word_size == 8 ); + const auto inputSpan = gsl::make_span(input.data(), input.shape[0]); + + const auto output = cnpy::npy_load(outputNumpyFile.string()); + REQUIRE( output.word_size == 8 ); + const auto outputSpan = gsl::make_span(output.data(), output.shape[0]); + auto size = std::min(outputSpan.size(), inputSpan.size()); + REQUIRE( size > 0 ); + + std::vector inputData; + std::vector expectedData; + inputData.reserve(size); + expectedData.reserve(size); + for (auto& data: inputSpan) + inputData.push_back(static_cast(data)); + for (auto& data: outputSpan) + expectedData.push_back(static_cast(data)); + + OnePoleFilter filter { gain }; + std::vector outputData (size); + filter.processLowpass(inputData, outputData); + for (size_t i = 0; i < size; ++i) + REQUIRE( outputData[i] == Approx(expectedData[i]) ); + + filter.reset(); + std::fill(outputData.begin(), outputData.end(), 0.0); + std::vector gains(size); + std::fill(gains.begin(), gains.end(), gain); + filter.processLowpassVariableGain(inputData, outputData, gains); + for (size_t i = 0; i < size; ++i) + REQUIRE( outputData[i] == Approx(expectedData[i]) ); +} + +TEST_CASE("[OnePoleFilter] Float") +{ + testInputOutput( + std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.1.npy", + std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_output_gain_0.1.npy", + 0.1f + ); + testInputOutput( + std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.3.npy", + std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_output_gain_0.3.npy", + 0.3f + ); + testInputOutput( + std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.5.npy", + std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_output_gain_0.5.npy", + 0.5f + ); + testInputOutput( + std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.7.npy", + std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_output_gain_0.7.npy", + 0.7f + ); + testInputOutput( + std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.9.npy", + std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_output_gain_0.9.npy", + 0.9f + ); +} + +TEST_CASE("[OnePoleFilter] Double") +{ + testInputOutput( + std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.1.npy", + std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_output_gain_0.1.npy", + 0.1f + ); + testInputOutput( + std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.3.npy", + std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_output_gain_0.3.npy", + 0.3f + ); + testInputOutput( + std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.5.npy", + std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_output_gain_0.5.npy", + 0.5f + ); + testInputOutput( + std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.7.npy", + std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_output_gain_0.7.npy", + 0.7f + ); + testInputOutput( + std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.9.npy", + std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_output_gain_0.9.npy", + 0.9f + ); +} diff --git a/tests/TestFiles/OnePoleFilter/OPF_input_gain_0.1.npy b/tests/TestFiles/OnePoleFilter/OPF_input_gain_0.1.npy new file mode 100644 index 0000000000000000000000000000000000000000..7c87bb65274b8c256611685b37a0891a1d0c28fd GIT binary patch literal 640 zcmbR27wQ`j$;eQ~P_3SlTAW;@Zl$1ZlV+i=qoAIaUsO_*m=~X4l#&V(cT3DEP6dh= zXCxM+0{I$dCOVor3bhL41Fm0(#1{Qhdu{*tpSe(>$`$)OwF_6Wcuv|^HI*A_SA5w2 z=R;*#>e)B;U#`uv?fJZW|F&M`Z^7>`@1Ohs?>nCl5A4qxmAvNv$KY^xwbqlS6MO8} zf4J;ar0{M3>SXT7?2QlX-OWlJoP=K4|7Qw1v)}8&{=VIvbAH`EVb3YG;3J#LOZy-4 zALe{n{lLCtS%T$qt#|w5@7XRdQM+YdlC0_$^X{$vj>bz9g10do;O4!;x_mOD!=dFR zpT5p!JCMuWexdZq*Zm<=Z@Y3T{oDVgbM?E22j1FO$$7qs*m}YKd3Tg@!jn(-C29uC z4-37t*IaE7Uh(YGe$HEpo+k`G+E4m(pE1e#x&3M{Ax*}KU+i`Ig_^3fSr71Dy51-_ z{jB|@$@`mM`hMAeu&Guo?Qj1#QMhvVg}vC{6V54? z%nnJF0uSo`zO`?=HKAWV>%0B#Lt;(FW{>TKqUVQhw)|zEA*1nl-{&{>-Jk4M$j^Sf zKh@bN-NJF7y~NVz;{7I%_kS{#?LQa&#=cK}mht}|_xAfd+^ErO{nI|zi~SEnz#aRP zKkM&zKVfj-{QSZ_BlfSo&TKwImf3slub&WD-tp<;pP%;II|`Y4WghJp{T%$6ZQ?2W z@41Q(w@&$PukCf`OK!#Q{fnY37xJ~fuy1SoG4a(@MhC7fC-QrHzU}{}Va{s3;Jy8( zYm1A|>ff@>Ief+aI-%T79+pp}kGu=PA}XEDk3V zcRvlw{A!0ygMG%A&pC&`KCq8I`_TN1-Xr_&wqtJ9E|=_my8QDO%m22IcxHEf z`uoTB64zb}h;F}azlnd--{@_sw!&&F;-{0*wUwH1TFZ-oqf9TfFe6W9+rMW0` z?VbH$>_MT8D$ntYSJz1pvz^n_W%;^Y2Z6NS!A(Rpfr*h{C~{^3IVIg@?&Ut-`qFeCm{KJUU$ z_N)$frrbOA+&=DOoAau_&+JoX#qn%Z{$T%YzVuaw+|TyE_a0yGT=m8NeVxV%mK(3_ zMVs_q-Ltu|-)^CXv`lo<{vhi)nu5>2+iM;DdT@E}dHc&RT-x8R+-q;K)!tER;fMX9 twtD88?0@#l<@J=BT)$vn?5xFU|NEc4;ro~7k_)cbALe7qSjKwa9st|-BP0L- literal 0 HcmV?d00001 diff --git a/tests/TestFiles/OnePoleFilter/OPF_input_gain_0.5.npy b/tests/TestFiles/OnePoleFilter/OPF_input_gain_0.5.npy new file mode 100644 index 0000000000000000000000000000000000000000..a7a3e7736b13c22b1c5521faf05356a9867d5cc7 GIT binary patch literal 640 zcmbR27wQ`j$;eQ~P_3SlTAW;@Zl$1ZlV+i=qoAIaUsO_*m=~X4l#&V(cT3DEP6dh= zXCxM+0{I$dCOVor3bhL41Fr7g>(1^y-|V^Wvd(+CZOeX>!|B;~pWm}TZFJU*_r_cM zZMHrCMOq*2zvuEMe}mg=`y~tuO?oC>wRbGmJ63aL@BUfew`~n9ytx1Ju02ccHow@< zDx252`slR%dMDPfO2%Ki7NC-7{C5l7DTV{$yHt@10xwKRE6GC^++*z3;?x(sFWN?2{)L*OWr`MSEi9h$7l<)snX~g94cx@D$@wX%PtRZgv&UYT| zzpb)C%uM*dz2vX`2Ng4)?YBL@?8M~2hx?n~ABix3`rH0+&Wf`a9^KskEavj6u=}6( vpU?fVr7ZIJ{{JgokL*}`d4KM^D(=>zr84Vx5iapssq51yZ6N|su zpF5UQztr)a{UxW@YeGU!+P_J-+jLX>&Hjvuhff6CKHjfn#=7Cv(!2KhCnU68OMmW9 zUtSrvz~{F8%%e?tkK7;bpJ%X^Z~ghF_AHYh@%gI#*njlJ)dL;>zS{fjeD}d_*=76R zOh&OEq(1D|spr1f^84NXNv%RQhhu;2k9WFtU*X9^`=Y9qhx1L|@6Rgen80cGe7{*w z(zW_;)9ow5l?`1#z1;sv@u9h;(Z&5UZ@r!3v*ghJ38Kyx27j;EPgz=$@7Q$RzC^wJ zuYv!~{fqS9D;2-{yMM0e>hGKP|Fqx7_pIpPj-U4L6spgznEPhG!{;Xn!OIWZ_lS3F zpBMemexlF2t$eSp??0JbvFOOl*Y;B%DjNOizGQ!Iz22OY@21#a6gr=ft;Xcw=o1~R z+r{9p`cp7J!>uRw>kqGQ3fc5|zwe$$a<|tP+27}iGWR%g!2aJ0+sCJOeYZb&a9)>f z%>jEZ*At(DQy$o_yu3zOG4z4Gl(Xer(`7I0@3U!SJ$Z4(US=)dam&{S_HW(r#G=dW z%>FNN^N#puT(sx%Uf_LJ_2&NkH4myqZ@sin>0WX5^Nlb2&+gS;+_7cb{_4x@SJ%&c xVjppuU-r)ZC;MaD_uhX0EwR*xWd%Hx-a$$JLWvQdFkGM$@_a07EbwV&%v&FSz+fv`_kF>pZvY?d_T*- zy(ZeXckd78;jT;4WjtV~DB8O+o$)~DrRCfIslD6ZtyAu9+rxO^-@C4N(@wwGZzROn zKkwmt`%j0POC36%*hd+@`MdwjC;Le^XT|2tJFwp`T}<0`(qa2G?_MT(Z?ZP56(|D@5MIa-rh4*cKW^5DmZzxMlkdX_{hF5JKF`M3VQ zGcWglG$Cv+768&MneO2?d2^t^v`&tW@dOT%0Ae;6-{*mv) z{mW-XX|Qp<+VALa<4)$J1N+^)+onFSe80bP?eUj#r$5*~YmiWD%eu3F7gyy6VZAr@ vriY6Hj;}tj|KwcvPqsUs?k`@Mt5H4Yu|419-FkAe*X_!x{6}N2Wt_DaT9j+o;I`HN(w?3tqHi|aGi-hC zbEm(`-t142Q?MS{3 zym~*M^H)vHjR*Iuc+5(j$98Fd%DbH!D<)mq-^6gm)yn?({vDay7ao1uzhA&iCgSS) zi}w1pr*_`>{J{RQiZ9QUTTkp|+M<^{5O{8{<8^N#ck)a7__}LJ_q5;Gvu-@nqWJ8& z{hqI1^($o`*^3@Oki7ZYE&Bi|W3Kl3*X{c&ryh`BebqiERIzfE*bVzNzb|xnmEN~c zi9Ir<+vkbB^xn_1qT+AtxoR7vx>mfgzdXz8O+xDv`@gq2&HNi4*e{FwyMFnvNA@Os s_ntH=JhuN86uR%r?Ys7q-JQhLu3ojzT>gIB1%r$Bm9eTm-3q7d0U|~s;{X5v literal 0 HcmV?d00001 diff --git a/tests/TestFiles/OnePoleFilter/OPF_output_gain_0.3.npy b/tests/TestFiles/OnePoleFilter/OPF_output_gain_0.3.npy new file mode 100644 index 0000000000000000000000000000000000000000..b17e6d959566191a7a3345dd3fc3d6105b683c00 GIT binary patch literal 640 zcmbR27wQ`j$;eQ~P_3SlTAW;@Zl$1ZlV+i=qoAIaUsO_*m=~X4l#&V(cT3DEP6dh= zXCxM+0{I$dCOVor3bhL41FjXHrnBv+-esS*?abnTM;_Q$wKBI{Z+l?B>1dao2GdD< ztvG}Dy%X-+?}_MHB6{JJ{TD8WwaK2l_cyUAasB6gVjo?7=-kEm5A0Vqh&(tOcies< z%eS(XDYxxEY3|WF5_{afRV#acPV2?}o7U+SyVjrGzu}^3`9bMJ_Dmk3xqpLR+OKUe z5>L?oYR`Y3;kwuV=k`+)U+kQ)=j48Oqe~mti$B;O;?KRK^~d%77Y_do{D0u^{%edO z3olMOXTL7t;4OWv$M$y(RGz)K{?vX))T1Zw6)U5(scFa6Z@P0#qM=G|6qTupyLu> z?FaiUzf5`86Z&w!%9r?3p?9bDha{}k)$U!rpKo`QVEXI}`*q#l8N~)Z*q`)uZETF31z_H#V@GdDeJ=d-l8=ugv+X``A9R|5oPun5*{DCsn$St$1Yr zt?u15&A*rJ4dP0w92id8*S@fpUf*@s-mIfLbFb7{`zq~4Z3Z(|@3(pu#=6n(`u+(b zXTzPQ-Pj+0WhbgkQ5k7fHM!VX7rZ#!W>jh&(Fo5I%pCnqhm zxX5?XUj5Q%f7fLv?HByhlAd?5#r{#-;v?%_5AJXHlk;%${4D#fnF|ha`(L+rI3#)K z+4b%A-g=VkUoPF)UlhFDc-8&O_Hx&SBIh5uVJ~g`YfZ(0m;3iOc=L&^`?~++)_|zM zZ7=sP__w*bWy7QWXJ3ERj5T?-ze3qS{Dks~{Y5Nur!wq#Xm9d(>8?)&2lgj;+t1AS zeSiP$P32`l6H?*|xe0Rm(@`rj<+0lpgi+(+vw*TYR v{RVEwUHtSP?ax&y=w+RJZNHiLMR$gO7x&-M?-L3-d&NE=uwZe=gH84T_~$W6 literal 0 HcmV?d00001 diff --git a/tests/TestFiles/OnePoleFilter/OPF_output_gain_0.7.npy b/tests/TestFiles/OnePoleFilter/OPF_output_gain_0.7.npy new file mode 100644 index 0000000000000000000000000000000000000000..dcbda93bc77f29c48777bc68e8465e9a900460b9 GIT binary patch literal 640 zcmbR27wQ`j$;eQ~P_3SlTAW;@Zl$1ZlV+i=qoAIaUsO_*m=~X4l#&V(cT3DEP6dh= zXCxM+0{I$dCOVor3bhL411{zk*DDg`%l5x$n=^M0Pp|!!U7@!MB2MgA{^2=!(~L{@ z_y06JwTS;<-%^>pRprJLdmTnQ#*GR`_pjWxGmSgv$$qJZa0TrRNB6UFUHSQK(f$4A zU-BD1Dm~gCcF0pF@5G_~85{I$ZcJNXe@5odn^{Fy_aE?hF7za9@BU9e?i=1~dTOtd zKBslV`lI{vymDhrf4$n@BHa^w>C~tFNilgdeuq5XpZi67YuCw(`^}#V%u)aQeE(l# z+kNx@-r7Gicgo?(%{TX-z9i$nwerFKd5(;`FYLaszu-j2hpY4F+M6DDoMOA|lD)#O zx2(HTm)IZ4{8T?@(cArNPF%Tb8hLU5ha0aN7LThd#wB) zyC>|wy~R=q-u;E&?G?11zcj9xVlT2_i-QHvgZ*56OY_d}Ua;R#xZz@g=u`Vw+&^44 ze0^bWzhR00?aepsgGEJ7Sl8UQ|F(b54~^xI?KP^c-E7}Hv}Zk~7CQO)F?;`ngs3>t zz55vsdwwsi3AQ(J-!b<<^2+^3`cJn#+keDf;-QtqiM(U`>z3))t`2^@-yvvv?^($d* zvw45B1Sa0uFOk2Pk=<~=eTID41@4_U_Ny7Ly^(h3!G8WDs|rq+-m$;^T%GmF{tNqE zAN;6#qIQ43SoqQn#XGLqKM|g=S8-l_a>rib zPp4+F(VhKPUz2ypxAqtHPjLED^l|^%a_iZW*BK5x|8a*!IsNDUpczdePEtSjE1yha&$#$~{{bDh zxwd+EAiH9Q3cAGhqUQ95>T zuKR2I$928dm;QaS_o+MAp|Sn${-P!CPe~>I+pln9;h*mdZ|~cNr^gh_ zZa=dBlD6*Jaj%Q^Nx$xOUt4u^fAZDO&UI5?*=JmSa^&avi}saLB`n(JzxN-y$YDA! z_~(A1%S(a_jh^p6#S#9PpY#6yd+(TgxF}x8Y?n})$ xV}F4md(yAE>-NUmwjVs`b$$PY+|V?Ri!1kE_wdr26Mfgd=DW(Ydk=T)2LQx$IMe_D literal 0 HcmV?d00001