/[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 2232 - (hide annotations) (download) (as text)
Mon Aug 8 13:40:04 2011 UTC (12 years, 8 months ago) by iliev
File MIME type: text/x-c++hdr
File size: 20216 byte(s)
* sfz engine: implemented opcode volume_smoothccN

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    
28    
29     namespace LinuxSampler {
30    
31 iliev 2224 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 iliev 2217 class SignalUnitRack;
78    
79 iliev 2205 /**
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 iliev 2207 * 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 iliev 2205 * 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 iliev 2207 * a parameter which signal source is an envelope
98 iliev 2205 * 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 iliev 2207 SignalUnit* pUnit; /* The source unit whose output signal
110     * will modulate the parameter.
111 iliev 2205 */
112    
113 iliev 2207 float Coeff; // The modulation coefficient
114 iliev 2205
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 iliev 2218 void Copy(const Parameter& Prm) {
133 iliev 2205 if (this == &Prm) return;
134    
135     pUnit = Prm.pUnit;
136     Coeff = Prm.Coeff;
137     }
138    
139    
140     /**
141 iliev 2207 * Calculates the transformation for this parameter
142 iliev 2205 * 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 iliev 2207 * 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 iliev 2205 */
157     virtual float GetValue() {
158 iliev 2207 return pUnit->Active() ? Transform(pUnit->GetLevel()) : 1.0f;
159 iliev 2205 }
160     };
161    
162    
163     public:
164     ArrayList<SignalUnit::Parameter> Params; // The list of parameters which are modulating the signal unit
165    
166 iliev 2217 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 iliev 2205 void operator=(const SignalUnit& Unit) { Copy(Unit); }
169    
170 iliev 2218 void Copy(const SignalUnit& Unit) {
171 iliev 2205 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 iliev 2207 /**
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 iliev 2205 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 iliev 2217 /**
252     * A helper method which checks whether the delay
253     * stage is finished.
254     */
255     bool DelayStage();
256    
257 iliev 2205 protected:
258 iliev 2217 SignalUnitRack* const pRack;
259    
260 iliev 2205 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 iliev 2217 class EndpointSignalUnit: public SignalUnit {
273 iliev 2205 public:
274 iliev 2217 EndpointSignalUnit(SignalUnitRack* rack): SignalUnit(rack) { }
275    
276 iliev 2205 /**
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 iliev 2219 /** Should return value in the range [-100, 100] (L <-> R) */
301     virtual float GetPan() = 0;
302    
303 iliev 2217 virtual float CalculateFilterCutoff(float cutoff) {
304     cutoff *= GetFilterCutoff();
305     return cutoff > 13500 ? 13500 : cutoff;
306     }
307 iliev 2205
308 iliev 2217 virtual float CalculatePitch(float pitch) {
309     return GetPitch() * pitch;
310 iliev 2205 }
311    
312 iliev 2217 virtual float CalculateResonance(float res) {
313     return GetResonance() * res;
314 iliev 2205 }
315 iliev 2219
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 iliev 2205 };
324    
325     /**
326 iliev 2232 * Used to smooth out the parameter changes.
327     */
328     class Smoother {
329     protected:
330     uint timeSteps; // The number of time steps to reach the goal
331     uint currentTimeStep;
332     uint8_t goal; // 0 - 127
333     uint8_t prev; // 0 - 127
334    
335     public:
336     /**
337     *
338     * @param time The time (in seconds) to reach the goal
339     * @param sampleRate
340     * @param val The initial value
341     */
342     void trigger(float time, float sampleRate, uint8_t val = 0) {
343     currentTimeStep = timeSteps = time * sampleRate;
344     prev = goal = val;
345     }
346    
347     /**
348     * Set the current value, which the smoother will not smooth out.
349     * If you want the value to be smoothen out, use update() instead.
350     */
351     void setValue( uint8_t val) {
352     currentTimeStep = timeSteps;
353     prev = goal = val;
354     }
355    
356     /**
357     * Sets a new value. The render function will return
358     * values gradually approaching this value.
359     */
360     void update(uint8_t val) {
361     if (val == goal) return;
362    
363     prev = prev + (goal - prev) * (currentTimeStep / (float)timeSteps);
364     goal = val;
365     currentTimeStep = 0;
366     }
367    
368     uint8_t render() {
369     if (currentTimeStep >= timeSteps) return goal;
370     return prev + (goal - prev) * (currentTimeStep++ / (float)timeSteps);
371     }
372    
373     bool isSmoothingOut() { return currentTimeStep < timeSteps; }
374     };
375    
376     /**
377 iliev 2205 * Continuous controller signal unit.
378 iliev 2224 * The level of this unit corresponds to the controllers changes
379     * and their influences.
380 iliev 2205 */
381 iliev 2217 class CCSignalUnit: public SignalUnit {
382 iliev 2227 public:
383     /** Listener which will be notified when the level of the unit is changed. */
384     class Listener {
385     public:
386     virtual void ValueChanged(CCSignalUnit* pUnit) = 0;
387     };
388    
389 iliev 2224 protected:
390     class CC {
391     public:
392 iliev 2230 uint8_t Controller; ///< MIDI controller number.
393     uint8_t Value; ///< Controller Value.
394     short int Curve; ///< specifies the curve type
395     float Influence;
396 iliev 2224
397 iliev 2232 Smoother* pSmoother;
398    
399     CC(uint8_t Controller = 0, float Influence = 0.0f, short int Curve = -1, Smoother* pSmoother = NULL) {
400 iliev 2224 this->Controller = Controller;
401     this->Value = 0;
402 iliev 2230 this->Curve = Curve;
403 iliev 2224 this->Influence = Influence;
404 iliev 2232 this->pSmoother = pSmoother;
405 iliev 2224 }
406    
407     CC(const CC& cc) { Copy(cc); }
408     void operator=(const CC& cc) { Copy(cc); }
409    
410     void Copy(const CC& cc) {
411     Controller = cc.Controller;
412     Value = cc.Value;
413     Influence = cc.Influence;
414 iliev 2230 Curve = cc.Curve;
415 iliev 2232 pSmoother = cc.pSmoother;
416 iliev 2224 }
417     };
418    
419     FixedArray<CC> Ctrls; // The MIDI controllers which modulates this signal unit.
420 iliev 2227 Listener* pListener;
421 iliev 2232 bool hasSmoothCtrls; // determines whether there are smooth controllers (used for optimization)
422     bool isSmoothingOut; // determines whether there is a CC which is in process of smoothing out (used for optimization)
423 iliev 2205
424     public:
425 iliev 2227
426     CCSignalUnit(SignalUnitRack* rack, Listener* l = NULL): SignalUnit(rack), Ctrls(128) {
427     pListener = l;
428 iliev 2232 hasSmoothCtrls = isSmoothingOut = false;
429 iliev 2205 }
430    
431 iliev 2224 CCSignalUnit(const CCSignalUnit& Unit): SignalUnit(Unit.pRack), Ctrls(128) { Copy(Unit); }
432 iliev 2218 void operator=(const CCSignalUnit& Unit) { Copy(Unit); }
433 iliev 2205
434 iliev 2218 void Copy(const CCSignalUnit& Unit) {
435 iliev 2224 Ctrls.copy(Unit.Ctrls);
436 iliev 2227 pListener = Unit.pListener;
437 iliev 2232 hasSmoothCtrls = Unit.hasSmoothCtrls;
438     isSmoothingOut = Unit.isSmoothingOut;
439 iliev 2218 SignalUnit::Copy(Unit);
440 iliev 2205 }
441    
442 iliev 2232 void AddCC(uint8_t Controller, float Influence, short int Curve = -1, Smoother* pSmoother = NULL) {
443     Ctrls.add(CC(Controller, Influence, Curve, pSmoother));
444     if (pSmoother != NULL) hasSmoothCtrls = true;
445 iliev 2224 }
446    
447     void RemoveAllCCs() {
448     Ctrls.clear();
449     }
450    
451 iliev 2232 virtual void Increment() {
452     if (hasSmoothCtrls && isSmoothingOut) Calculate();
453     }
454 iliev 2205
455 iliev 2224 virtual void Trigger() {
456     Calculate();
457     bActive = Level != 0;
458     }
459    
460 iliev 2205 virtual void ProcessCCEvent(uint8_t Controller, uint8_t Value) {
461 iliev 2224 bool recalculate = false;
462 iliev 2205
463 iliev 2224 for (int i = 0; i < Ctrls.size(); i++) {
464     if (Controller != Ctrls[i].Controller) continue;
465     if (Ctrls[i].Value == Value) continue;
466     Ctrls[i].Value = Value;
467 iliev 2232 if (Ctrls[i].pSmoother != NULL) Ctrls[i].pSmoother->update(Value);
468 iliev 2224 if (!bActive) bActive = true;
469     recalculate = true;
470     }
471 iliev 2205
472 iliev 2232 if (!(hasSmoothCtrls && isSmoothingOut) && recalculate) Calculate();
473 iliev 2205 }
474 iliev 2224
475     virtual void Calculate() {
476 iliev 2232 float l = 0;
477     isSmoothingOut = false;
478 iliev 2224 for (int i = 0; i < Ctrls.size(); i++) {
479 iliev 2232 if (Ctrls[i].pSmoother == NULL) {
480     l += Normalize(Ctrls[i].Value, Ctrls[i].Curve) * Ctrls[i].Influence;
481     } else {
482     if (Ctrls[i].pSmoother->isSmoothingOut()) isSmoothingOut = true;
483     l += Normalize(Ctrls[i].pSmoother->render(), Ctrls[i].Curve) * Ctrls[i].Influence;
484     }
485 iliev 2224 }
486 iliev 2232 if (Level != l) {
487     Level = l;
488     if (pListener != NULL) pListener->ValueChanged(this);
489     }
490 iliev 2224 }
491 iliev 2232
492     virtual float Normalize(uint8_t val, short int curve = -1) {
493     return val / 127.0f;
494     }
495 iliev 2205 };
496    
497     } // namespace LinuxSampler
498    
499     #endif /* __LS_SIGNALUNIT_H__ */

  ViewVC Help
Powered by ViewVC