--- linuxsampler/trunk/src/engines/common/Event.h 2017/05/19 14:23:12 3188 +++ linuxsampler/trunk/src/engines/common/Event.h 2019/08/24 11:22:52 3565 @@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2017 Christian Schoenebeck * + * Copyright (C) 2005 - 2019 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -31,6 +31,14 @@ #include "../EngineChannel.h" #include "../../scriptvm/common.h" +// On Windows RELATIVE might be defined as macro in wingdi.h, which would +// cause a compiler error of the same token used in this header file below. +// So we undefine that macro here for now (if present). +#ifdef RELATIVE +# warning Preprocessor conflict detected: Macro RELATIVE was declared by system headers; undefining it here. +# undef RELATIVE +#endif + namespace LinuxSampler { // just symbol prototyping @@ -58,6 +66,7 @@ public: EventGenerator(uint SampleRate); void UpdateFragmentTime(uint SamplesToProcess); + void SetSampleRate(uint SampleRate); Event CreateEvent(); Event CreateEvent(int32_t FragmentPos); @@ -172,19 +181,65 @@ enum synth_param_t { synth_param_volume, synth_param_volume_time, + synth_param_volume_curve, synth_param_pitch, synth_param_pitch_time, + synth_param_pitch_curve, synth_param_pan, + synth_param_pan_time, + synth_param_pan_curve, synth_param_cutoff, synth_param_resonance, synth_param_attack, synth_param_decay, + synth_param_sustain, synth_param_release, + synth_param_cutoff_attack, + synth_param_cutoff_decay, + synth_param_cutoff_sustain, + synth_param_cutoff_release, synth_param_amp_lfo_depth, synth_param_amp_lfo_freq, + synth_param_cutoff_lfo_depth, + synth_param_cutoff_lfo_freq, synth_param_pitch_lfo_depth, synth_param_pitch_lfo_freq, }; + enum class ValueScope : unsigned char { + /** + * The new synthesis parameter value should be applied + * relatively to itself (as normalized value range), and then + * applied relatively against other sources (i.e. LFOs, EGs) + * for the same synthesis parameter. + */ + SELF_RELATIVE = 1, + /** + * The new synthesis paramater value of itself should be + * replaced, and then applied relatively to other sources + * (i.e. LFOs, EGs) for the same synthesis parameter. + */ + 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() + /** + * The new synthesis parameter value should be applied + * relatively to itself (as normalized value range), and then + * applied directly (as normalized value range) as final value + * of this synthesis chain, thus all other sources (i.e. LFOs, + * EGs) should entirely be ignored. + */ + FINAL_SELF_RELATIVE = 2, + /** + * The new synthesis parameter value of itself should be + * replaced, and then applied directly (as normalized value + * range) as final value of this synthesis chain, thus all other + * sources (i.e. LFOs, EGs) should entirely be ignored. + */ + FINAL_NORM = 3, + /** + * Same as @c FINAL_NORM, but this one is already in the native + * unit (i.e. seconds, Hz) of this synthesis parameter. + */ + FINAL_NATIVE = 4, + }; union { /// Note-on and note-off event specifics struct _Note { @@ -229,17 +284,21 @@ note_id_t NoteID; ///< ID of Note whose voices shall be modified. synth_param_t Type; ///< Synthesis parameter which is to be changed. 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). float AbsValue; ///< New current absolute value of synthesis parameter (that is after @c Delta being applied). + ValueScope Scope; ///< How @c Delta should be applied against @c AbsValue, and how @c AbsValue should then actually be applied to the synthesis chain. + + inline bool isFinal() const { return Scope >= ValueScope::FINAL_SELF_RELATIVE; } } NoteSynthParam; } Param; EngineChannel* pEngineChannel; ///< Pointer to the EngineChannel where this event occured on, NULL means Engine global event (e.g. SysEx message). MidiInputPort* pMidiInputPort; ///< Pointer to the MIDI input port on which this event occured (NOTE: currently only for global events, that is SysEx messages) inline void Init() { + //FIXME: probably we should memset() zero entire structure here, due to potential union initialization conflicts (see comment on ValueScope::RELATIVE) Param.Note.ID = 0; Param.Note.ParentNoteID = 0; Param.NoteSynthParam.NoteID = 0; + Param.NoteSynthParam.Scope = ValueScope::RELATIVE; } inline int32_t FragmentPos() { if (iFragmentPos >= 0) return iFragmentPos; @@ -257,6 +316,17 @@ inline sched_time_t SchedTime() { return pEventGenerator->schedTimeAtCurrentFragmentStart() + FragmentPos(); } + inline static ValueScope scopeBy_FinalRelativeUnit(bool bFinal, bool bRelative, bool bNativeUnit) { + if (!bFinal && bRelative) + return ValueScope::SELF_RELATIVE; + if (!bFinal) + return ValueScope::RELATIVE; + if (bRelative) + return ValueScope::FINAL_SELF_RELATIVE; + if (bNativeUnit) + return ValueScope::FINAL_NATIVE; + return ValueScope::FINAL_NORM; + } protected: typedef EventGenerator::time_stamp_t time_stamp_t; Event(EventGenerator* pGenerator, EventGenerator::time_stamp_t Time); @@ -306,6 +376,12 @@ class VMEventHandler; class VMExecContext; + /** + * Maximum amount of child script handler instances one script handler is + * allowed to create by calling built-in script function fork(). + */ + #define MAX_FORK_PER_SCRIPT_HANDLER 8 + /** @brief Real-time instrument script event. * * Encapsulates one execution instance of a real-time instrument script for @@ -329,6 +405,14 @@ int executionSlices; ///< Amount of times this script event has been executed by the ScriptVM runner class. 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()). VMEventHandlerType_t handlerType; ///< Native representation of built-in script variable $NI_CALLBACK_TYPE, reflecting the script event type of this script event. + 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). + 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). + 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. + int forkIndex; ///< Only for fork() calls: distinguishment feature which is 0 for parent, 1 for 1st child, 2 for 2nd child, etc. + + void forkTo(ScriptEvent* e, bool bAutoAbort) const; + int countChildHandlers() const; + void addChildHandlerID(script_callback_id_t childID); }; /** @@ -340,7 +424,7 @@ * interpreted by this method to be "now". * * The meaning of @a fragmentPosBase becomes more important the larger - * the audio fragment size, and vice versa it bcomes less important the + * the audio fragment size, and vice versa it becomes less important the * smaller the audio fragment size. * * @param queue - destination scheduler queue @@ -350,7 +434,13 @@ */ template void EventGenerator::scheduleAheadMicroSec(RTAVLTree& queue, T& node, int32_t fragmentPosBase, uint64_t microseconds) { - 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 + // 1 us would yield in < 1 and thus would be offset == 0) + const sched_time_t offset = + (microseconds != 0LL) ? + 1.f + (float(uiSampleRate) * (float(microseconds) / 1000000.f)) + : 0.f; + node.scheduleTime = uiTotalSamplesProcessed + fragmentPosBase + offset; queue.insert(node); }