1 // Copyright 2017 Emilie Gillet.
2 //
3 // Author: Emilie Gillet (emilie.o.gillet@gmail.com)
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to enable, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 // THE SOFTWARE.
22 //
23 // See http://creativecommons.org/licenses/MIT/ for more information.
24 //
25 // -----------------------------------------------------------------------------
26 //
27 // Convert the phase counters generated by RampGenerator into actual variable
28 // slope waveforms (or variants, like EOA/EOR pulses).
29 
30 #ifndef TIDES_RAMP_SHAPER_H_
31 #define TIDES_RAMP_SHAPER_H_
32 
33 #include "stmlib/dsp/dsp.h"
34 #include "stmlib/dsp/parameter_interpolator.h"
35 #include "stmlib/dsp/polyblep.h"
36 
37 #include "tides2/ramp_generator.h"
38 
39 namespace tides {
40 
41 class RampShaper {
42  public:
RampShaper()43   RampShaper() { }
~RampShaper()44   ~RampShaper() { }
45 
Init()46   void Init() {
47     next_sample_ = 0.0f;
48     previous_phase_shift_ = 0.0f;
49     going_up_ = true;
50   }
51 
BandLimitedPulse(float phase,float frequency,float pw)52   inline float BandLimitedPulse(float phase, float frequency, float pw) {
53     CONSTRAIN(pw, frequency * 2.0f, 1.0f - 2.0f * frequency);
54     float this_sample = next_sample_;
55     float next_sample = 0.0f;
56 
57     float wrap_point = pw;
58     if (phase < pw * 0.5f) {
59       wrap_point = 0.0f;
60     } else if (phase > 0.5f + pw * 0.5f) {
61       wrap_point = 1.0f;
62     }
63 
64     if (going_up_ ^ (phase < pw)) {
65       float t = (phase - wrap_point) / frequency;
66       float discontinuity = 1.0f;
67       if (wrap_point != pw) {
68         discontinuity = -discontinuity;
69       }
70       if (frequency < 0.0f) {
71         discontinuity = -discontinuity;
72       }
73       this_sample += stmlib::ThisBlepSample(t) * discontinuity;
74       next_sample += stmlib::NextBlepSample(t) * discontinuity;
75       going_up_ = phase < pw;
76     }
77     next_sample += phase < pw ? 0.0f : 1.0f;
78     next_sample_ = next_sample;
79     return this_sample;
80   }
81 
82   template<RampMode ramp_mode, Range range>
Slope(float phase,float phase_shift,float frequency,float pw)83   inline float Slope(
84       float phase, float phase_shift, float frequency, float pw) {
85     if (ramp_mode == RAMP_MODE_AD) {
86       return SkewedRamp(phase, 0.0f, frequency, pw);
87     } else if (ramp_mode == RAMP_MODE_AR) {
88       return phase;
89     } else {
90       if (range == RANGE_CONTROL) {
91         return SkewedRamp(phase, phase_shift, frequency, pw);
92       } else {
93         return BandLimitedSlope(phase, phase_shift, frequency, pw);
94       }
95     }
96   }
97 
98   template<RampMode ramp_mode>
EOA(float phase,float frequency,float pw)99   inline float EOA(float phase, float frequency, float pw) {
100     if (ramp_mode == RAMP_MODE_LOOPING) {
101       return BandLimitedPulse(phase, frequency, pw);
102     } else if (ramp_mode == RAMP_MODE_AR) {
103       return phase >= 0.5f ? 1.0f : 0.0f;
104     } else {
105       return phase >= pw ? 1.0f : 0.0f;
106     }
107   }
108 
109   template<RampMode ramp_mode>
EOR(float phase,float frequency,float pw)110   inline float EOR(float phase, float frequency, float pw) {
111     if (ramp_mode == RAMP_MODE_LOOPING) {
112       return 1.0f - BandLimitedPulse(
113           phase, frequency, std::min(0.5f, 96.0f * frequency));
114     } else {
115       return phase >= 1.0f;
116     }
117   }
118 
119  private:
BandLimitedSlope(float phase,float phase_shift,float frequency,float pw)120   inline float BandLimitedSlope(
121       float phase, float phase_shift, float frequency, float pw) {
122     if (phase_shift) {
123       phase += phase_shift;
124       frequency += phase_shift - previous_phase_shift_;
125       previous_phase_shift_ = phase_shift;
126 
127       if (phase >= 1.0f) {
128         phase -= 1.0f;
129       } else if (phase < 0.0f) {
130         phase += 1.0f;
131       }
132     }
133 
134     CONSTRAIN(pw, fabsf(frequency) * 2.0f, 1.0f - 2.0f * fabsf(frequency));
135     float this_sample = next_sample_;
136     float next_sample = 0.0f;
137 
138     float wrap_point = pw;
139     if (phase < pw * 0.5f) {
140       wrap_point = 0.0f;
141     } else if (phase > 0.5f + pw * 0.5f) {
142       wrap_point = 1.0f;
143     }
144 
145     const float slope_up = 1.0f / pw;
146     const float slope_down = 1.0f / (1.0f - pw);
147     if (going_up_ ^ (phase < pw)) {
148       float t = (phase - wrap_point) / frequency;
149       float discontinuity = -(slope_up + slope_down) * frequency;
150       if (wrap_point != pw) {
151         discontinuity = -discontinuity;
152       }
153       if (frequency < 0.0f) {
154         discontinuity = -discontinuity;
155       }
156       this_sample += stmlib::ThisIntegratedBlepSample(t) * discontinuity;
157       next_sample += stmlib::NextIntegratedBlepSample(t) * discontinuity;
158       going_up_ = phase < pw;
159     }
160     next_sample += going_up_
161       ? phase * slope_up
162       : 1.0f - (phase - pw) * slope_down;
163     next_sample_ = next_sample;
164     return this_sample;
165   }
166 
SkewedRamp(float phase,float phase_shift,float frequency,float pw)167   inline float SkewedRamp(
168       float phase, float phase_shift, float frequency, float pw) {
169     if (phase_shift) {
170       phase += phase_shift;
171       frequency += phase_shift - previous_phase_shift_;
172       previous_phase_shift_ = phase_shift;
173 
174       if (phase >= 1.0f) {
175         phase -= 1.0f;
176       } else if (phase < 0.0f) {
177         phase += 1.0f;
178       }
179     }
180 
181     CONSTRAIN(pw, fabsf(frequency) * 2.0f, 1.0f - 2.0f * fabsf(frequency));
182     const float slope_up = 0.5f / pw;
183     const float slope_down = 0.5f / (1.0f - pw);
184     return phase < pw ? phase * slope_up : (phase - pw) * slope_down + 0.5f;
185   }
186 
187   float next_sample_;
188   float previous_phase_shift_;
189   bool going_up_;
190 
191   DISALLOW_COPY_AND_ASSIGN(RampShaper);
192 };
193 
194 class RampWaveshaper {
195  public:
RampWaveshaper()196   RampWaveshaper() { }
~RampWaveshaper()197   ~RampWaveshaper() { }
198 
Init()199   void Init() {
200     previous_input_ = 0.0f;
201     previous_output_ = 0.0f;
202     breakpoint_ = 0.0f;
203   }
204 
205   template<RampMode ramp_mode>
Shape(float input,const int16_t * shape,float shape_fractional)206   inline float Shape(
207       float input,
208       const int16_t* shape,
209       float shape_fractional) {
210     float ws_index = 1024.0f * input;
211     MAKE_INTEGRAL_FRACTIONAL(ws_index)
212     ws_index_integral &= 1023;
213     float x0 = static_cast<float>(shape[ws_index_integral]) / 32768.0f;
214     float x1 = static_cast<float>(shape[ws_index_integral + 1]) / 32768.0f;
215     float y0 = static_cast<float>(shape[ws_index_integral + 1025]) / 32768.0f;
216     float y1 = static_cast<float>(shape[ws_index_integral + 1026]) / 32768.0f;
217     float x = x0 + (x1 - x0) * ws_index_fractional;
218     float y = y0 + (y1 - y0) * ws_index_fractional;
219     float output = x + (y - x) * shape_fractional;
220 
221     if (ramp_mode != RAMP_MODE_AR) {
222       return output;
223     } else {
224       if (previous_input_ <= 0.5f && input > 0.5f) {
225         breakpoint_ = previous_output_;
226       } else if (previous_input_ > 0.5f && input < 0.5f) {
227         breakpoint_ = previous_output_;
228       } else if (input == 1.0f) {
229         breakpoint_ = 1.0f;
230       } else if (input == 0.5f) {
231         breakpoint_ = 0.0f;
232       }
233       if (input <= 0.5f) {
234         output = breakpoint_ + (1.0f - breakpoint_) * output;
235       } else {
236         output = breakpoint_ * output;
237       }
238       previous_input_ = input;
239       previous_output_ = output;
240       return output;
241     }
242   }
243 
244  private:
245   float previous_input_;
246   float previous_output_;
247   float breakpoint_;
248 
249   DISALLOW_COPY_AND_ASSIGN(RampWaveshaper);
250 };
251 
252 }  // namespace tides
253 
254 #endif  // TIDES_RAMP_SHAPER_H_
255