From e5c3eb9b8ce4e349f5116d391eae501494044b14 Mon Sep 17 00:00:00 2001 From: Leandro Nini Date: Wed, 3 Jun 2026 17:36:47 +0200 Subject: [PATCH 1/6] Attempt to model filter leakage (#12) --- src/Filter.h | 27 ++++++++++++++++++++------- src/Filter6581.cpp | 13 ++++++++++--- src/Filter6581.h | 2 ++ src/Filter8580.cpp | 13 ++++++++++--- src/Filter8580.h | 2 ++ 5 files changed, 44 insertions(+), 13 deletions(-) diff --git a/src/Filter.h b/src/Filter.h index 2bf9f6b..8656c3c 100644 --- a/src/Filter.h +++ b/src/Filter.h @@ -109,10 +109,10 @@ class Filter } // If voice 3 is off we still need to clock the waveform generator - inline static int32_t getSilentVoice(Voice& v) + inline int32_t getSilentVoice(Voice& v) { - v.wave()->output(); - return 0; + int32_t osc = v.wave()->output(); + return signalLeak(osc); } protected: @@ -138,6 +138,8 @@ class Filter */ inline unsigned int getFC() const { return static_cast(fc); } + virtual int32_t signalLeak(int32_t input) = 0; + virtual int32_t solveIntegrators() = 0; public: @@ -221,10 +223,21 @@ uint16_t Filter::clock(Voice& voice1, Voice& voice2, Voice& voice3) int32_t Vsum = 0; int32_t Vmix = 0; - (filt1 ? Vsum : Vmix) += V1; - (filt2 ? Vsum : Vmix) += V2; - (filt3 ? Vsum : Vmix) += V3; - (filtE ? Vsum : Vmix) += Ve; + int32_t lV1 = signalLeak(V1); + Vsum += filt1 ? V1 : lV1; + Vmix += filt1 ? lV1 : V1; + + int32_t lV2 = signalLeak(V2); + Vsum += filt2 ? V2 : lV2; + Vmix += filt2 ? lV2 : V2; + + int32_t lV3 = signalLeak(V3); + Vsum += filt3 ? V3 : lV3; + Vmix += filt3 ? lV3 : V3; + + int32_t lVe = signalLeak(Ve); + Vsum += filtE ? Ve : lVe; + Vmix += filtE ? lVe : Ve; Vhp = currentSummer[currentResonance[Vbp] + Vlp + Vsum]; diff --git a/src/Filter6581.cpp b/src/Filter6581.cpp index a09105e..8f330e5 100644 --- a/src/Filter6581.cpp +++ b/src/Filter6581.cpp @@ -29,15 +29,22 @@ namespace reSIDfp { +int32_t Filter6581::signalLeak(int32_t input) +{ + constexpr int32_t leak = static_cast(0.0075 * (1 << 12)); + return (input * leak) >> 12; +} + int32_t Filter6581::solveIntegrators() { Vbp = hpIntegrator.solve(Vhp); Vlp = bpIntegrator.solve(Vbp); int32_t Vfilt = 0; - if (lp) Vfilt += Vlp; - if (bp) Vfilt += Vbp; - if (hp) Vfilt += Vhp; + + Vfilt += lp ? Vlp : signalLeak(Vlp); + Vfilt += bp ? Vbp : signalLeak(Vbp); + Vfilt += hp ? Vhp : signalLeak(Vhp); // The filter input resistors are slightly bigger than the voice ones // Scale the values accordingly diff --git a/src/Filter6581.h b/src/Filter6581.h index bd71a97..1eec0b9 100644 --- a/src/Filter6581.h +++ b/src/Filter6581.h @@ -334,6 +334,8 @@ class Filter6581 final : public Filter */ void updateCenterFrequency() override; + int32_t signalLeak(int32_t input) override; + int32_t solveIntegrators() override; public: diff --git a/src/Filter8580.cpp b/src/Filter8580.cpp index 64faa3f..662124c 100644 --- a/src/Filter8580.cpp +++ b/src/Filter8580.cpp @@ -27,15 +27,22 @@ namespace reSIDfp { +int32_t Filter8580::signalLeak(int32_t input) +{ + constexpr int32_t leak = static_cast(0.0035 * (1 << 12)); + return (input * leak) >> 12; +} + int32_t Filter8580::solveIntegrators() { Vbp = hpIntegrator.solve(Vhp); Vlp = bpIntegrator.solve(Vbp); int32_t Vfilt = 0; - if (lp) Vfilt += Vlp; - if (bp) Vfilt += Vbp; - if (hp) Vfilt += Vhp; + + Vfilt += lp ? Vlp : signalLeak(Vlp); + Vfilt += bp ? Vbp : signalLeak(Vbp); + Vfilt += hp ? Vhp : signalLeak(Vhp); return Vfilt; } diff --git a/src/Filter8580.h b/src/Filter8580.h index 6d8e9ad..c5922e7 100644 --- a/src/Filter8580.h +++ b/src/Filter8580.h @@ -291,6 +291,8 @@ class Filter8580 final : public Filter */ void updateCenterFrequency() override; + int32_t signalLeak(int32_t input) override; + int32_t solveIntegrators() override; public: From acd743f637b824e75382de742427a1996b8699e4 Mon Sep 17 00:00:00 2001 From: Leandro Nini Date: Mon, 8 Jun 2026 20:25:48 +0200 Subject: [PATCH 2/6] Update --- src/Filter.h | 36 +++++++++++++++++++++--------------- src/Filter6581.cpp | 12 +++--------- src/Filter6581.h | 2 -- src/Filter8580.cpp | 12 +++--------- src/Filter8580.h | 2 -- 5 files changed, 27 insertions(+), 37 deletions(-) diff --git a/src/Filter.h b/src/Filter.h index 8656c3c..caf38c2 100644 --- a/src/Filter.h +++ b/src/Filter.h @@ -92,6 +92,11 @@ class Filter bool lp = false; //@} + // FIXME just some random numbers + double leakMixer = 0.015; + double leakFilter = 0.01; + double leakV3 = 0.02; + private: /// Current volume. uint8_t vol = 0; @@ -102,6 +107,7 @@ class Filter /// Selects which inputs to route through filter. uint8_t filt = 0; + private: inline int32_t getNormalizedVoice(Voice& v) const { @@ -112,7 +118,7 @@ class Filter inline int32_t getSilentVoice(Voice& v) { int32_t osc = v.wave()->output(); - return signalLeak(osc); + return signalLeak(osc, leakV3); } protected: @@ -138,10 +144,14 @@ class Filter */ inline unsigned int getFC() const { return static_cast(fc); } - virtual int32_t signalLeak(int32_t input) = 0; - virtual int32_t solveIntegrators() = 0; + static inline int32_t signalLeak(int32_t input, double leak) + { + int32_t leaked = static_cast(leak * (1 << 12)); + return (input * leaked) >> 12; + } + public: Filter(FilterModelConfig& fmc); @@ -223,21 +233,17 @@ uint16_t Filter::clock(Voice& voice1, Voice& voice2, Voice& voice3) int32_t Vsum = 0; int32_t Vmix = 0; - int32_t lV1 = signalLeak(V1); - Vsum += filt1 ? V1 : lV1; - Vmix += filt1 ? lV1 : V1; + Vsum += filt1 ? V1 : signalLeak(V1, leakMixer); + Vmix += filt1 ? signalLeak(V1, leakFilter) : V1; - int32_t lV2 = signalLeak(V2); - Vsum += filt2 ? V2 : lV2; - Vmix += filt2 ? lV2 : V2; + Vsum += filt2 ? V2 : signalLeak(V2, leakMixer); + Vmix += filt2 ? signalLeak(V2, leakFilter) : V2; - int32_t lV3 = signalLeak(V3); - Vsum += filt3 ? V3 : lV3; - Vmix += filt3 ? lV3 : V3; + Vsum += filt3 ? V3 : signalLeak(V3, leakMixer); + Vmix += filt3 ? signalLeak(V3, leakFilter) : V3; - int32_t lVe = signalLeak(Ve); - Vsum += filtE ? Ve : lVe; - Vmix += filtE ? lVe : Ve; + Vsum += filtE ? Ve : signalLeak(Ve, leakMixer); + Vmix += filtE ? signalLeak(Ve, leakFilter) : Ve; Vhp = currentSummer[currentResonance[Vbp] + Vlp + Vsum]; diff --git a/src/Filter6581.cpp b/src/Filter6581.cpp index 8f330e5..cf48a3f 100644 --- a/src/Filter6581.cpp +++ b/src/Filter6581.cpp @@ -29,12 +29,6 @@ namespace reSIDfp { -int32_t Filter6581::signalLeak(int32_t input) -{ - constexpr int32_t leak = static_cast(0.0075 * (1 << 12)); - return (input * leak) >> 12; -} - int32_t Filter6581::solveIntegrators() { Vbp = hpIntegrator.solve(Vhp); @@ -42,9 +36,9 @@ int32_t Filter6581::solveIntegrators() int32_t Vfilt = 0; - Vfilt += lp ? Vlp : signalLeak(Vlp); - Vfilt += bp ? Vbp : signalLeak(Vbp); - Vfilt += hp ? Vhp : signalLeak(Vhp); + Vfilt += lp ? Vlp : signalLeak(Vlp, leakFilter); + Vfilt += bp ? Vbp : signalLeak(Vbp, leakFilter); + Vfilt += hp ? Vhp : signalLeak(Vhp, leakFilter); // The filter input resistors are slightly bigger than the voice ones // Scale the values accordingly diff --git a/src/Filter6581.h b/src/Filter6581.h index 1eec0b9..bd71a97 100644 --- a/src/Filter6581.h +++ b/src/Filter6581.h @@ -334,8 +334,6 @@ class Filter6581 final : public Filter */ void updateCenterFrequency() override; - int32_t signalLeak(int32_t input) override; - int32_t solveIntegrators() override; public: diff --git a/src/Filter8580.cpp b/src/Filter8580.cpp index 662124c..2c67c80 100644 --- a/src/Filter8580.cpp +++ b/src/Filter8580.cpp @@ -27,12 +27,6 @@ namespace reSIDfp { -int32_t Filter8580::signalLeak(int32_t input) -{ - constexpr int32_t leak = static_cast(0.0035 * (1 << 12)); - return (input * leak) >> 12; -} - int32_t Filter8580::solveIntegrators() { Vbp = hpIntegrator.solve(Vhp); @@ -40,9 +34,9 @@ int32_t Filter8580::solveIntegrators() int32_t Vfilt = 0; - Vfilt += lp ? Vlp : signalLeak(Vlp); - Vfilt += bp ? Vbp : signalLeak(Vbp); - Vfilt += hp ? Vhp : signalLeak(Vhp); + Vfilt += lp ? Vlp : signalLeak(Vlp, leakFilter); + Vfilt += bp ? Vbp : signalLeak(Vbp, leakFilter); + Vfilt += hp ? Vhp : signalLeak(Vhp, leakFilter); return Vfilt; } diff --git a/src/Filter8580.h b/src/Filter8580.h index c5922e7..6d8e9ad 100644 --- a/src/Filter8580.h +++ b/src/Filter8580.h @@ -291,8 +291,6 @@ class Filter8580 final : public Filter */ void updateCenterFrequency() override; - int32_t signalLeak(int32_t input) override; - int32_t solveIntegrators() override; public: From eb64f97cb9c574629c33dad8dc037551dbc9c7ef Mon Sep 17 00:00:00 2001 From: Leandro Nini Date: Tue, 9 Jun 2026 20:09:55 +0200 Subject: [PATCH 3/6] Update --- src/Filter.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Filter.h b/src/Filter.h index caf38c2..ba349d4 100644 --- a/src/Filter.h +++ b/src/Filter.h @@ -113,14 +113,14 @@ class Filter { return fmc.getNormalizedVoice(v.output(), v.envelope()->output()); } - +/* // If voice 3 is off we still need to clock the waveform generator inline int32_t getSilentVoice(Voice& v) { int32_t osc = v.wave()->output(); return signalLeak(osc, leakV3); } - +*/ protected: /** * Update filter cutoff frequency. @@ -149,7 +149,8 @@ class Filter static inline int32_t signalLeak(int32_t input, double leak) { int32_t leaked = static_cast(leak * (1 << 12)); - return (input * leaked) >> 12; + //int32_t offset = 32767 * ((1 << 12) - leaked); + return (((input-32767) * leaked) >> 12) + 32767; } public: @@ -228,7 +229,7 @@ uint16_t Filter::clock(Voice& voice1, Voice& voice2, Voice& voice3) const int32_t V1 = getNormalizedVoice(voice1); const int32_t V2 = getNormalizedVoice(voice2); // Voice 3 is silenced by voice3off if it is not routed through the filter. - const int32_t V3 = (filt3 || !voice3off) ? getNormalizedVoice(voice3) : getSilentVoice(voice3); + const int32_t V3 = (filt3 || !voice3off) ? getNormalizedVoice(voice3) : signalLeak(getNormalizedVoice(voice3), leakV3); int32_t Vsum = 0; int32_t Vmix = 0; From 9833d2c22dbb1a62e30e7eaaceec305ce895f801 Mon Sep 17 00:00:00 2001 From: Leandro Nini Date: Wed, 10 Jun 2026 08:27:55 +0200 Subject: [PATCH 4/6] Update --- src/Filter.h | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/src/Filter.h b/src/Filter.h index ba349d4..f7d7b82 100644 --- a/src/Filter.h +++ b/src/Filter.h @@ -113,14 +113,7 @@ class Filter { return fmc.getNormalizedVoice(v.output(), v.envelope()->output()); } -/* - // If voice 3 is off we still need to clock the waveform generator - inline int32_t getSilentVoice(Voice& v) - { - int32_t osc = v.wave()->output(); - return signalLeak(osc, leakV3); - } -*/ + protected: /** * Update filter cutoff frequency. @@ -149,8 +142,7 @@ class Filter static inline int32_t signalLeak(int32_t input, double leak) { int32_t leaked = static_cast(leak * (1 << 12)); - //int32_t offset = 32767 * ((1 << 12) - leaked); - return (((input-32767) * leaked) >> 12) + 32767; + return ((input-32767) * leaked) >> 12; } public: @@ -228,23 +220,23 @@ uint16_t Filter::clock(Voice& voice1, Voice& voice2, Voice& voice3) { const int32_t V1 = getNormalizedVoice(voice1); const int32_t V2 = getNormalizedVoice(voice2); - // Voice 3 is silenced by voice3off if it is not routed through the filter. - const int32_t V3 = (filt3 || !voice3off) ? getNormalizedVoice(voice3) : signalLeak(getNormalizedVoice(voice3), leakV3); + const int32_t V3 = getNormalizedVoice(voice3); int32_t Vsum = 0; int32_t Vmix = 0; - Vsum += filt1 ? V1 : signalLeak(V1, leakMixer); - Vmix += filt1 ? signalLeak(V1, leakFilter) : V1; + Vsum += filt1 ? V1 : signalLeak(V1, leakFilter); + Vmix += filt1 ? signalLeak(V1, leakMixer) : V1; - Vsum += filt2 ? V2 : signalLeak(V2, leakMixer); - Vmix += filt2 ? signalLeak(V2, leakFilter) : V2; + Vsum += filt2 ? V2 : signalLeak(V2, leakFilter); + Vmix += filt2 ? signalLeak(V2, leakMixer) : V2; - Vsum += filt3 ? V3 : signalLeak(V3, leakMixer); - Vmix += filt3 ? signalLeak(V3, leakFilter) : V3; + Vsum += filt3 ? V3 : signalLeak(V3, leakFilter); + // Voice 3 is silenced by voice3off if it is not routed through the filter. + Vmix += filt3 ? signalLeak(V3, leakMixer) : voice3off ? signalLeak(V3, leakV3) : V3; - Vsum += filtE ? Ve : signalLeak(Ve, leakMixer); - Vmix += filtE ? signalLeak(Ve, leakFilter) : Ve; + Vsum += filtE ? Ve : signalLeak(Ve, leakFilter); + Vmix += filtE ? signalLeak(Ve, leakMixer) : Ve; Vhp = currentSummer[currentResonance[Vbp] + Vlp + Vsum]; From e0c681582b7b7ef9717dcd7ec8c53ddc40d82e31 Mon Sep 17 00:00:00 2001 From: Leandro Nini Date: Fri, 12 Jun 2026 10:07:18 +0200 Subject: [PATCH 5/6] Update leak values --- src/Filter.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Filter.h b/src/Filter.h index f7d7b82..a9e3e16 100644 --- a/src/Filter.h +++ b/src/Filter.h @@ -92,10 +92,9 @@ class Filter bool lp = false; //@} - // FIXME just some random numbers - double leakMixer = 0.015; - double leakFilter = 0.01; - double leakV3 = 0.02; + double leakMixer = 0.00255; // 6581: 0.00255 / 8580: 0.00119 + double leakFilter = 0.001725; // 6581: 0.001725 / 8580: 0.0008 + double leakV3 = 0.1125; // 6581: 0.1125 / 8580: 0.0525 private: /// Current volume. From fa918c0c33fdac146baa1df633ad12594766764c Mon Sep 17 00:00:00 2001 From: Leandro Nini Date: Fri, 12 Jun 2026 11:39:48 +0200 Subject: [PATCH 6/6] Tweak values --- src/Filter.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Filter.h b/src/Filter.h index a9e3e16..2d5dabc 100644 --- a/src/Filter.h +++ b/src/Filter.h @@ -92,9 +92,9 @@ class Filter bool lp = false; //@} - double leakMixer = 0.00255; // 6581: 0.00255 / 8580: 0.00119 - double leakFilter = 0.001725; // 6581: 0.001725 / 8580: 0.0008 - double leakV3 = 0.1125; // 6581: 0.1125 / 8580: 0.0525 + double leakMixer = 0.001628; // 6581: 0.002531 / 8580: 0.001628 + double leakFilter = 0.001733; // 6581: 0.001688 / 8580: 0.001733 + double leakV3 = 0.0525; // 6581: 0.1125 / 8580: 0.0525 private: /// Current volume.