72 lines
2.8 KiB
C++
72 lines
2.8 KiB
C++
#pragma once
|
|
|
|
#include <cstring>
|
|
#include <cmath>
|
|
#include "data/audioframe.h"
|
|
#include "nodelib/basics.h"
|
|
#include "audio/audioengine.h"
|
|
using namespace Xybrid::Audio;
|
|
|
|
namespace Xybrid::NodeLib {
|
|
/// 12db Chamberlin State Variable Filter
|
|
template<typename DT> class GenericSVFilter {
|
|
public:
|
|
/// Default oversampling level. Enough to mostly eliminate artifacting at high cutoff.
|
|
static const constexpr int DEFAULT_OVERSAMP = 3;
|
|
|
|
DT low = 0.0;
|
|
DT high = 0.0;
|
|
DT band = 0.0;
|
|
DT notch = 0.0;
|
|
|
|
GenericSVFilter() = default;
|
|
~GenericSVFilter() = default;
|
|
// nothing used here should care about taking the raw approach
|
|
inline GenericSVFilter<DT>(const GenericSVFilter<DT>& o) { std::memcpy(static_cast<void*>(this), static_cast<const void*>(&o), sizeof(o)); }
|
|
inline GenericSVFilter<DT>& operator=(const GenericSVFilter<DT>& o) { std::memcpy(static_cast<void*>(this), static_cast<const void*>(&o), sizeof(o)); return *this; }
|
|
|
|
void process(DT in, double cutoff, double resonance, int oversamp = DEFAULT_OVERSAMP) {
|
|
if (oversamp <= 0) return;
|
|
cutoff = std::max(cutoff, 1.0);
|
|
resonance = std::max(resonance, 0.01);
|
|
|
|
double f = 2.0 * std::sin(PI * cutoff / (audioEngine->curSampleRate() * oversamp));
|
|
double q = std::sqrt(1.0 - std::atan(std::sqrt(resonance)) * 2.0 / PI);
|
|
double damp = std::sqrt(q);
|
|
|
|
for (int i = 0; i < oversamp; i++) {
|
|
low += band*f;
|
|
high = in*damp - low - band*q;
|
|
band += high*f;
|
|
}
|
|
notch = high+low;
|
|
}
|
|
inline void reset() { low = 0.0; high = 0.0; band = 0.0; notch = 0.0; }
|
|
inline void normalize(double m) {
|
|
if constexpr (std::is_arithmetic_v<DT>) {
|
|
low = std::clamp(low, -m, m);
|
|
high = std::clamp(high, -m, m);
|
|
band = std::clamp(band, -m, m);
|
|
notch = std::clamp(notch, -m, m);
|
|
} else {
|
|
low = low.clamp(m);
|
|
high = high.clamp(m);
|
|
band = band.clamp(m);
|
|
notch = notch.clamp(m);
|
|
}
|
|
}
|
|
|
|
static inline double scaledResonance(double r) { return std::pow(10, r*5); }
|
|
|
|
};
|
|
|
|
// explicit instantiation declarations to eliminate warnings
|
|
extern template void Xybrid::NodeLib::GenericSVFilter<Data::AudioFrame>::process(Data::AudioFrame, double, double, int);
|
|
extern template void Xybrid::NodeLib::GenericSVFilter<double>::process(double, double, double, int);
|
|
|
|
/// 12db Chamberlin State Variable Filter
|
|
typedef GenericSVFilter<Xybrid::Data::AudioFrame> SVFilter;
|
|
/// 12db Chamberlin State Variable Filter (mono version)
|
|
typedef GenericSVFilter<double> SVFilterM;
|
|
}
|