Skip to content

Commit

Permalink
LowpassOnePole: New "frequency" parameter to set the coefficient usin…
Browse files Browse the repository at this point in the history
…g the Time Dataspace. Coefficients named mathematically so it is easier to figure out what's happening internally.
  • Loading branch information
Timothy Place committed Oct 8, 2015
1 parent 1480d33 commit feaecb1
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 6 deletions.
19 changes: 13 additions & 6 deletions includes/library/JamomaLowpassOnePole.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@
namespace Jamoma {

/** This AudioObject applies a basic <a href="https://en.wikipedia.org/wiki/Low-pass_filter">low-pass filter</a> to a Sample or SampleBundle.
Handy for use in smoothing control signals or damping high frequency components.
*/
class LowpassOnePole : public AudioObject {

AdaptingSampleBundle mY1 = {this}; ///< previous output sample for each channel
double mCoefficientF; ///< filter coefficient
double mOneMinusCoefficientF; ///< 1 - mCoefficientF
AdaptingSampleBundle mY1 = {this}; ///< previous output sample (for each channel)
double mA0; ///< gain coefficient
double mB1; ///< feedback coefficient

public:
static constexpr Classname classname = { "lowpass.1" };
Expand All @@ -34,12 +35,18 @@ namespace Jamoma {
0.5,
Range<double>(0.0, 1.0),
[this]{
mCoefficientF = coefficient;
mOneMinusCoefficientF = 1 - coefficient;
mA0 = coefficient;
mB1 = 1 - coefficient;
}
};


/** Set filter coefficient using a cutoff frequency.
@see http://musicdsp.org/showArchiveComment.php?ArchiveID=237
*/
Parameter<double, NativeUnit::Hz> frequency = { this, "frequency", 1000.0, [this]{coefficient = 1.0 - exp(-2.0 * 3.1415 * frequency / sampleRate);} };


/** This algorithm is an IIR filter, meaning that it relies on feedback. If the filter should
not be producing any signal (such as turning audio off and then back on in a host) or if the
feedback has become corrupted (such as might happen if a NaN is fed in) then it may be
Expand All @@ -61,7 +68,7 @@ namespace Jamoma {

Sample operator()(Sample x, int channel)
{
Sample y = (x * mCoefficientF) + (mY1[channel][0] * mOneMinusCoefficientF); // compute next output sample
Sample y = (x * mA0) + (mY1[channel][0] * mB1); // compute next output sample

ZeroDenormal(y);
mY1[channel][0] = y; // update history
Expand Down
78 changes: 78 additions & 0 deletions tests/LowpassOnePole/LowpassOnePole.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,84 @@ class LowpassOnePoleTest {

my_lowpass.coefficient = 1.2;
mTest->TEST_ASSERT("high coefficient limiting", my_lowpass.coefficient == 1.0);


// Testing coefficient setting via frequency
// The following Max 7 patcher can be convenient as an interactive reference for the
// relationship between frequency response and coefficient
/*
<pre><code>
----------begin_max5_patcher----------
2115.3oc6asrjiZCEcc2eEJplEcRY6fdAlYURVjJUMS9BRkpKYiraxfAG.mz
8L0ju8nm.1FroaaHdQVLF8BoiNb0U2G87k6uCtH6YQAD7dvuAt6tub+c2oaR
0vc152A2vedYBuPOL3FQQAes.NwzWo34Rc6XOOWawQ5VxV7GSCpZrn7kDgtc
WKY6JSDkkurUXVeHD761tR2sINU1odIw1F2xKW9Tb55GyEKKMuBwmMyaB.6o
9kfzkIy7plms4hBQZIuLNKcuWiV+ZteZt3FnoWcjpwud+8pelzSNJU72xM+Q
Tj34s4.zLvTfrzCSwx076.jYHJhIK7tUHv2CnTjm2rusEpze9kSkntoRJSSD
gdFR7Xt7xokSH5H2ysskoCpziOogzCcdekd7I9CpzypjL4jzFcPdMzwD.bAO
c8aVR.gYlGdGQLqxx2v0i1uZZx4aDkh7GEo7EFr4cZZjxLLNB0CdDeEE2Prt
D2PCp3FK3MItwlqE2PgCj3VmzTmmJGVc5Lq.2qkln3A8T41jrx+oE1fE5Zju
KJNak5bPQ7m06ejGlNwMW4qiSs2zp2TyqgKqITuBTH0vcgZ4MeGSx1mO1lEm
ZlIFxMW4REFhD9BQhd+8q70owk6hDfGh9op6jhx1vkvnZT+bt3O2IRW9B3ge
4yUi5.Fuw5JIx81.E6VT0lxJjpO.x9Vlkjka5vaFch6m.l7.Q81QI19T7xOk
JLerTZTbcn2lOVKollk5j0k8lDmJZzopJOutaIbWH0o00auJNQpxS2AuLKZQ
cOFNR9h7b82DuFHx1mHUKDgwdrV5sAlxVez7Jkl9jH2xXtY.XtGUqqvUXtqP
fqfuq.yUf5JPbEvtBHWA2L6lX275lV2r5lT2b5lRbCj4.lCWNX4PkCTNL4fj
CQN.YwiENVzXAiEKVnXQhEHVbXggEEVPXwfEBVDz3ZP45aW9ZiklaWayR6NO
OwdJjzPaF5P8R0eQ0GmbeP0CSZVDrBuFyqqnQH9CvFeuk89AXi4TeLtV3aJh
faHgY50J94cTGceZvz+dxd5keJRq5cJVaU8Ti4QSMptmZXjoAlgL2LjPyPPd
ysuucdT37n8QSpwrcUTgcQgxmvp0FJeBqf.T9DVgDn7IrBPP80XNbAkOgUvC
JeBqPIT9DVCVnp.rFzJ.XvCwAHBt4Gir7Xol+Ge9P1119K11MMatOpSMfAJo
G7b0ujv+WEXkX3oOO8exIiSJF+1EO.+9UzDOZGV3wHCpEdzflV30au1olypi
tgv3tXI7vxR3SyRCnilLuwzQSlwLU1E5l4k6v3xrMajxcGI.rpx.23TvSetE
BidhXyzW1v52igMr1ffQc9A26JJgKWKTa6J+AU.2YJmwsXBsu5ABI5wyFY0.
dyBaiiXCJGgoM4Hb3LV+3H7bbMoNpjDqMRZXiiGx3n6qmjPFIO+QmjZ8z1vd
oqyqlNIoAKr2emzLq1t+j9ZtkQdWk5pfW84Gjc6Z7EiL.2h10sFRyjAayRDf
jr+dqbv.qoxGSDjvK+1CpKv8l8q01g46caZZYUfolOyavuUAAdWax4jfgUio
8PsQbmv56sJAFqKwCV.D6hldGBzVlyHCrNyfyPSCn4kD7nXdoSR.2HlL2l1W
JDQK3K+DXYlX0p3kwxAAdXwin1RCHw6xUVfCZxLsYqYWGRrWhPl2iCIdWuqP
9lofVuBAGNdWg32URlGtqPVykNbrmPA+Qu1DJPjqfPgMRkcKTb4ed6Tkv3nQ
vY7yMiFgtRpDoxyqqediNA+DtWhivAmKwQTT64MhuYahNuQckxnB0HDEmNWQ
CQdgXcFTzkw4KS5Nrnlvw8VBK59cz+nhROcHQOHBjsEUTZi61sGtajAZiZMS
JIX8JuDl3cCTlqXeOHxjUh57UXVGn5VJ65Ck14ehHv5MC2U7WIrKK0Dx2W8q
Nl45hX0OHcQjd+xrWhM0Tdpt8olwnP1IChqF5.ndbP2KqphTUsYpvTT0QU6X
cEroBQWg.fibHg6RyCE+ehlGeS3+5slmNSY81m3EBvC47nXdZQm4rNUonOQ5
MTDnJ5d2RZiFnTz7V0EgtTUQVSEQUmBIUrj9Ln+DW1szI25nbacFsQUGpf1o
UVlBsSrrrOzN0xxpj2gLuK5jIMcnxLjKcoFUnSsoI0jyTSdTOVabmYBUq4wl
3QiZHaJLo5J1LX5qqXye4b.rZ0UYyDre1TGcUQcXyuQrMRTxiSZyhwpvHnBr
QpzHMcW+XdLO4UZL4Q+6sZXIkpeD1VpLZD7ERGAew+5QdQwKKmsU4SYwxmDa
3uG3nyh30o.45Jx4kY4uGDIRyjaRSkenT9ccsTREf.31Hc+qBoqPmzwRd9Kc
P0jyS017hgCeabcS2zzuo9j6A+kPqWdU66+AnHaW9R2dw8mlKnF.RRtTwnxs
XyAQ1aPOEGIo9ltSDEWn7vHpamq5KdB75AdXdiFdX8AOGPhCId7o2V7yAhFs
iG73AGzMF8biINqt5473Ib7NdguwNtStsvC0+l53kJkw2RvgdaAmaKcgz9n6
gLd3AG1C7PGuyVj9nKTY1H.ON3g1W7fFG7zGcyGPhCp7SefC8lBN3w6lTbut
oHXbszfdtOW9mAOahipibl5uoHlJ7Kz.cjBBo5+TPz0pWnq6N.cN8U3W2N.E
FpBVDMP67muD5LasytCL95w2t8ujd4ZQgF7R2p+CSP7BlnqFmZppc6GlK9qX
230nBxyktlVJ8KcWtwc2ms+2UDtIKRjmtK15moj1t25V7A9WV4.cIeqwYYIA
shuKobeVsoy1RJ6iRm0AeLd8SprYZ1aSreTxERR5.GvgajSXriHzAo4q2+u.
80vVA
-----------end_max5_patcher-----------
</code></pre>
*/

my_lowpass.sampleRate = 96000;

my_lowpass.frequency = 1000.0; // hz
mTest->TEST_ASSERT("coefficient for 1K cutoff @ 96K fs", mTest->compare((double)my_lowpass.coefficient, 0.06335217076965427));

my_lowpass.frequency = 4000.0; // hz
mTest->TEST_ASSERT("coefficient for 1K cutoff @ 96K fs", mTest->compare((double)my_lowpass.coefficient, 0.23032864479520065));

my_lowpass.sampleRate = 44100;

my_lowpass.frequency = 1000.0; // hz
mTest->TEST_ASSERT("coefficient for 1K cutoff @ 44.1K fs", mTest->compare((double)my_lowpass.coefficient, 0.132787865213287));

my_lowpass.frequency = 4000.0; // hz
mTest->TEST_ASSERT("coefficient for 1K cutoff @ 44.1K fs", mTest->compare((double)my_lowpass.coefficient, 0.43441043913502342));

}

};
Expand Down

0 comments on commit feaecb1

Please sign in to comment.