/[svn]/linuxsampler/trunk/src/engines/common/SignalUnit.h
ViewVC logotype

Contents of /linuxsampler/trunk/src/engines/common/SignalUnit.h

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2230 - (show annotations) (download) (as text)
Fri Aug 5 17:59:10 2011 UTC (12 years, 8 months ago) by iliev
File MIME type: text/x-c++hdr
File size: 17057 byte(s)
* sfz engine: implemented curves
* sfz engine: implemented opcodes volume_onccN, volume_curveccN

1 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2011 Grigor Iliev *
6 * *
7 * This program is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; either version 2 of the License, or *
10 * (at your option) any later version. *
11 * *
12 * This program 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 this program; if not, write to the Free Software *
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
20 * MA 02111-1307 USA *
21 ***************************************************************************/
22
23 #ifndef __LS_SIGNALUNIT_H__
24 #define __LS_SIGNALUNIT_H__
25
26 #include "../../common/ArrayList.h"
27
28
29 namespace LinuxSampler {
30
31 template<typename T>
32 class FixedArray {
33 public:
34 FixedArray(int capacity) {
35 iSize = 0;
36 iCapacity = capacity;
37 pData = new T[iCapacity];
38 }
39
40 ~FixedArray() {
41 delete pData;
42 pData = NULL;
43 }
44
45 inline int size() const { return iSize; }
46 inline int capacity() { return iCapacity; }
47
48 void add(T element) {
49 if (iSize >= iCapacity) throw Exception("Array out of bounds");
50 pData[iSize++] = element;
51 }
52
53
54 T increment() {
55 if (iSize >= iCapacity) throw Exception("Array out of bounds");
56 return pData[iSize++];
57 }
58
59 void clear() { iSize = 0; }
60
61 void copy(const FixedArray<T>& array) {
62 if(array.size() >= capacity()) throw Exception("Not enough space to copy array");
63 for (int i = 0; i < array.size(); i++) pData[i] = array[i];
64 iSize = array.size();
65 }
66
67 inline T& operator[](int idx) const {
68 return pData[idx];
69 }
70
71 private:
72 T* pData;
73 int iSize;
74 int iCapacity;
75 };
76
77 class SignalUnitRack;
78
79 /**
80 * A signal unit consist of internal signal generator (like envelope generator,
81 * low frequency oscillator, etc) with a number of generator parameters which
82 * influence/modulate/dynamically change the generator's signal in some manner.
83 * Each generator parameter (also called signal unit parameter) can receive
84 * signal from another signal unit and use this signal to dynamically change the
85 * behavior of the signal generator. In turn, the signal of this unit can be fed
86 * to another unit(s) and influence its parameters.
87 */
88 class SignalUnit {
89 public:
90
91 /**
92 * This class represents a parameter which will influence the signal
93 * unit to which it belongs in certain way.
94 * For example, let's say the signal unit is a low frequency oscillator
95 * with frequency 1Hz. If we want to modulate the LFO to start with 1Hz
96 * and increment its frequency to 5Hz in 1 second, we can add
97 * a parameter which signal source is an envelope
98 * generator with attack time of 1 second and coefficient 4. Thus, the
99 * normalized level of the EG will move from 0 to 1 in one second.
100 * On every time step (sample point) the normalized level
101 * will be multiplied by 4 (the parameter coefficient) and added to the
102 * LFO's frequency.
103 * So, after 1 second, the LFO frequency will be 1x4 + 1 = 5Hz.
104 * We can also add another parameter for modulating the LFO's pitch depth
105 * and so on.
106 */
107 class Parameter {
108 public:
109 SignalUnit* pUnit; /* The source unit whose output signal
110 * will modulate the parameter.
111 */
112
113 float Coeff; // The modulation coefficient
114
115
116 Parameter() : Coeff(1), pUnit(NULL) { }
117
118 /**
119 * @param unit The source unit used to influence this parameter.
120 * @param coeff The coefficient by which the normalized signal
121 * received from the source unit should be multiplied when a
122 * default transformation is done.
123 */
124 Parameter(SignalUnit* unit, float coeff = 1) {
125 pUnit = unit;
126 Coeff = coeff;
127 }
128
129 Parameter(const Parameter& Prm) { Copy(Prm); }
130 void operator=(const Parameter& Prm) { Copy(Prm); }
131
132 void Copy(const Parameter& Prm) {
133 if (this == &Prm) return;
134
135 pUnit = Prm.pUnit;
136 Coeff = Prm.Coeff;
137 }
138
139
140 /**
141 * Calculates the transformation for this parameter
142 * which should be applied to the parameter's value
143 * and multiplies by Coeff.
144 * This implementation of the method just multiplies by Coeff.
145 */
146 virtual float Transform(float val) {
147 return val * Coeff;
148 }
149
150 /**
151 * Gets the current value of the parameter.
152 * This implementation returns the current signal level of the
153 * source unit with applied transformation if the source unit is
154 * active, otherwise returns 1.
155 * Note that this method assume that pUnit is not NULL.
156 */
157 virtual float GetValue() {
158 return pUnit->Active() ? Transform(pUnit->GetLevel()) : 1.0f;
159 }
160 };
161
162
163 public:
164 ArrayList<SignalUnit::Parameter> Params; // The list of parameters which are modulating the signal unit
165
166 SignalUnit(SignalUnitRack* rack): pRack(rack), bActive(false), Level(0.0f), bCalculating(false), uiDelayTrigger(0) { }
167 SignalUnit(const SignalUnit& Unit): pRack(Unit.pRack) { Copy(Unit); }
168 void operator=(const SignalUnit& Unit) { Copy(Unit); }
169
170 void Copy(const SignalUnit& Unit) {
171 if (this == &Unit) return;
172
173 bActive = Unit.bActive;
174 Level = Unit.Level;
175 Params = Unit.Params;
176 uiDelayTrigger = Unit.uiDelayTrigger;
177 bCalculating = false;
178 }
179
180 /*
181 * Determines whether the unit is active.
182 * If the unit is not active, its level should be ignored.
183 * For endpoint unit this method determines whether
184 * the rendering should be stopped.
185 */
186 virtual bool Active() { return bActive; }
187
188 /**
189 * Override this method to process the current control change events.
190 * @param itEvent - iterator pointing to the event to be processed.
191 */
192 virtual void ProcessCCEvent(uint8_t Controller, uint8_t Value) { }
193
194 virtual void EnterReleaseStage() { }
195
196 virtual void CancelRelease() { }
197
198 /**
199 * Gets the normalized level of the unit for the current
200 * time step (sample point). The level is calculated if it's not
201 * calculated for the current step yet. Because the level depends on
202 * the parameters, their levels are calculated too.
203 */
204 virtual float GetLevel() {
205 if (!bRecalculate) return Level;
206
207 if (bCalculating) {
208 std::cerr << "SignalUnit: Loop detected. Aborted!";
209 return Level;
210 }
211
212 bCalculating = true;
213
214 for(int i = 0; i < Params.size(); i++) {
215 Params[i].GetValue();
216 }
217
218 bRecalculate = bCalculating = false;
219 return Level;
220 }
221
222 /**
223 * Will be called to increment the time with one sample point.
224 * The unit should recalculate or prepare for recalculation
225 * its current level on every call of this function.
226 * Note that it is not known whether all source signal unit's levels
227 * are recalculated before the call of this method. So, the calculations
228 * that depends on the unit's parameters should be postponed to
229 * the call of GetLevel().
230 */
231 virtual void Increment() { bRecalculate = true; }
232
233 /**
234 * Initializes and triggers the unit.
235 * Note that when a voice is the owner of a unit rack, all settings
236 * should be reset when this method is called, because the sampler
237 * is reusing the voice objects.
238 */
239 virtual void Trigger() = 0;
240
241 /**
242 * When the signal unit rack is triggered, it triggers all signal
243 * units it holds. If for some reason the triggering of a unit
244 * should be delayed, this method can be set to return non-zero value
245 * specifying the delay in time steps.
246 * Note that this is only a helper method and the implementation
247 * should be done manually.
248 */
249 virtual uint DelayTrigger() { return uiDelayTrigger; }
250
251 /**
252 * A helper method which checks whether the delay
253 * stage is finished.
254 */
255 bool DelayStage();
256
257 protected:
258 SignalUnitRack* const pRack;
259
260 bool bActive; /* Don't use it to check the active state of the unit!!!
261 * Use Active() instead! */
262 float Level;
263 bool bRecalculate; /* Determines whether the unit's level should be recalculated. */
264 bool bCalculating; /* Determines whether the unit is in process of calculating
265 * its level. Used for preventing infinite loops.
266 */
267
268 uint uiDelayTrigger; /* in time steps */
269
270 };
271
272 class EndpointSignalUnit: public SignalUnit {
273 public:
274 EndpointSignalUnit(SignalUnitRack* rack): SignalUnit(rack) { }
275
276 /**
277 * Gets the volume modulation value
278 * for the current time step (sample point).
279 */
280 virtual float GetVolume() = 0;
281
282 /**
283 * Gets the filter cutoff frequency modulation value
284 * for the current time step (sample point).
285 */
286 virtual float GetFilterCutoff() = 0;
287
288 /**
289 * Gets the pitch modulation value
290 * for the current time step (sample point).
291 */
292 virtual float GetPitch() = 0;
293
294 /**
295 * Gets the resonance modulation value
296 * for the current time step (sample point).
297 */
298 virtual float GetResonance() = 0;
299
300 /** Should return value in the range [-100, 100] (L <-> R) */
301 virtual float GetPan() = 0;
302
303 virtual float CalculateFilterCutoff(float cutoff) {
304 cutoff *= GetFilterCutoff();
305 return cutoff > 13500 ? 13500 : cutoff;
306 }
307
308 virtual float CalculatePitch(float pitch) {
309 return GetPitch() * pitch;
310 }
311
312 virtual float CalculateResonance(float res) {
313 return GetResonance() * res;
314 }
315
316 /** Should return value in the range [0, 127] (L <-> R) */
317 virtual uint8_t CaluclatePan(uint8_t pan) {
318 int p = pan + GetPan() * 0.63;
319 if (p < 0) return 0;
320 if (p > 127) return 127;
321 return p;
322 }
323 };
324
325 /**
326 * Continuous controller signal unit.
327 * The level of this unit corresponds to the controllers changes
328 * and their influences.
329 */
330 class CCSignalUnit: public SignalUnit {
331 public:
332 /** Listener which will be notified when the level of the unit is changed. */
333 class Listener {
334 public:
335 virtual void ValueChanged(CCSignalUnit* pUnit) = 0;
336 };
337
338 protected:
339 class CC {
340 public:
341 uint8_t Controller; ///< MIDI controller number.
342 uint8_t Value; ///< Controller Value.
343 short int Curve; ///< specifies the curve type
344 float Influence;
345
346 CC(uint8_t Controller = 0, float Influence = 0.0f, short int Curve = -1) {
347 this->Controller = Controller;
348 this->Value = 0;
349 this->Curve = Curve;
350 this->Influence = Influence;
351 }
352
353 CC(const CC& cc) { Copy(cc); }
354 void operator=(const CC& cc) { Copy(cc); }
355
356 void Copy(const CC& cc) {
357 Controller = cc.Controller;
358 Value = cc.Value;
359 Influence = cc.Influence;
360 Curve = cc.Curve;
361 }
362 };
363
364 FixedArray<CC> Ctrls; // The MIDI controllers which modulates this signal unit.
365 Listener* pListener;
366
367 public:
368
369 CCSignalUnit(SignalUnitRack* rack, Listener* l = NULL): SignalUnit(rack), Ctrls(128) {
370 pListener = l;
371 }
372
373 CCSignalUnit(const CCSignalUnit& Unit): SignalUnit(Unit.pRack), Ctrls(128) { Copy(Unit); }
374 void operator=(const CCSignalUnit& Unit) { Copy(Unit); }
375
376 void Copy(const CCSignalUnit& Unit) {
377 Ctrls.copy(Unit.Ctrls);
378 pListener = Unit.pListener;
379 SignalUnit::Copy(Unit);
380 }
381
382 void AddCC(uint8_t Controller, float Influence, short int Curve = -1) {
383 Ctrls.add(CC(Controller, Influence, Curve));
384 }
385
386 void RemoveAllCCs() {
387 Ctrls.clear();
388 }
389
390 virtual void Increment() { }
391
392 virtual void Trigger() {
393 Calculate();
394 bActive = Level != 0;
395 }
396
397 virtual void ProcessCCEvent(uint8_t Controller, uint8_t Value) {
398 bool recalculate = false;
399
400 for (int i = 0; i < Ctrls.size(); i++) {
401 if (Controller != Ctrls[i].Controller) continue;
402 if (Ctrls[i].Value == Value) continue;
403 Ctrls[i].Value = Value;
404 if (!bActive) bActive = true;
405 recalculate = true;
406 }
407
408 if (recalculate) {
409 Calculate();
410 if (pListener!= NULL) pListener->ValueChanged(this);
411 }
412 }
413
414 virtual void Calculate() {
415 Level = 0;
416 for (int i = 0; i < Ctrls.size(); i++) {
417 if (Ctrls[i].Value == 0) continue;
418 Level += (Ctrls[i].Value / 127.0f) * Ctrls[i].Influence;
419 }
420 }
421 };
422
423 } // namespace LinuxSampler
424
425 #endif /* __LS_SIGNALUNIT_H__ */

  ViewVC Help
Powered by ViewVC