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.effect.effect;
31 
32 const float pi = 3.14159265;
33 
34 
35 /// Allpass filter for introducting a 180 degrees phase shift at the center
36 /// frequency.  This is necessary for summing more than 2 bands created from
37 /// Linkwitz-Riley filters.
38 /// TODO: Inherit BiQuad directly to remove redundent code.
39 class Allpass : AudioEffect
40 {
41 public:
42 
43     this() nothrow @nogc
44     {
45 
46     }
47 
48     void initialize(float frequency, float q = 0.707) nothrow @nogc
49     {
50         _frequency = frequency;
51         float _w0 = 2 * pi * _frequency / _sampleRate;
52         float cs = cos(_w0);
53         float sn = sin(_w0);
54         float AL = sn / (2 * q);
55 
56         _a0 = 1 + AL;
57         _a1 = (-2 * cs) / _a0;
58         _a2 = (1 - AL) / _a0;
59         _b0 = (1 - AL) / _a0;
60         _b1 = (-2 * cs) / _a0;
61         _b2 = (1 + AL) / _a0;
62     }
63 
64     override float getNextSample(const float input)  nothrow @nogc
65     {
66         _w = input - _a1 * _w1 - _a2 * _w2;
67         _yn = _b0 * _w + _b1 *_w1 + _b2 * _w2;
68 
69         _w2 = _w1;
70         _w1 = _w;
71 
72         return _yn;
73     }
74 
75     override void reset() nothrow @nogc
76     {
77         _a0 = 0;
78         _a1 = 0;
79         _a2 = 0;
80         _b0 = 0;
81         _b1 = 0;
82         _b2 = 0;
83 
84         _w = 0;
85         _w1 = 0;
86         _w2 = 0;
87 
88         _yn = 0;
89     }
90 
91     void setFrequency(float frequency) nothrow @nogc
92     {
93         if(_frequency != frequency)
94             initialize(frequency, _sampleRate);
95     }
96 
97 private:
98     float _a0 = 0;
99     float _a1 = 0;
100     float _a2 = 0;
101     float _b0 = 0;
102     float _b1 = 0;
103     float _b2 = 0;
104 
105     float _w = 0;
106     float _w1 = 0;
107     float _w2 = 0;
108 
109     float _yn;
110 
111     float _frequency;
112 }
113 
114 unittest
115 {
116     import dplug.core.nogc;
117     
118     Allpass f = mallocNew!Allpass();
119     f.setSampleRate(44100);
120     f.setFrequency(10000);
121     testEffect(f, "Allpass", 44100 * 2, false);
122 }