Add the multi-mode filter, mono and stereo

This commit is contained in:
Jean Pierre Cimalando 2020-01-30 18:54:58 +01:00
parent b6130ccf02
commit ac4dfff729
10 changed files with 1145 additions and 0 deletions

64
scripts/generate_filters.sh Executable file
View file

@ -0,0 +1,64 @@
#!/bin/sh
set -e
if ! test -d "src"; then
echo "Please run this in the project root directory."
exit 1
fi
FAUSTARGS="-double -inpl"
# support GNU sed only, use gsed on a Mac
test -z "$SED" && SED=sed
faustgen() {
mkdir -p src/sfizz/gen/filters
local outfile=src/sfizz/gen/filters/sfz"$1".cxx
local code=`faust $FAUSTARGS -pn sfz"$1" -cn faust"$1" src/sfizz/dsp/filters/sfz_filters.dsp`
# find variable names of our controls
local cutoffVar=`echo "$code" | $SED -r 's%.*\("Cutoff", &[ \t]*([a-zA-Z0-9_]+).*%\1%;t;d'`
local resoVar=`echo "$code" | $SED -r 's%.*\("Resonance", &[ \t]*([a-zA-Z0-9_]+).*%\1%;t;d'`
local pkshVar=`echo "$code" | $SED -r 's%.*\("Peak/shelf gain", &[ \t]*([a-zA-Z0-9_]+).*%\1%;t;d'`
# suppress some faust-specific stuff we don't care
echo "$code" \
| fgrep -v -- '->declare(' \
| fgrep -v -- '->openHorizontalBox(' \
| fgrep -v -- '->openVerticalBox(' \
| fgrep -v -- '->closeBox(' \
| fgrep -v -- '->addHorizontalSlider(' \
| fgrep -v -- '->addVerticalSlider(' \
> "$outfile"
# direct access to parameter variables
$SED -r -i 's/\bprivate:/public:/' "$outfile"
# no virtuals please
$SED -r -i 's/\bvirtual\b//' "$outfile"
# rename the variables for us to access more easily
if test ! -z "$cutoffVar"; then
$SED -r -i 's/\b'"$cutoffVar"'\b/fCutoff/' "$outfile"
fi
if test ! -z "$resoVar"; then
$SED -r -i 's/\b'"$resoVar"'\b/fQ/' "$outfile"
fi
if test ! -z "$pkshVar"; then
$SED -r -i 's/\b'"$pkshVar"'\b/fPkShGain/' "$outfile"
fi
}
for f in \
Lpf1p Lpf2p Lpf4p Lpf6p \
Hpf1p Hpf2p Hpf4p Hpf6p \
Bpf1p Bpf2p Bpf4p Bpf6p \
Apf1p \
Brf1p Brf2p \
Lsh Hsh Peq \
Pink \
Lpf2pSv Hpf2pSv Bpf2pSv Brf2pSv
do
faustgen "$f"
faustgen "2ch$f"
done

View file

@ -9,6 +9,7 @@ set (SFIZZ_SOURCES
sfizz/Oversampler.cpp
sfizz/FloatEnvelopes.cpp
sfizz/Logger.cpp
sfizz/SfzFilter.cpp
)
include (SfizzSIMDSourceFilesCheck)

256
src/sfizz/SfzFilter.cpp Normal file
View file

