ref: 561914486a8689289f53d6a8bd7abfaca13142f4
parent: 02378efbe89406a42adf9e2d35ee9b4bdfa387c3
author: Olav Sørensen <olav.sorensen@live.no>
date: Tue Dec 27 16:39:18 EST 2022
Code cleanup
--- a/src/pt2_ledfilter.c
+++ /dev/null
@@ -1,46 +1,0 @@
-/* Biquad low-pass filter with resonance, from:
-** https://www.musicdsp.org/en/latest/Filters/38-lp-and-hp-filter.html
-*/
-
-#include "pt2_ledfilter.h"
-#include "pt2_math.h"
-
-void clearLEDFilterState(ledFilter_t *f)
-{
- f->LIn1 = f->LIn2 = f->LOut1 = f->LOut2 = 0.0;
- f->RIn1 = f->RIn2 = f->ROut1 = f->ROut2 = 0.0;
-}
-
-void calcLEDFilterCoeffs(double sr, double hz, double qfactor, ledFilter_t *filter)
-{
- const double c = 1.0 / pt2_tan((PT2_PI * hz) / sr);
- const double r = 1.0 / qfactor;
-
- filter->a1 = 1.0 / (1.0 + r * c + c * c);
- filter->a2 = 2.0 * filter->a1;
- filter->a3 = filter->a1;
- filter->b1 = 2.0 * (1.0 - c*c) * filter->a1;
- filter->b2 = (1.0 - r * c + c * c) * filter->a1;
-}
-
-void LEDFilter(ledFilter_t *f, const double *in, double *out)
-{
- const double LOut = (f->a1 * in[0]) + (f->a2 * f->LIn1) + (f->a3 * f->LIn2) - (f->b1 * f->LOut1) - (f->b2 * f->LOut2);
- const double ROut = (f->a1 * in[1]) + (f->a2 * f->RIn1) + (f->a3 * f->RIn2) - (f->b1 * f->ROut1) - (f->b2 * f->ROut2);
-
- // shift states
-
- f->LIn2 = f->LIn1;
- f->LIn1 = in[0];
- f->LOut2 = f->LOut1;
- f->LOut1 = LOut;
-
- f->RIn2 = f->RIn1;
- f->RIn1 = in[1];
- f->ROut2 = f->ROut1;
- f->ROut1 = ROut;
-
- // set output
- out[0] = LOut;
- out[1] = ROut;
-}
--- a/src/pt2_ledfilter.h
+++ /dev/null
@@ -1,15 +1,0 @@
-#pragma once
-
-#include <stdint.h>
-#include <stdbool.h>
-
-typedef struct ledFilter_t
-{
- double LIn1, LIn2, LOut1, LOut2;
- double RIn1, RIn2, ROut1, ROut2;
- double a1, a2, a3, b1, b2;
-} ledFilter_t;
-
-void clearLEDFilterState(ledFilter_t *f);
-void calcLEDFilterCoeffs(double sr, double hz, double qfactor, ledFilter_t *filter);
-void LEDFilter(ledFilter_t *f, const double *in, double *out);
--- a/src/pt2_paula.c
+++ b/src/pt2_paula.c
@@ -9,8 +9,7 @@
#include <stdbool.h>
#include "pt2_paula.h"
#include "pt2_blep.h"
-#include "pt2_rcfilter.h"
-#include "pt2_ledfilter.h"
+#include "pt2_rcfilters.h"
#include "pt2_math.h"
typedef struct voice_t
@@ -40,8 +39,8 @@
static int8_t nullSample[0xFFFF*2]; // buffer for NULL data pointer
static double dPaulaOutputFreq, dPeriodToDeltaDiv;
static blep_t blep[PAULA_VOICES];
-static rcFilter_t filterLo, filterHi;
-static ledFilter_t filterLED;
+static onePoleFilter_t filterLo, filterHi;
+static twoPoleFilter_t filterLED;
static paulaVoice_t paula[PAULA_VOICES];
void paulaSetup(double dOutputFreq, uint32_t amigaModel)
@@ -53,9 +52,9 @@
dPeriodToDeltaDiv = PAULA_PAL_CLK / dPaulaOutputFreq;
useLowpassFilter = useHighpassFilter = true;
- clearRCFilterState(&filterLo);
- clearRCFilterState(&filterHi);
- clearLEDFilterState(&filterLED);
+ clearOnePoleFilterState(&filterLo);
+ clearOnePoleFilterState(&filterHi);
+ clearTwoPoleFilterState(&filterLED);
/* Amiga 500/1200 filter emulation
**
@@ -111,17 +110,17 @@
if (amigaModel == MODEL_A500)
{
- // A500 1-pole (6db/oct) static RC low-pass filter:
+ // A500 1-pole (6db/oct) RC low-pass filter:
R = 360.0; // R321 (360 ohm)
C = 1e-7; // C321 (0.1uF)
cutoff = 1.0 / (PT2_TWO_PI * R * C); // ~4420.971Hz
- calcRCFilterCoeffs(dPaulaOutputFreq, cutoff, &filterLo);
+ setupOnePoleFilter(dPaulaOutputFreq, cutoff, &filterLo);
- // A500 1-pole (6dB/oct) static RC high-pass filter:
+ // A500 1-pole (6dB/oct) RC high-pass filter:
R = 1390.0; // R324 (1K ohm) + R325 (390 ohm)
C = 2.233e-5; // C334 (22uF) + C335 (0.33uF)
cutoff = 1.0 / (PT2_TWO_PI * R * C); // ~5.128Hz
- calcRCFilterCoeffs(dPaulaOutputFreq, cutoff, &filterHi);
+ setupOnePoleFilter(dPaulaOutputFreq, cutoff, &filterHi);
}
else
{
@@ -132,14 +131,14 @@
*/
useLowpassFilter = false;
- // A1200 1-pole (6dB/oct) static RC high-pass filter:
+ // A1200 1-pole (6dB/oct) RC high-pass filter:
R = 1360.0; // R324 (1K ohm resistor) + R325 (360 ohm resistor)
C = 2.2e-5; // C334 (22uF capacitor)
cutoff = 1.0 / (PT2_TWO_PI * R * C); // ~5.319Hz
- calcRCFilterCoeffs(dPaulaOutputFreq, cutoff, &filterHi);
+ setupOnePoleFilter(dPaulaOutputFreq, cutoff, &filterHi);
}
- // Sallen-Key low-pass filter ("LED" filter, same values on A500/A1200):
+ // 2-pole (12dB/oct) RC low-pass filter ("LED" filter, same values on A500/A1200):
R1 = 10000.0; // R322 (10K ohm)
R2 = 10000.0; // R323 (10K ohm)
C1 = 6.8e-9; // C322 (6800pF)
@@ -146,7 +145,7 @@
C2 = 3.9e-9; // C323 (3900pF)
cutoff = 1.0 / (PT2_TWO_PI * pt2_sqrt(R1 * R2 * C1 * C2)); // ~3090.533Hz
qfactor = pt2_sqrt(R1 * R2 * C1 * C2) / (C2 * (R1 + R2)); // ~0.660225
- calcLEDFilterCoeffs(dPaulaOutputFreq, cutoff, qfactor, &filterLED);
+ setupTwoPoleFilter(dPaulaOutputFreq, cutoff, qfactor, &filterLED);
}
void paulaDisableFilters(void) // disables low-pass/high-pass filter ("LED" filter is kept)
@@ -262,7 +261,7 @@
useLEDFilter = !!(data8 & 2);
if (useLEDFilter != oldLedFilterState)
- clearLEDFilterState(&filterLED);
+ clearTwoPoleFilterState(&filterLED);
}
break;
@@ -441,13 +440,13 @@
double dOut[2] = { dOutL[i], dOutR[i] };
if (useLowpassFilter)
- RCLowPassFilterStereo(&filterLo, dOut, dOut);
+ onePoleLPFilterStereo(&filterLo, dOut, dOut);
if (useLEDFilter)
- LEDFilter(&filterLED, dOut, dOut);
+ twoPoleLPFilterStereo(&filterLED, dOut, dOut);
if (useHighpassFilter)
- RCHighPassFilterStereo(&filterHi, dOut, dOut);
+ onePoleHPFilterStereo(&filterHi, dOut, dOut);
dOutL[i] = dOut[0];
dOutR[i] = dOut[1];
--- a/src/pt2_rcfilter.c
+++ /dev/null
@@ -1,58 +1,0 @@
-/* 1-pole 6dB/oct RC filters, from:
-** https://www.musicdsp.org/en/latest/Filters/116-one-pole-lp-and-hp.html
-**
-** There's no frequency pre-warping with tan(), but doing that would
-** result in a cutoff that sounded slightly too low.
-*/
-
-#include "pt2_math.h"
-#include "pt2_rcfilter.h"
-
-void calcRCFilterCoeffs(double sr, double hz, rcFilter_t *f)
-{
- const double a = (hz < sr / 2.0) ? pt2_cos((PT2_TWO_PI * hz) / sr) : 1.0;
- const double b = 2.0 - a;
- const double c = b - pt2_sqrt((b * b) - 1.0);
-
- f->c1 = 1.0 - c;
- f->c2 = c;
-}
-
-void clearRCFilterState(rcFilter_t *f)
-{
- f->tmp[0] = f->tmp[1] = 0.0;
-}
-
-void RCLowPassFilterStereo(rcFilter_t *f, const double *in, double *out)
-{
- // left channel
- f->tmp[0] = (f->c1 * in[0]) + (f->c2 * f->tmp[0]);
- out[0] = f->tmp[0];
-
- // right channel
- f->tmp[1] = (f->c1 * in[1]) + (f->c2 * f->tmp[1]);
- out[1] = f->tmp[1];
-}
-
-void RCHighPassFilterStereo(rcFilter_t *f, const double *in, double *out)
-{
- // left channel
- f->tmp[0] = (f->c1 * in[0]) + (f->c2 * f->tmp[0]);
- out[0] = in[0] - f->tmp[0];
-
- // right channel
- f->tmp[1] = (f->c1 * in[1]) + (f->c2 * f->tmp[1]);
- out[1] = in[1] - f->tmp[1];
-}
-
-void RCLowPassFilter(rcFilter_t *f, const double in, double *out)
-{
- f->tmp[0] = (f->c1 * in) + (f->c2 * f->tmp[0]);
- *out = f->tmp[0];
-}
-
-void RCHighPassFilter(rcFilter_t *f, const double in, double *out)
-{
- f->tmp[0] = (f->c1 * in) + (f->c2 * f->tmp[0]);
- *out = in - f->tmp[0];
-}
--- a/src/pt2_rcfilter.h
+++ /dev/null
@@ -1,16 +1,0 @@
-#pragma once
-
-#include <stdint.h>
-#include <stdbool.h>
-
-typedef struct rcFilter_t
-{
- double tmp[2], c1, c2;
-} rcFilter_t;
-
-void calcRCFilterCoeffs(double sr, double hz, rcFilter_t *f);
-void clearRCFilterState(rcFilter_t *f);
-void RCLowPassFilterStereo(rcFilter_t *f, const double *in, double *out);
-void RCHighPassFilterStereo(rcFilter_t *f, const double *in, double *out);
-void RCLowPassFilter(rcFilter_t *f, const double in, double *out);
-void RCHighPassFilter(rcFilter_t *f, const double in, double *out);
--- /dev/null
+++ b/src/pt2_rcfilters.c
@@ -1,0 +1,122 @@
+#include "pt2_math.h"
+#include "pt2_rcfilters.h"
+
+#define SMALL_NUMBER (1E-4)
+
+/* 1-pole RC low-pass/high-pass filter, based on:
+** https://www.musicdsp.org/en/latest/Filters/116-one-pole-lp-and-hp.html
+*/
+
+void setupOnePoleFilter(double audioRate, double cutOff, onePoleFilter_t *f)
+{
+ if (cutOff >= audioRate/2.0)
+ cutOff = (audioRate/2.0) - SMALL_NUMBER;
+
+ const double a = 2.0 - pt2_cos((PT2_TWO_PI * cutOff) / audioRate);
+ const double b = a - pt2_sqrt((a * a) - 1.0);
+
+ f->a1 = 1.0 - b;
+ f->a2 = b;
+}
+
+void clearOnePoleFilterState(onePoleFilter_t *f)
+{
+ f->tmpL = f->tmpR = 0.0;
+}
+
+void onePoleLPFilter(onePoleFilter_t *f, const double in, double *out)
+{
+ f->tmpL = (f->a1 * in) + (f->a2 * f->tmpL);
+ *out = f->tmpL;
+}
+
+void onePoleLPFilterStereo(onePoleFilter_t *f, const double *in, double *out)
+{
+ // left channel
+ f->tmpL = (f->a1 * in[0]) + (f->a2 * f->tmpL);
+ out[0] = f->tmpL;
+
+ // right channel
+ f->tmpR = (f->a1 * in[1]) + (f->a2 * f->tmpR);
+ out[1] = f->tmpR;
+}
+
+void onePoleHPFilter(onePoleFilter_t *f, const double in, double *out)
+{
+ f->tmpL = (f->a1 * in) + (f->a2 * f->tmpL);
+ *out = in - f->tmpL;
+}
+
+void onePoleHPFilterStereo(onePoleFilter_t *f, const double *in, double *out)
+{
+ // left channel
+ f->tmpL = (f->a1 * in[0]) + (f->a2 * f->tmpL);
+ out[0] = in[0] - f->tmpL;
+
+ // right channel
+ f->tmpR = (f->a1 * in[1]) + (f->a2 * f->tmpR);
+ out[1] = in[1] - f->tmpR;
+}
+
+/* 2-pole RC low-pass filter with Q factor, based on:
+** https://www.musicdsp.org/en/latest/Filters/38-lp-and-hp-filter.html
+*/
+
+void clearTwoPoleFilterState(twoPoleFilter_t *f)
+{
+ f->tmpL[0] = f->tmpL[1] = f->tmpL[2] = f->tmpL[3] = 0.0;
+ f->tmpR[0] = f->tmpR[1] = f->tmpR[2] = f->tmpR[3] = 0.0;
+}
+
+void setupTwoPoleFilter(double audioRate, double cutOff, double qFactor, twoPoleFilter_t *f)
+{
+ if (cutOff >= audioRate/2.0)
+ cutOff = (audioRate/2.0) - SMALL_NUMBER;
+
+ const double a = 1.0 / pt2_tan((PT2_PI * cutOff) / audioRate);
+ const double b = 1.0 / qFactor;
+
+ f->a1 = 1.0 / (1.0 + b * a + a * a);
+ f->a2 = 2.0 * f->a1;
+ f->b1 = 2.0 * (1.0 - a*a) * f->a1;
+ f->b2 = (1.0 - b * a + a * a) * f->a1;
+}
+
+void twoPoleLPFilter(twoPoleFilter_t *f, const double in, double *out)
+{
+ const double LOut = (f->a1 * in) + (f->a2 * f->tmpL[0]) + (f->a1 * f->tmpL[1]) - (f->b1 * f->tmpL[2]) - (f->b2 * f->tmpL[3]);
+
+ // shift states
+
+ f->tmpL[1] = f->tmpL[0];
+ f->tmpL[0] = in;
+ f->tmpL[3] = f->tmpL[2];
+ f->tmpL[2] = LOut;
+
+ // set output
+
+ *out = LOut;
+}
+
+void twoPoleLPFilterStereo(twoPoleFilter_t *f, const double *in, double *out)
+{
+ const double LOut = (f->a1 * in[0]) + (f->a2 * f->tmpL[0]) + (f->a1 * f->tmpL[1]) - (f->b1 * f->tmpL[2]) - (f->b2 * f->tmpL[3]);
+ const double ROut = (f->a1 * in[1]) + (f->a2 * f->tmpR[0]) + (f->a1 * f->tmpR[1]) - (f->b1 * f->tmpR[2]) - (f->b2 * f->tmpR[3]);
+
+ // shift states
+
+ f->tmpL[1] = f->tmpL[0];
+ f->tmpL[0] = in[0];
+ f->tmpL[3] = f->tmpL[2];
+ f->tmpL[2] = LOut;
+
+ f->tmpR[1] = f->tmpR[0];
+ f->tmpR[0] = in[1];
+ f->tmpR[3] = f->tmpR[2];
+ f->tmpR[2] = ROut;
+
+ // set output
+
+ out[0] = LOut;
+ out[1] = ROut;
+}
--- /dev/null
+++ b/src/pt2_rcfilters.h
@@ -1,0 +1,26 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+typedef struct onePoleFilter_t
+{
+ double tmpL, tmpR, a1, a2;
+} onePoleFilter_t;
+
+typedef struct twoPoleFilter_t
+{
+ double tmpL[4], tmpR[4], a1, a2, b1, b2;
+} twoPoleFilter_t;
+
+void setupOnePoleFilter(double audioRate, double cutOff, onePoleFilter_t *f);
+void clearOnePoleFilterState(onePoleFilter_t *f);
+void onePoleLPFilterStereo(onePoleFilter_t *f, const double *in, double *out);
+void onePoleHPFilterStereo(onePoleFilter_t *f, const double *in, double *out);
+void onePoleLPFilter(onePoleFilter_t *f, const double in, double *out);
+void onePoleHPFilter(onePoleFilter_t *f, const double in, double *out);
+
+void setupTwoPoleFilter(double audioRate, double cutOff, double qFactor, twoPoleFilter_t *f);
+void clearTwoPoleFilterState(twoPoleFilter_t *f);
+void twoPoleLPFilter(twoPoleFilter_t *f, const double in, double *out);
+void twoPoleLPFilterStereo(twoPoleFilter_t *f, const double *in, double *out);
--- a/src/pt2_sampler.c
+++ b/src/pt2_sampler.c
@@ -16,7 +16,7 @@
#include "pt2_sampler.h"
#include "pt2_config.h"
#include "pt2_bmp.h"
-#include "pt2_rcfilter.h"
+#include "pt2_rcfilters.h"
#include "pt2_chordmaker.h"
#include "pt2_replayer.h"
#include "pt2_visuals_sync.h"
@@ -538,7 +538,7 @@
void highPassSample(int32_t cutOff)
{
- rcFilter_t filterHi;
+ onePoleFilter_t filterHi;
assert(editor.currSample >= 0 && editor.currSample <= 30);
@@ -599,9 +599,9 @@
editor.hpCutOff = (uint16_t)dCutOff;
}
- calcRCFilterCoeffs(dBaseFreq, dCutOff, &filterHi);
+ setupOnePoleFilter(dBaseFreq, dCutOff, &filterHi);
- clearRCFilterState(&filterHi);
+ clearOnePoleFilterState(&filterHi);
if (to <= s->length)
{
const int8_t *smpPtr = &song->sampleData[s->offset];
@@ -608,7 +608,7 @@
for (int32_t i = from; i < to; i++)
{
double dSmp = smpPtr[i];
- RCHighPassFilter(&filterHi, dSmp, &dSampleData[i]);
+ onePoleHPFilter(&filterHi, dSmp, &dSampleData[i]);
}
}
@@ -637,7 +637,7 @@
void lowPassSample(int32_t cutOff)
{
- rcFilter_t filterLo;
+ onePoleFilter_t filterLo;
if (editor.sampleZero)
{
@@ -698,13 +698,13 @@
editor.lpCutOff = (uint16_t)dCutOff;
}
- calcRCFilterCoeffs(dBaseFreq, dCutOff, &filterLo);
+ setupOnePoleFilter(dBaseFreq, dCutOff, &filterLo);
// copy over sample data to double buffer
for (int32_t i = 0; i < s->length; i++)
dSampleData[i] = song->sampleData[s->offset+i];
- clearRCFilterState(&filterLo);
+ clearOnePoleFilterState(&filterLo);
if (to <= s->length)
{
const int8_t *smpPtr = &song->sampleData[s->offset];
@@ -711,7 +711,7 @@
for (int32_t i = from; i < to; i++)
{
double dSmp = smpPtr[i];
- RCLowPassFilter(&filterLo, dSmp, &dSampleData[i]);
+ onePoleLPFilter(&filterLo, dSmp, &dSampleData[i]);
}
}
--- a/vs2019_project/pt2-clone/pt2-clone.vcxproj
+++ b/vs2019_project/pt2-clone/pt2-clone.vcxproj
@@ -249,7 +249,6 @@
<ClInclude Include="..\..\src\pt2_helpers.h" />
<ClInclude Include="..\..\src\pt2_hpc.h" />
<ClInclude Include="..\..\src\pt2_keyboard.h" />
- <ClInclude Include="..\..\src\pt2_ledfilter.h" />
<ClInclude Include="..\..\src\pt2_math.h" />
<ClInclude Include="..\..\src\pt2_mod2wav.h" />
<ClInclude Include="..\..\src\pt2_module_loader.h" />
@@ -259,7 +258,7 @@
<ClInclude Include="..\..\src\pt2_pat2smp.h" />
<ClInclude Include="..\..\src\pt2_pattern_viewer.h" />
<ClInclude Include="..\..\src\pt2_paula.h" />
- <ClInclude Include="..\..\src\pt2_rcfilter.h" />
+ <ClInclude Include="..\..\src\pt2_rcfilters.h" />
<ClInclude Include="..\..\src\pt2_downsample2x.h" />
<ClInclude Include="..\..\src\pt2_replayer.h" />
<ClInclude Include="..\..\src\pt2_sample_loader.h" />
@@ -301,13 +300,12 @@
<ClCompile Include="..\..\src\pt2_helpers.c" />
<ClCompile Include="..\..\src\pt2_hpc.c" />
<ClCompile Include="..\..\src\pt2_keyboard.c" />
- <ClCompile Include="..\..\src\pt2_ledfilter.c" />
<ClCompile Include="..\..\src\pt2_main.c" />
<ClCompile Include="..\..\src\pt2_math.c" />
<ClCompile Include="..\..\src\pt2_mod2wav.c" />
<ClCompile Include="..\..\src\pt2_module_loader.c" />
<ClCompile Include="..\..\src\pt2_paula.c" />
- <ClCompile Include="..\..\src\pt2_rcfilter.c" />
+ <ClCompile Include="..\..\src\pt2_rcfilters.c" />
<ClCompile Include="..\..\src\pt2_replayer.c" />
<ClCompile Include="..\..\src\pt2_module_saver.c" />
<ClCompile Include="..\..\src\pt2_mouse.c" />
--- a/vs2019_project/pt2-clone/pt2-clone.vcxproj.filters
+++ b/vs2019_project/pt2-clone/pt2-clone.vcxproj.filters
@@ -90,7 +90,7 @@
<ClInclude Include="..\..\src\pt2_visuals_sync.h">
<Filter>headers</Filter>
</ClInclude>
- <ClInclude Include="..\..\src\pt2_rcfilter.h">
+ <ClInclude Include="..\..\src\pt2_rcfilters.h">
<Filter>headers</Filter>
</ClInclude>
<ClInclude Include="..\..\src\pt2_chordmaker.h">
@@ -117,9 +117,6 @@
<ClInclude Include="..\..\src\pt2_paula.h">
<Filter>headers</Filter>
</ClInclude>
- <ClInclude Include="..\..\src\pt2_ledfilter.h">
- <Filter>headers</Filter>
- </ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\src\pt2_audio.c" />
@@ -192,7 +189,7 @@
<ClCompile Include="..\..\src\pt2_replayer.c" />
<ClCompile Include="..\..\src\pt2_sampling.c" />
<ClCompile Include="..\..\src\pt2_visuals_sync.c" />
- <ClCompile Include="..\..\src\pt2_rcfilter.c" />
+ <ClCompile Include="..\..\src\pt2_rcfilters.c" />
<ClCompile Include="..\..\src\pt2_chordmaker.c" />
<ClCompile Include="..\..\src\pt2_downsample2x.c" />
<ClCompile Include="..\..\src\pt2_math.c" />
@@ -200,7 +197,6 @@
<ClCompile Include="..\..\src\pt2_xpk.c" />
<ClCompile Include="..\..\src\pt2_askbox.c" />
<ClCompile Include="..\..\src\pt2_paula.c" />
- <ClCompile Include="..\..\src\pt2_ledfilter.c" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\..\src\pt2-clone.rc" />