1 /**
2 * Copyright 2017 Cut Through Recordings
3 * License: MIT License
4 * Author(s): Ethan Reker
5 */
6 module ddsp.effect.moddelay;
7 
8 import ddsp.effect.effect;
9 import ddsp.osc.wtoscillator;
10 import ddsp.effect.digitaldelay;
11 import ddsp.util.memory;
12 
13 import std.math;
14 
15 import std.algorithm : max;
16 
17 /// General purpose class for Modulated Delay effects. This class is used with 
18 /// contraints on depth, offset, mix, and feedback to create other effects. (Flanger, Chorus, Tremolo, etc)
19 class ModDelay(T) : AudioEffect!T
20 {
21 public:
22 nothrow:
23 @nogc:
24 
25     this()
26     {
27         lfo = calloc!(WTOscillator!T).init();
28         delay = calloc!(DigitalDelay!T).init();
29     }
30 
31     override void setSampleRate(float sampleRate)
32     {
33         _sampleRate = sampleRate;
34         lfo.setSampleRate(sampleRate);
35         delay.setSampleRate(sampleRate);
36     }
37 
38     float calcDelayOffset(float lfoValue)
39     {
40         float startDelay = _min_delay + _delay_offset;
41         float lfoOffset = _mod_depth * ((lfoValue + 1) / 2 * (_max_delay - _min_delay)) + _min_delay;
42         return max(lfoOffset + startDelay, 0);
43     }
44 
45     /// Must be set before processing audio
46     void setDelayRange(float minDelay, float maxDelay)
47     {
48         _min_delay = minDelay;
49         _max_delay = maxDelay;
50     }
51 
52     void setParams(float rate, float depth, float mix, float feedback, int offset, int modType = 0)
53     {
54         _mod_rate = rate;
55         _mod_depth = depth;
56         _delay_offset = offset;
57         _feedback = feedback;
58         _mix = mix;
59 
60         //Frequency, sin, not bandlimited
61         lfo.setParams(rate, modType, true);
62         delay.setParams(0, feedback, mix);
63     }
64 
65     override T getNextSample(const T input)
66     {
67         float fYn = 0.0;
68         float fYqn = 0.0;
69         lfo.doOscillate(&fYn, &fYqn);
70 
71         float delaySamples = calcDelayOffset(fYn);
72         delay.setParams(delaySamples, _feedback, _mix);
73 
74         float output = delay.getNextSample(input);
75 
76         return output;
77     }
78 
79     override void reset()
80     {
81         lfo.reset();
82         delay.reset();
83     }
84 private:
85     float _mod_rate;
86     float _mod_depth;
87     float _mix;
88     float _feedback;
89     float _delay_offset;
90 
91     float _max_delay;
92     float _min_delay;
93 
94     WTOscillator!T lfo;
95     DigitalDelay!T delay;
96 }
97 
98 unittest 
99 {
100     ModDelay!float modDelay = new ModDelay!float();
101 }
102 
103 class Flanger(T) : AudioEffect!T
104 {
105 public:
106 nothrow:
107 @nogc:
108 
109     this()
110     {
111         _modDelay = calloc!(ModDelay!T).init();
112     }
113 
114 	override void setSampleRate(float sampleRate)
115 	{
116 		_sampleRate = sampleRate;
117 		_modDelay.setSampleRate(_sampleRate);
118 		_modDelay.setDelayRange(0, 7);
119 	}
120 
121     void setParams(float modRate, float modDepth, int oscType = 0)
122     {
123         _modDelay.setParams(modRate, modDepth, 0.5, 0.5, 0, oscType);
124     }
125 
126     override float getNextSample(const float input)
127     {
128         return _modDelay.getNextSample(input);
129     }
130 
131     override void reset()
132     {
133         _modDelay.reset();
134     }
135 
136 private:
137     ModDelay!T _modDelay;
138 }
139 
140 unittest
141 {
142     Flanger!float flanger = new Flanger!float();
143 }
144 
145 class Vibrato(T) : AudioEffect!T
146 {
147 public:
148 nothrow:
149 @nogc:
150 
151     this()
152     {
153         _modDelay = calloc!(ModDelay!T).init();
154     }
155 
156 	override void setSampleRate(float sampleRate)
157 	{
158 		_sampleRate = sampleRate;
159 		_modDelay.setSampleRate(_sampleRate);
160 		_modDelay.setDelayRange(0, 7);
161 	}
162 
163     void setParams(float modRate, float modDepth, int oscType = 0)
164     {
165         _modDelay.setParams(modRate, modDepth, 1.0, 0.0, 0, oscType);
166     }
167 
168     override T getNextSample(const T input)
169     {
170         return _modDelay.getNextSample(input);
171     }
172 
173     override void reset()
174     {
175         _modDelay.reset();
176     }
177 
178 private:
179     ModDelay!T _modDelay;
180 }
181 
182 unittest
183 {
184     Vibrato!float vibrato = new Vibrato!float();
185 }
186 
187 class Chorus(T) : AudioEffect!T
188 {
189 public:
190 nothrow:
191 @nogc:
192 
193     this()
194     {
195         _modDelay = calloc!(ModDelay!T).init();
196     }
197 
198 	override void setSampleRate(float sampleRate)
199 	{
200 		_sampleRate = sampleRate;
201 		_modDelay.setSampleRate(_sampleRate);
202 		_modDelay.setDelayRange(5, 30);
203 	}
204 
205     void setParams(float modRate, float modDepth, int oscType = 0)
206     {
207         _modDelay.setParams(modRate, modDepth, 0.5, 0.0, 0, oscType);
208     }
209 
210     override T getNextSample(const T input)
211     {
212         return _modDelay.getNextSample(input);
213     }
214 
215     override void reset()
216     {
217         _modDelay.reset();
218     }
219 
220 private:
221     ModDelay!T _modDelay;
222 }
223 
224 unittest
225 {
226     Chorus!float chorus = new Chorus!float();
227 }