1 /** 2 * Copyright 2017 Cut Through Recordings 3 * License: MIT License 4 * Author(s): Ethan Reker 5 * 6 * Based on equations from https://github.com/vinniefalco/DSPFilters 7 * -------------------------------------------------------------------------------- 8 * License: MIT License (http://www.opensource.org/licenses/mit-license.php) 9 * Copyright (c) 2009 by Vinnie Falco 10 * Permission is hereby granted, free of charge, to any person obtaining a copy 11 * of this software and associated documentation files (the "Software"), to deal 12 * in the Software without restriction, including without limitation the rights 13 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 * copies of the Software, and to permit persons to whom the Software is 15 * furnished to do so, subject to the following conditions: 16 * The above copyright notice and this permission notice shall be included in 17 * all copies or substantial portions of the Software. 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 * THE SOFTWARE. 25 */ 26 module ddsp.filter.allpass; 27 28 import std.math; 29 30 import ddsp.filter.biquad; 31 import ddsp.effect : AudioEffect; 32 33 import dplug.core : Vec, mallocNew, makeVec; 34 35 /// 1st order Allpass filter for introducting a 90 degrees phase shift at the center 36 /// frequency. 37 class AllpassO1(T) : BiQuad!T 38 { 39 public: 40 41 this() nothrow @nogc 42 { 43 super(); 44 } 45 46 override void calcCoefficients() 47 { 48 _alpha = (tan(pi * _frequency / _sampleRate) - 1) / (tan(pi * _frequency / _sampleRate) + 1); 49 50 _a0 = _alpha; 51 _a1 = 1.0; 52 _a2 = 0.0; 53 _b1 = _alpha; 54 _b2 = 0.0; 55 } 56 57 private: 58 float _alpha; 59 } 60 61 unittest 62 { 63 import dplug.core.nogc; 64 import ddsp.effect : testEffect; 65 66 AllpassO1!float f = mallocNew!(AllpassO1!float)(); 67 f.setSampleRate(44100); 68 f.setFrequency(10000); 69 testEffect(f, "AllpassO1", 44100 * 2, false); 70 } 71 72 73 /// 2nd order Allpass filter for introducting a 180 degrees phase shift at the center 74 /// frequency. This is necessary for summing more than 2 bands created from 2nd order 75 /// Linkwitz-Riley filters. 76 class AllpassO2(T) : BiQuad!T 77 { 78 public: 79 80 this() nothrow @nogc 81 { 82 super(); 83 } 84 85 override void calcCoefficients() 86 { 87 immutable float _w0 = 2 * PI * _frequency / _sampleRate; 88 immutable float cs = cos(_w0); 89 immutable float sn = sin(_w0); 90 immutable float AL = sn / (2 * 0.707f); 91 92 immutable float _b0 = 1 + AL; 93 _b1 = (-2 * cs) / _b0; 94 _b2 = (1 - AL) / _b0; 95 _a0 = (1 - AL) / _b0; 96 _a1 = (-2 * cs) / _b0; 97 _a2 = (1 + AL) / _b0; 98 } 99 } 100 101 /// Deprecated: use `AllpassO2` instead. 102 /// This is just an alias to `AllpassO2` since `Allpass` was renamed 103 /// to `AllpassO2` 104 alias Allpass = AllpassO2; 105 106 unittest 107 { 108 import dplug.core.nogc; 109 import ddsp.effect : testEffect; 110 111 AllpassO2!float f = mallocNew!(AllpassO2!float)(); 112 f.setSampleRate(44100); 113 f.setFrequency(10000); 114 testEffect(f, "AllpassO2", 44100 * 2, false); 115 } 116 117 /// Nth order Allpass filter created using 2nd and 1st order Allpass filters 118 /// Useful to correct phase on crossovers created using nth order Linkwitz-Riley 119 /// filters. 120 class AllpassNthOrder(T) : AudioEffect!T 121 { 122 public: 123 nothrow: 124 @nogc: 125 this(int order) 126 { 127 _order = order; 128 129 // if odd order then we need one 1st order component 130 if(_order % 2 != 0) 131 { 132 _1stOrderFilter = mallocNew!(AllpassO1!T)(); 133 } 134 135 foreach(i; 0..(_order / 2)) 136 { 137 _2ndOrderFilters.pushBack(mallocNew!(AllpassO2!T)()); 138 } 139 } 140 141 void setFrequency(float frequency) 142 { 143 if(_freqency != frequency) 144 { 145 _freqency = frequency; 146 147 if(_1stOrderFilter) 148 { 149 _1stOrderFilter.setFrequency(_freqency); 150 } 151 152 foreach(i; 0..(_order / 2)) 153 { 154 _2ndOrderFilters[i].setFrequency(_freqency); 155 } 156 } 157 } 158 159 override void setSampleRate(float sampleRate) 160 { 161 if(_sampleRate != sampleRate) 162 { 163 _sampleRate = sampleRate; 164 165 if(_1stOrderFilter) 166 { 167 _1stOrderFilter.setSampleRate(_sampleRate); 168 } 169 170 foreach(i; 0..(_order / 2)) 171 { 172 _2ndOrderFilters[i].setSampleRate(_sampleRate); 173 } 174 } 175 } 176 177 override float getNextSample(const(float) input) 178 { 179 float output = input; 180 if(_1stOrderFilter) 181 { 182 output = _1stOrderFilter.getNextSample(output); 183 } 184 185 foreach(i; 0..(_order / 2)) 186 { 187 output = _2ndOrderFilters[i].getNextSample(output); 188 } 189 190 return output; 191 } 192 193 override void reset() 194 { 195 if(_1stOrderFilter) 196 { 197 _1stOrderFilter.reset(); 198 } 199 200 foreach(i; 0..(_order / 2)) 201 { 202 _2ndOrderFilters[i].reset(); 203 } 204 } 205 206 private: 207 Vec!(AllpassO2!T) _2ndOrderFilters; 208 AllpassO1!T _1stOrderFilter; 209 210 int _order; 211 float _freqency; 212 }