@ -0,0 +1,256 @@
// SPDX-License-Identifier: BSD-2-Clause
// This code is part of the sfizz library and is licensed under a BSD 2-clause
// license. You should have receive a LICENSE.md file along with the code.
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#include "SfzFilter.h"
#include "SfzFilterDefs.h"
#include "SfzFilterImpls.cxx"
#include <cstring>
#include <cassert>
namespace sfz {
template <unsigned NCh>
struct Filter<NCh>::Impl {
FilterType fType = kFilterNone;
sfzLpf1p<NCh> fDspLpf1p;
sfzLpf2p<NCh> fDspLpf2p;
sfzLpf4p<NCh> fDspLpf4p;
sfzLpf6p<NCh> fDspLpf6p;
sfzHpf1p<NCh> fDspHpf1p;
sfzHpf2p<NCh> fDspHpf2p;
sfzHpf4p<NCh> fDspHpf4p;
sfzHpf6p<NCh> fDspHpf6p;
sfzBpf1p<NCh> fDspBpf1p;
sfzBpf2p<NCh> fDspBpf2p;
sfzBpf4p<NCh> fDspBpf4p;
sfzBpf6p<NCh> fDspBpf6p;
sfzApf1p<NCh> fDspApf1p;
sfzBrf1p<NCh> fDspBrf1p;
sfzBrf2p<NCh> fDspBrf2p;
sfzPink<NCh> fDspPink;
sfzLpf2pSv<NCh> fDspLpf2pSv;
sfzHpf2pSv<NCh> fDspHpf2pSv;
sfzBpf2pSv<NCh> fDspBpf2pSv;
sfzBrf2pSv<NCh> fDspBrf2pSv;
sfzLsh<NCh> fDspLsh;
sfzHsh<NCh> fDspHsh;
sfzPeq<NCh> fDspPeq;
template <class F> static void process(F &filter, const float *const in[NCh], float *const out[NCh], float cutoff, float q, float pksh, unsigned nframes);
template <class F> static void processModulated(F &filter, const float *const in[NCh], float *const out[NCh], const float *cutoff, const float *q, const float *pksh, unsigned nframes);
};
template <unsigned NCh>
Filter<NCh>::Filter()
: P{new Impl}
{
}
template <unsigned NCh>
Filter<NCh>::~Filter()
{
}
template <unsigned NCh>
void Filter<NCh>::init(double sampleRate)
{
P->fDspLpf1p.init(sampleRate);
P->fDspLpf2p.init(sampleRate);
P->fDspLpf4p.init(sampleRate);
P->fDspLpf6p.init(sampleRate);
P->fDspHpf1p.init(sampleRate);
P->fDspHpf2p.init(sampleRate);
P->fDspHpf4p.init(sampleRate);
P->fDspHpf6p.init(sampleRate);
P->fDspBpf1p.init(sampleRate);
P->fDspBpf2p.init(sampleRate);
P->fDspBpf4p.init(sampleRate);
P->fDspBpf6p.init(sampleRate);
P->fDspApf1p.init(sampleRate);
P->fDspBrf1p.init(sampleRate);
P->fDspBrf2p.init(sampleRate);
P->fDspPink.init(sampleRate);
P->fDspLpf2pSv.init(sampleRate);
P->fDspHpf2pSv.init(sampleRate);
P->fDspBpf2pSv.init(sampleRate);
P->fDspBrf2pSv.init(sampleRate);
P->fDspLsh.init(sampleRate);
P->fDspHsh.init(sampleRate);
P->fDspPeq.init(sampleRate);
}
template <unsigned NCh>
void Filter<NCh>::clear()
{
switch (P->fType) {
case kFilterNone: break;
case kFilterApf1p: P->fDspApf1p.instanceClear(); break;
case kFilterBpf1p: P->fDspBpf1p.instanceClear(); break;
case kFilterBpf2p: P->fDspBpf2p.instanceClear(); break;
case kFilterBpf4p: P->fDspBpf4p.instanceClear(); break;
case kFilterBpf6p: P->fDspBpf6p.instanceClear(); break;
case kFilterBrf1p: P->fDspBrf1p.instanceClear(); break;
case kFilterBrf2p: P->fDspBrf2p.instanceClear(); break;
case kFilterHpf1p: P->fDspHpf1p.instanceClear(); break;
case kFilterHpf2p: P->fDspHpf2p.instanceClear(); break;
case kFilterHpf4p: P->fDspHpf4p.instanceClear(); break;
case kFilterHpf6p: P->fDspHpf6p.instanceClear(); break;
case kFilterLpf1p: P->fDspLpf1p.instanceClear(); break;
case kFilterLpf2p: P->fDspLpf2p.instanceClear(); break;
case kFilterLpf4p: P->fDspLpf4p.instanceClear(); break;
case kFilterLpf6p: P->fDspLpf6p.instanceClear(); break;
case kFilterPink: P->fDspPink.instanceClear(); break;
case kFilterLpf2pSv: P->fDspLpf2pSv.instanceClear(); break;
case kFilterHpf2pSv: P->fDspHpf2pSv.instanceClear(); break;
case kFilterBpf2pSv: P->fDspBpf2pSv.instanceClear(); break;
case kFilterBrf2pSv: P->fDspBrf2pSv.instanceClear(); break;
case kFilterLsh: P->fDspLsh.instanceClear(); break;
case kFilterHsh: P->fDspHsh.instanceClear(); break;
case kFilterPeq: P->fDspPeq.instanceClear(); break;
}
}
template <unsigned NCh>
template <class F>
void Filter<NCh>::Impl::process(F &filter, const float *const in[NCh], float *const out[NCh], float cutoff, float q, float pksh, unsigned nframes)
{
filter.setCutoff(cutoff);
filter.setQ(q);
filter.setPkShGain(pksh);
filter.compute(nframes, const_cast<float **>(in), const_cast<float **>(out));
}
template <unsigned NCh>
void Filter<NCh>::process(const float *const in[NCh], float *const out[NCh], float cutoff, float q, float pksh, unsigned nframes)
{
if (P->fType == kFilterNone) {
for (unsigned c = 0; c < NCh; ++c) {
const float *ch_in = in[c];
float *ch_out = out[c];
if (ch_in != ch_out)
std::memcpy(ch_out, ch_in, nframes * sizeof(float));
}
return;
}
switch (P->fType) {
case kFilterApf1p: P->process(P->fDspApf1p, in, out, cutoff, q, pksh, nframes); break;
case kFilterBpf1p: P->process(P->fDspBpf1p, in, out, cutoff, q, pksh, nframes); break;
case kFilterBpf2p: P->process(P->fDspBpf2p, in, out, cutoff, q, pksh, nframes); break;
case kFilterBpf4p: P->process(P->fDspBpf4p, in, out, cutoff, q, pksh, nframes); break;
case kFilterBpf6p: P->process(P->fDspBpf6p, in, out, cutoff, q, pksh, nframes); break;
case kFilterBrf1p: P->process(P->fDspBrf1p, in, out, cutoff, q, pksh, nframes); break;
case kFilterBrf2p: P->process(P->fDspBrf2p, in, out, cutoff, q, pksh, nframes); break;
case kFilterHpf1p: P->process(P->fDspHpf1p, in, out, cutoff, q, pksh, nframes); break;
case kFilterHpf2p: P->process(P->fDspHpf2p, in, out, cutoff, q, pksh, nframes); break;
case kFilterHpf4p: P->process(P->fDspHpf4p, in, out, cutoff, q, pksh, nframes); break;
case kFilterHpf6p: P->process(P->fDspHpf6p, in, out, cutoff, q, pksh, nframes); break;
case kFilterLpf1p: P->process(P->fDspLpf1p, in, out, cutoff, q, pksh, nframes); break;
case kFilterLpf2p: P->process(P->fDspLpf2p, in, out, cutoff, q, pksh, nframes); break;
case kFilterLpf4p: P->process(P->fDspLpf4p, in, out, cutoff, q, pksh, nframes); break;
case kFilterLpf6p: P->process(P->fDspLpf6p, in, out, cutoff, q, pksh, nframes); break;
case kFilterPink: P->process(P->fDspPink, in, out, cutoff, q, pksh, nframes); break;
case kFilterLpf2pSv: P->process(P->fDspLpf2pSv, in, out, cutoff, q, pksh, nframes); break;
case kFilterHpf2pSv: P->process(P->fDspHpf2pSv, in, out, cutoff, q, pksh, nframes); break;
case kFilterBpf2pSv: P->process(P->fDspBpf2pSv, in, out, cutoff, q, pksh, nframes); break;
case kFilterBrf2pSv: P->process(P->fDspBrf2pSv, in, out, cutoff, q, pksh, nframes); break;
case kFilterLsh: P->process(P->fDspLsh, in, out, cutoff, q, pksh, nframes); break;
case kFilterHsh: P->process(P->fDspHsh, in, out, cutoff, q, pksh, nframes); break;
case kFilterPeq: P->process(P->fDspPeq, in, out, cutoff, q, pksh, nframes); break;
default: assert(false);
}
}
template <unsigned NCh>
template <class F>
void Filter<NCh>::Impl::processModulated(F &filter, const float *const in[NCh], float *const out[NCh], const float *cutoff, const float *q, const float *pksh, unsigned nframes)
{
unsigned frame = 0;
while (frame < nframes) {
unsigned current = nframes - frame;
if (current > kFilterControlInterval)
current = kFilterControlInterval;
const float *current_in[NCh];
float *current_out[NCh];
for (unsigned c = 0; c < NCh; ++c) {
current_in[c] = in[c] + frame;
current_out[c] = out[c] + frame;
}
filter.setCutoff(cutoff[frame]);
filter.setQ(q[frame]);
filter.setPkShGain(pksh[frame]);
filter.compute(current, const_cast<float **>(current_in), const_cast<float **>(current_out));
frame += current;
}
}
template <unsigned NCh>
void Filter<NCh>::processModulated(const float *const in[NCh], float *const out[NCh], const float *cutoff, const float *q, const float *pksh, unsigned nframes)
{
if (P->fType == kFilterNone) {
for (unsigned c = 0; c < NCh; ++c) {
const float *ch_in = in[c];
float *ch_out = out[c];
if (ch_in != ch_out)
std::memcpy(ch_out, ch_in, nframes * sizeof(float));
}
return;
}
switch (P->fType) {
case kFilterApf1p: P->processModulated(P->fDspApf1p, in, out, cutoff, q, pksh, nframes); break;
case kFilterBpf1p: P->processModulated(P->fDspBpf1p, in, out, cutoff, q, pksh, nframes); break;
case kFilterBpf2p: P->processModulated(P->fDspBpf2p, in, out, cutoff, q, pksh, nframes); break;
case kFilterBpf4p: P->processModulated(P->fDspBpf4p, in, out, cutoff, q, pksh, nframes); break;
case kFilterBpf6p: P->processModulated(P->fDspBpf6p, in, out, cutoff, q, pksh, nframes); break;
case kFilterBrf1p: P->processModulated(P->fDspBrf1p, in, out, cutoff, q, pksh, nframes); break;
case kFilterBrf2p: P->processModulated(P->fDspBrf2p, in, out, cutoff, q, pksh, nframes); break;
case kFilterHpf1p: P->processModulated(P->fDspHpf1p, in, out, cutoff, q, pksh, nframes); break;
case kFilterHpf2p: P->processModulated(P->fDspHpf2p, in, out, cutoff, q, pksh, nframes); break;
case kFilterHpf4p: P->processModulated(P->fDspHpf4p, in, out, cutoff, q, pksh, nframes); break;
case kFilterHpf6p: P->processModulated(P->fDspHpf6p, in, out, cutoff, q, pksh, nframes); break;
case kFilterLpf1p: P->processModulated(P->fDspLpf1p, in, out, cutoff, q, pksh, nframes); break;
case kFilterLpf2p: P->processModulated(P->fDspLpf2p, in, out, cutoff, q, pksh, nframes); break;
case kFilterLpf4p: P->processModulated(P->fDspLpf4p, in, out, cutoff, q, pksh, nframes); break;
case kFilterLpf6p: P->processModulated(P->fDspLpf6p, in, out, cutoff, q, pksh, nframes); break;
case kFilterPink: P->processModulated(P->fDspPink, in, out, cutoff, q, pksh, nframes); break;
case kFilterLpf2pSv: P->processModulated(P->fDspLpf2pSv, in, out, cutoff, q, pksh, nframes); break;
case kFilterHpf2pSv: P->processModulated(P->fDspHpf2pSv, in, out, cutoff, q, pksh, nframes); break;
case kFilterBpf2pSv: P->processModulated(P->fDspBpf2pSv, in, out, cutoff, q, pksh, nframes); break;
case kFilterBrf2pSv: P->processModulated(P->fDspBrf2pSv, in, out, cutoff, q, pksh, nframes); break;
case kFilterLsh: P->processModulated(P->fDspLsh, in, out, cutoff, q, pksh, nframes); break;
case kFilterHsh: P->processModulated(P->fDspHsh, in, out, cutoff, q, pksh, nframes); break;
case kFilterPeq: P->processModulated(P->fDspPeq, in, out, cutoff, q, pksh, nframes); break;
default: assert(false);
}
}
template <unsigned NCh>
FilterType Filter<NCh>::type() const
{
return P->fType;
}
template <unsigned NCh>
void Filter<NCh>::setType(FilterType type)
{
if (P->fType != type) {
P->fType = type;
clear();
}
}
template class Filter<1>;
template class Filter<2>;
} // namespace sfz

