Add the multi-mode filter, mono and stereo
This commit is contained in:
parent
b6130ccf02
commit
ac4dfff729
10 changed files with 1145 additions and 0 deletions
64
scripts/generate_filters.sh
Executable file
64
scripts/generate_filters.sh
Executable 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
|
||||
|
|
@ -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
256
src/sfizz/SfzFilter.cpp
Normal 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
100
src/sfizz/SfzFilter.h
Normal 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
19
src/sfizz/SfzFilterDefs.h
Normal 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
|
||||
153
src/sfizz/SfzFilterImpls.cxx
Normal file
153
src/sfizz/SfzFilterImpls.cxx
Normal 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> {};
|
||||
72
src/sfizz/dsp/filters/filters_modulable.dsp
Normal file
72
src/sfizz/dsp/filters/filters_modulable.dsp
Normal 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);
|
||||
// };
|
||||
212
src/sfizz/dsp/filters/rbj_filters.dsp
Normal file
212
src/sfizz/dsp/filters/rbj_filters.dsp
Normal 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;
|
||||
140
src/sfizz/dsp/filters/sallenkey_modulable.dsp
Normal file
140
src/sfizz/dsp/filters/sallenkey_modulable.dsp
Normal 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) : !,!,_;
|
||||
128
src/sfizz/dsp/filters/sfz_filters.dsp
Normal file
128
src/sfizz/dsp/filters/sfz_filters.dsp
Normal 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
|
||||
Loading…
Add table
Reference in a new issue