/[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 2205 - (hide annotations) (download) (as text)
Mon Jul 11 17:52:01 2011 UTC (12 years, 9 months ago) by iliev
File MIME type: text/x-c++hdr
File size: 17474 byte(s)
* Introduced Signal Units and Signal Unit Racks, which hopefully will meet
  the demands of the new engines for flexible signal processing.
* sf2: Initial implementation of vibrato LFO, fixes in modulation EG and
  and volume EG (work in progress)

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     /**
32     * A signal unit consist of internal signal generator (like envelope generator,
33     * low frequency oscillator, etc) with a number of generator parameters which
34     * influence/modulate/dynamically change the generator's signal in some manner.
35     * The generators' parameters (also called signal unit parameters) can receive
36     * the signal of one or more signal units (through modulators if need)
37     * and use the (modulated) signals of those units to dynamically change the
38     * behavior of the signal generator. In turn, the signal of those unit can be fed
39     * to another unit(s) and influence its parameters.
40     */
41     class SignalUnit {
42     public:
43    
44     /**
45     * Used as an intermediate link between a source signal unit and
46     * a destination unit parameter. Modulates the received signal from the
47     * source unit and feed it to the unit parameter to which it is connected.
48     */
49     class Modulator {
50     public:
51     SignalUnit* pUnit; /* The signal source which will be used for modulation.
52     * If pUnit is NULL the level is considered to be 1!
53     */
54     float Coeff; // The modulation coefficient
55    
56     Modulator() : pUnit(NULL) { }
57     Modulator(SignalUnit* Unit) { pUnit = Unit; }
58     Modulator(const Modulator& Mod) { Copy(Mod); }
59     void operator=(const Modulator& Mod) { Copy(Mod); }
60    
61     virtual void Copy(const Modulator& Mod) {
62     if (this == &Mod) return;
63    
64     pUnit = Mod.pUnit;
65     Coeff = Mod.Coeff;
66     }
67    
68     /**
69     * Gets the normalized level of the signal source for the current
70     * time step (sample point). Returns 1 if source unit is NULL or
71     * if the source unit is inactive.
72     */
73     virtual float GetLevel() {
74     if (pUnit == NULL) return 1.0f;
75     return pUnit->Active() ? pUnit->GetLevel() : 1.0f;
76     }
77    
78     /**
79     * Gets the modulated level of the source signal
80     * for the current time step (sample point).
81     */
82     virtual float GetValue() {
83     return Transform(GetLevel());
84     }
85    
86     /**
87     * Calculates the transformation that should be applied
88     * to the signal of the source unit and multiplies by Coeff.
89     * This implementation of the method just multiplies by Coeff,
90     * or returns 1 if the source unit is inactive.
91     */
92     virtual float Transform(float val) {
93     if (pUnit != NULL && !pUnit->Active()) return 1;
94     return val * Coeff;
95     }
96     };
97    
98     /**
99     * This class represents a parameter which will influence the signal
100     * unit to which it belongs in certain way.
101     * For example, let's say the signal unit is a low frequency oscillator
102     * with frequency 1Hz. If we want to modulate the LFO to start with 1Hz
103     * and increment its frequency to 5Hz in 1 second, we can add
104     * a parameter with one modulator which signal source is an envelope
105     * generator with attack time of 1 second and coefficient 4. Thus, the
106     * normalized level of the EG will move from 0 to 1 in one second.
107     * On every time step (sample point) the normalized level
108     * will be multiplied by 4 (the parameter coefficient) and added to the
109     * LFO's frequency.
110     * So, after 1 second, the LFO frequency will be 1x4 + 1 = 5Hz.
111     * We can also add another parameter for modulating the LFO's pitch depth
112     * and so on.
113     */
114     class Parameter {
115     public:
116     ArrayList<Modulator> Modulators; // A list of signal units which will modulate this parameter
117    
118     SignalUnit* pUnit; /* If pUnit is not NULL, the modulators are ignored and
119     * this unit is used as only source.
120     * This is done for efficiency reasons.
121     */
122    
123     float Coeff; // The global modulation coefficient
124    
125    
126     Parameter() : Coeff(1), pUnit(NULL) { }
127    
128     /**
129     * Most often we just need to directly feed the signal of single unit
130     * to a unit parameter without any additional modulation.
131     * This constructor creates a parameter with only a single source
132     * unit without additional modulation, optimized for efficiency.
133     * @param unit The source unit used to influence this parameter.
134     * @param coeff The coefficient by which the normalized signal
135     * received from the source unit should be multiplied when a
136     * default transformation is done.
137     */
138     Parameter(SignalUnit* unit, float coeff = 1) {
139     pUnit = unit;
140     Coeff = coeff;
141     }
142    
143     Parameter(const Parameter& Prm) { Copy(Prm); }
144     void operator=(const Parameter& Prm) { Copy(Prm); }
145    
146     virtual void Copy(const Parameter& Prm) {
147     if (this == &Prm) return;
148    
149     Modulators = Prm.Modulators;
150     pUnit = Prm.pUnit;
151     Coeff = Prm.Coeff;
152     }
153    
154    
155     /**
156     * Calculates the global transformation for this parameter
157     * which should be applied to the parameter's value
158     * and multiplies by Coeff.
159     * This implementation of the method just multiplies by Coeff.
160     */
161     virtual float Transform(float val) {
162     return val * Coeff;
163     }
164    
165     /**
166     * Gets the current value of the parameter (without transformation).
167     * This implementation just sum the modulators values.
168     * If only a single unit is used without additional modulation
169     * returns the source unit's level or 1 if the unit is not active.
170     */
171     virtual float GetValue() {
172     if (pUnit != NULL) {
173     return pUnit->Active() ? pUnit->GetLevel() : 1.0f;
174     }
175    
176     float val = 0;
177     for(int i = 0; i < Modulators.size(); i++) {
178     val += Modulators[i].GetValue();
179     }
180    
181     return val;
182     }
183    
184     /** Gets the final value - with applied transformation. */
185     virtual float GetFinalValue() {
186     return Transform(GetValue());
187     }
188     };
189    
190    
191     public:
192     ArrayList<SignalUnit::Parameter> Params; // The list of parameters which are modulating the signal unit
193    
194     SignalUnit() : bActive(false), Level(0.0f), bCalculating(false), uiDelayTrigger(0) { }
195     SignalUnit(const SignalUnit& Unit) { Copy(Unit); }
196     void operator=(const SignalUnit& Unit) { Copy(Unit); }
197    
198     virtual void Copy(const SignalUnit& Unit) {
199     if (this == &Unit) return;
200    
201     bActive = Unit.bActive;
202     Level = Unit.Level;
203     Params = Unit.Params;
204     uiDelayTrigger = Unit.uiDelayTrigger;
205     bCalculating = false;
206     }
207    
208     /*
209     * Determines whether the unit is active.
210     * If the unit is not active, its level should be ignored.
211     * For endpoint unit this method determines whether
212     * the rendering should be stopped.
213     */
214     virtual bool Active() { return bActive; }
215    
216     /**
217     * Override this method to process the current control change events.
218     * @param itEvent - iterator pointing to the event to be processed.
219     */
220     virtual void ProcessCCEvent(uint8_t Controller, uint8_t Value) { }
221    
222     virtual void EnterReleaseStage() { }
223    
224     virtual void CancelRelease() { }
225    
226     /**
227     * Gets the normalized level of the unit for the current
228     * time step (sample point). The level is calculated if it's not
229     * calculated for the current step yet. Because the level depends on
230     * the parameters, their levels are calculated too.
231     */
232     virtual float GetLevel() {
233     if (!bRecalculate) return Level;
234    
235     if (bCalculating) {
236     std::cerr << "SignalUnit: Loop detected. Aborted!";
237     return Level;
238     }
239    
240     bCalculating = true;
241    
242     for(int i = 0; i < Params.size(); i++) {
243     Params[i].GetValue();
244     }
245    
246     bRecalculate = bCalculating = false;
247     return Level;
248     }
249    
250     /**
251     * Will be called to increment the time with one sample point.
252     * The unit should recalculate or prepare for recalculation
253     * its current level on every call of this function.
254     * Note that it is not known whether all source signal unit's levels
255     * are recalculated before the call of this method. So, the calculations
256     * that depends on the unit's parameters should be postponed to
257     * the call of GetLevel().
258     */
259     virtual void Increment() { bRecalculate = true; }
260    
261     /** Initializes and triggers the unit. */
262     virtual void Trigger() = 0;
263    
264     /**
265     * When the signal unit rack is triggered, it triggers all signal
266     * units it holds. If for some reason the triggering of a unit
267     * should be delayed, this method can be set to return non-zero value
268     * specifying the delay in time steps.
269     * Note that this is only a helper method and the implementation
270     * should be done manually.
271     */
272     virtual uint DelayTrigger() { return uiDelayTrigger; }
273    
274     protected:
275     bool bActive; /* Don't use it to check the active state of the unit!!!
276     * Use Active() instead! */
277     float Level;
278     bool bRecalculate; /* Determines whether the unit's level should be recalculated. */
279     bool bCalculating; /* Determines whether the unit is in process of calculating
280     * its level. Used for preventing infinite loops.
281     */
282    
283     uint uiDelayTrigger; /* in time steps */
284    
285     };
286    
287     class EndpointSignalUnit: virtual public SignalUnit {
288     public:
289     /**
290     * Gets the volume modulation value
291     * for the current time step (sample point).
292     */
293     virtual float GetVolume() = 0;
294    
295     /**
296     * Gets the filter cutoff frequency modulation value
297     * for the current time step (sample point).
298     */
299     virtual float GetFilterCutoff() = 0;
300    
301     /**
302     * Gets the pitch modulation value
303     * for the current time step (sample point).
304     */
305     virtual float GetPitch() = 0;
306    
307     /**
308     * Gets the resonance modulation value
309     * for the current time step (sample point).
310     */
311     virtual float GetResonance() = 0;
312    
313     virtual float CalculateFilterCutoff(float cutoff) = 0;
314    
315     virtual float CalculatePitch(float pitch) = 0;
316    
317     virtual float CalculateResonance(float res) = 0;
318     };
319    
320    
321     class SignalUnitRack;
322    
323     template <class O /* The signal unit's owner */>
324     class SignalUnitBase: virtual public SignalUnit {
325     public:
326     SignalUnitBase() : pOwner(NULL) { }
327     SignalUnitBase(const SignalUnitBase& Unit) { Copy(Unit); }
328     void operator=(const SignalUnitBase& Unit) { Copy(Unit); }
329    
330     virtual void Copy(const SignalUnitBase& Unit) {
331     if (this == &Unit) return;
332    
333     pOwner = Unit.pOwner;
334     SignalUnit::Copy(Unit);
335     }
336    
337     protected:
338     O* pOwner; // The owner to which this rack belongs.
339    
340     SignalUnitRack* GetSignalUnitRack() { return pOwner->GetSignalUnitRack(); }
341    
342     public:
343    
344    
345     /**
346     * The owner of the unit is set by the rack
347     * just before the call to the unit's trigger method.
348     */
349     void SetOwner(O* Owner) { pOwner = Owner; }
350    
351     /**
352     * A helper method which checks whether the delay
353     * stage is finished.
354     */
355     bool DelayStage() {
356     return (DelayTrigger() >= GetSignalUnitRack()->GetCurrentStep());
357     }
358     };
359    
360     /**
361     * Continuous controller signal unit.
362     * The level of this unit corresponds to the controller changes
363     * and is normalized to be in the range from -1 to +1.
364     */
365     template<class O>
366     class CCSignalUnit: public SignalUnitBase<O> {
367     private:
368     uint8_t Ctrl; // The number of the MIDI controller which modulates this signal unit.
369    
370     public:
371     CCSignalUnit(uint8_t Controller) {
372     Ctrl = Controller;
373     }
374    
375     CCSignalUnit(const CCSignalUnit& Unit) { Copy(Unit); }
376     void operator=(const CCSignalUnit& Unit) { Copy(Unit); }
377    
378     virtual void Copy(const CCSignalUnit& Unit) {
379     SignalUnitBase<O>::Copy(Unit);
380     Ctrl = Unit.Ctrl;
381     }
382    
383     virtual void Increment() { }
384    
385     virtual void ProcessCCEvent(uint8_t Controller, uint8_t Value) {
386     if (Controller != Ctrl) return;
387    
388     // Normalize the value so it belongs to the interval [-1, +1]
389     SignalUnitBase<O>::Level = 2 * Value;
390     SignalUnitBase<O>::Level = SignalUnitBase<O>::Level/127.0f - 1.0f;
391    
392     if (!SignalUnitBase<O>::bActive) SignalUnitBase<O>::bActive = true;
393     }
394     };
395    
396     /**
397     * Endpoint signal unit.
398     */
399     template<class O>
400     class EndpointSignalUnitBase : public SignalUnitBase<O>, public EndpointSignalUnit {
401     public:
402    
403     virtual float CalculateFilterCutoff(float cutoff) {
404     return GetFilterCutoff() * cutoff;
405     }
406    
407     virtual float CalculatePitch(float pitch) {
408     return GetPitch() * pitch;
409     }
410    
411     virtual float CalculateResonance(float res) {
412     return GetResonance() * res;
413     }
414     };
415    
416     } // namespace LinuxSampler
417    
418     #endif /* __LS_SIGNALUNIT_H__ */

  ViewVC Help
Powered by ViewVC