100
src/sfizz/SfzFilter.h Normal file
View file

@ -0,0 +1,100 @@
// SPDX-License-Identifier: BSD-2-Clause
// This code is part of the sfizz library and is licensed under a BSD 2-clause
// license. You should have receive a LICENSE.md file along with the code.
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#pragma once
#include <memory>
namespace sfz {
enum FilterType : int;
/**
Multi-mode filter for SFZ v2
Available for mono and stereo. (NCh=1, NCh=2)
Parameters:
`cutoff`: it's the opcode `filN_cutoff` (Hz)
`q`: it's the opcode `filN_resonance` (dB)
`pksh`: it's the opcode `filN_gain` (dB)
*/
template <unsigned NCh>
class Filter {
public:
Filter();
~Filter();
/**
Set up the filter constants.
Run it exactly once after instantiating.
*/
void init(double sampleRate);
/**
Reinitialize the filter memory to zeros.
*/
void clear();
/**
Process one cycle of the filter without modulating cutoff or Q.
`cutoff` is a frequency expressed in Hz.
`q` is a resonance expressed in dB.
`pksh` is a peak/shelf gain expressed in dB.
`in[i]` and `out[i]` may refer to identical buffers, for in-place processing
*/
void process(const float *const in[NCh], float *const out[NCh], float cutoff, float q, float pksh, unsigned nframes);
/**
Process one cycle of the filter with cutoff and Q values varying over time.
`cutoff` is a frequency expressed in Hz.
`q` is a resonance expressed in dB.
`pksh` is a peak/shelf gain expressed in dB.
`in[i]` and `out[i]` may refer to identical buffers, for in-place processing
*/
void processModulated(const float *const in[NCh], float *const out[NCh], const float *cutoff, const float *q, const float *pksh, unsigned nframes);
/**
Get the type of filter.
*/
FilterType type() const;
/**
Set the type of filter.
*/
void setType(FilterType type);
private:
struct Impl;
std::unique_ptr<Impl> P;
};
enum FilterType : int {
kFilterNone,
kFilterApf1p,
kFilterBpf1p,
kFilterBpf2p,
kFilterBpf4p,
kFilterBpf6p,
kFilterBrf1p,
kFilterBrf2p,
kFilterHpf1p,
kFilterHpf2p,
kFilterHpf4p,
kFilterHpf6p,
kFilterLpf1p,
kFilterLpf2p,
kFilterLpf4p,
kFilterLpf6p,
kFilterPink,
kFilterLpf2pSv,
kFilterHpf2pSv,
kFilterBpf2pSv,
kFilterBrf2pSv,
kFilterLsh,
kFilterHsh,
kFilterPeq,
};
} // namespace sfz

19
src/sfizz/SfzFilterDefs.h Normal file
View file

