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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2251 - (hide annotations) (download) (as text)
Sat Aug 20 10:38:31 2011 UTC (12 years, 8 months ago) by iliev
File MIME type: text/x-c++hdr
File size: 21369 byte(s)
* sfz engine: *lfo_freqccN wasn't working when the respective *lfo_freq
  was not set or was set to zero
* sfz engine: lfoN_freq_onccX wasn't working when lfoN_freq
  was not set or was set to zero

1 iliev 2205 /***************************************************************************
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 iliev 2244 #include "../../common/Pool.h"
28 iliev 2205
29    
30     namespace LinuxSampler {
31    
32 iliev 2224 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 iliev 2234 delete[] pData;
43 iliev 2224 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 iliev 2238 T& increment() {
56 iliev 2224 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 iliev 2217 class SignalUnitRack;
79    
80 iliev 2205 /**
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 iliev 2207 * 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 iliev 2205 * 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 iliev 2207 * a parameter which signal source is an envelope
99 iliev 2205 * 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 iliev 2207 SignalUnit* pUnit; /* The source unit whose output signal
111     * will modulate the parameter.
112 iliev 2205 */
113    
114 iliev 2207 float Coeff; // The modulation coefficient
115 iliev 2205
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 iliev 2218 void Copy(const Parameter& Prm) {
134 iliev 2205 if (this == &Prm) return;
135    
136     pUnit = Prm.pUnit;
137     Coeff = Prm.Coeff;
138     }
139    
140    
141     /**
142 iliev 2207 * Calculates the transformation for this parameter
143 iliev 2205 * 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 iliev 2207 * 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 iliev 2205 */
158     virtual float GetValue() {
159 iliev 2207 return pUnit->Active() ? Transform(pUnit->GetLevel()) : 1.0f;
160 iliev 2205 }
161     };
162    
163    
164     public:
165     ArrayList<SignalUnit::Parameter> Params; // The list of parameters which are modulating the signal unit
166    
167 iliev 2251 SignalUnit(SignalUnitRack* rack): pRack(rack), bActive(false), Level(0.0f), bRecalculate(true), bCalculating(false), uiDelayTrigger(0) { }
168 iliev 2217 SignalUnit(const SignalUnit& Unit): pRack(Unit.pRack) { Copy(Unit); }
169 iliev 2205 void operator=(const SignalUnit& Unit) { Copy(Unit); }
170 iliev 2244 virtual ~SignalUnit() { }
171 iliev 2205
172 iliev 2218 void Copy(const SignalUnit& Unit) {
173 iliev 2205 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 iliev 2251 if (Params.empty() || !bRecalculate) return Level;
208 iliev 2205
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 iliev 2207 /**
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 iliev 2205 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 iliev 2217 /**
254     * A helper method which checks whether the delay
255     * stage is finished.
256     */
257     bool DelayStage();
258    
259 iliev 2205 protected:
260 iliev 2217 SignalUnitRack* const pRack;
261    
262 iliev 2205 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 iliev 2217 class EndpointSignalUnit: public SignalUnit {
275 iliev 2205 public:
276 iliev 2217 EndpointSignalUnit(SignalUnitRack* rack): SignalUnit(rack) { }
277    
278 iliev 2205 /**
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 iliev 2219 /** Should return value in the range [-100, 100] (L <-> R) */
303     virtual float GetPan() = 0;
304    
305 iliev 2217 virtual float CalculateFilterCutoff(float cutoff) {
306     cutoff *= GetFilterCutoff();
307     return cutoff > 13500 ? 13500 : cutoff;
308     }
309 iliev 2205
310 iliev 2217 virtual float CalculatePitch(float pitch) {
311     return GetPitch() * pitch;
312 iliev 2205 }
313    
314 iliev 2217 virtual float CalculateResonance(float res) {
315     return GetResonance() * res;
316 iliev 2205 }
317 iliev 2219
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 iliev 2205 };
326    
327     /**
328 iliev 2232 * 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     uint8_t goal; // 0 - 127
335     uint8_t prev; // 0 - 127
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, uint8_t 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( uint8_t 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(uint8_t val) {
363     if (val == goal) return;
364    
365     prev = prev + (goal - prev) * (currentTimeStep / (float)timeSteps);
366     goal = val;
367     currentTimeStep = 0;
368     }
369    
370     uint8_t 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    
378     /**
379 iliev 2205 * Continuous controller signal unit.
380 iliev 2224 * The level of this unit corresponds to the controllers changes
381     * and their influences.
382 iliev 2205 */
383 iliev 2217 class CCSignalUnit: public SignalUnit {
384 iliev 2227 public:
385     /** Listener which will be notified when the level of the unit is changed. */
386     class Listener {
387     public:
388     virtual void ValueChanged(CCSignalUnit* pUnit) = 0;
389     };
390    
391 iliev 2224 class CC {
392     public:
393 iliev 2230 uint8_t Controller; ///< MIDI controller number.
394     uint8_t Value; ///< Controller Value.
395     short int Curve; ///< specifies the curve type
396     float Influence;
397 iliev 2224
398 iliev 2232 Smoother* pSmoother;
399    
400     CC(uint8_t Controller = 0, float Influence = 0.0f, short int Curve = -1, Smoother* pSmoother = NULL) {
401 iliev 2224 this->Controller = Controller;
402     this->Value = 0;
403 iliev 2230 this->Curve = Curve;
404 iliev 2224 this->Influence = Influence;
405 iliev 2232 this->pSmoother = pSmoother;
406 iliev 2224 }
407    
408     CC(const CC& cc) { Copy(cc); }
409     void operator=(const CC& cc) { Copy(cc); }
410    
411     void Copy(const CC& cc) {
412     Controller = cc.Controller;
413     Value = cc.Value;
414     Influence = cc.Influence;
415 iliev 2230 Curve = cc.Curve;
416 iliev 2232 pSmoother = cc.pSmoother;
417 iliev 2224 }
418     };
419    
420 iliev 2244 protected:
421     RTList<CC>* pCtrls; // The MIDI controllers which modulates this signal unit.
422 iliev 2227 Listener* pListener;
423 iliev 2232 bool hasSmoothCtrls; // determines whether there are smooth controllers (used for optimization)
424     bool isSmoothingOut; // determines whether there is a CC which is in process of smoothing out (used for optimization)
425 iliev 2205
426     public:
427 iliev 2227
428 iliev 2244 CCSignalUnit(SignalUnitRack* rack, Listener* l = NULL): SignalUnit(rack), pCtrls(NULL) {
429 iliev 2227 pListener = l;
430 iliev 2232 hasSmoothCtrls = isSmoothingOut = false;
431 iliev 2205 }
432    
433 iliev 2244 CCSignalUnit(const CCSignalUnit& Unit): SignalUnit(Unit.pRack), pCtrls(NULL) { Copy(Unit); }
434 iliev 2218 void operator=(const CCSignalUnit& Unit) { Copy(Unit); }
435 iliev 2205
436 iliev 2244 virtual ~CCSignalUnit() {
437     if (pCtrls != NULL) delete pCtrls;
438     }
439    
440 iliev 2218 void Copy(const CCSignalUnit& Unit) {
441 iliev 2244 if (pCtrls != NULL) delete pCtrls;
442     pCtrls = new RTList<CC>(*(Unit.pCtrls));
443     if (pCtrls->poolIsEmpty() && pCtrls->count() < Unit.pCtrls->count()) {
444     std::cerr << "Maximum number of CC reached!" << std::endl;
445     }
446    
447 iliev 2227 pListener = Unit.pListener;
448 iliev 2232 hasSmoothCtrls = Unit.hasSmoothCtrls;
449     isSmoothingOut = Unit.isSmoothingOut;
450 iliev 2218 SignalUnit::Copy(Unit);
451 iliev 2205 }
452    
453 iliev 2244 virtual void InitCCList(Pool<CC>* pCCPool, Pool<Smoother>* pSmootherPool) {
454     if (pCtrls != NULL) delete pCtrls;
455     pCtrls = new RTList<CC>(pCCPool);
456     }
457    
458 iliev 2232 void AddCC(uint8_t Controller, float Influence, short int Curve = -1, Smoother* pSmoother = NULL) {
459 iliev 2244 if(pCtrls->poolIsEmpty()) {
460     std::cerr << "Maximum number of CC reached!" << std::endl;
461 iliev 2238 return;
462     }
463 iliev 2244 *(pCtrls->allocAppend()) = CC(Controller, Influence, Curve, pSmoother);
464 iliev 2232 if (pSmoother != NULL) hasSmoothCtrls = true;
465 iliev 2224 }
466    
467 iliev 2244 virtual void RemoveAllCCs() { pCtrls->clear(); }
468 iliev 2224
469 iliev 2244 int GetCCCount() { return pCtrls->count(); }
470 iliev 2235
471 iliev 2232 virtual void Increment() {
472     if (hasSmoothCtrls && isSmoothingOut) Calculate();
473     }
474 iliev 2205
475 iliev 2224 virtual void Trigger() {
476     Calculate();
477     bActive = Level != 0;
478     }
479    
480 iliev 2205 virtual void ProcessCCEvent(uint8_t Controller, uint8_t Value) {
481 iliev 2224 bool recalculate = false;
482 iliev 2205
483 iliev 2244 RTList<CC>::Iterator ctrl = pCtrls->first();
484     RTList<CC>::Iterator end = pCtrls->end();
485     for(; ctrl != end; ++ctrl) {
486     if (Controller != (*ctrl).Controller) continue;
487     if ((*ctrl).Value == Value) continue;
488     (*ctrl).Value = Value;
489     if ((*ctrl).pSmoother != NULL) (*ctrl).pSmoother->update(Value);
490 iliev 2224 if (!bActive) bActive = true;
491     recalculate = true;
492     }
493 iliev 2205
494 iliev 2232 if (!(hasSmoothCtrls && isSmoothingOut) && recalculate) Calculate();
495 iliev 2205 }
496 iliev 2224
497     virtual void Calculate() {
498 iliev 2232 float l = 0;
499     isSmoothingOut = false;
500 iliev 2244 RTList<CC>::Iterator ctrl = pCtrls->first();
501     RTList<CC>::Iterator end = pCtrls->end();
502     for(; ctrl != end; ++ctrl) {
503     if ((*ctrl).pSmoother == NULL) {
504     l += Normalize((*ctrl).Value, (*ctrl).Curve) * (*ctrl).Influence;
505 iliev 2232 } else {
506 iliev 2244 if ((*ctrl).pSmoother->isSmoothingOut()) isSmoothingOut = true;
507     l += Normalize((*ctrl).pSmoother->render(), (*ctrl).Curve) * (*ctrl).Influence;
508 iliev 2232 }
509 iliev 2224 }
510 iliev 2232 if (Level != l) {
511     Level = l;
512     if (pListener != NULL) pListener->ValueChanged(this);
513     }
514 iliev 2224 }
515 iliev 2232
516     virtual float Normalize(uint8_t val, short int curve = -1) {
517     return val / 127.0f;
518     }
519 iliev 2205 };
520    
521     } // namespace LinuxSampler
522    
523     #endif /* __LS_SIGNALUNIT_H__ */

  ViewVC Help
Powered by ViewVC