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

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

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 2205 by iliev, Mon Jul 11 17:52:01 2011 UTC revision 2232 by iliev, Mon Aug 8 13:40:04 2011 UTC
# Line 28  Line 28 
28    
29  namespace LinuxSampler {  namespace LinuxSampler {
30    
31        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        class SignalUnitRack;
78    
79      /**      /**
80       * A signal unit consist of internal signal generator (like envelope generator,       * A signal unit consist of internal signal generator (like envelope generator,
81       * low frequency oscillator, etc) with a number of generator parameters which       * low frequency oscillator, etc) with a number of generator parameters which
82       * influence/modulate/dynamically change the generator's signal in some manner.       * influence/modulate/dynamically change the generator's signal in some manner.
83       * The generators' parameters (also called signal unit parameters) can receive       * Each generator parameter (also called signal unit parameter) can receive
84       * the signal of one or more signal units (through modulators if need)       * signal from another signal unit and use this signal to dynamically change the
85       * and use the (modulated) signals of those units to dynamically change the       * behavior of the signal generator. In turn, the signal of this unit can be fed
      * behavior of the signal generator. In turn, the signal of those unit can be fed  
86       * to another unit(s) and influence its parameters.       * to another unit(s) and influence its parameters.
87       */       */
88      class SignalUnit {      class SignalUnit {
89          public:          public:
90    
91          /**          /**
          * Used as an intermediate link between a source signal unit and  
          * a destination unit parameter. Modulates the received signal from the  
          * source unit and feed it to the unit parameter to which it is connected.  
          */  
         class Modulator {  
             public:  
                 SignalUnit* pUnit; /* The signal source which will be used for modulation.  
                                     * If pUnit is NULL the level is considered to be 1!  
                                     */  
                 float Coeff; // The modulation coefficient  
                   
                 Modulator() : pUnit(NULL) { }  
                 Modulator(SignalUnit* Unit) { pUnit = Unit; }  
                 Modulator(const Modulator& Mod) { Copy(Mod); }  
                 void operator=(const Modulator& Mod) { Copy(Mod); }  
               
                 virtual void Copy(const Modulator& Mod) {  
                     if (this == &Mod) return;  
   
                     pUnit = Mod.pUnit;  
                     Coeff = Mod.Coeff;  
                 }  
                   
                 /**  
                  * Gets the normalized level of the signal source for the current  
                  * time step (sample point). Returns 1 if source unit is NULL or  
                  * if the source unit is inactive.  
                  */  
                 virtual float GetLevel() {  
                     if (pUnit == NULL) return 1.0f;  
                     return pUnit->Active() ? pUnit->GetLevel() : 1.0f;  
                 }  
                   
                 /**  
                  * Gets the modulated level of the source signal  
                  * for the current time step (sample point).  
                  */  
                 virtual float GetValue() {  
                     return Transform(GetLevel());  
                 }  
                   
                 /**  
                  * Calculates the transformation that should be applied  
                  * to the signal of the source unit and multiplies by Coeff.  
                  * This implementation of the method just multiplies by Coeff,  
                  * or returns 1 if the source unit is inactive.  
                  */  
                 virtual float Transform(float val) {  
                     if (pUnit != NULL && !pUnit->Active()) return 1;  
                     return val * Coeff;  
                 }  
         };  
           
         /**  
92           * This class represents a parameter which will influence the signal           * This class represents a parameter which will influence the signal
93           * unit to which it belongs in certain way.           * unit to which it belongs in certain way.
94           * For example, let's say the signal unit is a low frequency oscillator           * 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           * 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           * and increment its frequency to 5Hz in 1 second, we can add
97           * a parameter with one modulator which signal source is an envelope           * a parameter which signal source is an envelope
98           * generator with attack time of 1 second and coefficient 4. Thus, the           * 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.           * normalized level of the EG will move from 0 to 1 in one second.
100           * On every time step (sample point) the normalized level           * On every time step (sample point) the normalized level
# Line 113  namespace LinuxSampler { Line 106  namespace LinuxSampler {
106           */           */
107          class Parameter {          class Parameter {
108              public:              public:
109                  ArrayList<Modulator> Modulators; // A list of signal units which will modulate this parameter                  SignalUnit* pUnit; /* The source unit whose output signal
110                                                        * will modulate the parameter.
                 SignalUnit* pUnit; /* If pUnit is not NULL, the modulators are ignored and  
                                     * this unit is used as only source.  
                                     * This is done for efficiency reasons.  
111                                      */                                      */
112                                    
113                  float Coeff; // The global modulation coefficient                  float Coeff; // The modulation coefficient
114                                    
115                                    
116                  Parameter() : Coeff(1), pUnit(NULL) { }                  Parameter() : Coeff(1), pUnit(NULL) { }
117    
118                  /**                  /**
                  * Most often we just need to directly feed the signal of single unit  
                  * to a unit parameter without any additional modulation.  
                  * This constructor creates a parameter with only a single source  
                  * unit without additional modulation, optimized for efficiency.  
119                   * @param unit The source unit used to influence this parameter.                   * @param unit The source unit used to influence this parameter.
120                   * @param coeff The coefficient by which the normalized signal                   * @param coeff The coefficient by which the normalized signal
121                   * received from the source unit should be multiplied when a                   * received from the source unit should be multiplied when a
# Line 143  namespace LinuxSampler { Line 129  namespace LinuxSampler {
129                  Parameter(const Parameter& Prm) { Copy(Prm); }                  Parameter(const Parameter& Prm) { Copy(Prm); }
130                  void operator=(const Parameter& Prm) { Copy(Prm); }                  void operator=(const Parameter& Prm) { Copy(Prm); }
131                            
132                  virtual void Copy(const Parameter& Prm) {                  void Copy(const Parameter& Prm) {
133                      if (this == &Prm) return;                      if (this == &Prm) return;
134    
                     Modulators = Prm.Modulators;  
135                      pUnit = Prm.pUnit;                      pUnit = Prm.pUnit;
136                      Coeff = Prm.Coeff;                      Coeff = Prm.Coeff;
137                  }                  }
138                                    
139                                    
140                  /**                  /**
141                   * Calculates the global transformation for this parameter                   * Calculates the transformation for this parameter
142                   * which should be applied to the parameter's value                   * which should be applied to the parameter's value
143                   * and multiplies by Coeff.                   * and multiplies by Coeff.
144                   * This implementation of the method just multiplies by Coeff.                   * This implementation of the method just multiplies by Coeff.
# Line 163  namespace LinuxSampler { Line 148  namespace LinuxSampler {
148                  }                  }
149                                    
150                  /**                  /**
151                   * Gets the current value of the parameter (without transformation).                   * Gets the current value of the parameter.
152                   * This implementation just sum the modulators values.                   * This implementation returns the current signal level of the
153                   * If only a single unit is used without additional modulation                   * source unit with applied transformation if the source unit is
154                   * returns the source unit's level or 1 if the unit is not active.                   * active, otherwise returns 1.
155                     * Note that this method assume that pUnit is not NULL.
156                   */                   */
157                  virtual float GetValue() {                  virtual float GetValue() {
158                      if (pUnit != NULL) {                      return pUnit->Active() ? Transform(pUnit->GetLevel()) : 1.0f;
                         return pUnit->Active() ? pUnit->GetLevel() : 1.0f;  
                     }  
   
                     float val = 0;  
                     for(int i = 0; i < Modulators.size(); i++) {  
                         val += Modulators[i].GetValue();  
                     }  
                       
                     return val;  
                 }  
                   
                 /** Gets the final value - with applied transformation. */  
                 virtual float GetFinalValue() {  
                     return Transform(GetValue());  
159                  }                  }
160          };          };
161    
# Line 191  namespace LinuxSampler { Line 163  namespace LinuxSampler {
163          public:          public:
164              ArrayList<SignalUnit::Parameter> Params; // The list of parameters which are modulating the signal unit              ArrayList<SignalUnit::Parameter> Params; // The list of parameters which are modulating the signal unit
165                            
166              SignalUnit() : bActive(false), Level(0.0f), bCalculating(false), uiDelayTrigger(0) { }              SignalUnit(SignalUnitRack* rack): pRack(rack), bActive(false), Level(0.0f), bCalculating(false), uiDelayTrigger(0) { }
167              SignalUnit(const SignalUnit& Unit) { Copy(Unit); }              SignalUnit(const SignalUnit& Unit): pRack(Unit.pRack) { Copy(Unit); }
168              void operator=(const SignalUnit& Unit) { Copy(Unit); }              void operator=(const SignalUnit& Unit) { Copy(Unit); }
169                            
170              virtual void Copy(const SignalUnit& Unit) {              void Copy(const SignalUnit& Unit) {
171                  if (this == &Unit) return;                  if (this == &Unit) return;
172    
173                  bActive = Unit.bActive;                  bActive = Unit.bActive;
# Line 258  namespace LinuxSampler { Line 230  namespace LinuxSampler {
230               */               */
231              virtual void Increment() { bRecalculate = true; }              virtual void Increment() { bRecalculate = true; }
232    
233              /** Initializes and triggers the unit. */              /**
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              virtual void Trigger() = 0;              virtual void Trigger() = 0;
240                            
241              /**              /**
# Line 271  namespace LinuxSampler { Line 248  namespace LinuxSampler {
248               */               */
249              virtual uint DelayTrigger() { return uiDelayTrigger; }              virtual uint DelayTrigger() { return uiDelayTrigger; }
250                            
251                /**
252                 * A helper method which checks whether the delay
253                 * stage is finished.
254                 */
255                bool DelayStage();
256                
257          protected:          protected:
258                SignalUnitRack* const pRack;
259    
260              bool   bActive; /* Don't use it to check the active state of the unit!!!              bool   bActive; /* Don't use it to check the active state of the unit!!!
261                               * Use Active() instead! */                               * Use Active() instead! */
262              float  Level;              float  Level;
# Line 284  namespace LinuxSampler { Line 269  namespace LinuxSampler {
269                    
270      };      };
271            
272      class EndpointSignalUnit: virtual public SignalUnit {      class EndpointSignalUnit: public SignalUnit {
273          public:          public:
274                EndpointSignalUnit(SignalUnitRack* rack): SignalUnit(rack) { }
275    
276              /**              /**
277               * Gets the volume modulation value               * Gets the volume modulation value
278               * for the current time step (sample point).               * for the current time step (sample point).
# Line 310  namespace LinuxSampler { Line 297  namespace LinuxSampler {
297               */               */
298              virtual float GetResonance() = 0;              virtual float GetResonance() = 0;
299                            
300              virtual float CalculateFilterCutoff(float cutoff) = 0;              /** Should return value in the range [-100, 100] (L <-> R) */
301                virtual float GetPan() = 0;
302                            
303              virtual float CalculatePitch(float pitch) = 0;              virtual float CalculateFilterCutoff(float cutoff) {
304                    cutoff *= GetFilterCutoff();
305                    return cutoff > 13500 ? 13500 : cutoff;
306                }
307                            
308              virtual float CalculateResonance(float res) = 0;              virtual float CalculatePitch(float pitch) {
309      };                  return GetPitch() * pitch;
310                    }
   
     class SignalUnitRack;  
   
     template <class O /* The signal unit's owner */>  
     class SignalUnitBase: virtual public SignalUnit {  
         public:  
             SignalUnitBase() : pOwner(NULL) { }  
             SignalUnitBase(const SignalUnitBase& Unit) { Copy(Unit); }  
             void operator=(const SignalUnitBase& Unit) { Copy(Unit); }  
311                            
312              virtual void Copy(const SignalUnitBase& Unit) {              virtual float CalculateResonance(float res) {
313                  if (this == &Unit) return;                  return GetResonance() * res;
   
                 pOwner = Unit.pOwner;  
                 SignalUnit::Copy(Unit);  
314              }              }
315                
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        };
324        
325        /**
326         * Used to smooth out the parameter changes.
327         */
328        class Smoother {
329          protected:          protected:
330              O* pOwner; // The owner to which this rack belongs.              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                            
             SignalUnitRack* GetSignalUnitRack() { return pOwner->GetSignalUnitRack(); }  
           
335          public:          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               * The owner of the unit is set by the rack               * Set the current value, which the smoother will not smooth out.
349               * just before the call to the unit's trigger method.               * If you want the value to be smoothen out, use update() instead.
350               */               */
351              void SetOwner(O* Owner) { pOwner = Owner; }              void setValue( uint8_t val) {
352                    currentTimeStep = timeSteps;
353                    prev = goal = val;
354                }
355                            
356              /**              /**
357               * A helper method which checks whether the delay               * Sets a new value. The render function will return
358               * stage is finished.               * values gradually approaching this value.
359               */               */
360              bool DelayStage() {              void update(uint8_t val) {
361                  return (DelayTrigger() >= GetSignalUnitRack()->GetCurrentStep());                  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       * Continuous controller signal unit.       * Continuous controller signal unit.
378       * The level of this unit corresponds to the controller changes       * The level of this unit corresponds to the controllers changes
379       * and is normalized to be in the range from -1 to +1.       * and their influences.
380       */       */
381      template<class O>      class CCSignalUnit: public SignalUnit {
382      class CCSignalUnit: public SignalUnitBase<O> {          public:
383          private:              /** Listener which will be notified when the level of the unit is changed. */
384              uint8_t Ctrl; // The number of the MIDI controller which modulates this signal unit.              class Listener {
385                    public:
386                        virtual void ValueChanged(CCSignalUnit* pUnit) = 0;
387                };
388                
389            protected:
390                class CC {
391                    public:
392                        uint8_t   Controller;  ///< MIDI controller number.
393                        uint8_t   Value;       ///< Controller Value.
394                        short int Curve;       ///< specifies the curve type
395                        float     Influence;
396                        
397                        Smoother* pSmoother;
398                        
399                        CC(uint8_t Controller = 0, float Influence = 0.0f, short int Curve = -1, Smoother* pSmoother = NULL) {
400                            this->Controller = Controller;
401                            this->Value = 0;
402                            this->Curve = Curve;
403                            this->Influence = Influence;
404                            this->pSmoother = pSmoother;
405                        }
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                            Curve = cc.Curve;
415                            pSmoother = cc.pSmoother;
416                        }
417                };
418                
419                FixedArray<CC> Ctrls; // The MIDI controllers which modulates this signal unit.
420                Listener* pListener;
421                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    
424          public:          public:
425              CCSignalUnit(uint8_t Controller) {              
426                  Ctrl = Controller;              CCSignalUnit(SignalUnitRack* rack, Listener* l = NULL): SignalUnit(rack), Ctrls(128) {
427                    pListener = l;
428                    hasSmoothCtrls = isSmoothingOut = false;
429              }              }
430                            
431              CCSignalUnit(const CCSignalUnit& Unit) { Copy(Unit); }              CCSignalUnit(const CCSignalUnit& Unit): SignalUnit(Unit.pRack), Ctrls(128) { Copy(Unit); }
432              void operator=(const CCSignalUnit& Unit) { Copy(Unit); }              void operator=(const CCSignalUnit& Unit) { Copy(Unit); }
433                            
434              virtual void Copy(const CCSignalUnit& Unit) {              void Copy(const CCSignalUnit& Unit) {
435                  SignalUnitBase<O>::Copy(Unit);                  Ctrls.copy(Unit.Ctrls);
436                  Ctrl = Unit.Ctrl;                  pListener = Unit.pListener;
437                    hasSmoothCtrls = Unit.hasSmoothCtrls;
438                    isSmoothingOut = Unit.isSmoothingOut;
439                    SignalUnit::Copy(Unit);
440                }
441                
442                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                }
446                
447                void RemoveAllCCs() {
448                    Ctrls.clear();
449                }
450                
451                virtual void Increment() {
452                    if (hasSmoothCtrls && isSmoothingOut) Calculate();
453              }              }
454                            
455              virtual void Increment() { }              virtual void Trigger() {
456                    Calculate();
457                    bActive = Level != 0;
458                }
459                            
460              virtual void ProcessCCEvent(uint8_t Controller, uint8_t Value) {              virtual void ProcessCCEvent(uint8_t Controller, uint8_t Value) {
461                  if (Controller != Ctrl) return;                  bool recalculate = false;
462                                    
463                  // Normalize the value so it belongs to the interval [-1, +1]                  for (int i = 0; i < Ctrls.size(); i++) {
464                  SignalUnitBase<O>::Level = 2 * Value;                      if (Controller != Ctrls[i].Controller) continue;
465                  SignalUnitBase<O>::Level = SignalUnitBase<O>::Level/127.0f - 1.0f;                      if (Ctrls[i].Value == Value) continue;
466                        Ctrls[i].Value = Value;
467                        if (Ctrls[i].pSmoother != NULL) Ctrls[i].pSmoother->update(Value);
468                        if (!bActive) bActive = true;
469                        recalculate = true;
470                    }
471                                    
472                  if (!SignalUnitBase<O>::bActive) SignalUnitBase<O>::bActive = true;                  if (!(hasSmoothCtrls && isSmoothingOut) && recalculate) Calculate();
             }  
     };  
   
     /**  
      * Endpoint signal unit.  
      */  
     template<class O>  
     class EndpointSignalUnitBase : public SignalUnitBase<O>, public EndpointSignalUnit {  
         public:  
   
             virtual float CalculateFilterCutoff(float cutoff) {  
                 return GetFilterCutoff() * cutoff;  
473              }              }
474                            
475              virtual float CalculatePitch(float pitch) {              virtual void Calculate() {
476                  return GetPitch() * pitch;                  float l = 0;
477                    isSmoothingOut = false;
478                    for (int i = 0; i < Ctrls.size(); i++) {
479                        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                    }
486                    if (Level != l) {
487                        Level = l;
488                        if (pListener != NULL) pListener->ValueChanged(this);
489                    }
490              }              }
491                            
492              virtual float CalculateResonance(float res) {              virtual float Normalize(uint8_t val, short int curve = -1) {
493                  return GetResonance() * res;                  return val / 127.0f;
494              }              }
495      };      };
496            

Legend:
Removed from v.2205  
changed lines
  Added in v.2232

  ViewVC Help
Powered by ViewVC