@ -0,0 +1,19 @@
// SPDX-License-Identifier: BSD-2-Clause
// This code is part of the sfizz library and is licensed under a BSD 2-clause
// license. You should have receive a LICENSE.md file along with the code.
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#pragma once
namespace sfz {
enum {
/**
Minimum interval in frames between recomputations of coefficients of the
modulated filter. The lower, the more CPU resources are consumed.
*/
kFilterControlInterval = 16,
};
} // namespace sfz

View file

@ -0,0 +1,153 @@
// SPDX-License-Identifier: BSD-2-Clause
// This code is part of the sfizz library and is licensed under a BSD 2-clause
// license. You should have receive a LICENSE.md file along with the code.
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
struct dsp {};
struct Meta {};
struct UI {};
#include "gen/filters/sfzApf1p.cxx"
#include "gen/filters/sfzBpf1p.cxx"
#include "gen/filters/sfzBpf2p.cxx"
#include "gen/filters/sfzBpf4p.cxx"
#include "gen/filters/sfzBpf6p.cxx"
#include "gen/filters/sfzBrf1p.cxx"
#include "gen/filters/sfzBrf2p.cxx"
#include "gen/filters/sfzHpf1p.cxx"
#include "gen/filters/sfzHpf2p.cxx"
#include "gen/filters/sfzHpf4p.cxx"
#include "gen/filters/sfzHpf6p.cxx"
#include "gen/filters/sfzLpf1p.cxx"
#include "gen/filters/sfzLpf2p.cxx"
#include "gen/filters/sfzLpf4p.cxx"
#include "gen/filters/sfzLpf6p.cxx"
#include "gen/filters/sfzPink.cxx"
#include "gen/filters/sfzLpf2pSv.cxx"
#include "gen/filters/sfzHpf2pSv.cxx"
#include "gen/filters/sfzBpf2pSv.cxx"
#include "gen/filters/sfzBrf2pSv.cxx"
#include "gen/filters/sfzLsh.cxx"
#include "gen/filters/sfzHsh.cxx"
#include "gen/filters/sfzPeq.cxx"
#include "gen/filters/sfz2chApf1p.cxx"
#include "gen/filters/sfz2chBpf1p.cxx"
#include "gen/filters/sfz2chBpf2p.cxx"
#include "gen/filters/sfz2chBpf4p.cxx"
#include "gen/filters/sfz2chBpf6p.cxx"
#include "gen/filters/sfz2chBrf1p.cxx"
#include "gen/filters/sfz2chBrf2p.cxx"
#include "gen/filters/sfz2chHpf1p.cxx"
#include "gen/filters/sfz2chHpf2p.cxx"
#include "gen/filters/sfz2chHpf4p.cxx"
#include "gen/filters/sfz2chHpf6p.cxx"
#include "gen/filters/sfz2chLpf1p.cxx"
#include "gen/filters/sfz2chLpf2p.cxx"
#include "gen/filters/sfz2chLpf4p.cxx"
#include "gen/filters/sfz2chLpf6p.cxx"
#include "gen/filters/sfz2chPink.cxx"
#include "gen/filters/sfz2chLpf2pSv.cxx"
#include "gen/filters/sfz2chHpf2pSv.cxx"
#include "gen/filters/sfz2chBpf2pSv.cxx"
#include "gen/filters/sfz2chBrf2pSv.cxx"
#include "gen/filters/sfz2chLsh.cxx"
#include "gen/filters/sfz2chHsh.cxx"
#include "gen/filters/sfz2chPeq.cxx"
template <class F> struct sfzFilter : public F {
void setCutoff(float v) { F::fCutoff = v; }
void setQ(float v) { F::fQ = v; }
void setPkShGain(float) {}
};
template <class F> struct sfzFilterNoQ : public F {
void setCutoff(float v) { F::fCutoff = v; }
void setQ(float) {}
void setPkShGain(float) {}
};
template <class F> struct sfzFilterNoCutoff : public F {
void setCutoff(float) {}
void setQ(float) {}
void setPkShGain(float) {}
};
template <class F> struct sfzFilterEq : public F {
void setCutoff(float v) { F::fCutoff = v; }
void setQ(float v) { F::fQ = v; }
void setPkShGain(float v) { F::fPkShGain = v; }
};
template <unsigned NCh> struct sfzLpf1p;
template <unsigned NCh> struct sfzLpf2p;
template <unsigned NCh> struct sfzLpf4p;
template <unsigned NCh> struct sfzLpf6p;
template <unsigned NCh> struct sfzHpf1p;
template <unsigned NCh> struct sfzHpf2p;
template <unsigned NCh> struct sfzHpf4p;
template <unsigned NCh> struct sfzHpf6p;
template <unsigned NCh> struct sfzBpf1p;
template <unsigned NCh> struct sfzBpf2p;
template <unsigned NCh> struct sfzBpf4p;
template <unsigned NCh> struct sfzBpf6p;
template <unsigned NCh> struct sfzApf1p;
template <unsigned NCh> struct sfzBrf1p;
template <unsigned NCh> struct sfzBrf2p;
template <unsigned NCh> struct sfzPink;
template <unsigned NCh> struct sfzLpf2pSv;
template <unsigned NCh> struct sfzHpf2pSv;
template <unsigned NCh> struct sfzBpf2pSv;
template <unsigned NCh> struct sfzBrf2pSv;
template <unsigned NCh> struct sfzLsh;
template <unsigned NCh> struct sfzHsh;
template <unsigned NCh> struct sfzPeq;
template<> struct sfzLpf1p<1> : public sfzFilterNoQ<faustLpf1p> {};
template<> struct sfzLpf2p<1> : public sfzFilter<faustLpf2p> {};
template<> struct sfzLpf4p<1> : public sfzFilter<faustLpf4p> {};
template<> struct sfzLpf6p<1> : public sfzFilter<faustLpf6p> {};
template<> struct sfzHpf1p<1> : public sfzFilterNoQ<faustHpf1p> {};
template<> struct sfzHpf2p<1> : public sfzFilter<faustHpf2p> {};
template<> struct sfzHpf4p<1> : public sfzFilter<faustHpf4p> {};
template<> struct sfzHpf6p<1> : public sfzFilter<faustHpf6p> {};
template<> struct sfzBpf1p<1> : public sfzFilterNoQ<faustBpf1p> {};
template<> struct sfzBpf2p<1> : public sfzFilter<faustBpf2p> {};
template<> struct sfzBpf4p<1> : public sfzFilter<faustBpf4p> {};
template<> struct sfzBpf6p<1> : public sfzFilter<faustBpf6p> {};
template<> struct sfzApf1p<1> : public sfzFilterNoQ<faustApf1p> {};
template<> struct sfzBrf1p<1> : public sfzFilterNoQ<faustBrf1p> {};
template<> struct sfzBrf2p<1> : public sfzFilter<faustBrf2p> {};
template<> struct sfzPink<1> : public sfzFilterNoCutoff<faustPink> {};
template<> struct sfzLpf2pSv<1> : public sfzFilter<faustLpf2pSv> {};
template<> struct sfzHpf2pSv<1> : public sfzFilter<faustHpf2pSv> {};
template<> struct sfzBpf2pSv<1> : public sfzFilter<faustBpf2pSv> {};
template<> struct sfzBrf2pSv<1> : public sfzFilter<faustBrf2pSv> {};
template<> struct sfzLsh<1> : public sfzFilterEq<faustLsh> {};
template<> struct sfzHsh<1> : public sfzFilterEq<faustHsh> {};
template<> struct sfzPeq<1> : public sfzFilterEq<faustPeq> {};
template<> struct sfzLpf1p<2> : public sfzFilterNoQ<faust2chLpf1p> {};
template<> struct sfzLpf2p<2> : public sfzFilter<faust2chLpf2p> {};
template<> struct sfzLpf4p<2> : public sfzFilter<faust2chLpf4p> {};
template<> struct sfzLpf6p<2> : public sfzFilter<faust2chLpf6p> {};
template<> struct sfzHpf1p<2> : public sfzFilterNoQ<faust2chHpf1p> {};
template<> struct sfzHpf2p<2> : public sfzFilter<faust2chHpf2p> {};
template<> struct sfzHpf4p<2> : public sfzFilter<faust2chHpf4p> {};
template<> struct sfzHpf6p<2> : public sfzFilter<faust2chHpf6p> {};
template<> struct sfzBpf1p<2> : public sfzFilterNoQ<faust2chBpf1p> {};
template<> struct sfzBpf2p<2> : public sfzFilter<faust2chBpf2p> {};
template<> struct sfzBpf4p<2> : public sfzFilter<faust2chBpf4p> {};
template<> struct sfzBpf6p<2> : public sfzFilter<faust2chBpf6p> {};
template<> struct sfzApf1p<2> : public sfzFilterNoQ<faust2chApf1p> {};
template<> struct sfzBrf1p<2> : public sfzFilterNoQ<faust2chBrf1p> {};
template<> struct sfzBrf2p<2> : public sfzFilter<faust2chBrf2p> {};
template<> struct sfzPink<2> : public sfzFilterNoCutoff<faust2chPink> {};
template<> struct sfzLpf2pSv<2> : public sfzFilter<faust2chLpf2pSv> {};
template<> struct sfzHpf2pSv<2> : public sfzFilter<faust2chHpf2pSv> {};
template<> struct sfzBpf2pSv<2> : public sfzFilter<faust2chBpf2pSv> {};
template<> struct sfzBrf2pSv<2> : public sfzFilter<faust2chBrf2pSv> {};
template<> struct sfzLsh<2> : public sfzFilterEq<faust2chLsh> {};
template<> struct sfzHsh<2> : public sfzFilterEq<faust2chHsh> {};
template<> struct sfzPeq<2> : public sfzFilterEq<faust2chPeq> {};

