1 /*
2     SynthHelper.h
3 
4     Copyright 2011, Alan Calvert
5     Copyright 2021, Kristian Amlie
6 
7     This file is part of yoshimi, which is free software: you can
8     redistribute it and/or modify it under the terms of the GNU General
9     Public License as published by the Free Software Foundation, either
10     version 2 of the License, or (at your option) any later version.
11 
12     yoshimi is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16 
17     You should have received a copy of the GNU General Public License
18     along with yoshimi.  If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 #ifndef SYNTHHELPER_H
22 #define SYNTHHELPER_H
23 
24 #include <cmath>
25 
26 #define DEFAULT_PARAM_INTERPOLATION_LENGTH_MSECS 50.0f
27 
28 namespace synth {
29 
30 
31 // Provides a convenient way to interpolate between samples. You provide a
32 // starting value, and each time you provide new value, it will start
33 // interpolating between the values. It takes into account new values that
34 // appear while an interpolation is happening.
35 template <typename T>
36 class InterpolatedValue
37 {
38     public:
InterpolatedValue(T startValue,int sampleRate_)39         InterpolatedValue(T startValue, int sampleRate_)
40             : oldValue(startValue)
41             , newValue(startValue)
42             , targetValue(startValue)
43             , sampleRate(sampleRate_)
44         {
45             setInterpolationLength(DEFAULT_PARAM_INTERPOLATION_LENGTH_MSECS);
46         }
47 
setInterpolationLength(float msecs)48         void setInterpolationLength(float msecs)
49         {
50             float samples = msecs / 1000.0f * sampleRate;
51             // Round up so we are as smooth as possible.
52             interpolationLength = ceilf(samples);
53             interpolationPos = interpolationLength;
54         }
55 
isInterpolating()56         bool isInterpolating() const
57         {
58             return interpolationPos < interpolationLength;
59         }
60 
setTargetValue(T value)61         void setTargetValue(T value)
62         {
63             targetValue = value;
64             if (!isInterpolating() && targetValue != newValue)
65             {
66                 newValue = value;
67                 interpolationPos = 0;
68             }
69         }
70 
71        // The value interpolated from.
getOldValue()72         T getOldValue() const
73         {
74             return oldValue;
75         }
76 
77        // The value interpolated to (not necessarily the same as the last set
78        // target point).
getNewValue()79         T getNewValue() const
80         {
81             return newValue;
82         }
83 
getTargetValue()84         T getTargetValue() const
85         {
86             return targetValue;
87         }
88 
factor()89         float factor() const
90         {
91             return (float)interpolationPos / (float)interpolationLength;
92         }
93 
getValue()94         T getValue() const
95         {
96             return getOldValue() * (1.0f - factor()) + getNewValue() * factor();
97         }
98 
getAndAdvanceValue()99         T getAndAdvanceValue()
100         {
101             T v = getValue();
102             advanceValue();
103             return v;
104         }
105 
advanceValue()106         void advanceValue()
107         {
108             if (interpolationPos >= interpolationLength)
109                 return;
110 
111             if (++interpolationPos < interpolationLength)
112                 return;
113 
114             oldValue = newValue;
115             if (targetValue != newValue)
116             {
117                 newValue = targetValue;
118                 interpolationPos = 0;
119             }
120         }
121 
advanceValue(int samples)122         void advanceValue(int samples)
123         {
124             if (interpolationPos + samples < interpolationLength)
125                 interpolationPos += samples;
126             else if (interpolationPos + samples >= interpolationLength * 2)
127             {
128                 oldValue = newValue = targetValue;
129                 interpolationPos = interpolationLength;
130             }
131             else
132             {
133                 oldValue = newValue;
134                 newValue = targetValue;
135                 interpolationPos += samples - interpolationLength;
136             }
137         }
138 
139     private:
140         T oldValue;
141         T newValue;
142         T targetValue;
143 
144         int interpolationLength;
145         int interpolationPos;
146 
147     protected:
148         int sampleRate;
149 };
150 
151 // Default-initialized variant of InterpolatedValue. Use only if you must (for
152 // example in an array), normally it is better to use the fully initialized
153 // one. If you use this, then you should call setSampleRate immediately after
154 // construction.
155 template <typename T>
156 class InterpolatedValueDfl : public InterpolatedValue<T>
157 {
158     public:
InterpolatedValueDfl()159         InterpolatedValueDfl()
160             : InterpolatedValue<T>(0, 44100)
161         {
162         }
163 
setSampleRate(int sampleRate)164         void setSampleRate(int sampleRate)
165         {
166             this->sampleRate = sampleRate;
167             this->setInterpolationLength(DEFAULT_PARAM_INTERPOLATION_LENGTH_MSECS);
168         }
169 };
170 
171 
aboveAmplitudeThreshold(float a,float b)172 inline bool aboveAmplitudeThreshold(float a, float b)
173 {
174     return ((2.0f * fabsf(b - a) / fabsf(b + a + 0.0000000001f)) > 0.0001f);
175 }
176 
177 
interpolateAmplitude(float a,float b,int x,int size)178 inline float interpolateAmplitude(float a, float b, int x, int size)
179 {
180     return a + (b - a) * (float)x / (float)size;
181 }
182 
183 
velF(float velocity,unsigned char scaling)184 inline float velF(float velocity, unsigned char scaling)
185 {
186     if (scaling == 127 || velocity > 0.99f)
187         return 1.0f;
188     else
189         return powf(velocity, (powf(8.0f, (64.0f - (float)scaling) / 64.0f)));
190 }
191 
192 
193 
getDetune(unsigned char type,unsigned short int coarsedetune,unsigned short int finedetune)194 inline float getDetune(unsigned char type,
195                        unsigned short int coarsedetune,
196                        unsigned short int finedetune)
197 {
198     float det = 0.0f;
199     float octdet = 0.0f;
200     float cdet = 0.0f;
201     float findet = 0.0f;
202     int octave = coarsedetune / 1024; // get Octave
203 
204     if (octave >= 8)
205         octave -= 16;
206     octdet = octave * 1200.0f;
207 
208     int cdetune = coarsedetune % 1024; // coarse and fine detune
209     if (cdetune > 512)
210         cdetune -= 1024;
211     int fdetune = finedetune - 8192;
212 
213     switch (type)
214     {
215         // case 1 is used for the default (see below)
216         case 2:
217             cdet = fabs(cdetune * 10.0f);
218             findet = fabs(fdetune / 8192.0f) * 10.0f;
219             break;
220 
221         case 3:
222             cdet = fabsf(cdetune * 100.0f);
223             findet = powf(10.0f, fabs(fdetune / 8192.0f) * 3.0f) / 10.0f - 0.1f;
224             break;
225 
226         case 4:
227             cdet = fabs(cdetune * 701.95500087f); // perfect fifth
228             findet = (powf(2.0f, fabs(fdetune / 8192.0f) * 12.0f) - 1.0f) / 4095.0f * 1200.0f;
229             break;
230 
231             // case ...: need to update N_DETUNE_TYPES, if you'll add more
232         default:
233             cdet = fabs(cdetune * 50.0f);
234             findet = fabs(fdetune / 8192.0f) * 35.0f; // almost like "Paul's Sound Designer 2"
235             break;
236     }
237     if (finedetune < 8192)
238         findet = -findet;
239     if (cdetune < 0)
240         cdet = -cdet;
241     det = octdet + cdet + findet;
242     return det;
243 }
244 
245 
246 }//(End)namespace synth
247 #endif /*SYNTHHELPER_H*/
248