3 |
* LinuxSampler - modular, streaming capable sampler * |
* LinuxSampler - modular, streaming capable sampler * |
4 |
* * |
* * |
5 |
* Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * |
* Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * |
6 |
* Copyright (C) 2005 - 2016 Christian Schoenebeck * |
* Copyright (C) 2005 - 2019 Christian Schoenebeck * |
7 |
* * |
* * |
8 |
* This program is free software; you can redistribute it and/or modify * |
* This program is free software; you can redistribute it and/or modify * |
9 |
* it under the terms of the GNU General Public License as published by * |
* it under the terms of the GNU General Public License as published by * |
29 |
#include "../../common/RTAVLTree.h" |
#include "../../common/RTAVLTree.h" |
30 |
#include "../../common/Pool.h" |
#include "../../common/Pool.h" |
31 |
#include "../EngineChannel.h" |
#include "../EngineChannel.h" |
32 |
|
#include "../../scriptvm/common.h" |
33 |
|
|
34 |
|
// On Windows RELATIVE might be defined as macro in wingdi.h, which would |
35 |
|
// cause a compiler error of the same token used in this header file below. |
36 |
|
// So we undefine that macro here for now (if present). |
37 |
|
#ifdef RELATIVE |
38 |
|
# warning Preprocessor conflict detected: Macro RELATIVE was declared by system headers; undefining it here. |
39 |
|
# undef RELATIVE |
40 |
|
#endif |
41 |
|
|
42 |
namespace LinuxSampler { |
namespace LinuxSampler { |
43 |
|
|
66 |
public: |
public: |
67 |
EventGenerator(uint SampleRate); |
EventGenerator(uint SampleRate); |
68 |
void UpdateFragmentTime(uint SamplesToProcess); |
void UpdateFragmentTime(uint SamplesToProcess); |
69 |
|
void SetSampleRate(uint SampleRate); |
70 |
Event CreateEvent(); |
Event CreateEvent(); |
71 |
Event CreateEvent(int32_t FragmentPos); |
Event CreateEvent(int32_t FragmentPos); |
72 |
|
|
77 |
RTList<ScriptEvent>::Iterator popNextScheduledScriptEvent(RTAVLTree<ScriptEvent>& queue, Pool<ScriptEvent>& pool, sched_time_t end); |
RTList<ScriptEvent>::Iterator popNextScheduledScriptEvent(RTAVLTree<ScriptEvent>& queue, Pool<ScriptEvent>& pool, sched_time_t end); |
78 |
|
|
79 |
/** |
/** |
80 |
|
* Returns the scheduler time for the first sample point of the |
81 |
|
* current audio fragment cycle. |
82 |
|
*/ |
83 |
|
sched_time_t schedTimeAtCurrentFragmentStart() const { |
84 |
|
return uiTotalSamplesProcessed; |
85 |
|
} |
86 |
|
|
87 |
|
/** |
88 |
* Returns the scheduler time for the first sample point of the next |
* Returns the scheduler time for the first sample point of the next |
89 |
* audio fragment cycle. |
* audio fragment cycle. |
90 |
*/ |
*/ |
110 |
}; |
}; |
111 |
|
|
112 |
/** |
/** |
113 |
|
* Unique numeric ID of an event which can be used to retrieve access to |
114 |
|
* the actual @c Event object. Once the event associated with a certain ID |
115 |
|
* was released (back to its event pool), this numeric ID becomes invalid |
116 |
|
* and Pool< Event >::fromID() will detect this circumstance and will |
117 |
|
* return an invalid Iterator, and thus will prevent you from misusing an |
118 |
|
* event which no longer "exists". |
119 |
|
* |
120 |
|
* Note that an @c Event object usually just "exists" for exactly on audio |
121 |
|
* fragment cycle: that is it exists right from the beginning of the audio |
122 |
|
* fragment cycle where it was caused (i.e. where its MIDI data was |
123 |
|
* received by the respective engine channel) and will disappear |
124 |
|
* automatically at the end of that audio fragment cycle. |
125 |
|
*/ |
126 |
|
typedef pool_element_id_t event_id_t; |
127 |
|
|
128 |
|
/** |
129 |
|
* Unique numeric ID of a note which can be used to retrieve access to the |
130 |
|
* actual @c Note object. Once the note associated with a certain ID was |
131 |
|
* released (back to its note pool), this numeric ID becomes invalid and |
132 |
|
* Pool< Note >::fromID() will detect this circumstance and will return |
133 |
|
* an invalid Iterator, and thus will prevent you from misusing a note |
134 |
|
* which no longer is "alive". |
135 |
|
* |
136 |
|
* A @c Note object exists right when the respective MIDI note-on event |
137 |
|
* was received by the respective engine channel, and remains existent |
138 |
|
* until the caused note and all its voices were finally freed (which might |
139 |
|
* even be long time after the respective note-off event was received, |
140 |
|
* depending on the duration of the voice's release stages etc.). |
141 |
|
*/ |
142 |
|
typedef pool_element_id_t note_id_t; |
143 |
|
|
144 |
|
/** |
145 |
|
* Unique numeric ID of a script callback ID instance which can be used to |
146 |
|
* retrieve access to the actual @c ScriptEvent object. Once the script |
147 |
|
* callback instance associated with a certain ID stopped its execution |
148 |
|
* (that is completely stopped, not just suspended) then this numeric ID |
149 |
|
* becomes invalid and Pool< ScriptEvent >::fromID() will detect this |
150 |
|
* circumstance and will return an invalid Iterator, and thus will prevent |
151 |
|
* you from misusing a script callback instance which no longer "exists". |
152 |
|
*/ |
153 |
|
typedef pool_element_id_t script_callback_id_t; |
154 |
|
|
155 |
|
/** |
156 |
* Events are usually caused by a MIDI source or an internal modulation |
* Events are usually caused by a MIDI source or an internal modulation |
157 |
* controller like LFO or EG. An event should only be created by an |
* controller like LFO or EG. An event should only be created by an |
158 |
* EventGenerator! |
* EventGenerator! |
163 |
public: |
public: |
164 |
Event(){} |
Event(){} |
165 |
enum type_t { |
enum type_t { |
166 |
type_note_on, |
type_note_on, ///< (real) MIDI note-on event |
167 |
type_note_off, |
type_note_off, ///< (real) MIDI note-off event |
168 |
type_pitchbend, |
type_pitchbend, ///< MIDI pitch bend wheel change event |
169 |
type_control_change, |
type_control_change, ///< MIDI CC event |
170 |
type_sysex, ///< MIDI system exclusive message |
type_sysex, ///< MIDI system exclusive message |
171 |
type_cancel_release, ///< transformed either from a note-on or sustain-pedal-down event |
type_cancel_release_key, ///< transformed either from a (real) MIDI note-on or sustain-pedal-down event |
172 |
type_release, ///< transformed either from a note-off or sustain-pedal-up event |
type_release_key, ///< transformed either from a (real) MIDI note-off or sustain-pedal-up event |
173 |
|
type_release_note, ///< transformed from a type_stop_note event |
174 |
type_channel_pressure, ///< a.k.a. aftertouch |
type_channel_pressure, ///< a.k.a. aftertouch |
175 |
type_note_pressure, ///< polyphonic key pressure (aftertouch) |
type_note_pressure, ///< polyphonic key pressure (aftertouch) |
176 |
|
type_play_note, ///< caused by a call to built-in instrument script function play_note() |
177 |
|
type_stop_note, ///< caused by a call to built-in instrument script function note_off() |
178 |
|
type_kill_note, ///< caused by a call to built-in instrument script function fade_out() |
179 |
|
type_note_synth_param, ///< change a note's synthesis parameters (upon real-time instrument script function calls, i.e. change_vol(), change_tune(), change_pan(), etc.) |
180 |
} Type; |
} Type; |
181 |
|
enum synth_param_t { |
182 |
|
synth_param_volume, |
183 |
|
synth_param_volume_time, |
184 |
|
synth_param_volume_curve, |
185 |
|
synth_param_pitch, |
186 |
|
synth_param_pitch_time, |
187 |
|
synth_param_pitch_curve, |
188 |
|
synth_param_pan, |
189 |
|
synth_param_pan_time, |
190 |
|
synth_param_pan_curve, |
191 |
|
synth_param_cutoff, |
192 |
|
synth_param_resonance, |
193 |
|
synth_param_attack, |
194 |
|
synth_param_decay, |
195 |
|
synth_param_sustain, |
196 |
|
synth_param_release, |
197 |
|
synth_param_cutoff_attack, |
198 |
|
synth_param_cutoff_decay, |
199 |
|
synth_param_cutoff_sustain, |
200 |
|
synth_param_cutoff_release, |
201 |
|
synth_param_amp_lfo_depth, |
202 |
|
synth_param_amp_lfo_freq, |
203 |
|
synth_param_cutoff_lfo_depth, |
204 |
|
synth_param_cutoff_lfo_freq, |
205 |
|
synth_param_pitch_lfo_depth, |
206 |
|
synth_param_pitch_lfo_freq, |
207 |
|
}; |
208 |
|
enum class ValueScope : unsigned char { |
209 |
|
/** |
210 |
|
* The new synthesis parameter value should be applied |
211 |
|
* relatively to itself (as normalized value range), and then |
212 |
|
* applied relatively against other sources (i.e. LFOs, EGs) |
213 |
|
* for the same synthesis parameter. |
214 |
|
*/ |
215 |
|
SELF_RELATIVE = 1, |
216 |
|
/** |
217 |
|
* The new synthesis paramater value of itself should be |
218 |
|
* replaced, and then applied relatively to other sources |
219 |
|
* (i.e. LFOs, EGs) for the same synthesis parameter. |
220 |
|
*/ |
221 |
|
RELATIVE = 0, //IMPORANT: must remain 0 because of the union structure below which would otherwise i.e. assign invalid pointers/IDs to Param.Note structure in Init() |
222 |
|
/** |
223 |
|
* The new synthesis parameter value should be applied |
224 |
|
* relatively to itself (as normalized value range), and then |
225 |
|
* applied directly (as normalized value range) as final value |
226 |
|
* of this synthesis chain, thus all other sources (i.e. LFOs, |
227 |
|
* EGs) should entirely be ignored. |
228 |
|
*/ |
229 |
|
FINAL_SELF_RELATIVE = 2, |
230 |
|
/** |
231 |
|
* The new synthesis parameter value of itself should be |
232 |
|
* replaced, and then applied directly (as normalized value |
233 |
|
* range) as final value of this synthesis chain, thus all other |
234 |
|
* sources (i.e. LFOs, EGs) should entirely be ignored. |
235 |
|
*/ |
236 |
|
FINAL_NORM = 3, |
237 |
|
/** |
238 |
|
* Same as @c FINAL_NORM, but this one is already in the native |
239 |
|
* unit (i.e. seconds, Hz) of this synthesis parameter. |
240 |
|
*/ |
241 |
|
FINAL_NATIVE = 4, |
242 |
|
}; |
243 |
union { |
union { |
244 |
/// Note-on and note-off event specifics |
/// Note-on and note-off event specifics |
245 |
struct _Note { |
struct _Note { |
248 |
uint8_t Velocity; ///< Trigger or release velocity of note-on / note-off event. |
uint8_t Velocity; ///< Trigger or release velocity of note-on / note-off event. |
249 |
int8_t Layer; ///< Layer index (usually only used if a note-on event has to be postponed, e.g. due to shortage of free voices). |
int8_t Layer; ///< Layer index (usually only used if a note-on event has to be postponed, e.g. due to shortage of free voices). |
250 |
int8_t ReleaseTrigger; ///< If new voice should be a release triggered voice (actually boolean field and usually only used if a note-on event has to be postponed, e.g. due to shortage of free voices). |
int8_t ReleaseTrigger; ///< If new voice should be a release triggered voice (actually boolean field and usually only used if a note-on event has to be postponed, e.g. due to shortage of free voices). |
251 |
|
note_id_t ID; ///< Unique numeric ID of the @c Note object associated with this note event. |
252 |
|
note_id_t ParentNoteID; ///< If not zero: Unique numeric ID of the parent @c Note object that shall become parent of resulting new Note object of this Event. So this is used to associate a new note with a previous note, i.e. to release the new note once the parent note was released. |
253 |
void* pRegion; ///< Engine specific pointer to instrument region |
void* pRegion; ///< Engine specific pointer to instrument region |
254 |
} Note; |
} Note; |
255 |
/// Control change event specifics |
/// Control change event specifics |
279 |
uint8_t Key; ///< MIDI note number where key pressure (polyphonic aftertouch) changed. |
uint8_t Key; ///< MIDI note number where key pressure (polyphonic aftertouch) changed. |
280 |
uint8_t Value; ///< New pressure value for note. |
uint8_t Value; ///< New pressure value for note. |
281 |
} NotePressure; |
} NotePressure; |
282 |
|
///< Note synthesis parameter change event's specifics (used for real-time instrument script built-in functions which may alter synthesis parameters on note level). |
283 |
|
struct _NoteSynthParam { |
284 |
|
note_id_t NoteID; ///< ID of Note whose voices shall be modified. |
285 |
|
synth_param_t Type; ///< Synthesis parameter which is to be changed. |
286 |
|
float Delta; ///< The value change that should be applied against the note's current synthesis parameter value. |
287 |
|
float AbsValue; ///< New current absolute value of synthesis parameter (that is after @c Delta being applied). |
288 |
|
ValueScope Scope; ///< How @c Delta should be applied against @c AbsValue, and how @c AbsValue should then actually be applied to the synthesis chain. |
289 |
|
|
290 |
|
inline bool isFinal() const { return Scope >= ValueScope::FINAL_SELF_RELATIVE; } |
291 |
|
} NoteSynthParam; |
292 |
} Param; |
} Param; |
|
/// Sampler format specific informations and variables. |
|
|
union { |
|
|
/// Gigasampler/GigaStudio format specifics. |
|
|
struct _Gig { |
|
|
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. |
|
|
uint8_t DimBits; ///< Used only in conjunction with DimMask: Dimension bits that shall be selected. |
|
|
} Gig; |
|
|
} Format; |
|
293 |
EngineChannel* pEngineChannel; ///< Pointer to the EngineChannel where this event occured on, NULL means Engine global event (e.g. SysEx message). |
EngineChannel* pEngineChannel; ///< Pointer to the EngineChannel where this event occured on, NULL means Engine global event (e.g. SysEx message). |
294 |
MidiInputPort* pMidiInputPort; ///< Pointer to the MIDI input port on which this event occured (NOTE: currently only for global events, that is SysEx messages) |
MidiInputPort* pMidiInputPort; ///< Pointer to the MIDI input port on which this event occured (NOTE: currently only for global events, that is SysEx messages) |
295 |
|
|
296 |
|
inline void Init() { |
297 |
|
//FIXME: probably we should memset() zero entire structure here, due to potential union initialization conflicts (see comment on ValueScope::RELATIVE) |
298 |
|
Param.Note.ID = 0; |
299 |
|
Param.Note.ParentNoteID = 0; |
300 |
|
Param.NoteSynthParam.NoteID = 0; |
301 |
|
Param.NoteSynthParam.Scope = ValueScope::RELATIVE; |
302 |
|
} |
303 |
inline int32_t FragmentPos() { |
inline int32_t FragmentPos() { |
304 |
if (iFragmentPos >= 0) return iFragmentPos; |
if (iFragmentPos >= 0) return iFragmentPos; |
305 |
iFragmentPos = pEventGenerator->ToFragmentPos(TimeStamp); |
iFragmentPos = pEventGenerator->ToFragmentPos(TimeStamp); |
309 |
inline void ResetFragmentPos() { |
inline void ResetFragmentPos() { |
310 |
iFragmentPos = -1; |
iFragmentPos = -1; |
311 |
} |
} |
312 |
|
inline void CopyTimeFrom(const Event& other) { |
313 |
|
TimeStamp = other.TimeStamp; |
314 |
|
iFragmentPos = other.iFragmentPos; |
315 |
|
} |
316 |
|
inline sched_time_t SchedTime() { |
317 |
|
return pEventGenerator->schedTimeAtCurrentFragmentStart() + FragmentPos(); |
318 |
|
} |
319 |
|
inline static ValueScope scopeBy_FinalRelativeUnit(bool bFinal, bool bRelative, bool bNativeUnit) { |
320 |
|
if (!bFinal && bRelative) |
321 |
|
return ValueScope::SELF_RELATIVE; |
322 |
|
if (!bFinal) |
323 |
|
return ValueScope::RELATIVE; |
324 |
|
if (bRelative) |
325 |
|
return ValueScope::FINAL_SELF_RELATIVE; |
326 |
|
if (bNativeUnit) |
327 |
|
return ValueScope::FINAL_NATIVE; |
328 |
|
return ValueScope::FINAL_NORM; |
329 |
|
} |
330 |
protected: |
protected: |
331 |
typedef EventGenerator::time_stamp_t time_stamp_t; |
typedef EventGenerator::time_stamp_t time_stamp_t; |
332 |
Event(EventGenerator* pGenerator, EventGenerator::time_stamp_t Time); |
Event(EventGenerator* pGenerator, EventGenerator::time_stamp_t Time); |
345 |
*/ |
*/ |
346 |
class SchedulerNode : public RTAVLNode { |
class SchedulerNode : public RTAVLNode { |
347 |
public: |
public: |
348 |
|
using RTAVLNode::reset; // make reset() method public |
349 |
|
|
350 |
sched_time_t scheduleTime; ///< Time ahead in future (in sample points) when this object shall be processed. This value is compared with EventGenerator's uiTotalSamplesProcessed member variable. |
sched_time_t scheduleTime; ///< Time ahead in future (in sample points) when this object shall be processed. This value is compared with EventGenerator's uiTotalSamplesProcessed member variable. |
351 |
|
|
352 |
/// Required operator implementation for RTAVLTree class. |
/// Required operator implementation for RTAVLTree class. |
358 |
inline bool operator<(const SchedulerNode& other) const { |
inline bool operator<(const SchedulerNode& other) const { |
359 |
return this->scheduleTime < other.scheduleTime; |
return this->scheduleTime < other.scheduleTime; |
360 |
} |
} |
361 |
|
|
362 |
|
/// This is actually just for code readability. |
363 |
|
inline RTAVLTreeBase* currentSchedulerQueue() const { return rtavlTree(); } |
364 |
}; |
}; |
365 |
|
|
366 |
/** |
/** |
376 |
class VMEventHandler; |
class VMEventHandler; |
377 |
class VMExecContext; |
class VMExecContext; |
378 |
|
|
379 |
|
/** |
380 |
|
* Maximum amount of child script handler instances one script handler is |
381 |
|
* allowed to create by calling built-in script function fork(). |
382 |
|
*/ |
383 |
|
#define MAX_FORK_PER_SCRIPT_HANDLER 8 |
384 |
|
|
385 |
/** @brief Real-time instrument script event. |
/** @brief Real-time instrument script event. |
386 |
* |
* |
387 |
* Encapsulates one execution instance of a real-time instrument script for |
* Encapsulates one execution instance of a real-time instrument script for |
397 |
*/ |
*/ |
398 |
class ScriptEvent : public SchedulerNode { |
class ScriptEvent : public SchedulerNode { |
399 |
public: |
public: |
400 |
Event cause; ///< Original external event that triggered this script event (i.e. MIDI note on event, MIDI CC event, etc.). |
Event cause; ///< Copy of original external @c Event that triggered this script event (i.e. MIDI note on event, MIDI CC event, etc.). |
401 |
int id; ///< Unique ID of the external event that triggered this script event. |
pool_element_id_t id; ///< Native representation of built-in script variable $EVENT_ID. For scripts' "note" event handler this will reflect the unique ID of the @c Note object, for all other event handlers the unique ID of the original external @c Event object that triggered this script event. |
402 |
VMEventHandler** handlers; ///< The script's event handlers (callbacks) to be processed (NULL terminated list). |
VMEventHandler** handlers; ///< The script's event handlers (callbacks) to be processed (NULL terminated list). |
403 |
VMExecContext* execCtx; ///< Script's current execution state (polyphonic variables and execution stack). |
VMExecContext* execCtx; ///< Script's current execution state (polyphonic variables and execution stack). |
404 |
int currentHandler; ///< Current index in 'handlers' list above. |
int currentHandler; ///< Current index in 'handlers' list above. |
405 |
int executionSlices; ///< Amount of times this script event has been executed by the ScriptVM runner class. |
int executionSlices; ///< Amount of times this script event has been executed by the ScriptVM runner class. |
406 |
|
bool ignoreAllWaitCalls; ///< If true: calling any built-in wait*() script function should be ignored (this variable may be set with the 2nd argument of built-in script function stop_wait()). |
407 |
|
VMEventHandlerType_t handlerType; ///< Native representation of built-in script variable $NI_CALLBACK_TYPE, reflecting the script event type of this script event. |
408 |
|
script_callback_id_t parentHandlerID; ///< Only in case this script handler instance was created by calling built-in script function fork(): callback ID of the parent event handler instance which created this child. For regular event handler instances which were not created by fork(), this variable reflects 0 (which is always considered an invalid handler ID). |
409 |
|
script_callback_id_t childHandlerID[MAX_FORK_PER_SCRIPT_HANDLER+1]; ///< In case built-in script function fork() was called by this script handler instance: A zero terminated ID list of all child event handler instances (note: children will not vanish from this list after they terminated). |
410 |
|
bool autoAbortByParent; ///< Only if this is a child event handler created by calling fork(): if this is true then this child will automatically aborted if the parent event handler terminates. |
411 |
|
int forkIndex; ///< Only for fork() calls: distinguishment feature which is 0 for parent, 1 for 1st child, 2 for 2nd child, etc. |
412 |
|
|
413 |
|
void forkTo(ScriptEvent* e, bool bAutoAbort) const; |
414 |
|
int countChildHandlers() const; |
415 |
|
void addChildHandlerID(script_callback_id_t childID); |
416 |
}; |
}; |
417 |
|
|
418 |
/** |
/** |
419 |
* Insert given @a node into the supplied timing @a queue with a scheduled |
* Insert given @a node into the supplied timing @a queue with a scheduled |
420 |
* timing position given by @a fragmentPosBase and @a microseconds, where |
* timing position given by @a fragmentPosBase and @a microseconds, where |
421 |
* @a microseconds reflects the amount microseconds in future from "now" |
* @a microseconds reflects the amount of microseconds in future from "now" |
422 |
* where the node shall be scheduled, and @a fragmentPos identifies the |
* where the node shall be scheduled, and @a fragmentPos identifies the |
423 |
* sample point within the current audio fragment cycle which shall be |
* sample point within the current audio fragment cycle which shall be |
424 |
* interpreted by this method to be "now". |
* interpreted by this method to be "now". |
425 |
* |
* |
426 |
* The meaning of @a fragmentPosBase becomes more important the larger |
* The meaning of @a fragmentPosBase becomes more important the larger |
427 |
* the audio fragment size, and vice versa it bcomes less important the |
* the audio fragment size, and vice versa it becomes less important the |
428 |
* smaller the audio fragment size. |
* smaller the audio fragment size. |
429 |
* |
* |
430 |
* @param queue - destination scheduler queue |
* @param queue - destination scheduler queue |
434 |
*/ |
*/ |
435 |
template<typename T> |
template<typename T> |
436 |
void EventGenerator::scheduleAheadMicroSec(RTAVLTree<T>& queue, T& node, int32_t fragmentPosBase, uint64_t microseconds) { |
void EventGenerator::scheduleAheadMicroSec(RTAVLTree<T>& queue, T& node, int32_t fragmentPosBase, uint64_t microseconds) { |
437 |
node.scheduleTime = uiTotalSamplesProcessed + fragmentPosBase + float(uiSampleRate) * (float(microseconds) / 1000000.f); |
// round up (+1) if microseconds is not zero (i.e. because 44.1 kHz and |
438 |
|
// 1 us would yield in < 1 and thus would be offset == 0) |
439 |
|
const sched_time_t offset = |
440 |
|
(microseconds != 0LL) ? |
441 |
|
1.f + (float(uiSampleRate) * (float(microseconds) / 1000000.f)) |
442 |
|
: 0.f; |
443 |
|
node.scheduleTime = uiTotalSamplesProcessed + fragmentPosBase + offset; |
444 |
queue.insert(node); |
queue.insert(node); |
445 |
} |
} |
446 |
|
|