/*
* Klang – a node+text-based synthesizer library
*
* This file is part of the *wellen* library (https://github.com/dennisppaul/wellen).
* Copyright (c) 2022 Dennis P Paul.
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This library 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* [ NODE_VCF_MOOG_LHBP ]
* +---------------------+
* | |
* IN00--| SIGNAL SIGNAL |--OUT00
* IN01--| CUTOFF |
* IN02--| RESONANCE |
* | |
* +---------------------+
*/
#ifndef NodeVCFMoogLHBP_hpp
#define NodeVCFMoogLHBP_hpp
#include "NodeVCF.hpp"
namespace klang {
class NodeVCFMoogLHBP : public NodeVCF {
public:
const static uint8_t HIGH_PASS = 0;
const static uint8_t LOW_PASS = 1;
const static uint8_t BAND_PASS = 2;
void set_filter_type(const uint8_t pFilterType) {
mFilterType = pFilterType;
}
void set_command(KLANG_CMD_TYPE pCommand, KLANG_CMD_TYPE* pPayLoad) {
switch (pCommand) {
case KLANG_SET_CUTOFF_F32:
set_cutoff(KlangMath::FLOAT_32(pPayLoad));
break;
case KLANG_SET_RESONANCE_F32:
set_resonance(KlangMath::FLOAT_32(pPayLoad));
break;
case KLANG_SET_TYPE_I8:
set_filter_type(pPayLoad[0]);
break;
}
}
protected:
float process(float pInput) {
/* from https://www.musicdsp.org/en/latest/Filters/25-moog-vcf-variation-1.html */
// Set coefficients given frequency & resonance [0.0...1.0]
float t1, t2; // temporary buffers
float q = 1.0f - mCutoff;
float p = mCutoff + 0.8f * mCutoff * q;
float f = p + p - 1.0f;
q = mResonance * (1.0f + 0.5f * q * (1.0f - q + 5.6f * q * q));
// Filter (in [-1.0...+1.0])
float in = _constrain(pInput, -1, 1); // hard clip
in -= q * b[4]; // feedback
t1 = b[1];
b[1] = (in + b[0]) * p - b[1] * f;
t2 = b[2];
b[2] = (b[1] + t1) * p - b[2] * f;
t1 = b[3];
b[3] = (b[2] + t2) * p - b[3] * f;
b[4] = (b[3] + t1) * p - b[4] * f;
b[4] = b[4] - b[4] * b[4] * b[4] * 0.166667f; // clipping
// inelegantly squash denormals
if (b[4] != b[4]) {
for (uint8_t i = 0; i < 5; i++) {
b[i] = 0;
}
}
b[0] = in;
switch (mFilterType) {
case HIGH_PASS:
return in - b[4];
break;
case LOW_PASS:
return b[4];
break;
case BAND_PASS:
return 3.0f * (b[3] - b[4]);
break;
}
return 0;
}
private:
uint8_t mFilterType = LOW_PASS;
float b[5];
float _constrain(float value, float min, float max) {
return value > max ? max : (value < min ? min : value);
}
};
} // namespace klang
#endif /* NodeVCFMoogLHBP_hpp */