View file

@ -0,0 +1,72 @@
// -*- mode: faust; -*-
declare author "Jean Pierre Cimalando";
declare license "BSD-2-Clause";
import("stdfaust.lib");
rbj = library("rbj_filters.dsp");
//-------------------------------------------------------------------------
// Biquad filter from normalized coefficients
// b0,b1,b2,a1,a2 : normalized coefficients
//-------------------------------------------------------------------------
biquad(b0,b1,b2,a1,a2) = fi.iir((b0,b1,b2),(a1,a2));
//-------------------------------------------------------------------------
// Biquad filter using smoothed coefficients
// s : a smoothing function applied to each coefficient
// b0,b1,b2,a1,a2 : normalized coefficients
//-------------------------------------------------------------------------
smoothBiquad(s,b0,b1,b2,a1,a2) = biquad(b0:s,b1:s,b2:s,a1:s,a2:s);
//-------------------------------------------------------------------------
// RBJ filter of a specific type using smoothed coefficients
// s : a smoothing function applied to each coefficient
// f : cutoff frequency
// g : gain in decibel, for peaking and shelving types only
// q : height of the resonant peak in linear units
//-------------------------------------------------------------------------
rbjLpfSmooth(s,f,g,q,x) = (rbj.filtercoeff(f,g,q).LPF,x) : smoothBiquad(s);
rbjHpfSmooth(s,f,g,q,x) = (rbj.filtercoeff(f,g,q).HPF,x) : smoothBiquad(s);
rbjBpfSmooth(s,f,g,q,x) = (rbj.filtercoeff(f,g,q).BPF,x) : smoothBiquad(s);
rbjNotchSmooth(s,f,g,q,x) = (rbj.filtercoeff(f,g,q).notch,x) : smoothBiquad(s);
rbjApfSmooth(s,f,g,q,x) = (rbj.filtercoeff(f,g,q).APF,x) : smoothBiquad(s);
rbjPeakingEqSmooth(s,f,g,q,x) = (rbj.filtercoeff(f,g,q).peakingEQ,x) : smoothBiquad(s);
rbjPeakingNotchSmooth(s,f,g,q,x) = (rbj.filtercoeff(f,g,q).peakNotch,x) : smoothBiquad(s);
rbjLowShelfSmooth(s,f,g,q,x) = (rbj.filtercoeff(f,g,q).lowShelf,x) : smoothBiquad(s);
rbjHighShelfSmooth(s,f,g,q,x) = (rbj.filtercoeff(f,g,q).highShelf,x) : smoothBiquad(s);
//-------------------------------------------------------------------------
// 1-pole low-pass filter
// s : a smoothing function applied to each coefficient
// f : cutoff frequency
//-------------------------------------------------------------------------
lp1Smooth(s,f) = fi.iir((1-p),(0-p)) with {
p = exp(-2.*ma.PI*f/ma.SR) : s;
};
//-------------------------------------------------------------------------
// 1-pole high-pass filter
// s : a smoothing function applied to each coefficient
// f : cutoff frequency
//-------------------------------------------------------------------------
hp1Smooth(s,f) = fi.iir((0.5*(1.+p),-0.5*(1+p)),(0.-p)) with {
p = exp(-2.*ma.PI*f/ma.SR) : s;
};
//-------------------------------------------------------------------------
// 1-pole all-pass filter
// s : a smoothing function applied to each coefficient
// f : cutoff frequency
//-------------------------------------------------------------------------
ap1Smooth(s,f) = fi.iir((a,1.),(a)) with {
a = (-1.+2.*ma.PI*f/ma.SR) : s;
};
//------------------------------------------------------------------------------
// Example
//------------------------------------------------------------------------------
// process = rbjLpfSmooth(si.smoo, cutoff, 0.0, resonance : ba.db2linear) with {
// cutoff = hslider("[1] Cutoff [unit:Hz] [scale:log]", 440.0, 50.0, 10000.0, 1.0);
// resonance = hslider("[2] Resonance [unit:dB]", 0.0, 0.0, 40.0, 0.1);
// };

