BogaudioModules

BogaudioModules for VCV Rack
Log | Files | Refs | README | LICENSE

commit 53d8f31000d0c9e12c3733d6a2c65b3a0e1569c2
parent 45308431307da401441b97341eafcf17f4175df4
Author: Matt Demanett <matt@demanett.net>
Date:   Sat, 18 Apr 2020 23:29:43 -0400

Speed up filters somewhat with rack::simd.

Diffstat:
MMakefile | 7++++++-
Mbenchmarks/filter_benchmark.cpp | 110+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/dsp/filters/equalizer.hpp | 2+-
Msrc/dsp/filters/multimode.cpp | 122++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
Msrc/dsp/filters/multimode.hpp | 35+++++++++++++++++++++++++++++++----
Mtest/testmain.cpp | 98+++++++++++++++++++++++++++++++++++++++++++------------------------------------
6 files changed, 304 insertions(+), 70 deletions(-)

diff --git a/Makefile b/Makefile @@ -7,6 +7,10 @@ ifdef TEST FLAGS += -DTEST=1 endif +ifndef NO_RACK_SIMD +FLAGS += -DRACK_SIMD=1 +endif + DSP_SOURCES = $(wildcard src/dsp/*cpp src/dsp/filters/*cpp) SOURCES = $(wildcard src/*.cpp) $(DSP_SOURCES) @@ -31,7 +35,8 @@ TESTMAIN_OBJECTS = $(patsubst %, build/%.o, $(TESTMAIN_SOURCES)) TESTMAIN_DEPS = $(patsubst %, build/%.d, $(TESTMAIN_SOURCES)) -include $(TESTMAIN_DEPS) testmain: $(TESTMAIN_OBJECTS) - $(CXX) -o $@ $^ ../../build/src/util.cpp.o + # $(CXX) -o $@ $^ ../../build/src/util.cpp.o + $(CXX) -o $@ $^ testmain_clean: rm -f testmain $(TESTMAIN_OBJECTS) diff --git a/benchmarks/filter_benchmark.cpp b/benchmarks/filter_benchmark.cpp @@ -2,6 +2,7 @@ #include <benchmark/benchmark.h> #include "dsp/noise.hpp" +#include "dsp/filters/multimode.hpp" #include "dsp/filters/resample.hpp" // #include "dsp/decimator.hpp" // rack @@ -82,3 +83,112 @@ static void BM_AnalogFrequency(benchmark::State& state) { } } BENCHMARK(BM_AnalogFrequency); + +#ifdef RACK_SIMD +static void BM_Biquad4(benchmark::State& state) { + WhiteNoiseGenerator r; + const int n = 128; + float buf[n]; + for (int i = 0; i < n; ++i) { + buf[i] = std::abs(r.next()) * 0.5f * M_PI; + } + + Biquad4 biquads; + for (int i = 0; i < 4; ++i) { + biquads.setParams(i, 0.012672, 0.025345, 0.012672, 1.102730, -1.974655, 0.922615); + } + int i = 0; + for (auto _ : state) { + benchmark::DoNotOptimize(biquads.next(buf[i])); + i = (i + 1) % n; + } +} +BENCHMARK(BM_Biquad4); +#endif + +static void BM_BiquadBank4(benchmark::State& state) { + WhiteNoiseGenerator r; + const int n = 128; + float buf[n]; + for (int i = 0; i < n; ++i) { + buf[i] = std::abs(r.next()) * 0.5f * M_PI; + } + + MultimodeDesigner<4> designer; + BiquadBank<float, 4> biquads; + float outGain; + designer.setParams( + biquads, + outGain, + 44100.0f, + MultimodeTypes::BUTTERWORTH_TYPE, + 4, + MultimodeTypes::BANDPASS_MODE, + 2000.0f, + 1.0f + ); + int i = 0; + for (auto _ : state) { + benchmark::DoNotOptimize(biquads.next(buf[i])); + i = (i + 1) % n; + } +} +BENCHMARK(BM_BiquadBank4); + +static void BM_BiquadBank16(benchmark::State& state) { + WhiteNoiseGenerator r; + const int n = 128; + float buf[n]; + for (int i = 0; i < n; ++i) { + buf[i] = std::abs(r.next()) * 0.5f * M_PI; + } + + MultimodeDesigner<16> designer; + BiquadBank<float, 16> biquads; + float outGain; + designer.setParams( + biquads, + outGain, + 44100.0f, + MultimodeTypes::BUTTERWORTH_TYPE, + 16, + MultimodeTypes::BANDPASS_MODE, + 2000.0f, + 1.0f + ); + int i = 0; + for (auto _ : state) { + benchmark::DoNotOptimize(biquads.next(buf[i])); + i = (i + 1) % n; + } +} +BENCHMARK(BM_BiquadBank16); + +static void BM_BiquadBank16_2Pole(benchmark::State& state) { + WhiteNoiseGenerator r; + const int n = 128; + float buf[n]; + for (int i = 0; i < n; ++i) { + buf[i] = std::abs(r.next()) * 0.5f * M_PI; + } + + MultimodeDesigner<16> designer; + BiquadBank<float, 16> biquads; + float outGain; + designer.setParams( + biquads, + outGain, + 44100.0f, + MultimodeTypes::BUTTERWORTH_TYPE, + 2, + MultimodeTypes::BANDPASS_MODE, + 2000.0f, + 1.0f + ); + int i = 0; + for (auto _ : state) { + benchmark::DoNotOptimize(biquads.next(buf[i])); + i = (i + 1) % n; + } +} +BENCHMARK(BM_BiquadBank16_2Pole); diff --git a/src/dsp/filters/equalizer.hpp b/src/dsp/filters/equalizer.hpp @@ -13,7 +13,7 @@ struct Equalizer : Filter { Amplifier _midAmp; Amplifier _highAmp; FourPoleButtworthLowpassFilter _lowFilter; - TowPoleButtworthBandpassFilter _midFilter; + TwoPoleButtworthBandpassFilter _midFilter; FourPoleButtworthHighpassFilter _highFilter; void setParams( diff --git a/src/dsp/filters/multimode.cpp b/src/dsp/filters/multimode.cpp @@ -7,40 +7,129 @@ using namespace bogaudio::dsp; +#ifdef RACK_SIMD + +void Biquad4::setParams(int i, float a0, float a1, float a2, float b0, float b1, float b2) { + assert(i >= 0 && i < 4); + float ib0 = 1.0 / b0; + _a0[i] = a0 * ib0; + _a1[i] = a1 * ib0; + _a2[i] = a2 * ib0; + _b1[i] = b1 * ib0; + _b2[i] = b2 * ib0; +} + +void Biquad4::reset() { + _x[0] = _x[1] = _x[2] = 0.0; + _y[0] = _y[1] = _y[2] = 0.0; +} + +void Biquad4::setN(int n) { + assert(n <= 4); + for (; n < 4; ++n) { + setParams(n, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0); + } +} + +float Biquad4::next(float sample) { + if (_disable) { + return sample; + } + + _x[2] = _x[1]; + _x[1] = _x[0]; + + // slower: _x[0] = _mm_shuffle_ps(_y[0].v, _y[0].v, _MM_SHUFFLE(2, 1, 0, 0)); _x[0][0] = sample; + _x[0] = float_4(sample, _y[0][0], _y[0][1], _y[0][2]); + + _y[2] = _y[1]; + _y[1] = _y[0]; + _y[0] = ((_a0 * _x[0]) + (_a1 * _x[1]) + (_a2 * _x[2])) - ((_b1 * _y[1]) + (_b2 * _y[2])); + return _y[0][3]; +} + +template<> void BiquadBank<MultimodeTypes::T, 4>::setParams(int i, MultimodeTypes::T a0, MultimodeTypes::T a1, MultimodeTypes::T a2, MultimodeTypes::T b0, MultimodeTypes::T b1, MultimodeTypes::T b2) { + assert(i >= 0 && i < 4); + _biquads->setParams(i, a0, a1, a2, b0, b1, b2); +} + +template<> void BiquadBank<MultimodeTypes::T, 4>::reset() { + _biquads->reset(); +} + +template<> void BiquadBank<MultimodeTypes::T, 4>::setN(int n) { + for (; n < 4; ++n) { + _biquads->setParams(n, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0); + } +} + +template<> float BiquadBank<MultimodeTypes::T, 4>::next(float sample) { + return _biquads->next(sample); +} + +template<> void BiquadBank<MultimodeTypes::T, 16>::setParams(int i, MultimodeTypes::T a0, MultimodeTypes::T a1, MultimodeTypes::T a2, MultimodeTypes::T b0, MultimodeTypes::T b1, MultimodeTypes::T b2) { + assert(i >= 0 && i < 16); + _biquads[i / 4].setParams(i % 4, a0, a1, a2, b0, b1, b2); +} + +template<> void BiquadBank<MultimodeTypes::T, 16>::reset() { + _biquads[0].reset(); + _biquads[1].reset(); + _biquads[2].reset(); + _biquads[3].reset(); +} + +template<> void BiquadBank<MultimodeTypes::T, 16>::setN(int n) { + for (int i = n, nn = n + (4 - n % 4); i < nn; ++i) { + _biquads[i / 4].setParams(i % 4, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0); + } + for (int i = 0; i < 4; ++i) { + _biquads[i].disable(4 * i > n); + } +} + +template<> float BiquadBank<MultimodeTypes::T, 16>::next(float sample) { + sample = _biquads[0].next(sample); + sample = _biquads[1].next(sample); + sample = _biquads[2].next(sample); + return _biquads[3].next(sample); +} + +#else + template<typename T, int N> void BiquadBank<T, N>::setParams(int i, T a0, T a1, T a2, T b0, T b1, T b2) { assert(i >= 0 && i < N); _biquads[i].setParams(a0, a1, a2, b0, b1, b2); } -template<typename T, int N> void BiquadBank<T, N>::reset(int from) { - assert(from >= 0); - for (; from < N; ++from) { - _biquads[from].reset(); +template<typename T, int N> void BiquadBank<T, N>::reset() { + for (int i = 0; i < N; ++i) { + _biquads[i].reset(); } } -template<> float BiquadBank<MultimodeTypes::T, 4>::next(float sample) { - assert(_n <= 4); - for (int i = 0; i < _n; ++i) { - sample = _biquads[i].next(sample); +template<typename T, int N> void BiquadBank<T, N>::setN(int n) { + assert(n <= N); + _n = n; + for (; n < N; ++n) { + _biquads[n].reset(); } - return sample; } -template<> float BiquadBank<MultimodeTypes::T, 16>::next(float sample) { - assert(_n <= 16); +template<typename T, int N> float BiquadBank<T, N>::next(float sample) { for (int i = 0; i < _n; ++i) { sample = _biquads[i].next(sample); } return sample; } +#endif + template struct bogaudio::dsp::BiquadBank<MultimodeTypes::T, 4>; template struct bogaudio::dsp::BiquadBank<MultimodeTypes::T, 16>; template<int N> void MultimodeDesigner<N>::setParams( - bool& changed, BiquadBank<T, N>& biquads, float& outGain, float sampleRate, @@ -59,7 +148,6 @@ template<int N> void MultimodeDesigner<N>::setParams( bool repole = _type != type || _mode != mode || _nPoles != poles || (type == CHEBYSHEV_TYPE && (mode == LOWPASS_MODE || mode == HIGHPASS_MODE) && _qbw != qbw); bool redesign = repole || _frequency != frequency || _qbw != qbw || _sampleRate != sampleRate || _bandwidthMode != bwm; - changed = redesign; _sampleRate = sampleRate; _half2PiST = M_PI * (1.0f / sampleRate); _type = type; @@ -120,9 +208,8 @@ template<int N> void MultimodeDesigner<N>::setParams( switch (_mode) { case LOWPASS_MODE: case HIGHPASS_MODE: { - biquads.reset(_nBiquads); _nBiquads = _nPoles / 2 + _nPoles % 2; - biquads.setSectionsInUse(_nBiquads); + biquads.setN(_nBiquads); // T iq = (1.0 / std::sqrt(2.0)) - 0.65 * _qbw; T iq = (T)0.8 - (T)0.6 * _qbw; @@ -181,9 +268,8 @@ template<int N> void MultimodeDesigner<N>::setParams( case BANDPASS_MODE: case BANDREJECT_MODE: { - biquads.reset(_nBiquads); _nBiquads = ((_nPoles / 2) * 2) + (_nPoles % 2); - biquads.setSectionsInUse(_nBiquads); + biquads.setN(_nBiquads); T wdl = 0.0; T wdh = 0.0; @@ -346,9 +432,7 @@ template<int N> void MultimodeBase<N>::design( float qbw, BandwidthMode bwm ) { - bool changed = false; _designer.setParams( - changed, _biquads, _outGain, sampleRate, diff --git a/src/dsp/filters/multimode.hpp b/src/dsp/filters/multimode.hpp @@ -4,17 +4,45 @@ #include "filters/filter.hpp" +#ifdef RACK_SIMD +#include "simd/vector.hpp" +using rack::simd::float_4; +#endif + namespace bogaudio { namespace dsp { +#ifdef RACK_SIMD + struct Biquad4 { + float_4 _a0; + float_4 _a1; + float_4 _a2; + float_4 _b1; + float_4 _b2; + float_4 _x[3] {}; + float_4 _y[3] {}; + bool _disable = false; + + void setParams(int i, float a0, float a1, float a2, float b0, float b1, float b2); + void reset(); + void setN(int n); + inline void disable(bool disable) { _disable = disable; } + float next(float sample); + }; +#endif + template<typename T, int N> struct BiquadBank : Filter { +#ifdef RACK_SIMD + Biquad4 _biquads[N / 4]; +#else BiquadFilter<T> _biquads[N]; int _n = N; +#endif void setParams(int i, T a0, T a1, T a2, T b0, T b1, T b2); - void reset(int from = 0); - inline void setSectionsInUse(int n) { _n = n; } + void reset(); + void setN(int n); float next(float sample) override; }; @@ -99,7 +127,6 @@ struct MultimodeDesigner : MultimodeTypes { int _nBiquads = 0; void setParams( - bool& changed, BiquadBank<T, N>& biquads, float& outGain, float sampleRate, @@ -187,7 +214,7 @@ struct FourPoleButtworthHighpassFilter : MultimodeBase<4> { } }; -struct TowPoleButtworthBandpassFilter : MultimodeBase<4> { +struct TwoPoleButtworthBandpassFilter : MultimodeBase<4> { inline void setParams( float sampleRate, float frequency, diff --git a/test/testmain.cpp b/test/testmain.cpp @@ -6,54 +6,62 @@ #include <math.h> #include <algorithm> -#include "util/math.hpp" // Rack +// #include "util/math.hpp" // Rack +#include "simd/vector.hpp" // Rack +using rack::simd::float_4; -#include "dsp/fixed.hpp" - -using namespace bogaudio::dsp; +// #include "dsp/fixed.hpp" +// using namespace bogaudio::dsp; int main() { - { - fixed_16_16 x = 5; - fixed_16_16 y = 1; - x = 3; - printf("X=%d\n", (int)(x + y)); - y = 2; - printf("X=%d\n", (int)(x - y)); - x = y + 5; - printf("X=%d\n", (int)x); - x = y - 3; - printf("X=%d\n", (int)x); - - x += 2.5; - printf("X=%d\n", (int)x); - printf("X=%f\n", (float)x); + // { + // fixed_16_16 x = 5; + // fixed_16_16 y = 1; + // x = 3; + // printf("X=%d\n", (int)(x + y)); + // y = 2; + // printf("X=%d\n", (int)(x - y)); + // x = y + 5; + // printf("X=%d\n", (int)x); + // x = y - 3; + // printf("X=%d\n", (int)x); + // + // x += 2.5; + // printf("X=%d\n", (int)x); + // printf("X=%f\n", (float)x); + // + // x = y - 0.3; + // printf("X=%d\n", (int)x); + // printf("X=%f\n", (float)x); + // } + // + // { + // fixed_32_32 x = 5; + // fixed_32_32 y = 1; + // x = 3; + // printf("X=%d\n", (int)(x + y)); + // y = 2; + // printf("X=%d\n", (int)(x - y)); + // x = y + 5; + // printf("X=%d\n", (int)x); + // x = y - 3; + // printf("X=%d\n", (int)x); + // + // x += 2.5; + // printf("X=%d\n", (int)x); + // printf("X=%f\n", (float)x); + // + // x = y - 0.3; + // printf("X=%d\n", (int)x); + // printf("X=%f\n", (float)x); + // } + // + // return 0; - x = y - 0.3; - printf("X=%d\n", (int)x); - printf("X=%f\n", (float)x); + float_4 a(1.0, 2.0, 3.0, 4.0); + float_4 b = _mm_shuffle_ps(a.v, a.v, _MM_SHUFFLE(2, 1, 0, 0)); + b[0] = 0.0; + for (int i = 0; i < 4; ++i) { + printf("a=%f b=%f\n", a[i], b[i]); } - - { - fixed_32_32 x = 5; - fixed_32_32 y = 1; - x = 3; - printf("X=%d\n", (int)(x + y)); - y = 2; - printf("X=%d\n", (int)(x - y)); - x = y + 5; - printf("X=%d\n", (int)x); - x = y - 3; - printf("X=%d\n", (int)x); - - x += 2.5; - printf("X=%d\n", (int)x); - printf("X=%f\n", (float)x); - - x = y - 0.3; - printf("X=%d\n", (int)x); - printf("X=%f\n", (float)x); - } - - return 0; }