1 /**
2 * Copyright 2017 Cut Through Recordings
3 * License: MIT License
4 * Author(s): Ethan Reker
5 */
6 module ddsp.filter.shelf;
7 
8 import ddsp.filter.biquad;
9 import ddsp.effect.effect;
10 
11 import std.math;
12 
13 /// The equations for calculating the BiQuad Coefficients are based off of those from vinniefalco/DSPFilters
14 class LowShelf : AudioEffect
15 {
16 public:
17 
18     void setGain(float gain) nothrow @nogc
19     {
20         if(_gain != gain)
21         {
22             _gain = gain;
23             calcCoefficients();
24         }
25     }
26 
27     override float getNextSample(const float input)  nothrow @nogc
28     {
29         _w = input - _a1 * _w1 - _a2 * _w2;
30         _yn = _b0 * _w + _b1 *_w1 + _b2 * _w2;
31 
32         _w2 = _w1;
33         _w1 = _w;
34 
35         return _yn;
36     }
37 
38     override void reset() nothrow @nogc
39     {
40         _w = 0;
41         _w1 = 0;
42         _w2 = 0;
43 
44         _yn = 0;
45     }
46 
47     void setFrequency(float frequency) nothrow @nogc
48     {
49         if(_frequency != frequency)
50         {
51             _frequency = frequency;
52             calcCoefficients();
53         }
54     }
55     
56     override string toString()
57     {
58         import std.conv;
59         return "a0: " ~ to!string(_a0) ~
60                "a1: " ~ to!string(_a1) ~
61                "a2: " ~ to!string(_a2) ~
62                "b0: " ~ to!string(_b0) ~
63                "b1: " ~ to!string(_b1) ~
64                "b2: " ~ to!string(_b2);
65     }
66 
67 private:
68     float _a0 = 0;
69     float _a1 = 0;
70     float _a2 = 0;
71     float _b0 = 0;
72     float _b1 = 0;
73     float _b2 = 0;
74 
75     float _w = 0;
76     float _w1 = 0;
77     float _w2 = 0;
78 
79     float _yn;
80 
81     float _frequency;
82     float _gain;
83 
84     void calcCoefficients() nothrow @nogc
85     {
86         float shelfSlope = 2.0f;
87         float A  = pow (10, _gain/40);
88         float w0 = 2 * pi * _frequency / _sampleRate;
89         float cs = cos (w0);
90         float sn = sin (w0);
91         float AL = sn / 2 * sqrt ((A + 1/A) * (1 / shelfSlope - 1) + 2);
92         float sq = 2 * sqrt(A) * AL;
93         _b0 =    A*( (A+1) - (A-1)*cs + sq );
94         _b1 =  2*A*( (A-1) - (A+1)*cs );
95         _b2 =    A*( (A+1) - (A-1)*cs - sq );
96         _a0 =        (A+1) + (A-1)*cs + sq;
97         _a1 =   -2*( (A-1) + (A+1)*cs );
98         _a2 =        (A+1) + (A-1)*cs - sq;
99 
100         _b0 /= _a0;
101         _b1 /= _a0;
102         _b2 /= _a0;
103         _a1 /= _a0;
104         _a2 /= _a0;
105     }
106 }
107 
108 unittest
109 {
110     import dplug.core.nogc;
111     import dplug.core.alignedbuffer;
112     import ddsp.effect.effect;
113 
114     Vec!LowShelf filters = makeVec!LowShelf;
115     foreach(channel; 0..2)
116     {
117         filters.pushBack(mallocNew!LowShelf);
118         filters[channel].setSampleRate(44100.0f);
119         filters[channel].setFrequency(150.0f);
120         filters[channel].setGain(3.0f);
121     }
122 
123     //testEffect(AudioEffect effect, string name, size_t bufferSize = 20000, bool outputResults = false)
124     foreach(filter; filters)
125         testEffect(filter, "LowShelf" , 20000, false);
126 }