/[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 2382 - (show annotations) (download) (as text)
Sun Dec 2 16:30:42 2012 UTC (11 years, 3 months ago) by persson
File MIME type: text/x-c++hdr
File size: 22998 byte(s)
* all engines: add pan CC value to instrument pan parameter before
  applying panning, instead of using two separate pan functions in
  series (#182)
* sfz parser: allow -200 to 200 for pan_oncc opcode (#182)
* gig engine: handle special case when pan parameter in gig file has
  max or min value
* CoreMIDI: fixed memory deallocation error

1 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2011 - 2012 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 CalculatePan(int 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 * Decreases the delay by Sample time steps.
328 * This method is used to delay the sample playback.
329 * While the endpoint unit is in delay stage the rack is not incremented.
330 */
331 void DecreaseDelay(uint Samples) {
332 uiDelayTrigger -= Samples;
333 }
334 };
335
336 /**
337 * Used to smooth out the parameter changes.
338 */
339 class Smoother {
340 protected:
341 uint timeSteps; // The number of time steps to reach the goal
342 uint currentTimeStep;
343 float goal;
344 float prev;
345
346 public:
347 /**
348 *
349 * @param time The time (in seconds) to reach the goal
350 * @param sampleRate
351 * @param val The initial value
352 */
353 void trigger(float time, float sampleRate, float val = 0) {
354 currentTimeStep = timeSteps = time * sampleRate;
355 prev = goal = val;
356 }
357
358 /**
359 * Set the current value, which the smoother will not smooth out.
360 * If you want the value to be smoothen out, use update() instead.
361 */
362 void setValue( float val) {
363 currentTimeStep = timeSteps;
364 prev = goal = val;
365 }
366
367 /**
368 * Sets a new value. The render function will return
369 * values gradually approaching this value.
370 */
371 void update(float val) {
372 if (val == goal) return;
373
374 prev = prev + (goal - prev) * (currentTimeStep / (float)timeSteps);
375 goal = val;
376 currentTimeStep = 0;
377 }
378
379 float render() {
380 if (currentTimeStep >= timeSteps) return goal;
381 return prev + (goal - prev) * (currentTimeStep++ / (float)timeSteps);
382 }
383
384 bool isSmoothingOut() { return currentTimeStep < timeSteps; }
385
386 float getGoal() { return goal; }
387 };
388
389 /**
390 * Continuous controller signal unit.
391 * The level of this unit corresponds to the controllers changes
392 * and their influences.
393 */
394 class CCSignalUnit: public SignalUnit {
395 public:
396 /** Listener which will be notified when the level of the unit is changed. */
397 class Listener {
398 public:
399 virtual void ValueChanged(CCSignalUnit* pUnit) = 0;
400 };
401
402 class CC {
403 public:
404 uint8_t Controller; ///< MIDI controller number.
405 uint8_t Value; ///< Controller Value.
406 short int Curve; ///< specifies the curve type
407 float Influence;
408 float Step;
409
410 Smoother* pSmoother;
411
412 CC (
413 uint8_t Controller = 0,
414 float Influence = 0.0f,
415 short int Curve = -1,
416 Smoother* pSmoother = NULL,
417 float Step = 0
418 ) {
419 this->Controller = Controller;
420 this->Value = 0;
421 this->Curve = Curve;
422 this->Influence = Influence;
423 this->pSmoother = pSmoother;
424 this->Step = Step;
425 }
426
427 CC(const CC& cc) { Copy(cc); }
428 void operator=(const CC& cc) { Copy(cc); }
429
430 void Copy(const CC& cc) {
431 Controller = cc.Controller;
432 Value = cc.Value;
433 Influence = cc.Influence;
434 Curve = cc.Curve;
435 pSmoother = cc.pSmoother;
436 Step = cc.Step;
437 }
438 };
439
440 protected:
441 RTList<CC>* pCtrls; // The MIDI controllers which modulates this signal unit.
442 Listener* pListener;
443 bool hasSmoothCtrls; // determines whether there are smooth controllers (used for optimization)
444 bool isSmoothingOut; // determines whether there is a CC which is in process of smoothing out (used for optimization)
445
446 public:
447
448 CCSignalUnit(SignalUnitRack* rack, Listener* l = NULL): SignalUnit(rack), pCtrls(NULL) {
449 pListener = l;
450 hasSmoothCtrls = isSmoothingOut = false;
451 }
452
453 CCSignalUnit(const CCSignalUnit& Unit): SignalUnit(Unit.pRack), pCtrls(NULL) { Copy(Unit); }
454 void operator=(const CCSignalUnit& Unit) { Copy(Unit); }
455
456 virtual ~CCSignalUnit() {
457 if (pCtrls != NULL) delete pCtrls;
458 }
459
460 void Copy(const CCSignalUnit& Unit) {
461 if (pCtrls != NULL) delete pCtrls;
462 pCtrls = new RTList<CC>(*(Unit.pCtrls));
463 if (pCtrls->poolIsEmpty() && pCtrls->count() < Unit.pCtrls->count()) {
464 std::cerr << "Maximum number of CC reached!" << std::endl;
465 }
466
467 pListener = Unit.pListener;
468 hasSmoothCtrls = Unit.hasSmoothCtrls;
469 isSmoothingOut = Unit.isSmoothingOut;
470 SignalUnit::Copy(Unit);
471 }
472
473 virtual void InitCCList(Pool<CC>* pCCPool, Pool<Smoother>* pSmootherPool) {
474 if (pCtrls != NULL) delete pCtrls;
475 pCtrls = new RTList<CC>(pCCPool);
476 }
477
478 void AddCC(uint8_t Controller, float Influence, short int Curve = -1, Smoother* pSmoother = NULL, float Step = 0) {
479 if(pCtrls->poolIsEmpty()) {
480 std::cerr << "Maximum number of CC reached!" << std::endl;
481 return;
482 }
483 *(pCtrls->allocAppend()) = CC(Controller, Influence, Curve, pSmoother, Step);
484 if (pSmoother != NULL) hasSmoothCtrls = true;
485 }
486
487 virtual void RemoveAllCCs() { pCtrls->clear(); }
488
489 int GetCCCount() { return pCtrls->count(); }
490
491 bool HasCCs() { return GetCCCount() > 0; }
492
493 virtual void Increment() {
494 if (hasSmoothCtrls && isSmoothingOut) Calculate();
495 }
496
497 virtual void Trigger() {
498 Calculate();
499 bActive = Level != 0;
500 }
501
502 virtual void ProcessCCEvent(uint8_t Controller, uint8_t Value) {
503 bool recalculate = false;
504
505 RTList<CC>::Iterator ctrl = pCtrls->first();
506 RTList<CC>::Iterator end = pCtrls->end();
507 for(; ctrl != end; ++ctrl) {
508 if (Controller != (*ctrl).Controller) continue;
509 if ((*ctrl).Value == Value) continue;
510
511 (*ctrl).Value = Value;
512
513 if ((*ctrl).Step > 0 && (*ctrl).pSmoother != NULL) {
514 float oldGoal = (*ctrl).pSmoother->getGoal();
515 float newGoal = Normalize(Value, (*ctrl).Curve) * (*ctrl).Influence;
516 newGoal = ((int) (newGoal / (*ctrl).Step)) * (*ctrl).Step;
517 if (oldGoal != newGoal) (*ctrl).pSmoother->update(newGoal);
518 }
519
520 if ((*ctrl).pSmoother != NULL && (*ctrl).Step <= 0) (*ctrl).pSmoother->update(Value);
521 if (!bActive) bActive = true;
522 recalculate = true;
523 }
524
525 if (!(hasSmoothCtrls && isSmoothingOut) && recalculate) Calculate();
526 }
527
528 virtual void Calculate() {
529 float l = 0;
530 isSmoothingOut = false;
531 RTList<CC>::Iterator ctrl = pCtrls->first();
532 RTList<CC>::Iterator end = pCtrls->end();
533 for(; ctrl != end; ++ctrl) {
534 if ((*ctrl).pSmoother == NULL) {
535 float val = Normalize((*ctrl).Value, (*ctrl).Curve) * (*ctrl).Influence;
536 if ((*ctrl).Step > 0) val = ( (int)(val / (*ctrl).Step) ) * (*ctrl).Step;
537 l += val;
538 } else {
539 if ((*ctrl).pSmoother->isSmoothingOut()) isSmoothingOut = true;
540
541 if ((*ctrl).Step > 0) {
542 l += (*ctrl).pSmoother->render();
543 } else {
544 l += Normalize((*ctrl).pSmoother->render(), (*ctrl).Curve) * (*ctrl).Influence;
545 }
546 }
547 }
548 if (Level != l) {
549 Level = l;
550 if (pListener != NULL) pListener->ValueChanged(this);
551 }
552 }
553
554 virtual float Normalize(uint8_t val, short int curve = -1) {
555 return val / 127.0f;
556 }
557 };
558
559 } // namespace LinuxSampler
560
561 #endif /* __LS_SIGNALUNIT_H__ */

  ViewVC Help
Powered by ViewVC