/[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 2264 - (show annotations) (download) (as text)
Mon Aug 22 10:00:01 2011 UTC (12 years, 7 months ago) by iliev
File MIME type: text/x-c++hdr
File size: 22577 byte(s)
* sfz engine: implemented opcodes pitch_onccN,
  pitch_curveccN, pitch_smoothccN, pitch_stepccN

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 #include "../../common/Pool.h"
28
29
30 namespace LinuxSampler {
31
32 template<typename T>
33 class FixedArray {
34 public:
35 FixedArray(int capacity) {
36 iSize = 0;
37 iCapacity = capacity;
38 pData = new T[iCapacity];
39 }
40
41 ~FixedArray() {
42 delete[] pData;
43 pData = NULL;
44 }
45
46 inline int size() const { return iSize; }
47 inline int capacity() { return iCapacity; }
48
49 void add(T element) {
50 if (iSize >= iCapacity) throw Exception("Array out of bounds");
51 pData[iSize++] = element;
52 }
53
54
55 T& increment() {
56 if (iSize >= iCapacity) throw Exception("Array out of bounds");
57 return pData[iSize++];
58 }
59
60 void clear() { iSize = 0; }
61
62 void copy(const FixedArray<T>& array) {
63 if(array.size() >= capacity()) throw Exception("Not enough space to copy array");
64 for (int i = 0; i < array.size(); i++) pData[i] = array[i];
65 iSize = array.size();
66 }
67
68 inline T& operator[](int idx) const {
69 return pData[idx];
70 }
71
72 private:
73 T* pData;
74 int iSize;
75 int iCapacity;
76 };
77
78 class SignalUnitRack;
79
80 /**
81 * A signal unit consist of internal signal generator (like envelope generator,
82 * low frequency oscillator, etc) with a number of generator parameters which
83 * influence/modulate/dynamically change the generator's signal in some manner.
84 * Each generator parameter (also called signal unit parameter) can receive
85 * signal from another signal unit and use this signal to dynamically change the
86 * behavior of the signal generator. In turn, the signal of this unit can be fed
87 * to another unit(s) and influence its parameters.
88 */
89 class SignalUnit {
90 public:
91
92 /**
93 * This class represents a parameter which will influence the signal
94 * unit to which it belongs in certain way.
95 * For example, let's say the signal unit is a low frequency oscillator
96 * with frequency 1Hz. If we want to modulate the LFO to start with 1Hz
97 * and increment its frequency to 5Hz in 1 second, we can add
98 * a parameter which signal source is an envelope
99 * generator with attack time of 1 second and coefficient 4. Thus, the
100 * normalized level of the EG will move from 0 to 1 in one second.
101 * On every time step (sample point) the normalized level
102 * will be multiplied by 4 (the parameter coefficient) and added to the
103 * LFO's frequency.
104 * So, after 1 second, the LFO frequency will be 1x4 + 1 = 5Hz.
105 * We can also add another parameter for modulating the LFO's pitch depth
106 * and so on.
107 */
108 class Parameter {
109 public:
110 SignalUnit* pUnit; /* The source unit whose output signal
111 * will modulate the parameter.
112 */
113
114 float Coeff; // The modulation coefficient
115
116
117 Parameter() : Coeff(1), pUnit(NULL) { }
118
119 /**
120 * @param unit The source unit used to influence this parameter.
121 * @param coeff The coefficient by which the normalized signal
122 * received from the source unit should be multiplied when a
123 * default transformation is done.
124 */
125 Parameter(SignalUnit* unit, float coeff = 1) {
126 pUnit = unit;
127 Coeff = coeff;
128 }
129
130 Parameter(const Parameter& Prm) { Copy(Prm); }
131 void operator=(const Parameter& Prm) { Copy(Prm); }
132
133 void Copy(const Parameter& Prm) {
134 if (this == &Prm) return;
135
136 pUnit = Prm.pUnit;
137 Coeff = Prm.Coeff;
138 }
139
140
141 /**
142 * Calculates the transformation for this parameter
143 * which should be applied to the parameter's value
144 * and multiplies by Coeff.
145 * This implementation of the method just multiplies by Coeff.
146 */
147 virtual float Transform(float val) {
148 return val * Coeff;
149 }
150
151 /**
152 * Gets the current value of the parameter.
153 * This implementation returns the current signal level of the
154 * source unit with applied transformation if the source unit is
155 * active, otherwise returns 1.
156 * Note that this method assume that pUnit is not NULL.
157 */
158 virtual float GetValue() {
159 return pUnit->Active() ? Transform(pUnit->GetLevel()) : 1.0f;
160 }
161 };
162
163
164 public:
165 ArrayList<SignalUnit::Parameter> Params; // The list of parameters which are modulating the signal unit
166
167 SignalUnit(SignalUnitRack* rack): pRack(rack), bActive(false), Level(0.0f), bRecalculate(true), bCalculating(false), uiDelayTrigger(0) { }
168 SignalUnit(const SignalUnit& Unit): pRack(Unit.pRack) { Copy(Unit); }
169 void operator=(const SignalUnit& Unit) { Copy(Unit); }
170 virtual ~SignalUnit() { }
171
172 void Copy(const SignalUnit& Unit) {
173 if (this == &Unit) return;
174
175 bActive = Unit.bActive;
176 Level = Unit.Level;
177 Params = Unit.Params;
178 uiDelayTrigger = Unit.uiDelayTrigger;
179 bCalculating = false;
180 }
181
182 /*
183 * Determines whether the unit is active.
184 * If the unit is not active, its level should be ignored.
185 * For endpoint unit this method determines whether
186 * the rendering should be stopped.
187 */
188 virtual bool Active() { return bActive; }
189
190 /**
191 * Override this method to process the current control change events.
192 * @param itEvent - iterator pointing to the event to be processed.
193 */
194 virtual void ProcessCCEvent(uint8_t Controller, uint8_t Value) { }
195
196 virtual void EnterReleaseStage() { }
197
198 virtual void CancelRelease() { }
199
200 /**
201 * Gets the normalized level of the unit for the current
202 * time step (sample point). The level is calculated if it's not
203 * calculated for the current step yet. Because the level depends on
204 * the parameters, their levels are calculated too.
205 */
206 virtual float GetLevel() {
207 if (Params.empty() || !bRecalculate) return Level;
208
209 if (bCalculating) {
210 std::cerr << "SignalUnit: Loop detected. Aborted!";
211 return Level;
212 }
213
214 bCalculating = true;
215
216 for(int i = 0; i < Params.size(); i++) {
217 Params[i].GetValue();
218 }
219
220 bRecalculate = bCalculating = false;
221 return Level;
222 }
223
224 /**
225 * Will be called to increment the time with one sample point.
226 * The unit should recalculate or prepare for recalculation
227 * its current level on every call of this function.
228 * Note that it is not known whether all source signal unit's levels
229 * are recalculated before the call of this method. So, the calculations
230 * that depends on the unit's parameters should be postponed to
231 * the call of GetLevel().
232 */
233 virtual void Increment() { bRecalculate = true; }
234
235 /**
236 * Initializes and triggers the unit.
237 * Note that when a voice is the owner of a unit rack, all settings
238 * should be reset when this method is called, because the sampler
239 * is reusing the voice objects.
240 */
241 virtual void Trigger() = 0;
242
243 /**
244 * When the signal unit rack is triggered, it triggers all signal
245 * units it holds. If for some reason the triggering of a unit
246 * should be delayed, this method can be set to return non-zero value
247 * specifying the delay in time steps.
248 * Note that this is only a helper method and the implementation
249 * should be done manually.
250 */
251 virtual uint DelayTrigger() { return uiDelayTrigger; }
252
253 /**
254 * A helper method which checks whether the delay
255 * stage is finished.
256 */
257 bool DelayStage();
258
259 protected:
260 SignalUnitRack* const pRack;
261
262 bool bActive; /* Don't use it to check the active state of the unit!!!
263 * Use Active() instead! */
264 float Level;
265 bool bRecalculate; /* Determines whether the unit's level should be recalculated. */
266 bool bCalculating; /* Determines whether the unit is in process of calculating
267 * its level. Used for preventing infinite loops.
268 */
269
270 uint uiDelayTrigger; /* in time steps */
271
272 };
273
274 class EndpointSignalUnit: public SignalUnit {
275 public:
276 EndpointSignalUnit(SignalUnitRack* rack): SignalUnit(rack) { }
277
278 /**
279 * Gets the volume modulation value
280 * for the current time step (sample point).
281 */
282 virtual float GetVolume() = 0;
283
284 /**
285 * Gets the filter cutoff frequency modulation value
286 * for the current time step (sample point).
287 */
288 virtual float GetFilterCutoff() = 0;
289
290 /**
291 * Gets the pitch modulation value
292 * for the current time step (sample point).
293 */
294 virtual float GetPitch() = 0;
295
296 /**
297 * Gets the resonance modulation value
298 * for the current time step (sample point).
299 */
300 virtual float GetResonance() = 0;
301
302 /** Should return value in the range [-100, 100] (L <-> R) */
303 virtual float GetPan() = 0;
304
305 virtual float CalculateFilterCutoff(float cutoff) {
306 cutoff *= GetFilterCutoff();
307 return cutoff > 13500 ? 13500 : cutoff;
308 }
309
310 virtual float CalculatePitch(float pitch) {
311 return GetPitch() * pitch;
312 }
313
314 virtual float CalculateResonance(float res) {
315 return GetResonance() * res;
316 }
317
318 /** Should return value in the range [0, 127] (L <-> R) */
319 virtual uint8_t CaluclatePan(uint8_t pan) {
320 int p = pan + GetPan() * 0.63;
321 if (p < 0) return 0;
322 if (p > 127) return 127;
323 return p;
324 }
325 };
326
327 /**
328 * Used to smooth out the parameter changes.
329 */
330 class Smoother {
331 protected:
332 uint timeSteps; // The number of time steps to reach the goal
333 uint currentTimeStep;
334 float goal;
335 float prev;
336
337 public:
338 /**
339 *
340 * @param time The time (in seconds) to reach the goal
341 * @param sampleRate
342 * @param val The initial value
343 */
344 void trigger(float time, float sampleRate, float val = 0) {
345 currentTimeStep = timeSteps = time * sampleRate;
346 prev = goal = val;
347 }
348
349 /**
350 * Set the current value, which the smoother will not smooth out.
351 * If you want the value to be smoothen out, use update() instead.
352 */
353 void setValue( float val) {
354 currentTimeStep = timeSteps;
355 prev = goal = val;
356 }
357
358 /**
359 * Sets a new value. The render function will return
360 * values gradually approaching this value.
361 */
362 void update(float val) {
363 if (val == goal) return;
364
365 prev = prev + (goal - prev) * (currentTimeStep / (float)timeSteps);
366 goal = val;
367 currentTimeStep = 0;
368 }
369
370 float render() {
371 if (currentTimeStep >= timeSteps) return goal;
372 return prev + (goal - prev) * (currentTimeStep++ / (float)timeSteps);
373 }
374
375 bool isSmoothingOut() { return currentTimeStep < timeSteps; }
376
377 float getGoal() { return goal; }
378 };
379
380 /**
381 * Continuous controller signal unit.
382 * The level of this unit corresponds to the controllers changes
383 * and their influences.
384 */
385 class CCSignalUnit: public SignalUnit {
386 public:
387 /** Listener which will be notified when the level of the unit is changed. */
388 class Listener {
389 public:
390 virtual void ValueChanged(CCSignalUnit* pUnit) = 0;
391 };
392
393 class CC {
394 public:
395 uint8_t Controller; ///< MIDI controller number.
396 uint8_t Value; ///< Controller Value.
397 short int Curve; ///< specifies the curve type
398 float Influence;
399 float Step;
400
401 Smoother* pSmoother;
402
403 CC (
404 uint8_t Controller = 0,
405 float Influence = 0.0f,
406 short int Curve = -1,
407 Smoother* pSmoother = NULL,
408 float Step = 0
409 ) {
410 this->Controller = Controller;
411 this->Value = 0;
412 this->Curve = Curve;
413 this->Influence = Influence;
414 this->pSmoother = pSmoother;
415 this->Step = Step;
416 }
417
418 CC(const CC& cc) { Copy(cc); }
419 void operator=(const CC& cc) { Copy(cc); }
420
421 void Copy(const CC& cc) {
422 Controller = cc.Controller;
423 Value = cc.Value;
424 Influence = cc.Influence;
425 Curve = cc.Curve;
426 pSmoother = cc.pSmoother;
427 Step = cc.Step;
428 }
429 };
430
431 protected:
432 RTList<CC>* pCtrls; // The MIDI controllers which modulates this signal unit.
433 Listener* pListener;
434 bool hasSmoothCtrls; // determines whether there are smooth controllers (used for optimization)
435 bool isSmoothingOut; // determines whether there is a CC which is in process of smoothing out (used for optimization)
436
437 public:
438
439 CCSignalUnit(SignalUnitRack* rack, Listener* l = NULL): SignalUnit(rack), pCtrls(NULL) {
440 pListener = l;
441 hasSmoothCtrls = isSmoothingOut = false;
442 }
443
444 CCSignalUnit(const CCSignalUnit& Unit): SignalUnit(Unit.pRack), pCtrls(NULL) { Copy(Unit); }
445 void operator=(const CCSignalUnit& Unit) { Copy(Unit); }
446
447 virtual ~CCSignalUnit() {
448 if (pCtrls != NULL) delete pCtrls;
449 }
450
451 void Copy(const CCSignalUnit& Unit) {
452 if (pCtrls != NULL) delete pCtrls;
453 pCtrls = new RTList<CC>(*(Unit.pCtrls));
454 if (pCtrls->poolIsEmpty() && pCtrls->count() < Unit.pCtrls->count()) {
455 std::cerr << "Maximum number of CC reached!" << std::endl;
456 }
457
458 pListener = Unit.pListener;
459 hasSmoothCtrls = Unit.hasSmoothCtrls;
460 isSmoothingOut = Unit.isSmoothingOut;
461 SignalUnit::Copy(Unit);
462 }
463
464 virtual void InitCCList(Pool<CC>* pCCPool, Pool<Smoother>* pSmootherPool) {
465 if (pCtrls != NULL) delete pCtrls;
466 pCtrls = new RTList<CC>(pCCPool);
467 }
468
469 void AddCC(uint8_t Controller, float Influence, short int Curve = -1, Smoother* pSmoother = NULL, float Step = 0) {
470 if(pCtrls->poolIsEmpty()) {
471 std::cerr << "Maximum number of CC reached!" << std::endl;
472 return;
473 }
474 *(pCtrls->allocAppend()) = CC(Controller, Influence, Curve, pSmoother, Step);
475 if (pSmoother != NULL) hasSmoothCtrls = true;
476 }
477
478 virtual void RemoveAllCCs() { pCtrls->clear(); }
479
480 int GetCCCount() { return pCtrls->count(); }
481
482 virtual void Increment() {
483 if (hasSmoothCtrls && isSmoothingOut) Calculate();
484 }
485
486 virtual void Trigger() {
487 Calculate();
488 bActive = Level != 0;
489 }
490
491 virtual void ProcessCCEvent(uint8_t Controller, uint8_t Value) {
492 bool recalculate = false;
493
494 RTList<CC>::Iterator ctrl = pCtrls->first();
495 RTList<CC>::Iterator end = pCtrls->end();
496 for(; ctrl != end; ++ctrl) {
497 if (Controller != (*ctrl).Controller) continue;
498 if ((*ctrl).Value == Value) continue;
499
500 (*ctrl).Value = Value;
501
502 if ((*ctrl).Step > 0 && (*ctrl).pSmoother != NULL) {
503 float oldGoal = (*ctrl).pSmoother->getGoal();
504 float newGoal = Normalize(Value, (*ctrl).Curve) * (*ctrl).Influence;
505 newGoal = ((int) (newGoal / (*ctrl).Step)) * (*ctrl).Step;
506 if (oldGoal != newGoal) (*ctrl).pSmoother->update(newGoal);
507 }
508
509 if ((*ctrl).pSmoother != NULL && (*ctrl).Step <= 0) (*ctrl).pSmoother->update(Value);
510 if (!bActive) bActive = true;
511 recalculate = true;
512 }
513
514 if (!(hasSmoothCtrls && isSmoothingOut) && recalculate) Calculate();
515 }
516
517 virtual void Calculate() {
518 float l = 0;
519 isSmoothingOut = false;
520 RTList<CC>::Iterator ctrl = pCtrls->first();
521 RTList<CC>::Iterator end = pCtrls->end();
522 for(; ctrl != end; ++ctrl) {
523 if ((*ctrl).pSmoother == NULL) {
524 float val = Normalize((*ctrl).Value, (*ctrl).Curve) * (*ctrl).Influence;
525 if ((*ctrl).Step > 0) val = ( (int)(val / (*ctrl).Step) ) * (*ctrl).Step;
526 l += val;
527 } else {
528 if ((*ctrl).pSmoother->isSmoothingOut()) isSmoothingOut = true;
529
530 if ((*ctrl).Step > 0) {
531 l += (*ctrl).pSmoother->render();
532 } else {
533 l += Normalize((*ctrl).pSmoother->render(), (*ctrl).Curve) * (*ctrl).Influence;
534 }
535 }
536 }
537 if (Level != l) {
538 Level = l;
539 if (pListener != NULL) pListener->ValueChanged(this);
540 }
541 }
542
543 virtual float Normalize(uint8_t val, short int curve = -1) {
544 return val / 127.0f;
545 }
546 };
547
548 } // namespace LinuxSampler
549
550 #endif /* __LS_SIGNALUNIT_H__ */

  ViewVC Help
Powered by ViewVC