View file

@ -0,0 +1,212 @@
/**
Note(jpc): This is RBJ filter extracted from faustlibraries master branch.
Unlike some prior versions found in faust 2.x, this one has been
permissively relicensed.
*/
/************************************************************************
************************************************************************
FAUST library file
Copyright (C) 2019-2020 GRAME, Centre National de Creation Musicale
---------------------------------------------------------------------
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA.
EXCEPTION TO THE LGPL LICENSE : As a special exception, you may create a
larger FAUST program which directly or indirectly imports this library
file and still distribute the compiled code generated by the FAUST
compiler, or a modified version of this compiled code, under your own
copyright and license. This EXCEPTION TO THE LGPL LICENSE explicitly
grants you the right to freely choose the license for the resulting
compiled code. In particular the resulting compiled code has no obligation
to be LGPL or GPL. For example you are free to choose a commerci
************************************************************************
************************************************************************/
declare name "MaxMSP compatibility Library";
declare author "GRAME";
declare copyright "GRAME";
declare version "1.1";
declare license "LGPL with exception";
ba = library("basics.lib");
ma = library("maths.lib");
//-------------------------------------------------------------------------
//
// Implementation of MaxMSP filtercoeff
//
// from : Cookbook formulae for audio EQ biquad filter coefficients
// by : Robert Bristow-Johnson <rbj@audioimagination.com>
// URL : http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
//
//-------------------------------------------------------------------------
filtercoeff(f0, dBgain, Q) = environment
{
//----------------------------------------
// biquad coeffs for various filters
// usage : filtercoeff(f0, dBgain, Q).LPF
//----------------------------------------
LPF = rbjcoef(a0, a1, a2, b0, b1, b2)
with {
b0 = (1 - cos(w0))/2;
b1 = 1 - cos(w0);
b2 = (1 - cos(w0))/2;
a0 = 1 + alpha;
a1 = -2*cos(w0);
a2 = 1 - alpha;
};
HPF = rbjcoef(a0, a1, a2, b0, b1, b2)
with {
b0 = (1 + cos(w0))/2;
b1 = -1 - cos(w0);
b2 = (1 + cos(w0))/2;
a0 = 1 + alpha;
a1 = -2*cos(w0);
a2 = 1 - alpha;
};
BPF = rbjcoef(a0, a1, a2, b0, b1, b2) // constant 0 dB peak gain
with {
b0 = alpha;
b1 = 0;
b2 = -alpha;
a0 = 1 + alpha;
a1 = -2*cos(w0);
a2 = 1 - alpha;
};
notch = rbjcoef(a0, a1, a2, b0, b1, b2)
with {
b0 = 1;
b1 = -2*cos(w0);
b2 = 1;
a0 = 1 + alpha;
a1 = -2*cos(w0);
a2 = 1 - alpha;
};
APF = rbjcoef(a0, a1, a2, b0, b1, b2)
with {
b0 = 1 - alpha;
b1 = -2*cos(w0);
b2 = 1 + alpha;
a0 = 1 + alpha;
a1 = -2*cos(w0);
a2 = 1 - alpha;
};
peakingEQ = rbjcoef(a0, a1, a2, b0, b1, b2)
with {
b0 = 1 + alpha*A;
b1 = -2*cos(w0);
b2 = 1 - alpha*A;
a0 = 1 + alpha/A;
a1 = -2*cos(w0);
a2 = 1 - alpha/A;
};
peakNotch = rbjcoef(a0, a1, a2, b0, b1, b2)
with {
b0 = 1 + alpha*G;
b1 = -2*cos(w0);
b2 = 1 - alpha*G;
a0 = 1 + alpha/G;
a1 = -2*cos(w0);
a2 = 1 - alpha/G;
};
lowShelf = rbjcoef(a0, a1, a2, b0, b1, b2)
with {
b0 = A*((A+1) - (A-1)*cos(w0) + 2*sqrt(A)*alpha);
b1 = 2*A*((A-1) - (A+1)*cos(w0));
b2 = A*((A+1) - (A-1)*cos(w0) - 2*sqrt(A)*alpha);
a0 = (A+1) + (A-1)*cos(w0) + 2*sqrt(A)*alpha;
a1 = -2*((A-1) + (A+1)*cos(w0));
a2 = (A+1) + (A-1)*cos(w0) - 2*sqrt(A)*alpha;
};
highShelf = rbjcoef(a0, a1, a2, b0, b1, b2)
with {
b0 = A*((A+1) + (A-1)*cos(w0) + 2*sqrt(A)*alpha);
b1 = -2*A*((A-1) + (A+1)*cos(w0));
b2 = A*((A+1) + (A-1)*cos(w0) - 2*sqrt(A)*alpha);
a0 = (A+1) - (A-1)*cos(w0) + 2*sqrt(A)*alpha;
a1 = 2*((A-1) - (A+1)*cos(w0));
a2 = (A+1) - (A-1)*cos(w0) - 2*sqrt(A)*alpha;
};
// --------------------- implementation ------------------------------
// convert rbj coeffs to biquad coeffs
rbjcoef(a0,a1,a2,b0,b1,b2) = (b0/a0, b1/a0, b2/a0, a1/a0, a2/a0);
// common values
// alpha = sin(w0)/(2*Q);
// w0 = 2*ma.PI*f0/Fs;
alpha = sin(w0)/(2*max(0.001,Q));
w0 = 2*ma.PI*max(0,f0)/Fs;
Fs = ma.SR;
A = 10^(dBgain/40); // (for peaking and shelving EQ filters only)
G = sqrt(max(0.00001, dBgain)); // When gain is a linear values (i.e. not in dB)
};
//-------------------------------------------------------------------------
// Implementation of MaxMSP biquad~
// y[n] = a0 * x[n] + a1 * x[n-1] + a2 * x[n-2] - b1 * y[n-1] - b2 * y[n-2]
//-------------------------------------------------------------------------
biquad(x,a0,a1,a2,b1,b2) = x : + ~ ((-1)*conv2(b1, b2)) : conv3(a0, a1, a2)
with {
conv2(c0,c1,x) = c0*x+c1*x';
conv3(c0,c1,c2,x) = c0*x+c1*x'+c2*x'';
};
//-------------------------------------------------------------------------
//
// Filters using filtercoeff and biquad
//
//-------------------------------------------------------------------------
// Low Pass Filter
LPF(x, f0, gain, Q) = x , filtercoeff(f0,gain,Q).LPF : biquad;
// High Pass Filter
HPF(x, f0, gain, Q) = x , filtercoeff(f0,gain,Q).HPF : biquad;
// Band Pass Filter
BPF(x, f0, gain, Q) = x , filtercoeff(f0,gain,Q).BPF : biquad;
// notch Filter
notch(x, f0, gain, Q) = x , filtercoeff(f0,gain,Q).notch : biquad;
// All Pass Filter
APF(x, f0, gain, Q) = x , filtercoeff(f0,gain,Q).APF : biquad;
// ????
peakingEQ(x, f0, gain, Q) = x , filtercoeff(f0,gain,Q).peakingEQ : biquad;
// Max peakNotch is like peakingEQ but with a linear gain
peakNotch(x, f0, gain, Q) = x , filtercoeff(f0,gain,Q).peakNotch : biquad;
// ????
lowShelf(x, f0, gain, Q) = x , filtercoeff(f0,gain,Q).lowShelf : biquad;
// ????
highShelf(x, f0, gain, Q) = x , filtercoeff(f0,gain,Q).highShelf : biquad;

