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 - 2020 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 * |
31 |
#include "../EngineChannel.h" |
#include "../EngineChannel.h" |
32 |
#include "../../scriptvm/common.h" |
#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 |
|
|
44 |
// just symbol prototyping |
// just symbol prototyping |
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 |
|
|
167 |
type_note_off, ///< (real) MIDI note-off event |
type_note_off, ///< (real) MIDI note-off event |
168 |
type_pitchbend, ///< MIDI pitch bend wheel change event |
type_pitchbend, ///< MIDI pitch bend wheel change event |
169 |
type_control_change, ///< MIDI CC event |
type_control_change, ///< MIDI CC event |
170 |
|
type_rpn, ///< Transformed from a raw RPN CC MIDI event. |
171 |
|
type_nrpn, ///< Transformed from a raw NRPN CC MIDI event. |
172 |
type_sysex, ///< MIDI system exclusive message |
type_sysex, ///< MIDI system exclusive message |
173 |
type_cancel_release_key, ///< transformed either from a (real) MIDI note-on or sustain-pedal-down event |
type_cancel_release_key, ///< transformed either from a (real) MIDI note-on or sustain-pedal-down event |
174 |
type_release_key, ///< transformed either from a (real) MIDI note-off or sustain-pedal-up event |
type_release_key, ///< transformed either from a (real) MIDI note-off or sustain-pedal-up event |
177 |
type_note_pressure, ///< polyphonic key pressure (aftertouch) |
type_note_pressure, ///< polyphonic key pressure (aftertouch) |
178 |
type_play_note, ///< caused by a call to built-in instrument script function play_note() |
type_play_note, ///< caused by a call to built-in instrument script function play_note() |
179 |
type_stop_note, ///< caused by a call to built-in instrument script function note_off() |
type_stop_note, ///< caused by a call to built-in instrument script function note_off() |
180 |
|
type_kill_note, ///< caused by a call to built-in instrument script function fade_out() |
181 |
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.) |
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.) |
182 |
} Type; |
} Type; |
183 |
enum synth_param_t { |
enum synth_param_t { |
184 |
synth_param_volume, |
synth_param_volume, |
185 |
|
synth_param_volume_time, |
186 |
|
synth_param_volume_curve, |
187 |
synth_param_pitch, |
synth_param_pitch, |
188 |
|
synth_param_pitch_time, |
189 |
|
synth_param_pitch_curve, |
190 |
synth_param_pan, |
synth_param_pan, |
191 |
|
synth_param_pan_time, |
192 |
|
synth_param_pan_curve, |
193 |
synth_param_cutoff, |
synth_param_cutoff, |
194 |
synth_param_resonance, |
synth_param_resonance, |
195 |
|
synth_param_attack, |
196 |
|
synth_param_decay, |
197 |
|
synth_param_sustain, |
198 |
|
synth_param_release, |
199 |
|
synth_param_cutoff_attack, |
200 |
|
synth_param_cutoff_decay, |
201 |
|
synth_param_cutoff_sustain, |
202 |
|
synth_param_cutoff_release, |
203 |
|
synth_param_amp_lfo_depth, |
204 |
|
synth_param_amp_lfo_freq, |
205 |
|
synth_param_cutoff_lfo_depth, |
206 |
|
synth_param_cutoff_lfo_freq, |
207 |
|
synth_param_pitch_lfo_depth, |
208 |
|
synth_param_pitch_lfo_freq, |
209 |
|
}; |
210 |
|
enum class ValueScope : unsigned char { |
211 |
|
/** |
212 |
|
* The new synthesis parameter value should be applied |
213 |
|
* relatively to itself (as normalized value range), and then |
214 |
|
* applied relatively against other sources (i.e. LFOs, EGs) |
215 |
|
* for the same synthesis parameter. |
216 |
|
*/ |
217 |
|
SELF_RELATIVE = 1, |
218 |
|
/** |
219 |
|
* The new synthesis paramater value of itself should be |
220 |
|
* replaced, and then applied relatively to other sources |
221 |
|
* (i.e. LFOs, EGs) for the same synthesis parameter. |
222 |
|
*/ |
223 |
|
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() |
224 |
|
/** |
225 |
|
* The new synthesis parameter value should be applied |
226 |
|
* relatively to itself (as normalized value range), and then |
227 |
|
* applied directly (as normalized value range) as final value |
228 |
|
* of this synthesis chain, thus all other sources (i.e. LFOs, |
229 |
|
* EGs) should entirely be ignored. |
230 |
|
*/ |
231 |
|
FINAL_SELF_RELATIVE = 2, |
232 |
|
/** |
233 |
|
* The new synthesis parameter value of itself should be |
234 |
|
* replaced, and then applied directly (as normalized value |
235 |
|
* range) as final value of this synthesis chain, thus all other |
236 |
|
* sources (i.e. LFOs, EGs) should entirely be ignored. |
237 |
|
*/ |
238 |
|
FINAL_NORM = 3, |
239 |
|
/** |
240 |
|
* Same as @c FINAL_NORM, but this one is already in the native |
241 |
|
* unit (i.e. seconds, Hz) of this synthesis parameter. |
242 |
|
*/ |
243 |
|
FINAL_NATIVE = 4, |
244 |
}; |
}; |
245 |
union { |
union { |
246 |
/// Note-on and note-off event specifics |
/// Note-on and note-off event specifics |
260 |
uint8_t Controller; ///< MIDI controller number of control change event. |
uint8_t Controller; ///< MIDI controller number of control change event. |
261 |
uint8_t Value; ///< Controller Value of control change event. |
uint8_t Value; ///< Controller Value of control change event. |
262 |
} CC; |
} CC; |
263 |
|
/// Used for both RPN & NRPN events |
264 |
|
struct _RPN { |
265 |
|
uint8_t Channel; ///< MIDI channel (0..15) |
266 |
|
uint16_t Parameter; ///< Merged 14 bit representation of parameter number (that is MSB and LSB combined). |
267 |
|
uint16_t Value; ///< Merged 14 bit representation of new (N)RPN value (that is MSB and LSB combined). |
268 |
|
uint8_t ParameterMSB() const { return Parameter >> 7; } |
269 |
|
uint8_t ParameterLSB() const { return Parameter & 127; } |
270 |
|
uint8_t ValueMSB() const { return Value >> 7; } |
271 |
|
uint8_t ValueLSB() const { return Value & 127; } |
272 |
|
} RPN, NRPN; |
273 |
/// Pitchbend event specifics |
/// Pitchbend event specifics |
274 |
struct _Pitch { |
struct _Pitch { |
275 |
uint8_t Channel; ///< MIDI channel (0..15) |
uint8_t Channel; ///< MIDI channel (0..15) |
296 |
note_id_t NoteID; ///< ID of Note whose voices shall be modified. |
note_id_t NoteID; ///< ID of Note whose voices shall be modified. |
297 |
synth_param_t Type; ///< Synthesis parameter which is to be changed. |
synth_param_t Type; ///< Synthesis parameter which is to be changed. |
298 |
float Delta; ///< The value change that should be applied against the note's current synthesis parameter value. |
float Delta; ///< The value change that should be applied against the note's current synthesis parameter value. |
|
bool Relative; ///< Whether @c Delta should be applied relatively against the note's current synthesis parameter value (false means the paramter's current value is simply replaced by Delta). |
|
299 |
float AbsValue; ///< New current absolute value of synthesis parameter (that is after @c Delta being applied). |
float AbsValue; ///< New current absolute value of synthesis parameter (that is after @c Delta being applied). |
300 |
|
ValueScope Scope; ///< How @c Delta should be applied against @c AbsValue, and how @c AbsValue should then actually be applied to the synthesis chain. |
301 |
|
|
302 |
|
inline bool isFinal() const { return Scope >= ValueScope::FINAL_SELF_RELATIVE; } |
303 |
} NoteSynthParam; |
} NoteSynthParam; |
304 |
} Param; |
} Param; |
305 |
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). |
306 |
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) |
307 |
|
|
308 |
inline void Init() { |
inline void Init() { |
309 |
Param.Note.ID = 0; |
memset(&Param, 0, sizeof(Param)); |
|
Param.Note.ParentNoteID = 0; |
|
|
Param.NoteSynthParam.NoteID = 0; |
|
310 |
} |
} |
311 |
inline int32_t FragmentPos() { |
inline int32_t FragmentPos() { |
312 |
if (iFragmentPos >= 0) return iFragmentPos; |
if (iFragmentPos >= 0) return iFragmentPos; |
324 |
inline sched_time_t SchedTime() { |
inline sched_time_t SchedTime() { |
325 |
return pEventGenerator->schedTimeAtCurrentFragmentStart() + FragmentPos(); |
return pEventGenerator->schedTimeAtCurrentFragmentStart() + FragmentPos(); |
326 |
} |
} |
327 |
|
inline static ValueScope scopeBy_FinalRelativeUnit(bool bFinal, bool bRelative, bool bNativeUnit) { |
328 |
|
if (!bFinal && bRelative) |
329 |
|
return ValueScope::SELF_RELATIVE; |
330 |
|
if (!bFinal) |
331 |
|
return ValueScope::RELATIVE; |
332 |
|
if (bRelative) |
333 |
|
return ValueScope::FINAL_SELF_RELATIVE; |
334 |
|
if (bNativeUnit) |
335 |
|
return ValueScope::FINAL_NATIVE; |
336 |
|
return ValueScope::FINAL_NORM; |
337 |
|
} |
338 |
protected: |
protected: |
339 |
typedef EventGenerator::time_stamp_t time_stamp_t; |
typedef EventGenerator::time_stamp_t time_stamp_t; |
340 |
Event(EventGenerator* pGenerator, EventGenerator::time_stamp_t Time); |
Event(EventGenerator* pGenerator, EventGenerator::time_stamp_t Time); |
346 |
int32_t iFragmentPos; ///< Position in the current fragment this event refers to. |
int32_t iFragmentPos; ///< Position in the current fragment this event refers to. |
347 |
}; |
}; |
348 |
|
|
349 |
|
inline Pool<Event>::Iterator prevEventOf(const Pool<Event>::Iterator& itEvent) { |
350 |
|
if (!itEvent) return Pool<Event>::Iterator(); |
351 |
|
Pool<Event>::Iterator itPrev = itEvent; |
352 |
|
return --itPrev; |
353 |
|
} |
354 |
|
|
355 |
|
inline Pool<Event>::Iterator nextEventOf(const Pool<Event>::Iterator& itEvent) { |
356 |
|
if (!itEvent) return Pool<Event>::Iterator(); |
357 |
|
Pool<Event>::Iterator itNext = itEvent; |
358 |
|
return ++itNext; |
359 |
|
} |
360 |
|
|
361 |
|
inline bool isPrevEventCCNr(const Pool<Event>::Iterator& itEvent, uint8_t CCNr) { |
362 |
|
Pool<Event>::Iterator itPrev = prevEventOf(itEvent); |
363 |
|
if (!itPrev) return false; |
364 |
|
return itPrev->Type == Event::type_control_change && |
365 |
|
itPrev->Param.CC.Controller == CCNr; |
366 |
|
} |
367 |
|
|
368 |
|
inline bool isNextEventCCNr(const Pool<Event>::Iterator& itEvent, uint8_t CCNr) { |
369 |
|
Pool<Event>::Iterator itNext = nextEventOf(itEvent); |
370 |
|
if (!itNext) return false; |
371 |
|
return itNext->Type == Event::type_control_change && |
372 |
|
itNext->Param.CC.Controller == CCNr; |
373 |
|
} |
374 |
|
|
375 |
/** |
/** |
376 |
* Used to sort timing relevant objects (i.e. events) into timing/scheduler |
* Used to sort timing relevant objects (i.e. events) into timing/scheduler |
377 |
* queue. This class is just intended as base class and should be derived |
* queue. This class is just intended as base class and should be derived |
410 |
class VMEventHandler; |
class VMEventHandler; |
411 |
class VMExecContext; |
class VMExecContext; |
412 |
|
|
413 |
|
/** |
414 |
|
* Maximum amount of child script handler instances one script handler is |
415 |
|
* allowed to create by calling built-in script function fork(). |
416 |
|
*/ |
417 |
|
#define MAX_FORK_PER_SCRIPT_HANDLER 8 |
418 |
|
|
419 |
/** @brief Real-time instrument script event. |
/** @brief Real-time instrument script event. |
420 |
* |
* |
421 |
* Encapsulates one execution instance of a real-time instrument script for |
* Encapsulates one execution instance of a real-time instrument script for |
439 |
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. |
440 |
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()). |
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()). |
441 |
VMEventHandlerType_t handlerType; ///< Native representation of built-in script variable $NI_CALLBACK_TYPE, reflecting the script event type of this script event. |
VMEventHandlerType_t handlerType; ///< Native representation of built-in script variable $NI_CALLBACK_TYPE, reflecting the script event type of this script event. |
442 |
|
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). |
443 |
|
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). |
444 |
|
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. |
445 |
|
int forkIndex; ///< Only for fork() calls: distinguishment feature which is 0 for parent, 1 for 1st child, 2 for 2nd child, etc. |
446 |
|
|
447 |
|
void forkTo(ScriptEvent* e, bool bAutoAbort) const; |
448 |
|
int countChildHandlers() const; |
449 |
|
void addChildHandlerID(script_callback_id_t childID); |
450 |
}; |
}; |
451 |
|
|
452 |
/** |
/** |
458 |
* interpreted by this method to be "now". |
* interpreted by this method to be "now". |
459 |
* |
* |
460 |
* The meaning of @a fragmentPosBase becomes more important the larger |
* The meaning of @a fragmentPosBase becomes more important the larger |
461 |
* the audio fragment size, and vice versa it bcomes less important the |
* the audio fragment size, and vice versa it becomes less important the |
462 |
* smaller the audio fragment size. |
* smaller the audio fragment size. |
463 |
* |
* |
464 |
* @param queue - destination scheduler queue |
* @param queue - destination scheduler queue |
468 |
*/ |
*/ |
469 |
template<typename T> |
template<typename T> |
470 |
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) { |
471 |
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 |
472 |
|
// 1 us would yield in < 1 and thus would be offset == 0) |
473 |
|
const sched_time_t offset = |
474 |
|
(microseconds != 0LL) ? |
475 |
|
1.f + (float(uiSampleRate) * (float(microseconds) / 1000000.f)) |
476 |
|
: 0.f; |
477 |
|
node.scheduleTime = uiTotalSamplesProcessed + fragmentPosBase + offset; |
478 |
queue.insert(node); |
queue.insert(node); |
479 |
} |
} |
480 |
|
|