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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3561 - (show annotations) (download) (as text)
Fri Aug 23 11:44:00 2019 UTC (4 years, 8 months ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 19694 byte(s)
NKSP: Added standard units support for numbers and final "!" operator:

* NKSP strictness: Variable names, function names and preprocessor condition
  names must start with a regular character (a-z or A-Z); starting them with
  a digit or underscore is no longer allowed.

* NKSP parser fix: equal comparison operator "=" and not equal comparison
  operator "#" must only accept integer operands.

* NKSP language: Implemented support for standard units like Hertz, seconds,
  Bel including support for metric unit prefixes; so one can now e.g.
  conveniently use numbers in scripts like "5us" meaning "5 microseconds",
  or e.g. "12kHz" meaning "12 kilo Hertz", or e.g. "-14mdB" meaning
  "minus 14 Millidecibel", or e.g. "28c" meaning "28 cents" (for tuning).

* NKSP language: Introduced "final" operator "!" which is specifically
  intended for synthesis parameter values to denote that the synthesis
  parameter value is intended to be the "final" value for that synthesis
  parameter that should explicitly be used by the engine and thus causing
  the sampler engine to ignore all other modulation sources for the same
  synthesis parameter (like e.g. LFO, EG); by simply prefixing a value,
  variable or formula with this new "!" operator the expression is marked as
  being "final".

* Bumped version (2.1.1.svn4).

1 /*
2 * Copyright (c) 2016 - 2019 Christian Schoenebeck
3 *
4 * http://www.linuxsampler.org
5 *
6 * This file is part of LinuxSampler and released under the same terms.
7 * See README file for details.
8 */
9
10 #ifndef LS_NOTE_H
11 #define LS_NOTE_H
12
13 #include "../../common/Pool.h"
14 #include "Event.h"
15 #include "Fade.h"
16
17 #define DEFAULT_NOTE_VOLUME_TIME_S 0.013f /* 13ms */
18 #define DEFAULT_NOTE_PITCH_TIME_S 0.013f /* 13ms */
19 #define DEFAULT_NOTE_PAN_TIME_S 0.013f /* 13ms */
20
21 namespace LinuxSampler {
22
23 /// Whether release trigger sample(s) should be played and if yes under which circumstance(s). Options are bit flags to be able to combine them bitwise.
24 enum release_trigger_t {
25 release_trigger_none = 0, ///< Don't play release trigger sample.
26 release_trigger_noteoff = 1, ///< Play release trigger sample on MIDI note-off event.
27 release_trigger_sustain_maxvelocity = (1 << 1), ///< Play release trigger sample on sustain pedal up, use 127 as MIDI velocity.
28 release_trigger_sustain_keyvelocity = (1 << 2) ///< Play release trigger sample on sustain pedal up, use latest MIDI note-on velocity on key.
29 };
30
31 /// convenience macro for checking playing release trigger sample by sustain pedal in general
32 #define release_trigger_sustain \
33 (release_trigger_sustain_maxvelocity | release_trigger_sustain_keyvelocity)
34
35 // remove strictness of C++ regarding raw bitwise operations (on type release_trigger_t)
36 inline release_trigger_t operator|(release_trigger_t a, release_trigger_t b) {
37 return (release_trigger_t) ((int)a | (int)b);
38 }
39 inline release_trigger_t& operator|=(release_trigger_t& a, release_trigger_t b) {
40 a = (release_trigger_t) ((int)a | (int)b);
41 return a;
42 }
43
44 /**
45 * Abstract base class of its deriving @c Note class, this class (NoteBase)
46 * is not intended to be instantiated directly. It just provides access to
47 * the parts of a Note object which do not depend on any C++ template
48 * parameter.
49 */
50 class NoteBase {
51 public:
52 enum class ValueScope : unsigned char {
53 RELATIVE = (unsigned char) Event::ValueScope::RELATIVE,
54 FINAL_NORM = (unsigned char) Event::ValueScope::FINAL_NORM,
55 FINAL_NATIVE = (unsigned char) Event::ValueScope::FINAL_NATIVE,
56 };
57
58 /**
59 * General purpose note parameter value which might be both either in
60 * normalized value range (0..1) or in a native unit (i.e. seconds, Hz)
61 * depending on member variable @c Scope.
62 */
63 struct Param {
64 float Value;
65 ValueScope Scope;
66
67 Param() {
68 Value = 1.f;
69 Scope = ValueScope::RELATIVE;
70 }
71
72 bool isFinal() const {
73 return Scope == ValueScope::FINAL_NORM ||
74 Scope == ValueScope::FINAL_NATIVE;
75 }
76
77 template<typename T>
78 inline void applyTo(T& dst) {
79 if (isFinal())
80 dst = Value;
81 else
82 dst *= Value;
83 }
84 };
85
86 /**
87 * Parameter value being in normalized value range (0..1).
88 */
89 struct Norm {
90 float Value;
91 bool Final;
92
93 Norm() {
94 Value = 1.f;
95 Final = false;
96 }
97
98 template<typename T>
99 inline void applyTo(T& dst) {
100 if (Final)
101 dst = Value;
102 else
103 dst *= Value;
104 }
105 };
106
107 /**
108 * Parameter value being in signed normalized value range (-1..+1).
109 */
110 struct SNorm {
111 float Value;
112 bool Final;
113 int64_t Sources; ///< Might be used for calculating an average pan value in differential way: amount of times the @c Value had been changed and shall be calculated relatively upon.
114
115 SNorm() {
116 Value = 0.f;
117 Final = false;
118 Sources = 0;
119 }
120 };
121
122 int hostKey; ///< Key on which this is @c Note is allocated on. This is usually the note-on event's note number, however in case of a child note this will rather be the parent note's key instead!
123 note_id_t parentNoteID; ///< If not null: unique ID of the parent note of this note (see comments of field @c pChildNotes).
124 RTList<note_id_t>* pChildNotes; ///< Note ID list of "child" notes of this note. These are special notes that must be released once this note gets released.
125 Event cause; ///< Copy of the original event (usually a note-on event) which caused this note.
126 event_id_t eventID; ///< Unique ID of the actual original @c Event which caused this note.
127 sched_time_t triggerSchedTime; ///< Engine's scheduler time when this note was launched.
128 /// Optional synthesis parameters that might be overridden (by calling real-time instrument script functions like change_vol(), change_pitch(), etc.).
129 struct _Override {
130 Norm Volume; ///< as linear amplification ratio (1.0 being neutral)
131 float VolumeTime; ///< Transition duration (in seconds) for changes to @c Volume.
132 Norm Pitch; ///< as linear frequency ratio (1.0 being neutral)
133 float PitchTime; ///< Transition duration (in seconds) for changes to @c Pitch.
134 SNorm Pan; ///< between -1.0 (most left) and +1.0 (most right) and 0.0 being neutral.
135 float PanTime; ///< Transition duration (in seconds) for changes to @c Pan.
136 Param Cutoff; ///< between 0.0 and 1.0
137 Norm Resonance; ///< between 0.0 and 1.0
138 Param Attack; ///< between 0.0 and 1.0
139 Param Decay; ///< between 0.0 and 1.0
140 Norm Sustain; ///< between 0.0 and 1.0
141 Param Release; ///< between 0.0 and 1.0
142 Param CutoffAttack; ///< between 0.0 and 1.0
143 Param CutoffDecay; ///< between 0.0 and 1.0
144 Norm CutoffSustain;///< between 0.0 and 1.0
145 Param CutoffRelease;///< between 0.0 and 1.0
146 Norm AmpLFODepth; ///< between 0.0 and 1.0
147 Param AmpLFOFreq; ///< between 0.0 and 1.0
148 Norm CutoffLFODepth;///< between 0.0 and 1.0
149 Param CutoffLFOFreq; ///< between 0.0 and 1.0
150 Norm PitchLFODepth; ///< between 0.0 and 1.0
151 Param PitchLFOFreq; ///< between 0.0 and 1.0
152 fade_curve_t VolumeCurve;
153 fade_curve_t PitchCurve;
154 fade_curve_t PanCurve;
155 int SampleOffset; ///< Where the sample shall start playback in microseconds (otherwise this is -1 for being ignored).
156 } Override;
157 /// Sampler format specific informations and variables.
158 union _Format {
159 /// Gigasampler/GigaStudio format specifics.
160 struct _Gig {
161 uint8_t DimMask; ///< May be used to override the Dimension zone to be selected for a new voice: each 1 bit means that respective bit shall be overridden by taking the respective bit from DimBits instead.
162 uint8_t DimBits; ///< Used only in conjunction with DimMask: Dimension bits that shall be selected.
163 } Gig;
164 } Format;
165 vmint userPar[4]; ///< Used only for real-time instrument script functions set_event_par() and get_event_par() to store script author's user specific data ($EVENT_PAR_0 to $EVENT_PAR_3).
166
167 inline
168 void apply(RTList<Event>::Iterator& itEvent, Param _Override::*noteParam) {
169 const Event::ValueScope& scope = itEvent->Param.NoteSynthParam.Scope;
170 switch (scope) {
171 case Event::ValueScope::SELF_RELATIVE:
172 if ((this->Override.*noteParam).Scope == ValueScope::FINAL_NATIVE)
173 (this->Override.*noteParam) = Param();
174 itEvent->Param.NoteSynthParam.AbsValue =
175 ((this->Override.*noteParam).Value *= itEvent->Param.NoteSynthParam.Delta);
176 (this->Override.*noteParam).Scope = ValueScope::RELATIVE;
177 break;
178 case Event::ValueScope::RELATIVE:
179 (this->Override.*noteParam).Value =
180 itEvent->Param.NoteSynthParam.AbsValue =
181 itEvent->Param.NoteSynthParam.Delta;
182 (this->Override.*noteParam).Scope = ValueScope::RELATIVE;
183 break;
184 case Event::ValueScope::FINAL_SELF_RELATIVE:
185 if ((this->Override.*noteParam).Scope == ValueScope::FINAL_NATIVE)
186 (this->Override.*noteParam) = Param();
187 itEvent->Param.NoteSynthParam.AbsValue =
188 ((this->Override.*noteParam).Value *= itEvent->Param.NoteSynthParam.Delta);
189 (this->Override.*noteParam).Scope = ValueScope::FINAL_NORM;
190 break;
191 case Event::ValueScope::FINAL_NORM:
192 (this->Override.*noteParam).Value =
193 itEvent->Param.NoteSynthParam.AbsValue =
194 itEvent->Param.NoteSynthParam.Delta;
195 (this->Override.*noteParam).Scope = ValueScope::FINAL_NORM;
196 break;
197 case Event::ValueScope::FINAL_NATIVE:
198 (this->Override.*noteParam).Value =
199 itEvent->Param.NoteSynthParam.AbsValue =
200 itEvent->Param.NoteSynthParam.Delta;
201 (this->Override.*noteParam).Scope = ValueScope::FINAL_NATIVE;
202 break;
203 }
204 }
205
206 inline
207 void apply(RTList<Event>::Iterator& itEvent, Norm _Override::*noteParam) {
208 const Event::ValueScope& scope = itEvent->Param.NoteSynthParam.Scope;
209 switch (scope) {
210 case Event::ValueScope::SELF_RELATIVE:
211 itEvent->Param.NoteSynthParam.AbsValue =
212 ((this->Override.*noteParam).Value *= itEvent->Param.NoteSynthParam.Delta);
213 (this->Override.*noteParam).Final = false;
214 break;
215 case Event::ValueScope::RELATIVE:
216 (this->Override.*noteParam).Value =
217 itEvent->Param.NoteSynthParam.AbsValue =
218 itEvent->Param.NoteSynthParam.Delta;
219 (this->Override.*noteParam).Final = false;
220 break;
221 case Event::ValueScope::FINAL_SELF_RELATIVE:
222 itEvent->Param.NoteSynthParam.AbsValue =
223 ((this->Override.*noteParam).Value *= itEvent->Param.NoteSynthParam.Delta);
224 (this->Override.*noteParam).Final = true;
225 break;
226 case Event::ValueScope::FINAL_NORM:
227 (this->Override.*noteParam).Value =
228 itEvent->Param.NoteSynthParam.AbsValue =
229 itEvent->Param.NoteSynthParam.Delta;
230 (this->Override.*noteParam).Final = true;
231 break;
232 case Event::ValueScope::FINAL_NATIVE:
233 dmsg(1,("BUG: Attempt to assign a value in native unit to a Note parameter being in normalized value range only!\n"));
234 break;
235 }
236 }
237
238 inline
239 void apply(RTList<Event>::Iterator& itEvent, SNorm _Override::*noteParam) {
240 const Event::ValueScope& scope = itEvent->Param.NoteSynthParam.Scope;
241 switch (scope) {
242 case Event::ValueScope::SELF_RELATIVE:
243 itEvent->Param.NoteSynthParam.AbsValue =
244 (this->Override.*noteParam).Value = RTMath::RelativeSummedAvg(
245 (this->Override.*noteParam).Value,
246 itEvent->Param.NoteSynthParam.Delta,
247 ++(this->Override.*noteParam).Sources
248 );
249 (this->Override.*noteParam).Final = false;
250 break;
251 case Event::ValueScope::RELATIVE:
252 (this->Override.*noteParam).Value =
253 itEvent->Param.NoteSynthParam.AbsValue =
254 itEvent->Param.NoteSynthParam.Delta;
255 (this->Override.*noteParam).Sources = 1;
256 (this->Override.*noteParam).Final = false;
257 break;
258 case Event::ValueScope::FINAL_SELF_RELATIVE:
259 itEvent->Param.NoteSynthParam.AbsValue =
260 (this->Override.*noteParam).Value = RTMath::RelativeSummedAvg(
261 (this->Override.*noteParam).Value,
262 itEvent->Param.NoteSynthParam.Delta,
263 ++(this->Override.*noteParam).Sources
264 );
265 (this->Override.*noteParam).Final = true;
266 break;
267 case Event::ValueScope::FINAL_NORM:
268 (this->Override.*noteParam).Value =
269 itEvent->Param.NoteSynthParam.AbsValue =
270 itEvent->Param.NoteSynthParam.Delta;
271 (this->Override.*noteParam).Sources = 1;
272 (this->Override.*noteParam).Final = true;
273 break;
274 case Event::ValueScope::FINAL_NATIVE:
275 dmsg(1,("BUG: Attempt to assign a value in native unit to a Note parameter being in signed normalized value range only!\n"));
276 break;
277 }
278 }
279
280 inline static ValueScope scopeBy_FinalUnit(bool bFinal, bool bNativeUnit) {
281 if (!bFinal) return ValueScope::RELATIVE;
282 return (bNativeUnit) ? ValueScope::FINAL_NATIVE : ValueScope::FINAL_NORM;
283 }
284 protected:
285 NoteBase() : hostKey(0), parentNoteID(0), pChildNotes(NULL) {
286 Override.Volume = Norm();
287 Override.VolumeTime = DEFAULT_NOTE_VOLUME_TIME_S;
288 Override.Pitch = Norm();
289 Override.PitchTime = DEFAULT_NOTE_PITCH_TIME_S;
290 Override.Pan = SNorm();
291 Override.PanTime = DEFAULT_NOTE_PAN_TIME_S;
292 Override.Cutoff = Param();
293 Override.Resonance = Norm();
294 Override.Attack = Param();
295 Override.Decay = Param();
296 Override.Sustain = Norm();
297 Override.Release = Param();
298 Override.CutoffAttack = Param();
299 Override.CutoffDecay = Param();
300 Override.CutoffSustain = Norm();
301 Override.CutoffRelease = Param();
302 Override.AmpLFODepth = Norm();
303 Override.AmpLFOFreq = Param();
304 Override.CutoffLFODepth = Norm();
305 Override.CutoffLFOFreq = Param();
306 Override.PitchLFODepth = Norm();
307 Override.PitchLFOFreq = Param();
308 Override.VolumeCurve = DEFAULT_FADE_CURVE;
309 Override.PitchCurve = DEFAULT_FADE_CURVE;
310 Override.PanCurve = DEFAULT_FADE_CURVE;
311 Override.SampleOffset = -1;
312
313 Format = _Format();
314
315 userPar[0] = 0;
316 userPar[1] = 0;
317 userPar[2] = 0;
318 userPar[3] = 0;
319 }
320 };
321
322 /**
323 * Contains the voices caused by one specific note, as well as basic
324 * information about the note itself. You can see a Note object as one
325 * specific event in time where one or more voices were spawned at the same
326 * time and all those voices due to the same cause.
327 *
328 * For example when you press down and hold the sustain pedal, and then
329 * trigger the same note on the keyboard multiple times, for each key
330 * strokes a separate Note instance is created. Assuming you have a layered
331 * sound with 4 layers, then for each note that is triggered 4 voices will
332 * be spawned and assigned to the same Note object. By grouping those voices
333 * to one specific Note object, it allows to control the synthesis paramters
334 * of those layered voices simultaniously.
335 *
336 * If your instrument contains a real-time instrument script, then that
337 * script might also trigger additional voices programmatically (by
338 * calling the built-in script function play_note()). Each time the script
339 * calls play_note() a new Note instance is created and the script may then
340 * further control the voices of specific notes independently from each
341 * other. For example for each key stroke on your keyboard the instrument
342 * script might trigger 3 additional notes programmatically and assign a
343 * different tuning filter parameters for each one of the 3 notes
344 * independently.
345 */
346 template<class V>
347 class Note : public NoteBase {
348 public:
349 RTList<V>* pActiveVoices; ///< Contains the active voices associated with this note.
350
351 Note() : NoteBase(), pActiveVoices(NULL) {}
352
353 virtual ~Note() {
354 if (pChildNotes) delete pChildNotes;
355 if (pActiveVoices) delete pActiveVoices;
356 }
357
358 void init(Pool<V>* pVoicePool, Pool<note_id_t>* pNoteIDPool) {
359 if (pActiveVoices) delete pActiveVoices;
360 pActiveVoices = new RTList<V>(pVoicePool);
361 if (pChildNotes) delete pChildNotes;
362 pChildNotes = new RTList<note_id_t>(pNoteIDPool);
363 }
364
365 void reset() {
366 hostKey = 0;
367 parentNoteID = 0;
368 if (pChildNotes)
369 pChildNotes->clear();
370 cause = Event();
371 eventID = 0;
372 Override.Volume = Norm();
373 Override.VolumeTime = DEFAULT_NOTE_VOLUME_TIME_S;
374 Override.Pitch = Norm();
375 Override.PitchTime = DEFAULT_NOTE_PITCH_TIME_S;
376 Override.Pan = SNorm();
377 Override.PanTime = DEFAULT_NOTE_PAN_TIME_S;
378 Override.Cutoff = Param();
379 Override.Resonance = Norm();
380 Override.Attack = Param();
381 Override.Decay = Param();
382 Override.Sustain = Norm();
383 Override.Release = Param();
384 Override.CutoffAttack = Param();
385 Override.CutoffDecay = Param();
386 Override.CutoffSustain = Norm();
387 Override.CutoffRelease = Param();
388 Override.AmpLFODepth = Norm();
389 Override.AmpLFOFreq = Param();
390 Override.CutoffLFODepth = Norm();
391 Override.CutoffLFOFreq = Param();
392 Override.PitchLFODepth = Norm();
393 Override.PitchLFOFreq = Param();
394 Override.VolumeCurve = DEFAULT_FADE_CURVE;
395 Override.PitchCurve = DEFAULT_FADE_CURVE;
396 Override.PanCurve = DEFAULT_FADE_CURVE;
397 Override.SampleOffset = -1;
398 Format = _Format();
399 userPar[0] = 0;
400 userPar[1] = 0;
401 userPar[2] = 0;
402 userPar[3] = 0;
403 if (pActiveVoices) {
404 typename RTList<V>::Iterator itVoice = pActiveVoices->first();
405 typename RTList<V>::Iterator itVoicesEnd = pActiveVoices->end();
406 for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key
407 itVoice->VoiceFreed();
408 }
409 pActiveVoices->clear();
410 }
411 }
412 };
413
414 } // namespace LinuxSampler
415
416 #endif // LS_NOTE_H

  ViewVC Help
Powered by ViewVC