View file

@ -0,0 +1,140 @@
/**
Note(jpc): this is a personal edit of the Sallen-Key state-variable filter
from `vaeffects.lib`. This changes:
- the frequency control, now in Hz instead of 0-1
- the smooth parameter, allowing for slower parameter transitions
while keeping some expensive computation out of the frame loop
*/
import("stdfaust.lib");
declare author "Eric Tarr";
declare license "MIT-style STK-4.3 license";
//================================Sallen Key Filters======================================
// The following filters were implemented based on VA models of synthesizer
// filters.
//
// The modeling approach is based on a Topology Preserving Transform (TPT) to
// resolve the delay-free feedback loop in the corresponding analog filters.
//
// The primary processing block used to build other filters (Moog, Korg, etc.) is
// based on a 1st-order Sallen-Key filter.
//
// The filters included in this script are 1st-order LPF/HPF and 2nd-order
// state-variable filters capable of LPF, HPF, and BPF.
//
// #### Resources:
//
// * Vadim Zavalishin (2018) "The Art of VA Filter Design", v2.1.0
// <https://www.native-instruments.com/fileadmin/ni_media/downloads/pdf/VAFilterDesign_2.1.0.pdf>
// * Will Pirkle (2014) "Resolving Delay-Free Loops in Recursive Filters Using
// the Modified Härmä Method", AES 137 <http://www.aes.org/e-lib/browse.cfm?elib=17517>
// * Description and diagrams of 1st- and 2nd-order TPT filters:
// <https://www.willpirkle.com/706-2/>
//========================================================================================
//------------------`(fi.)sallenKey2ndOrder`-----------------
// Sallen-Key generic multi-outputs 2nd order filter.
//
// This is a 2nd-order Sallen-Key state-variable filter. The idea is that by
// "tapping" into different points in the circuit, different filters
// (LPF,BPF,HPF) can be achieved. See Figure 4.6 of
// <https://www.willpirkle.com/706-2/>
//
// This is also a good example of the next step for generalizing the Faust
// programming approach used for all these VA filters. In this case, there are
// three things to calculate each recursive step (y,s1,s2). For each thing, the
// circuit is only calculated up to that point.
//
// Comparing the LPF to BPF, the output signal (y) is calculated similarly.
// Except, the output of the BPF stops earlier in the circuit. Similarly, the
// states (s1 and s2) only differ in that s2 includes a couple more terms
// beyond what is used for s1.
//
// #### Usage
//
// ```
// _ : sallenKey2ndOrder(smooth,freq,Q) : _,_,_
// ```
//
// Where:
//
// * `freq`: cutoff frequency
// * `Q`: q
//---------------------------------------------------------------------
sallenKey2ndOrder(smooth,freq,Q) = _<:(s1,s2,ylpf,ybpf,yhpf) : !,!,_,_,_
letrec{
's1 = -(s2):-(s1*FBs1):*(alpha0):*(g*2):+(s1);
's2 = -(s2):-(s1*FBs1):*(alpha0):*(g):+(s1):*(g*2):+(s2);
// Compute the LPF, BPF, HPF outputs
'ylpf = -(s2):-(s1*FBs1):*(alpha0):*(g*2):+(s1):*(g):+(s2);
'ybpf = -(s2):-(s1*FBs1):*(alpha0):*(g):+(s1);
'yhpf = -(s2):-(s1*FBs1):*(alpha0);
}
with{
wd = 2*ma.PI*freq;
T = 1/ma.SR;
wa = (2/T)*tan(wd*T/2);
g = (wa*T/2):smooth;
G = g/(1.0 + g);
R = 1/(2*Q);
FBs1 = (2*R+g):smooth;
alpha0 = (1/(1 + 2*R*g + g*g)):smooth;
};
//------------------`(fi.)sallenKey2ndOrderLPF`-----------------
// Sallen-Key 2nd order lowpass filter (see description above).
//
//
// #### Usage
//
// ```
// _ : sallenKey2ndOrderLPF(smooth,freq,Q) : _
// ```
//
// Where:
//
// * `freq`: cutoff frequency
// * `Q`: q
//---------------------------------------------------------------------
// Specialize the generic implementation: keep the first LPF output, the compiler will only generate the needed code
sallenKey2ndOrderLPF(smooth,freq,Q) = sallenKey2ndOrder(smooth,freq,Q) : _,!,!;
//------------------`(fi.)sallenKey2ndOrderBPF`-----------------
// Sallen-Key 2nd order bandpass filter (see description above).
//
//
// #### Usage
//
// ```
// _ : sallenKey2ndOrderBPF(smooth,freq,Q) : _
// ```
//
// Where:
//
// * `freq`: cutoff frequency
// * `Q`: q
//---------------------------------------------------------------------
// Specialize the generic implementation: keep the second BPF output, the compiler will only generate the needed code
sallenKey2ndOrderBPF(smooth,freq,Q) = sallenKey2ndOrder(smooth,freq,Q) : !,_,!;
//------------------`(fi.)sallenKey2ndOrderHPF`-----------------
// Sallen-Key 2nd order highpass filter (see description above).
//
//
// #### Usage
//
// ```
// _ : sallenKey2ndOrderHPF(smooth,freq,Q) : _
// ```
//
// Where:
//
// * `freq`: cutoff frequency
// * `Q`: q
//---------------------------------------------------------------------
// Specialize the generic implementation: keep the third HPF output, the compiler will only generate the needed code
sallenKey2ndOrderHPF(smooth,freq,Q) = sallenKey2ndOrder(smooth,freq,Q) : !,!,_;

