From c5c6c5ff728fbcbf3acce2faa409d2c5d63c1e67 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 12 Nov 2025 22:06:48 -0500 Subject: [PATCH] Add textbook filter construction. --- Components/SID/SID.cpp | 2 +- .../xcschemes/Clock Signal.xcscheme | 2 +- SignalProcessing/BiquadFilter.hpp | 123 ++++++++++++++++++ 3 files changed, 125 insertions(+), 2 deletions(-) diff --git a/Components/SID/SID.cpp b/Components/SID/SID.cpp index 318c8cf1f..d30b927fa 100644 --- a/Components/SID/SID.cpp +++ b/Components/SID/SID.cpp @@ -81,7 +81,7 @@ void SID::write(const Numeric::SizedInt<5> address, const uint8_t value) { } void SID::update_filter() { - + // TODO! } uint8_t SID::read(const Numeric::SizedInt<5> address) { diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme index 14b3f64d2..9c1a3c3d1 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme @@ -62,7 +62,7 @@ +#include + namespace SignalProcessing { class BiquadFilter { @@ -17,6 +20,126 @@ public: coefficients_[0] = int16_t(1 << 15); } + enum class Type { + LowPass, + HighPass, + BandPass, + Notch, + AllPass, + Peaking, + LowShelf, + HighShelf + }; + BiquadFilter( + const Type type, + const float sample_rate, + const float frequency, + const float resonance, + const float gain, + const bool normalise + ) { + const float w0 = 2.0f * std::numbers::pi_v * frequency / sample_rate; + const float alpha = std::sin(w0) / (2.0f * resonance); + const float cos_w0 = std::cos(w0); + + float coefficients[5]; + float magnitude = 1.0f; + switch(type) { + case Type::LowPass: + coefficients[0] = (1.0f - cos_w0) / 2.0f; + coefficients[1] = 1.0f + cos_w0; + coefficients[2] = (1.0f - cos_w0) / 2.0f; + magnitude = 1.0f + alpha; + coefficients[3] = -2.0f * cos_w0; + coefficients[4] = 1.0f - alpha; + break; + + case Type::HighPass: + coefficients[0] = (1.0f - cos_w0) / 2.0f; + coefficients[1] = -(1.0f + cos_w0); + coefficients[2] = (1.0f - cos_w0) / 2.0f; + magnitude = 1.0f + alpha; + coefficients[3] = -2.0f * cos_w0; + coefficients[4] = 1.0f - alpha; + break; + + case Type::BandPass: + coefficients[0] = alpha; + coefficients[1] = 0.0f; + coefficients[2] = -alpha; + magnitude = 1.0f + alpha; + coefficients[3] = -2.0f * cos_w0; + coefficients[0] = 1.0f - alpha; + break; + + case Type::Notch: + coefficients[0] = 1.0f; + coefficients[1] = -2.0f * cos_w0; + coefficients[2] = 1.0f; + magnitude = 1.0f + alpha; + coefficients[3] = -2.0f * cos_w0; + coefficients[4] = 1.0f - alpha; + break; + + case Type::AllPass: + coefficients[0] = 1.0f - alpha; + coefficients[1] = -2.0f * cos_w0; + coefficients[2] = 1.0f + alpha; + magnitude = 1.0f + alpha; + coefficients[3] = -2.0f * cos_w0; + coefficients[4] = 1.0f - alpha; + break; + + case Type::Peaking: { + const float a = std::pow(10.0f, gain / 40.0f); + + coefficients[0] = 1.0f + (alpha * a); + coefficients[1] = -2.0f * cos_w0; + coefficients[2] = 1.0f - (alpha * a); + magnitude = 1.0f + (alpha / a); + coefficients[3] = -2.0f * cos_w0; + coefficients[4] = 1.0f - (alpha / a); + } break; + + case Type::LowShelf: { + const float a_ls = std::pow(10.0f, gain / 40.0f); + const float sqrt_a = std::sqrt(a_ls); + const float alpha_ls = + std::sin(w0) / 2.0f * std::sqrt((a_ls + 1.0f / a_ls) * (1.0f / resonance - 1.0f) + 2.0f); + + coefficients[0] = a_ls * ((a_ls + 1.0f) - (a_ls - 1.0f) * cos_w0 + 2.0f * sqrt_a * alpha_ls); + coefficients[1] = 2.0f * a_ls * ((a_ls - 1.0f) - (a_ls + 1.0f) * cos_w0); + coefficients[2] = a_ls * ((a_ls + 1.0f) - (a_ls - 1.0f) * cos_w0 - 2.0f * sqrt_a * alpha_ls); + magnitude = (a_ls + 1.0f) + (a_ls - 1.0f) * cos_w0 + 2.0f * sqrt_a * alpha_ls; + coefficients[3] = -2.0f * ((a_ls - 1) + (a_ls + 1) * cos_w0); + coefficients[4] = (a_ls + 1.0f) + (a_ls - 1.0f) * cos_w0 - 2.0f * sqrt_a * alpha_ls; + } break; + + case Type::HighShelf: { + const float a_hs = std::pow(10.0f, gain / 40.0f); + const float sqrt_a_hs = std::sqrt(a_hs); + const float alpha_hs = + std::sin(w0) / 2.0f * std::sqrt((a_hs + 1.0f / a_hs) * (1.0f / resonance - 1.0f) + 2.0f); + + coefficients[0] = a_hs * ((a_hs + 1.0f) + (a_hs - 1.0f) * cos_w0 + 2.0f * sqrt_a_hs * alpha_hs); + coefficients[1] = -2.0f * a_hs * ((a_hs - 1.0f) + (a_hs + 1.0f) * cos_w0); + coefficients[2] = a_hs * ((a_hs + 1.0f) + (a_hs - 1.0f) * cos_w0 - 2.0f * sqrt_a_hs * alpha_hs); + magnitude = (a_hs + 1.0f) - (a_hs - 1.0f) * cos_w0 + 2.0f * sqrt_a_hs * alpha_hs; + coefficients[3] = 2.0f * ((a_hs - 1.0f) - (a_hs + 1.0f) * cos_w0); + coefficients[4] = (a_hs + 1.0f) - (a_hs - 1.0f) * cos_w0 - 2.0f * sqrt_a_hs * alpha_hs; + } break; + } + + if(normalise) { + for(int c = 0; c < 5; c++) { + coefficients[c] /= magnitude; + } + } + for(int c = 0; c < 5; c++) { + coefficients_[c] = int16_t(coefficients[c] * 32767.0f); + } + } + int16_t apply(const int16_t input) { const int16_t output = ( coefficients_[0] * input +