/[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 2382 - (hide 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 iliev 2205 /***************************************************************************
2     * *
3     * LinuxSampler - modular, streaming capable sampler *
4     * *
5 persson 2382 * Copyright (C) 2011 - 2012 Grigor Iliev *
6 iliev 2205 * *
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 persson 2382 virtual uint8_t CalculatePan(int pan) {
320 iliev 2219 int p = pan + GetPan() * 0.63;
321     if (p < 0) return 0;
322     if (p > 127) return 127;
323     return p;
324     }
325 iliev 2297
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 iliev 2205 };
335    
336     /**
337 iliev 2232 * Used to smooth out the parameter changes.
338     */
339     class Smoother {
340     protected:
341 iliev 2264 uint timeSteps; // The number of time steps to reach the goal
342     uint currentTimeStep;
343     float goal;
344     float prev;
345 iliev 2232
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 iliev 2264 void trigger(float time, float sampleRate, float val = 0) {
354 iliev 2232 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 iliev 2264 void setValue( float val) {
363 iliev 2232 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 iliev 2264 void update(float val) {
372 iliev 2232 if (val == goal) return;
373    
374     prev = prev + (goal - prev) * (currentTimeStep / (float)timeSteps);
375     goal = val;
376     currentTimeStep = 0;
377     }
378    
379 iliev 2264 float render() {
380 iliev 2232 if (currentTimeStep >= timeSteps) return goal;
381     return prev + (goal - prev) * (currentTimeStep++ / (float)timeSteps);
382     }
383    
384     bool isSmoothingOut() { return currentTimeStep < timeSteps; }
385 iliev 2264
386     float getGoal() { return goal; }
387 iliev 2232 };
388    
389     /**
390 iliev 2205 * Continuous controller signal unit.
391 iliev 2224 * The level of this unit corresponds to the controllers changes
392     * and their influences.
393 iliev 2205 */
394 iliev 2217 class CCSignalUnit: public SignalUnit {
395 iliev 2227 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 iliev 2224 class CC {
403     public:
404 iliev 2230 uint8_t Controller; ///< MIDI controller number.
405     uint8_t Value; ///< Controller Value.
406     short int Curve; ///< specifies the curve type
407     float Influence;
408 iliev 2264 float Step;
409 iliev 2224
410 iliev 2232 Smoother* pSmoother;
411    
412 iliev 2264 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 iliev 2224 this->Controller = Controller;
420     this->Value = 0;
421 iliev 2230 this->Curve = Curve;
422 iliev 2224 this->Influence = Influence;
423 iliev 2232 this->pSmoother = pSmoother;
424 iliev 2264 this->Step = Step;
425 iliev 2224 }
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 iliev 2264 Value = cc.Value;
433     Influence = cc.Influence;
434     Curve = cc.Curve;
435     pSmoother = cc.pSmoother;
436     Step = cc.Step;
437 iliev 2224 }
438     };
439    
440 iliev 2244 protected:
441     RTList<CC>* pCtrls; // The MIDI controllers which modulates this signal unit.
442 iliev 2227 Listener* pListener;
443 iliev 2232 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 iliev 2205
446     public:
447 iliev 2227
448 iliev 2244 CCSignalUnit(SignalUnitRack* rack, Listener* l = NULL): SignalUnit(rack), pCtrls(NULL) {
449 iliev 2227 pListener = l;
450 iliev 2232 hasSmoothCtrls = isSmoothingOut = false;
451 iliev 2205 }
452    
453 iliev 2244 CCSignalUnit(const CCSignalUnit& Unit): SignalUnit(Unit.pRack), pCtrls(NULL) { Copy(Unit); }
454 iliev 2218 void operator=(const CCSignalUnit& Unit) { Copy(Unit); }
455 iliev 2205
456 iliev 2244 virtual ~CCSignalUnit() {
457     if (pCtrls != NULL) delete pCtrls;
458     }
459    
460 iliev 2218 void Copy(const CCSignalUnit& Unit) {
461 iliev 2244 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 iliev 2227 pListener = Unit.pListener;
468 iliev 2232 hasSmoothCtrls = Unit.hasSmoothCtrls;
469     isSmoothingOut = Unit.isSmoothingOut;
470 iliev 2218 SignalUnit::Copy(Unit);
471 iliev 2205 }
472    
473 iliev 2244 virtual void InitCCList(Pool<CC>* pCCPool, Pool<Smoother>* pSmootherPool) {
474     if (pCtrls != NULL) delete pCtrls;
475     pCtrls = new RTList<CC>(pCCPool);
476     }
477    
478 iliev 2264 void AddCC(uint8_t Controller, float Influence, short int Curve = -1, Smoother* pSmoother = NULL, float Step = 0) {
479 iliev 2244 if(pCtrls->poolIsEmpty()) {
480     std::cerr << "Maximum number of CC reached!" << std::endl;
481 iliev 2238 return;
482     }
483 iliev 2264 *(pCtrls->allocAppend()) = CC(Controller, Influence, Curve, pSmoother, Step);
484 iliev 2232 if (pSmoother != NULL) hasSmoothCtrls = true;
485 iliev 2224 }
486    
487 iliev 2244 virtual void RemoveAllCCs() { pCtrls->clear(); }
488 iliev 2224
489 iliev 2244 int GetCCCount() { return pCtrls->count(); }
490 iliev 2235
491 iliev 2296 bool HasCCs() { return GetCCCount() > 0; }
492    
493 iliev 2232 virtual void Increment() {
494     if (hasSmoothCtrls && isSmoothingOut) Calculate();
495     }
496 iliev 2205
497 iliev 2224 virtual void Trigger() {
498     Calculate();
499     bActive = Level != 0;
500     }
501    
502 iliev 2205 virtual void ProcessCCEvent(uint8_t Controller, uint8_t Value) {
503 iliev 2224 bool recalculate = false;
504 iliev 2205
505 iliev 2244 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 iliev 2264
511 iliev 2244 (*ctrl).Value = Value;
512 iliev 2264
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 iliev 2224 if (!bActive) bActive = true;
522     recalculate = true;
523     }
524 iliev 2205
525 iliev 2232 if (!(hasSmoothCtrls && isSmoothingOut) && recalculate) Calculate();
526 iliev 2205 }
527 iliev 2224
528     virtual void Calculate() {
529 iliev 2232 float l = 0;
530     isSmoothingOut = false;
531 iliev 2244 RTList<CC>::Iterator ctrl = pCtrls->first();
532     RTList<CC>::Iterator end = pCtrls->end();
533     for(; ctrl != end; ++ctrl) {
534     if ((*ctrl).pSmoother == NULL) {
535 iliev 2264 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 iliev 2232 } else {
539 iliev 2244 if ((*ctrl).pSmoother->isSmoothingOut()) isSmoothingOut = true;
540 iliev 2264
541     if ((*ctrl).Step > 0) {
542     l += (*ctrl).pSmoother->render();
543     } else {
544     l += Normalize((*ctrl).pSmoother->render(), (*ctrl).Curve) * (*ctrl).Influence;
545     }
546 iliev 2232 }
547 iliev 2224 }
548 iliev 2232 if (Level != l) {
549     Level = l;
550     if (pListener != NULL) pListener->ValueChanged(this);
551     }
552 iliev 2224 }
553 iliev 2232
554     virtual float Normalize(uint8_t val, short int curve = -1) {
555     return val / 127.0f;
556     }
557 iliev 2205 };
558    
559     } // namespace LinuxSampler
560    
561     #endif /* __LS_SIGNALUNIT_H__ */

  ViewVC Help
Powered by ViewVC