1 /** 2 * Copyright 2017 Cut Through Recordings 3 * License: MIT License 4 * Author(s): Ethan Reker 5 */ 6 module ddsp.filter.highpass; 7 8 import ddsp.filter.biquad; 9 import ddsp.effect : AudioEffect; 10 import ddsp.filter.lowpass : calculateQValuesForButterworth; 11 12 import std.math; 13 14 import dplug.core : mallocNew, mallocSlice; 15 16 /// First order highpass filter 17 class HighpassO1(T) : BiQuad!T 18 { 19 public: 20 nothrow: 21 @nogc: 22 23 override void calcCoefficients() nothrow @nogc 24 { 25 _thetac = 2 * PI * _frequency / _sampleRate; 26 _gamma = cos(_thetac) / (1 + sin(_thetac)); 27 _a0 = (1 + _gamma) / 2; 28 _a1 = -_a0; 29 _a2 = 0.0; 30 _b1 = -_gamma; 31 _b2 = 0.0; 32 } 33 34 private: 35 float _thetac; 36 float _gamma; 37 } 38 39 unittest 40 { 41 HighpassO1!float hpq1 = new HighpassO1!float(); 42 } 43 44 /// Second order highpass filter 45 class HighpassO2(T) : BiQuad!T 46 { 47 public: 48 void setQualityFactor(float Q) nothrow @nogc 49 { 50 if(Q != _q) 51 { 52 _q = Q; 53 calcCoefficients(); 54 } 55 } 56 57 override void calcCoefficients() nothrow @nogc 58 { 59 _thetac = 2 * PI * _frequency / _sampleRate; 60 _d = 1 / _q; 61 _beta = 0.5 * (1 - (_d / 2) * sin(_thetac)) / (1 + (_d / 2) * sin(_thetac)); 62 _gamma = (0.5 + _beta) * cos(_thetac); 63 64 _a0 = (0.5 + _beta + _gamma) / 2; 65 _a1 = - (0.5 + _beta + _gamma); 66 _a2 = _a0; 67 _b1 = -2.0 * _gamma; 68 _b2 = 2.0 * _beta; 69 } 70 71 private: 72 float _thetac; 73 float _q = 0.707f; 74 float _beta; 75 float _gamma; 76 float _d; 77 } 78 79 unittest 80 { 81 HighpassO2!float hpq2 = new HighpassO2!float(); 82 } 83 84 /// Second order butterworth highpass filter 85 class ButterworthHP(T) : BiQuad!T 86 { 87 public: 88 this() nothrow @nogc 89 { 90 super(); 91 } 92 override void calcCoefficients() nothrow @nogc 93 { 94 _C = tan(PI * _frequency / _sampleRate); 95 _a0 = 1.0f / (1.0f + sqrt(2.0f) * _C + (_C * _C)); 96 _a1 = -2.0f * _a0; 97 _a2 = _a0; 98 _b1 = 2.0f * _a0 * (_C * _C - 1.0f); 99 _b2 = _a0 * (1.0f - sqrt(2.0f) * _C + _C * _C); 100 } 101 102 private: 103 float _C; 104 } 105 106 unittest 107 { 108 ButterworthHP!float butterworthHP = new ButterworthHP!float(); 109 } 110 111 /// Second order LinkwitzRiley highpass filter 112 class LinkwitzRileyHP(T) : BiQuad!T 113 { 114 public: 115 nothrow: 116 @nogc: 117 118 this() 119 { 120 super(); 121 } 122 123 override void calcCoefficients() nothrow @nogc 124 { 125 _theta = pi * _frequency / _sampleRate; 126 _omega = pi * _frequency; 127 _kappa = _omega / tan(_theta); 128 _delta = _kappa * _kappa + _omega * _omega + 2 * _kappa * _omega; 129 130 _a0 = (_kappa * _kappa) / _delta; 131 _a1 = -2 * _a0; 132 133 _a2 = _a0; 134 _b1 = (-2 * _kappa * _kappa + 2 * _omega * _omega) / _delta; 135 _b2 = (-2 * _kappa * _omega + _kappa * _kappa + _omega * _omega) / _delta; 136 } 137 138 // Useful in determining if setSampleRate and setFrequency need to be called 139 // Uses short circuiting to squeeze a little more efficiency out of check 140 bool isInitialized() 141 { 142 return !(isNaN(_theta) || isNaN(_omega) || isNaN(_kappa) || isNaN(_delta)); 143 } 144 private: 145 float _theta; 146 float _omega; 147 float _kappa; 148 float _delta; 149 } 150 151 unittest 152 { 153 LinkwitzRileyHP!float linkwitzRileyHP = new LinkwitzRileyHP!float(); 154 } 155 156 class LinkwitzRileyHPNthOrder(T) : AudioEffect!T 157 { 158 public: 159 nothrow: 160 @nogc: 161 162 this(int order) 163 { 164 assert(order % 2 == 0, "LR filter order must be even"); 165 _order = order; 166 167 _bw1 = mallocNew!(ButterworthHPNthOrder!T)(order / 2); 168 _bw2 = mallocNew!(ButterworthHPNthOrder!T)(order / 2); 169 } 170 171 void setFrequency(float frequency) 172 { 173 if(frequency != _frequency) 174 { 175 _frequency = frequency; 176 _bw1.setFrequency(_frequency); 177 _bw2.setFrequency(_frequency); 178 } 179 } 180 181 override float getNextSample(const(float) input) 182 { 183 return _bw2.getNextSample(_bw1.getNextSample(input)); 184 } 185 186 override void reset() 187 { 188 _bw1.reset(); 189 _bw2.reset(); 190 } 191 192 override void setSampleRate(float sampleRate) 193 { 194 if(sampleRate != _sampleRate) 195 { 196 _sampleRate = sampleRate; 197 _bw1.setSampleRate(_sampleRate); 198 _bw2.setSampleRate(_sampleRate); 199 } 200 } 201 202 private: 203 ButterworthHPNthOrder!T _bw1; 204 ButterworthHPNthOrder!T _bw2; 205 206 int _order; 207 float _frequency; 208 } 209 210 unittest 211 { 212 LinkwitzRileyHPNthOrder!float lr8thOrder = mallocNew!(LinkwitzRileyHPNthOrder!float)(8); 213 lr8thOrder.setSampleRate(44100); 214 lr8thOrder.setFrequency(400); 215 216 } 217 218 class ButterworthHPNthOrder(T) : AudioEffect!T 219 { 220 public: 221 nothrow: 222 @nogc: 223 this(int order) 224 { 225 _order = order; 226 _secondOrderHighpasses = mallocSlice!(HighpassO2!float)(order / 2); 227 foreach(index; 0..(order / 2)) 228 { 229 _secondOrderHighpasses[index] = mallocNew!(HighpassO2!float)(); 230 } 231 } 232 233 void setFrequency(float frequency) 234 { 235 if(frequency != _frequency) 236 { 237 _frequency = frequency; 238 float[] qValues = calculateQValuesForButterworth(_order); 239 foreach(index, hpf; _secondOrderHighpasses) 240 { 241 float qValue = qValues[index]; 242 hpf.setFrequency(frequency); 243 hpf.setQualityFactor(qValue); 244 } 245 } 246 } 247 248 override float getNextSample(const(float) input) 249 { 250 float output = input; 251 foreach(hpf; _secondOrderHighpasses) 252 { 253 output = hpf.getNextSample(output); 254 } 255 return output; 256 } 257 258 override void reset() 259 { 260 foreach(hpf; _secondOrderHighpasses) 261 { 262 if(hpf) 263 { 264 hpf.reset(); 265 } 266 } 267 } 268 269 override void setSampleRate(float sampleRate) 270 { 271 if(sampleRate != _sampleRate) 272 { 273 _sampleRate = sampleRate; 274 foreach(index; 0.._secondOrderHighpasses.length) 275 { 276 _secondOrderHighpasses[index].setSampleRate(sampleRate); 277 } 278 } 279 } 280 281 private: 282 HighpassO2!float[] _secondOrderHighpasses; 283 284 int _order; 285 float _frequency; 286 } 287 288 unittest 289 { 290 import std.stdio; 291 writeln("****************************"); 292 writeln("* Butterworth Filter tests *"); 293 writeln("****************************"); 294 295 writeln("Q Value Calculations"); 296 float[] actual = calculateQValuesForButterworth(4); 297 float[] expected = [0.541196100146197, 1.3065629648763764]; 298 assert( actual == expected, "Failed for order = 4"); 299 writeln("passed for order = 4"); 300 301 ButterworthHPNthOrder!float butterworth4 = mallocNew!(ButterworthHPNthOrder!float)(4); 302 butterworth4.setSampleRate(44100.0f); 303 butterworth4.setFrequency(10000); 304 305 float[] impulse = [1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f]; 306 float[] hpfOutput = []; 307 foreach(sample; impulse) 308 { 309 hpfOutput ~= butterworth4.getNextSample(sample); 310 } 311 312 writeln("Butterworth N=4 Output:"); 313 writeln(hpfOutput); 314 315 }