/[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 2296 - (hide annotations) (download) (as text)
Thu Dec 8 20:03:47 2011 UTC (12 years, 3 months ago) by iliev
File MIME type: text/x-c++hdr
File size: 22645 byte(s)
* fixed crash when trying to create an effect instance with controls
  which min and/or max values depend on the sample rate
* experimental support for per voice equalization (work in progress)
* sfz engine: implemented opcodes eq1_freq, eq2_freq, eq3_freq,
  eq1_freqccN, eq2_freqccN, eq3_freqccN, eq1_bw, eq2_bw, eq3_bw,
  eq1_bwccN, eq2_bwccN, eq3_bwccN, eq1_gain, eq2_gain, eq3_gain,
  eq1_gainccN, eq2_gainccN, eq3_gainccN

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 iliev 2264 uint timeSteps; // The number of time steps to reach the goal
333     uint currentTimeStep;
334     float goal;
335     float prev;
336 iliev 2232
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 iliev 2264 void trigger(float time, float sampleRate, float val = 0) {
345 iliev 2232 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 iliev 2264 void setValue( float val) {
354 iliev 2232 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 iliev 2264 void update(float val) {
363 iliev 2232 if (val == goal) return;
364    
365     prev = prev + (goal - prev) * (currentTimeStep / (float)timeSteps);
366     goal = val;
367     currentTimeStep = 0;
368     }
369    
370 iliev 2264 float render() {
371 iliev 2232 if (currentTimeStep >= timeSteps) return goal;
372     return prev + (goal - prev) * (currentTimeStep++ / (float)timeSteps);
373     }
374    
375     bool isSmoothingOut() { return currentTimeStep < timeSteps; }
376 iliev 2264
377     float getGoal() { return goal; }
378 iliev 2232 };
379    
380     /**
381 iliev 2205 * Continuous controller signal unit.
382 iliev 2224 * The level of this unit corresponds to the controllers changes
383     * and their influences.
384 iliev 2205 */
385 iliev 2217 class CCSignalUnit: public SignalUnit {
386 iliev 2227 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 iliev 2224 class CC {
394     public:
395 iliev 2230 uint8_t Controller; ///< MIDI controller number.
396     uint8_t Value; ///< Controller Value.
397     short int Curve; ///< specifies the curve type
398     float Influence;
399 iliev 2264 float Step;
400 iliev 2224
401 iliev 2232 Smoother* pSmoother;
402    
403 iliev 2264 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 iliev 2224 this->Controller = Controller;
411     this->Value = 0;
412 iliev 2230 this->Curve = Curve;
413 iliev 2224 this->Influence = Influence;
414 iliev 2232 this->pSmoother = pSmoother;
415 iliev 2264 this->Step = Step;
416 iliev 2224 }
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 iliev 2264 Value = cc.Value;
424     Influence = cc.Influence;
425     Curve = cc.Curve;
426     pSmoother = cc.pSmoother;
427     Step = cc.Step;
428 iliev 2224 }
429     };
430    
431 iliev 2244 protected:
432     RTList<CC>* pCtrls; // The MIDI controllers which modulates this signal unit.
433 iliev 2227 Listener* pListener;
434 iliev 2232 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 iliev 2205
437     public:
438 iliev 2227
439 iliev 2244 CCSignalUnit(SignalUnitRack* rack, Listener* l = NULL): SignalUnit(rack), pCtrls(NULL) {
440 iliev 2227 pListener = l;
441 iliev 2232 hasSmoothCtrls = isSmoothingOut = false;
442 iliev 2205 }
443    
444 iliev 2244 CCSignalUnit(const CCSignalUnit& Unit): SignalUnit(Unit.pRack), pCtrls(NULL) { Copy(Unit); }
445 iliev 2218 void operator=(const CCSignalUnit& Unit) { Copy(Unit); }
446 iliev 2205
447 iliev 2244 virtual ~CCSignalUnit() {
448     if (pCtrls != NULL) delete pCtrls;
449     }
450    
451 iliev 2218 void Copy(const CCSignalUnit& Unit) {
452 iliev 2244 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 iliev 2227 pListener = Unit.pListener;
459 iliev 2232 hasSmoothCtrls = Unit.hasSmoothCtrls;
460     isSmoothingOut = Unit.isSmoothingOut;
461 iliev 2218 SignalUnit::Copy(Unit);
462 iliev 2205 }
463    
464 iliev 2244 virtual void InitCCList(Pool<CC>* pCCPool, Pool<Smoother>* pSmootherPool) {
465     if (pCtrls != NULL) delete pCtrls;
466     pCtrls = new RTList<CC>(pCCPool);
467     }
468    
469 iliev 2264 void AddCC(uint8_t Controller, float Influence, short int Curve = -1, Smoother* pSmoother = NULL, float Step = 0) {
470 iliev 2244 if(pCtrls->poolIsEmpty()) {
471     std::cerr << "Maximum number of CC reached!" << std::endl;
472 iliev 2238 return;
473     }
474 iliev 2264 *(pCtrls->allocAppend()) = CC(Controller, Influence, Curve, pSmoother, Step);
475 iliev 2232 if (pSmoother != NULL) hasSmoothCtrls = true;
476 iliev 2224 }
477    
478 iliev 2244 virtual void RemoveAllCCs() { pCtrls->clear(); }
479 iliev 2224
480 iliev 2244 int GetCCCount() { return pCtrls->count(); }
481 iliev 2235
482 iliev 2296 bool HasCCs() { return GetCCCount() > 0; }
483    
484 iliev 2232 virtual void Increment() {
485     if (hasSmoothCtrls && isSmoothingOut) Calculate();
486     }
487 iliev 2205
488 iliev 2224 virtual void Trigger() {
489     Calculate();
490     bActive = Level != 0;
491     }
492    
493 iliev 2205 virtual void ProcessCCEvent(uint8_t Controller, uint8_t Value) {
494 iliev 2224 bool recalculate = false;
495 iliev 2205
496 iliev 2244 RTList<CC>::Iterator ctrl = pCtrls->first();
497     RTList<CC>::Iterator end = pCtrls->end();
498     for(; ctrl != end; ++ctrl) {
499     if (Controller != (*ctrl).Controller) continue;
500     if ((*ctrl).Value == Value) continue;
501 iliev 2264
502 iliev 2244 (*ctrl).Value = Value;
503 iliev 2264
504     if ((*ctrl).Step > 0 && (*ctrl).pSmoother != NULL) {
505     float oldGoal = (*ctrl).pSmoother->getGoal();
506     float newGoal = Normalize(Value, (*ctrl).Curve) * (*ctrl).Influence;
507     newGoal = ((int) (newGoal / (*ctrl).Step)) * (*ctrl).Step;
508     if (oldGoal != newGoal) (*ctrl).pSmoother->update(newGoal);
509     }
510    
511     if ((*ctrl).pSmoother != NULL && (*ctrl).Step <= 0) (*ctrl).pSmoother->update(Value);
512 iliev 2224 if (!bActive) bActive = true;
513     recalculate = true;
514     }
515 iliev 2205
516 iliev 2232 if (!(hasSmoothCtrls && isSmoothingOut) && recalculate) Calculate();
517 iliev 2205 }
518 iliev 2224
519     virtual void Calculate() {
520 iliev 2232 float l = 0;
521     isSmoothingOut = false;
522 iliev 2244 RTList<CC>::Iterator ctrl = pCtrls->first();
523     RTList<CC>::Iterator end = pCtrls->end();
524     for(; ctrl != end; ++ctrl) {
525     if ((*ctrl).pSmoother == NULL) {
526 iliev 2264 float val = Normalize((*ctrl).Value, (*ctrl).Curve) * (*ctrl).Influence;
527     if ((*ctrl).Step > 0) val = ( (int)(val / (*ctrl).Step) ) * (*ctrl).Step;
528     l += val;
529 iliev 2232 } else {
530 iliev 2244 if ((*ctrl).pSmoother->isSmoothingOut()) isSmoothingOut = true;
531 iliev 2264
532     if ((*ctrl).Step > 0) {
533     l += (*ctrl).pSmoother->render();
534     } else {
535     l += Normalize((*ctrl).pSmoother->render(), (*ctrl).Curve) * (*ctrl).Influence;
536     }
537 iliev 2232 }
538 iliev 2224 }
539 iliev 2232 if (Level != l) {
540     Level = l;
541     if (pListener != NULL) pListener->ValueChanged(this);
542     }
543 iliev 2224 }
544 iliev 2232
545     virtual float Normalize(uint8_t val, short int curve = -1) {
546     return val / 127.0f;
547     }
548 iliev 2205 };
549    
550     } // namespace LinuxSampler
551    
552     #endif /* __LS_SIGNALUNIT_H__ */

  ViewVC Help
Powered by ViewVC