commit 37d7005d546d008393055d519231e375b76ec8bd
parent 28fce5b7cf9f2cc014889b9c44a8da3adb2bb4cb
Author: Matt Demanett <matt@demanett.net>
Date: Fri, 17 Apr 2020 18:25:49 -0400
Split up and templatize MultimodeFilter; this realizes some minor memory savings; is preparation for speed improvements.
Diffstat:
6 files changed, 291 insertions(+), 126 deletions(-)
diff --git a/src/FFB.cpp b/src/FFB.cpp
@@ -8,30 +8,27 @@ void FFB::Engine::sampleRateChange() {
}
auto bp = [this, sr](int i, float cutoff) {
- _filters[i].setParams(
+ _bandPasses[i].setParams(
sr,
- MultimodeFilter::BUTTERWORTH_TYPE,
- 4.0f,
- MultimodeFilter::BANDPASS_MODE,
cutoff,
0.22f / BOGAUDIO_DSP_MULTIMODEFILTER_MAXBWPITCH,
MultimodeFilter::PITCH_BANDWIDTH_MODE
);
};
- _filters[ 0].setParams(sr, MultimodeFilter::BUTTERWORTH_TYPE, 12.0f, MultimodeFilter::LOWPASS_MODE, 95.0f, 0.0f);
- bp(1, 125.0f);
- bp(2, 175.0f);
- bp(3, 250.0f);
- bp(4, 350.0f);
- bp(5, 500.0f);
- bp(6, 700.0f);
- bp(7, 1000.0f);
- bp(8, 1400.0f);
- bp(9, 2000.0f);
- bp(10, 2800.0f);
- bp(11, 4000.0f);
- bp(12, 5600.0f);
- _filters[13].setParams(sr, MultimodeFilter::BUTTERWORTH_TYPE, 12.0f, MultimodeFilter::HIGHPASS_MODE, 6900.0f, 0.0f);
+ _lowPass.setParams(sr, MultimodeFilter::BUTTERWORTH_TYPE, 12.0f, MultimodeFilter::LOWPASS_MODE, 95.0f, 0.0f);
+ bp(0, 125.0f);
+ bp(1, 175.0f);
+ bp(2, 250.0f);
+ bp(3, 350.0f);
+ bp(4, 500.0f);
+ bp(5, 700.0f);
+ bp(6, 1000.0f);
+ bp(7, 1400.0f);
+ bp(8, 2000.0f);
+ bp(9, 2800.0f);
+ bp(10, 4000.0f);
+ bp(11, 5600.0f);
+ _highPass.setParams(sr, MultimodeFilter::BUTTERWORTH_TYPE, 12.0f, MultimodeFilter::HIGHPASS_MODE, 6900.0f, 0.0f);
}
void FFB::sampleRateChange() {
@@ -85,13 +82,15 @@ void FFB::processChannel(const ProcessArgs& args, int c) {
float in = inputs[IN_INPUT].getVoltage(c);
float outAll = 0.0f;
- float outOdd = 0.0f;
- float outEven = 0.0f;
- for (int i = 0; i < 14; ++i) {
- float out = e._amplifiers[i].next(e._filters[i].next(in));
+ outAll += e._amplifiers[0].next(e._lowPass.next(in));
+ outAll += e._amplifiers[13].next(e._highPass.next(in));
+ float outOdd = outAll;
+ float outEven = outAll;
+ for (int i = 1; i <= 12; ++i) {
+ float out = e._amplifiers[i].next(e._bandPasses[i - 1].next(in));
outAll += out;
- outOdd += (i == 0 || i == 13 || i % 2 == 1) * out;
- outEven += (i == 0 || i == 13 || i % 2 == 0) * out;
+ outOdd += (i % 2 == 1) * out;
+ outEven += (i % 2 == 0) * out;
}
outputs[ALL_OUTPUT].setChannels(_channels);
diff --git a/src/FFB.hpp b/src/FFB.hpp
@@ -47,7 +47,9 @@ struct FFB : BGModule {
};
struct Engine {
- MultimodeFilter _filters[14];
+ MultimodeFilter _lowPass;
+ FourPoleButtworthBandpassFilter _bandPasses[12];
+ MultimodeFilter _highPass;
Amplifier _amplifiers[14];
bogaudio::dsp::SlewLimiter _slews[14];
diff --git a/src/dsp/filters/equalizer.cpp b/src/dsp/filters/equalizer.cpp
@@ -16,35 +16,13 @@ void Equalizer::setParams(
assert(highDb >= cutDb && highDb <= gainDb);
_lowAmp.setLevel(lowDb);
- _lowFilter.setParams(
- sampleRate,
- MultimodeFilter::BUTTERWORTH_TYPE,
- 4,
- MultimodeFilter::LOWPASS_MODE,
- 100.0f,
- 0.0f
- );
+ _lowFilter.setParams(sampleRate, 100.0f, 0.0f);
_midAmp.setLevel(midDb);
- _midFilter.setParams(
- sampleRate,
- MultimodeFilter::BUTTERWORTH_TYPE,
- 2,
- MultimodeFilter::BANDPASS_MODE,
- 350.0f,
- 0.55f,
- MultimodeFilter::PITCH_BANDWIDTH_MODE
- );
+ _midFilter.setParams(sampleRate, 350.0f, 0.55f, MultimodeFilter::PITCH_BANDWIDTH_MODE);
_highAmp.setLevel(highDb);
- _highFilter.setParams(
- sampleRate,
- MultimodeFilter::BUTTERWORTH_TYPE,
- 4,
- MultimodeFilter::HIGHPASS_MODE,
- 1000.0f,
- 0.0f
- );
+ _highFilter.setParams(sampleRate, 1000.0f, 0.0f);
}
float Equalizer::next(float sample) {
diff --git a/src/dsp/filters/equalizer.hpp b/src/dsp/filters/equalizer.hpp
@@ -12,9 +12,9 @@ struct Equalizer : Filter {
Amplifier _lowAmp;
Amplifier _midAmp;
Amplifier _highAmp;
- MultimodeFilter _lowFilter;
- MultimodeFilter _midFilter;
- MultimodeFilter _highFilter;
+ FourPoleButtworthLowpassFilter _lowFilter;
+ TowPoleButtworthBandpassFilter _midFilter;
+ FourPoleButtworthHighpassFilter _highFilter;
void setParams(
float sampleRate,
diff --git a/src/dsp/filters/multimode.cpp b/src/dsp/filters/multimode.cpp
@@ -7,7 +7,42 @@
using namespace bogaudio::dsp;
-void MultimodeFilter::setParams(
+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<> float BiquadBank<double, 4>::next(float sample) {
+ assert(_n <= 4);
+ for (int i = 0; i < _n; ++i) {
+ sample = _biquads[i].next(sample);
+ }
+ return sample;
+}
+
+template<> float BiquadBank<double, 16>::next(float sample) {
+ assert(_n <= 16);
+ for (int i = 0; i < _n; ++i) {
+ sample = _biquads[i].next(sample);
+ }
+ return sample;
+}
+
+template struct bogaudio::dsp::BiquadBank<double, 4>;
+template struct bogaudio::dsp::BiquadBank<double, 16>;
+
+
+template<int N> void MultimodeDesigner<N>::setParams(
+ bool& changed,
+ BiquadBank<T, N>& biquads,
+ float& outGain,
float sampleRate,
Type type,
int poles,
@@ -16,13 +51,15 @@ void MultimodeFilter::setParams(
float qbw,
BandwidthMode bwm
) {
- assert(poles >= minPoles && poles <= maxPoles);
+ assert(N >= minPoles && N <= maxPoles);
+ assert(poles >= minPoles && poles <= N);
assert(poles % modPoles == 0);
assert(frequency >= minFrequency && frequency <= maxFrequency);
assert(qbw >= minQbw && qbw <= maxQbw);
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;
@@ -43,7 +80,7 @@ void MultimodeFilter::setParams(
_poles[j] = Pole(-re, im, re + re, re * re + im * im);
}
- _outGain = 1.0f;
+ outGain = 1.0f;
break;
}
@@ -68,8 +105,8 @@ void MultimodeFilter::setParams(
_poles[j] = Pole(-re, im, re + re, re * re + im * im);
}
- _outGain = 1.0 / (e * std::pow(2.0, (T)(_nPoles - 1)));
- // _outGain = 1.0f / std::pow(2.0f, (T)(_nPoles - 1));
+ outGain = 1.0 / (e * std::pow(2.0, (T)(_nPoles - 1)));
+ // outGain = 1.0f / std::pow(2.0f, (T)(_nPoles - 1));
break;
}
@@ -83,11 +120,9 @@ void MultimodeFilter::setParams(
switch (_mode) {
case LOWPASS_MODE:
case HIGHPASS_MODE: {
- int nf = _nPoles / 2 + _nPoles % 2;
- for (int i = _nFilters; i < nf; ++i) {
- _filters[i].reset();
- }
- _nFilters = nf;
+ biquads.reset(_nBiquads);
+ _nBiquads = _nPoles / 2 + _nPoles % 2;
+ biquads.setSectionsInUse(_nBiquads);
// T iq = (1.0 / std::sqrt(2.0)) - 0.65 * _qbw;
T iq = (T)0.8 - (T)0.6 * _qbw;
@@ -96,49 +131,49 @@ void MultimodeFilter::setParams(
if (_mode == LOWPASS_MODE) {
int ni = 0;
- int nf = _nFilters;
+ int nb = _nBiquads;
if (_nPoles % 2 == 1) {
++ni;
- --nf;
+ --nb;
T wap = wa * std::real(_poles[0].p);
- _filters[0].setParams(wa, wa, 0.0, wap + (T)1.0, wap - (T)1.0, (T)0.0);
+ biquads.setParams(0, wa, wa, 0.0, wap + (T)1.0, wap - (T)1.0, (T)0.0);
}
T a0 = wa2;
T a1 = wa2 + wa2;
T a2 = wa2;
- for (int i = 0; i < nf; ++i) {
+ for (int i = 0; i < nb; ++i) {
Pole& pole = _poles[ni + i];
T ywa2 = pole.y * wa2;
T ywa21 = ywa2 + (T)1.0;
- T x = (((T)(i == nf / 2) * (iq - (T)1.0)) + (T)1.0) * pole.x;
+ T x = (((T)(i == nb / 2) * (iq - (T)1.0)) + (T)1.0) * pole.x;
T xwa = x * wa;
T b0 = ywa21 - xwa;
T b1 = (T)-2.0 + (ywa2 + ywa2);
T b2 = ywa21 + xwa;
- _filters[ni + i].setParams(a0, a1, a2, b0, b1, b2);
+ biquads.setParams(ni + i, a0, a1, a2, b0, b1, b2);
}
}
else {
int ni = 0;
- int nf = _nFilters;
+ int nb = _nBiquads;
if (_nPoles % 2 == 1) {
++ni;
- --nf;
+ --nb;
T rp = std::real(_poles[0].p);
- _filters[0].setParams(1.0, -1.0, 0.0, wa + rp, wa - rp, 0.0);
+ biquads.setParams(0, 1.0, -1.0, 0.0, wa + rp, wa - rp, 0.0);
}
T a0 = 1.0;
T a1 = -2.0f;
T a2 = 1.0;
- for (int i = 0; i < nf; ++i) {
+ for (int i = 0; i < nb; ++i) {
Pole& pole = _poles[ni + i];
T wa2y = wa2 + pole.y;
- T x = (((T)(i == nf / 2) * (iq - (T)1.0)) + (T)1.0) * pole.x;
+ T x = (((T)(i == nb / 2) * (iq - (T)1.0)) + (T)1.0) * pole.x;
T xwa = x * wa;
T b0 = wa2y - xwa;
T b1 = (wa2 + wa2) - (pole.y + pole.y);
T b2 = wa2y + xwa;
- _filters[ni + i].setParams(a0, a1, a2, b0, b1, b2);
+ biquads.setParams(ni + i, a0, a1, a2, b0, b1, b2);
}
}
break;
@@ -146,11 +181,9 @@ void MultimodeFilter::setParams(
case BANDPASS_MODE:
case BANDREJECT_MODE: {
- int nf = ((_nPoles / 2) * 2) + (_nPoles % 2);
- for (int i = _nFilters; i < nf; ++i) {
- _filters[i].reset();
- }
- _nFilters = nf;
+ biquads.reset(_nBiquads);
+ _nBiquads = ((_nPoles / 2) * 2) + (_nPoles % 2);
+ biquads.setSectionsInUse(_nBiquads);
T wdl = 0.0;
T wdh = 0.0;
@@ -183,12 +216,13 @@ void MultimodeFilter::setParams(
T a2 = -w;
int ni = 0;
- int nf = _nFilters;
+ int nb = _nBiquads;
if (_nPoles % 2 == 1) {
++ni;
- --nf;
+ --nb;
T wp = w * std::real(_poles[0].p);
- _filters[0].setParams(
+ biquads.setParams(
+ 0,
a0,
a1,
a2,
@@ -197,7 +231,7 @@ void MultimodeFilter::setParams(
(T)1.0 - wp + w02
);
}
- for (int i = 0; i < nf; i += 2) {
+ for (int i = 0; i < nb; i += 2) {
Pole& pole = _poles[ni + i / 2];
TC x = pole.p2;
x *= w2;
@@ -223,13 +257,13 @@ void MultimodeFilter::setParams(
T b0 = (T)1.0 + f1a + f2a;
T b1 = (T)-2.0 + (f2a + f2a);
T b2 = (T)1.0 - f1a + f2a;
- _filters[ni + i].setParams(a0, a1, a2, b0, b1, b2);
+ biquads.setParams(ni + i, a0, a1, a2, b0, b1, b2);
}
{
T b0 = (T)1.0 + f1b + f2b;
T b1 = (T)-2.0 + (f2b + f2b);
T b2 = (T)1.0 - f1b + f2b;
- _filters[ni + i + 1].setParams(a0, a1, a2, b0, b1, b2);
+ biquads.setParams(ni + i + 1, a0, a1, a2, b0, b1, b2);
}
}
}
@@ -239,13 +273,14 @@ void MultimodeFilter::setParams(
T a2 = a0;
int ni = 0;
- int nf = _nFilters;
+ int nb = _nBiquads;
if (_nPoles % 2 == 1) {
++ni;
- --nf;
+ --nb;
T rp = std::real(_poles[0].p);
T rpw02 = rp * w02;
- _filters[0].setParams(
+ biquads.setParams(
+ 0,
a0,
a1,
a2,
@@ -254,7 +289,7 @@ void MultimodeFilter::setParams(
rp - w + rpw02
);
}
- for (int i = 0; i < nf; i += 2) {
+ for (int i = 0; i < nb; i += 2) {
Pole& pole = _poles[ni + i / 2];
TC x = pole.p2;
x *= (T)-4.0 * w02;
@@ -278,13 +313,13 @@ void MultimodeFilter::setParams(
T b0 = pole.r + f1a + f2a;
T b1 = (T)-2.0 * pole.r + (f2a + f2a);
T b2 = pole.r - f1a + f2a;
- _filters[ni + i].setParams(a0, a1, a2, b0, b1, b2);
+ biquads.setParams(ni + i, a0, a1, a2, b0, b1, b2);
}
{
T b0 = pole.r + f1b + f2b;
T b1 = (T)-2.0 * pole.r + (f2b + f2b);
T b2 = pole.r - f1b + f2b;
- _filters[ni + i + 1].setParams(a0, a1, a2, b0, b1, b2);
+ biquads.setParams(ni + i + 1, a0, a1, a2, b0, b1, b2);
}
}
}
@@ -298,15 +333,41 @@ void MultimodeFilter::setParams(
}
}
-float MultimodeFilter::next(float sample) {
- for (int i = 0; i < _nFilters; ++i) {
- sample = _filters[i].next(sample);
- }
- return _outGain * sample;
+template struct bogaudio::dsp::MultimodeDesigner<4>;
+template struct bogaudio::dsp::MultimodeDesigner<16>;
+
+
+template<int N> void MultimodeBase<N>::design(
+ float sampleRate,
+ Type type,
+ int poles,
+ Mode mode,
+ float frequency,
+ float qbw,
+ BandwidthMode bwm
+) {
+ bool changed = false;
+ _designer.setParams(
+ changed,
+ _biquads,
+ _outGain,
+ sampleRate,
+ type,
+ poles,
+ mode,
+ frequency,
+ qbw,
+ bwm
+ );
}
-void MultimodeFilter::reset() {
- for (int i = 0; i < _nFilters; ++i) {
- _filters[i].reset();
- }
+template<int N> float MultimodeBase<N>::next(float sample) {
+ return _outGain * _biquads.next(sample);
}
+
+template<int N> void MultimodeBase<N>::reset() {
+ _biquads.reset();
+}
+
+template struct bogaudio::dsp::MultimodeBase<4>;
+template struct bogaudio::dsp::MultimodeBase<16>;
diff --git a/src/dsp/filters/multimode.hpp b/src/dsp/filters/multimode.hpp
@@ -7,6 +7,17 @@
namespace bogaudio {
namespace dsp {
+template<typename T, int N>
+struct BiquadBank : Filter {
+ BiquadFilter<T> _biquads[N];
+ int _n = N;
+
+ 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; }
+ float next(float sample) override;
+};
+
// hacky workaround for certain linkers here; see https://github.com/bogaudio/BogaudioModules/issues/104
#define BOGAUDIO_DSP_MULTIMODEFILTER_MINFREQUENCY 1.0f
#define BOGAUDIO_DSP_MULTIMODEFILTER_MAXFREQUENCY 21000.0f
@@ -17,30 +28,10 @@ namespace dsp {
#define BOGAUDIO_DSP_MULTIMODEFILTER_MINBWPITCH (1.0f / (1.0f * 12.0f * 100.0f / 25.0f))
#define BOGAUDIO_DSP_MULTIMODEFILTER_MAXBWPITCH 2.0f
-struct MultimodeFilter : Filter {
+struct MultimodeTypes {
typedef double T;
typedef std::complex<T> TC;
- struct Pole {
- TC p;
- T x = 0.0;
- T y = 0.0;
- TC pc;
- TC p2;
- TC i2p;
- TC i2pc;
- T r = 0.0;
-
- Pole() {}
- Pole(T re, T im, T x, T y) : p(TC(re, im)), x(x), y(y) {
- pc = std::conj(p);
- p2 = p * p;
- i2p = (T)1.0 / ((T)2.0 * p);
- i2pc = (T)1.0 / ((T)2.0 * pc);
- r = std::abs(p);
- }
- };
-
enum Type {
UNKNOWN_TYPE,
BUTTERWORTH_TYPE,
@@ -60,6 +51,29 @@ struct MultimodeFilter : Filter {
LINEAR_BANDWIDTH_MODE,
PITCH_BANDWIDTH_MODE
};
+};
+
+template<int N>
+struct MultimodeDesigner : MultimodeTypes {
+ struct Pole {
+ TC p;
+ T x = 0.0;
+ T y = 0.0;
+ TC pc;
+ TC p2;
+ TC i2p;
+ TC i2pc;
+ T r = 0.0;
+
+ Pole() {}
+ Pole(T re, T im, T x, T y) : p(TC(re, im)), x(x), y(y) {
+ pc = std::conj(p);
+ p2 = p * p;
+ i2p = (T)1.0 / ((T)2.0 * p);
+ i2pc = (T)1.0 / ((T)2.0 * pc);
+ r = std::abs(p);
+ }
+ };
static constexpr int minPoles = 1;
static constexpr int maxPoles = 16;
@@ -81,13 +95,30 @@ struct MultimodeFilter : Filter {
float _frequency = -1.0f;
float _qbw = -1.0f;
BandwidthMode _bandwidthMode = UNKNOWN_BANDWIDTH_MODE;
-
Pole _poles[maxPoles / 2];
- BiquadFilter<T> _filters[maxPoles] {};
- int _nFilters = 1;
- float _outGain = 1.0f;
+ int _nBiquads = 0;
void setParams(
+ bool& changed,
+ BiquadBank<T, N>& biquads,
+ float& outGain,
+ float sampleRate,
+ Type type,
+ int poles,
+ Mode mode,
+ float frequency,
+ float qbw,
+ BandwidthMode bwm = PITCH_BANDWIDTH_MODE
+ );
+};
+
+template<int N>
+struct MultimodeBase : MultimodeTypes, Filter {
+ MultimodeDesigner<N> _designer;
+ BiquadBank<T, N> _biquads;
+ float _outGain = 1.0f;
+
+ void design(
float sampleRate,
Type type,
int poles,
@@ -100,5 +131,99 @@ struct MultimodeFilter : Filter {
void reset();
};
+struct MultimodeFilter : MultimodeBase<16> {
+ inline void setParams(
+ float sampleRate,
+ Type type,
+ int poles,
+ Mode mode,
+ float frequency,
+ float qbw,
+ BandwidthMode bwm = PITCH_BANDWIDTH_MODE
+ ) {
+ design(
+ sampleRate,
+ type,
+ poles,
+ mode,
+ frequency,
+ qbw,
+ bwm
+ );
+ }
+};
+
+struct FourPoleButtworthLowpassFilter : MultimodeBase<4> {
+ inline void setParams(
+ float sampleRate,
+ float frequency,
+ float q
+ ) {
+ design(
+ sampleRate,
+ BUTTERWORTH_TYPE,
+ 4,
+ LOWPASS_MODE,
+ frequency,
+ q
+ );
+ }
+};
+
+struct FourPoleButtworthHighpassFilter : MultimodeBase<4> {
+ inline void setParams(
+ float sampleRate,
+ float frequency,
+ float q
+ ) {
+ design(
+ sampleRate,
+ BUTTERWORTH_TYPE,
+ 4,
+ HIGHPASS_MODE,
+ frequency,
+ q
+ );
+ }
+};
+
+struct TowPoleButtworthBandpassFilter : MultimodeBase<4> {
+ inline void setParams(
+ float sampleRate,
+ float frequency,
+ float bw,
+ BandwidthMode bwm = PITCH_BANDWIDTH_MODE
+ ) {
+ design(
+ sampleRate,
+ BUTTERWORTH_TYPE,
+ 2,
+ BANDPASS_MODE,
+ frequency,
+ bw,
+ bwm
+ );
+ }
+};
+
+struct FourPoleButtworthBandpassFilter : MultimodeBase<4> {
+ inline void setParams(
+ float sampleRate,
+ float frequency,
+ float bw,
+ BandwidthMode bwm = PITCH_BANDWIDTH_MODE
+ ) {
+ design(
+ sampleRate,
+ BUTTERWORTH_TYPE,
+ 4,
+ BANDPASS_MODE,
+ frequency,
+ bw,
+ bwm
+ );
+ }
+};
+
} // namespace dsp
} // namespace bogaudio