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