Updates
This commit is contained in:
parent
4f998cad8f
commit
8af7b7bfa7
23 changed files with 611 additions and 18 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -1,4 +1,6 @@
|
|||
build
|
||||
build-arm
|
||||
build-subl
|
||||
.vscode
|
||||
.vscode
|
||||
perf.data
|
||||
perf.data.old
|
||||
6
.gitmodules
vendored
6
.gitmodules
vendored
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
168
benchmarks/OPF_high_vs_low.cpp
Normal file
168
benchmarks/OPF_high_vs_low.cpp
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
#include <benchmark/benchmark.h>
|
||||
#include "gsl/gsl-lite.hpp"
|
||||
#include <random>
|
||||
#include <algorithm>
|
||||
#include "../sources/Intrinsics.h"
|
||||
|
||||
constexpr float filterGain { 0.25f };
|
||||
|
||||
void lowpass(gsl::span<const float> input, gsl::span<float> 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<const float> input, gsl::span<float> 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<const float> input, gsl::span<float> 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<const float> input, gsl::span<float> 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<const float> input, gsl::span<float> 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<float> input(state.range(0));
|
||||
std::vector<float> output(state.range(0));
|
||||
std::random_device rd { };
|
||||
std::mt19937 gen { rd() };
|
||||
|
||||
std::normal_distribution<float> 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<float> input(state.range(0));
|
||||
std::vector<float> output(state.range(0));
|
||||
std::random_device rd { };
|
||||
std::mt19937 gen { rd() };
|
||||
|
||||
std::normal_distribution<float> 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<float> input(state.range(0));
|
||||
std::vector<float> output(state.range(0));
|
||||
std::random_device rd { };
|
||||
std::mt19937 gen { rd() };
|
||||
|
||||
std::normal_distribution<float> 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<float> input(state.range(0));
|
||||
std::vector<float> output(state.range(0));
|
||||
std::random_device rd { };
|
||||
std::mt19937 gen { rd() };
|
||||
|
||||
std::normal_distribution<float> 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<float> input(state.range(0));
|
||||
std::vector<float> output(state.range(0));
|
||||
std::random_device rd { };
|
||||
std::mt19937 gen { rd() };
|
||||
|
||||
std::normal_distribution<float> 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();
|
||||
111
benchmarks/Spans.cpp
Normal file
111
benchmarks/Spans.cpp
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
#include <benchmark/benchmark.h>
|
||||
#include "gsl/gsl-lite.hpp"
|
||||
#include "absl/types/span.h"
|
||||
#include <random>
|
||||
#include <algorithm>
|
||||
|
||||
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<const float> input, gsl::span<float> 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<const float> input, absl::Span<float> 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<float> input(state.range(0));
|
||||
std::vector<float> output(state.range(0));
|
||||
std::random_device rd { };
|
||||
std::mt19937 gen { rd() };
|
||||
|
||||
std::normal_distribution<float> 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<float> input(state.range(0));
|
||||
std::vector<float> output(state.range(0));
|
||||
std::random_device rd { };
|
||||
std::mt19937 gen { rd() };
|
||||
|
||||
std::normal_distribution<float> 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<float> input(state.range(0));
|
||||
std::vector<float> output(state.range(0));
|
||||
std::random_device rd { };
|
||||
std::mt19937 gen { rd() };
|
||||
|
||||
std::normal_distribution<float> 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();
|
||||
1
external/cnpy
vendored
Submodule
1
external/cnpy
vendored
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 4e8810b1a8637695171ed346ce68f6984e585ef4
|
||||
1
external/gsl-lite
vendored
Submodule
1
external/gsl-lite
vendored
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit df36f6378336280043d7e06d8013e6fa4af6abd7
|
||||
35
old/StateVariableFilter.cpp
Normal file
35
old/StateVariableFilter.cpp
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
#include "StateVariableFilter.h"
|
||||
#include <tuple>
|
||||
|
||||
// template<>
|
||||
// template<>
|
||||
// auto StateVariableFilter<SVFReturn::BP>::process<float>(const float& input [[maybe_unused]])
|
||||
// {
|
||||
// return 0.0;
|
||||
// }
|
||||
|
||||
// template<>
|
||||
// template<>
|
||||
// auto StateVariableFilter<SVFReturn::BP_LP>::process<float>(const float& input [[maybe_unused]])
|
||||
// {
|
||||
// return std::make_pair<float, float>(0.0, 0.0);
|
||||
// }
|
||||
|
||||
// template<>
|
||||
// template<>
|
||||
// auto StateVariableFilter<SVFReturn::BP_LP_HP>::process<float>(const float& input [[maybe_unused]])
|
||||
// {
|
||||
// return std::make_tuple<float, float, float>(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]])
|
||||
{
|
||||
|
||||
}
|
||||
27
old/StateVariableFilter.h
Normal file
27
old/StateVariableFilter.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#pragma once
|
||||
#include "Globals.h"
|
||||
#include <tuple>
|
||||
#include <array>
|
||||
|
||||
// enum class SVFReturn { BP_LP_HP, BP_LP, BP};
|
||||
|
||||
// template<SVFReturn ReturnType = SVFReturn::BP>
|
||||
class StateVariableFilter
|
||||
{
|
||||
public:
|
||||
StateVariableFilter() = default;
|
||||
StateVariableFilter(float R, float gain)
|
||||
: R(R), gain(gain)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Only use specializations
|
||||
template<class InputType, class... OutputTypes>
|
||||
auto process(const InputType& input, OutputTypes... outputs) = delete;
|
||||
|
||||
private:
|
||||
std::array<float, 2> state;
|
||||
float R { 0.0 };
|
||||
float gain { 1.0 };
|
||||
};
|
||||
36
old/StateVariableFilterSSE.cpp
Normal file
36
old/StateVariableFilterSSE.cpp
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
#include "StateVariableFilter.h"
|
||||
#include <tuple>
|
||||
#include "Intrinsics.h"
|
||||
|
||||
// template<>
|
||||
// template<>
|
||||
// auto StateVariableFilter<SVFReturn::BP>::process<__m128>(const __m128& input [[maybe_unused]])
|
||||
// {
|
||||
// return 0.0;
|
||||
// }
|
||||
|
||||
// template<>
|
||||
// template<>
|
||||
// auto StateVariableFilter<SVFReturn::BP_LP>::process<__m128>(const __m128& input [[maybe_unused]])
|
||||
// {
|
||||
// return std::make_pair<float, float>(0.0, 0.0);
|
||||
// }
|
||||
|
||||
// template<>
|
||||
// template<>
|
||||
// auto StateVariableFilter<SVFReturn::BP_LP_HP>::process<__m128>(const __m128& input [[maybe_unused]])
|
||||
// {
|
||||
// return std::make_tuple<float, float, float>(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]])
|
||||
{
|
||||
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
#include <string_view>
|
||||
#include <absl/container/flat_hash_map.h>
|
||||
#include <map>
|
||||
#include <readerwriterqueue.h>
|
||||
#include "readerwriterqueue.h"
|
||||
#include <thread>
|
||||
|
||||
namespace sfz
|
||||
|
|
|
|||
94
sources/OnePoleFilter.h
Normal file
94
sources/OnePoleFilter.h
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
#include "Globals.h"
|
||||
#include <cmath>
|
||||
#include "gsl/gsl-lite.hpp"
|
||||
|
||||
template<class Type=float>
|
||||
class OnePoleFilter
|
||||
{
|
||||
public:
|
||||
OnePoleFilter() = default;
|
||||
// Normalized cutoff with respect to the sampling rate
|
||||
template<class C>
|
||||
static Type normalizedGain(Type cutoff, C sampleRate)
|
||||
{
|
||||
return std::tan( cutoff / static_cast<Type>(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<const Type> input, gsl::span<Type> 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<const Type> input, gsl::span<Type> 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<const Type> input, gsl::span<Type> lowpass, gsl::span<const Type> 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<const Type> input, gsl::span<Type> highpass, gsl::span<const Type> 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;
|
||||
}
|
||||
};
|
||||
102
tests/OnePoleFilterT.cpp
Normal file
102
tests/OnePoleFilterT.cpp
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
#include "../sources/OnePoleFilter.h"
|
||||
#include "catch2/catch.hpp"
|
||||
#include "cnpy.h"
|
||||
#include "gsl/gsl-lite.hpp"
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
using namespace Catch::literals;
|
||||
|
||||
template<class Type>
|
||||
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<double>(), input.shape[0]);
|
||||
|
||||
const auto output = cnpy::npy_load(outputNumpyFile.string());
|
||||
REQUIRE( output.word_size == 8 );
|
||||
const auto outputSpan = gsl::make_span(output.data<double>(), output.shape[0]);
|
||||
auto size = std::min(outputSpan.size(), inputSpan.size());
|
||||
REQUIRE( size > 0 );
|
||||
|
||||
std::vector<Type> inputData;
|
||||
std::vector<Type> expectedData;
|
||||
inputData.reserve(size);
|
||||
expectedData.reserve(size);
|
||||
for (auto& data: inputSpan)
|
||||
inputData.push_back(static_cast<Type>(data));
|
||||
for (auto& data: outputSpan)
|
||||
expectedData.push_back(static_cast<Type>(data));
|
||||
|
||||
OnePoleFilter filter { gain };
|
||||
std::vector<Type> 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<Type> 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<float>(
|
||||
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<float>(
|
||||
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<float>(
|
||||
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<float>(
|
||||
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<float>(
|
||||
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<double>(
|
||||
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<double>(
|
||||
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<double>(
|
||||
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<double>(
|
||||
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<double>(
|
||||
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
|
||||
);
|
||||
}
|
||||
BIN
tests/TestFiles/OnePoleFilter/OPF_input_gain_0.1.npy
Normal file
BIN
tests/TestFiles/OnePoleFilter/OPF_input_gain_0.1.npy
Normal file
Binary file not shown.
BIN
tests/TestFiles/OnePoleFilter/OPF_input_gain_0.3.npy
Normal file
BIN
tests/TestFiles/OnePoleFilter/OPF_input_gain_0.3.npy
Normal file
Binary file not shown.
BIN
tests/TestFiles/OnePoleFilter/OPF_input_gain_0.5.npy
Normal file
BIN
tests/TestFiles/OnePoleFilter/OPF_input_gain_0.5.npy
Normal file
Binary file not shown.
BIN
tests/TestFiles/OnePoleFilter/OPF_input_gain_0.7.npy
Normal file
BIN
tests/TestFiles/OnePoleFilter/OPF_input_gain_0.7.npy
Normal file
Binary file not shown.
BIN
tests/TestFiles/OnePoleFilter/OPF_input_gain_0.9.npy
Normal file
BIN
tests/TestFiles/OnePoleFilter/OPF_input_gain_0.9.npy
Normal file
Binary file not shown.
BIN
tests/TestFiles/OnePoleFilter/OPF_output_gain_0.1.npy
Normal file
BIN
tests/TestFiles/OnePoleFilter/OPF_output_gain_0.1.npy
Normal file
Binary file not shown.
BIN
tests/TestFiles/OnePoleFilter/OPF_output_gain_0.3.npy
Normal file
BIN
tests/TestFiles/OnePoleFilter/OPF_output_gain_0.3.npy
Normal file
Binary file not shown.
BIN
tests/TestFiles/OnePoleFilter/OPF_output_gain_0.5.npy
Normal file
BIN
tests/TestFiles/OnePoleFilter/OPF_output_gain_0.5.npy
Normal file
Binary file not shown.
BIN
tests/TestFiles/OnePoleFilter/OPF_output_gain_0.7.npy
Normal file
BIN
tests/TestFiles/OnePoleFilter/OPF_output_gain_0.7.npy
Normal file
Binary file not shown.
BIN
tests/TestFiles/OnePoleFilter/OPF_output_gain_0.9.npy
Normal file
BIN
tests/TestFiles/OnePoleFilter/OPF_output_gain_0.9.npy
Normal file
Binary file not shown.
Loading…
Add table
Reference in a new issue