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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2205 - (show annotations) (download) (as text)
Mon Jul 11 17:52:01 2011 UTC (12 years, 8 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 /***************************************************************************
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