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 }