View file

@ -0,0 +1,128 @@
// -*- mode: faust; -*-
declare author "Jean Pierre Cimalando";
declare license "BSD-2-Clause";
import("stdfaust.lib");
fm = library("filters_modulable.dsp");
sk = library("sallenkey_modulable.dsp");
//==============================================================================
// Generators
// the SFZ *noise generator
sfzNoise = no.noise : *(gate) : *(0.25);
//==============================================================================
// Filters
// the SFZ lowpass 1-pole filter
sfzLpf1p = fm.lp1Smooth(smoothCoefs,cutoff);
// the SFZ lowpass 2-pole filter
sfzLpf2p = fm.rbjLpfSmooth(smoothCoefs,cutoff,0.,Q);
// the SFZ lowpass 4-pole filter
sfzLpf4p = sfzLpf2p : sfzLpf2p;
// the SFZ lowpass 6-pole filter
sfzLpf6p = sfzLpf2p : sfzLpf2p : sfzLpf2p;
// the SFZ highpass 1-pole filter
sfzHpf1p = fm.hp1Smooth(smoothCoefs,cutoff);
// the SFZ highpass 2-pole filter
sfzHpf2p = fm.rbjHpfSmooth(smoothCoefs,cutoff,0.,Q);
// the SFZ highpass 4-pole filter
sfzHpf4p = sfzHpf2p : sfzHpf2p;
// the SFZ highpass 6-pole filter
sfzHpf6p = sfzHpf2p : sfzHpf2p : sfzHpf2p;
// the SFZ bandpass 1-pole filter
sfzBpf1p = sfzLpf1p : sfzHpf1p;
// the SFZ bandpass 2-pole filter
sfzBpf2p = fm.rbjBpfSmooth(smoothCoefs,cutoff,0.,Q);
// the SFZ bandpass 4-pole filter
// Note: bpf_4p not in specification but here anyway
sfzBpf4p = sfzBpf2p : sfzBpf2p;
// the SFZ bandpass 6-pole filter
// Note: bpf_6p not in specification but here anyway
sfzBpf6p = sfzBpf2p : sfzBpf2p : sfzBpf2p;
// the SFZ allpass 1-pole filter
sfzApf1p = fm.ap1Smooth(smoothCoefs,cutoff);
// the SFZ notch 1-pole filter
// Note: this thing is my invention, may not be correct.
// in Sforzando, 1p seems implemented the same as 2p.
sfzBrf1p = _ <: (_, (sfzApf1p : sfzApf1p)) :> +;
// the SFZ notch 2-pole filter
sfzBrf2p = fm.rbjNotchSmooth(smoothCoefs,cutoff,0.,Q);
// the SFZ pink filter
sfzPink = no.pink_filter;
// the SFZ 2-pole state-variable lowpass filter
sfzLpf2pSv = sk.sallenKey2ndOrderLPF(smoothCoefs,cutoff,Q);
// the SFZ 2-pole state-variable highpass filter
sfzHpf2pSv = sk.sallenKey2ndOrderHPF(smoothCoefs,cutoff,Q);
// the SFZ 2-pole state-variable bandpass filter
// Note: attenuate in order to have the resonant peak at 0 dB (same as sfzBpf2p)
sfzBpf2pSv = sk.sallenKey2ndOrderBPF(smoothCoefs,cutoff,Q) : *((1./Q):smoothCoefs);
// the SFZ 2-pole state-variable notch filter
sfzBrf2pSv = _ <: (sfzLpf2pSv, sfzHpf2pSv) :> +;
// the SFZ low-shelf filter
sfzLsh = fm.rbjLowShelfSmooth(smoothCoefs,cutoff,pkShGain,Q);
// the SFZ high-shelf filter
sfzHsh = fm.rbjHighShelfSmooth(smoothCoefs,cutoff,pkShGain,Q);
// the SFZ peaking EQ filter
sfzPeq = fm.rbjPeakingEqSmooth(smoothCoefs,cutoff,pkShGain,Q);
//==============================================================================
// Filters (stereo)
sfz2chLpf1p = par(i,2,sfzLpf1p);
sfz2chLpf2p = par(i,2,sfzLpf2p);
sfz2chLpf4p = par(i,2,sfzLpf4p);
sfz2chLpf6p = par(i,2,sfzLpf6p);
sfz2chHpf1p = par(i,2,sfzHpf1p);
sfz2chHpf2p = par(i,2,sfzHpf2p);
sfz2chHpf4p = par(i,2,sfzHpf4p);
sfz2chHpf6p = par(i,2,sfzHpf6p);
sfz2chBpf1p = par(i,2,sfzBpf1p);
sfz2chBpf2p = par(i,2,sfzBpf2p);
sfz2chBpf4p = par(i,2,sfzBpf4p);
sfz2chBpf6p = par(i,2,sfzBpf6p);
sfz2chApf1p = par(i,2,sfzApf1p);
sfz2chBrf1p = par(i,2,sfzBrf1p);
sfz2chBrf2p = par(i,2,sfzBrf2p);
sfz2chPink = par(i,2,sfzPink);
sfz2chLpf2pSv = par(i,2,sfzLpf2pSv);
sfz2chHpf2pSv = par(i,2,sfzHpf2pSv);
sfz2chBpf2pSv = par(i,2,sfzBpf2pSv);
sfz2chBrf2pSv = par(i,2,sfzBrf2pSv);
sfz2chLsh = par(i,2,sfzLsh);
sfz2chHsh = par(i,2,sfzHsh);
sfz2chPeq = par(i,2,sfzPeq);
//==============================================================================
// Filter parameters
cutoff = hslider("[01] Cutoff [unit:Hz] [scale:log]", 440.0, 50.0, 10000.0, 1.0);
Q = vslider("[02] Resonance [unit:dB]", 0.0, 0.0, 40.0, 0.1) : ba.db2linear;
pkShGain = vslider("[03] Peak/shelf gain [unit:dB]", 0.0, 0.0, 40.0, 0.1);
// smoothing function to prevent fast changes of filter coefficients
smoothCoefs = si.smoo; // TODO check if this is appropriate otherwise replace