--- linuxsampler/trunk/src/engines/common/Event.h 2016/04/10 18:22:23 2871 +++ linuxsampler/trunk/src/engines/common/Event.h 2017/05/19 14:23:12 3188 @@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2016 Christian Schoenebeck * + * Copyright (C) 2005 - 2017 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 * @@ -29,6 +29,7 @@ #include "../../common/RTAVLTree.h" #include "../../common/Pool.h" #include "../EngineChannel.h" +#include "../../scriptvm/common.h" namespace LinuxSampler { @@ -67,6 +68,14 @@ RTList::Iterator popNextScheduledScriptEvent(RTAVLTree& queue, Pool& pool, sched_time_t end); /** + * Returns the scheduler time for the first sample point of the + * current audio fragment cycle. + */ + sched_time_t schedTimeAtCurrentFragmentStart() const { + return uiTotalSamplesProcessed; + } + + /** * Returns the scheduler time for the first sample point of the next * audio fragment cycle. */ @@ -92,6 +101,49 @@ }; /** + * Unique numeric ID of an event which can be used to retrieve access to + * the actual @c Event object. Once the event associated with a certain ID + * was released (back to its event pool), this numeric ID becomes invalid + * and Pool< Event >::fromID() will detect this circumstance and will + * return an invalid Iterator, and thus will prevent you from misusing an + * event which no longer "exists". + * + * Note that an @c Event object usually just "exists" for exactly on audio + * fragment cycle: that is it exists right from the beginning of the audio + * fragment cycle where it was caused (i.e. where its MIDI data was + * received by the respective engine channel) and will disappear + * automatically at the end of that audio fragment cycle. + */ + typedef pool_element_id_t event_id_t; + + /** + * Unique numeric ID of a note which can be used to retrieve access to the + * actual @c Note object. Once the note associated with a certain ID was + * released (back to its note pool), this numeric ID becomes invalid and + * Pool< Note >::fromID() will detect this circumstance and will return + * an invalid Iterator, and thus will prevent you from misusing a note + * which no longer is "alive". + * + * A @c Note object exists right when the respective MIDI note-on event + * was received by the respective engine channel, and remains existent + * until the caused note and all its voices were finally freed (which might + * even be long time after the respective note-off event was received, + * depending on the duration of the voice's release stages etc.). + */ + typedef pool_element_id_t note_id_t; + + /** + * Unique numeric ID of a script callback ID instance which can be used to + * retrieve access to the actual @c ScriptEvent object. Once the script + * callback instance associated with a certain ID stopped its execution + * (that is completely stopped, not just suspended) then this numeric ID + * becomes invalid and Pool< ScriptEvent >::fromID() will detect this + * circumstance and will return an invalid Iterator, and thus will prevent + * you from misusing a script callback instance which no longer "exists". + */ + typedef pool_element_id_t script_callback_id_t; + + /** * Events are usually caused by a MIDI source or an internal modulation * controller like LFO or EG. An event should only be created by an * EventGenerator! @@ -102,16 +154,37 @@ public: Event(){} enum type_t { - type_note_on, - type_note_off, - type_pitchbend, - type_control_change, + type_note_on, ///< (real) MIDI note-on event + type_note_off, ///< (real) MIDI note-off event + type_pitchbend, ///< MIDI pitch bend wheel change event + type_control_change, ///< MIDI CC event type_sysex, ///< MIDI system exclusive message - type_cancel_release, ///< transformed either from a note-on or sustain-pedal-down event - type_release, ///< transformed either from a note-off or sustain-pedal-up event + type_cancel_release_key, ///< transformed either from a (real) MIDI note-on or sustain-pedal-down event + type_release_key, ///< transformed either from a (real) MIDI note-off or sustain-pedal-up event + type_release_note, ///< transformed from a type_stop_note event type_channel_pressure, ///< a.k.a. aftertouch type_note_pressure, ///< polyphonic key pressure (aftertouch) + type_play_note, ///< caused by a call to built-in instrument script function play_note() + type_stop_note, ///< caused by a call to built-in instrument script function note_off() + type_kill_note, ///< caused by a call to built-in instrument script function fade_out() + 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; + enum synth_param_t { + synth_param_volume, + synth_param_volume_time, + synth_param_pitch, + synth_param_pitch_time, + synth_param_pan, + synth_param_cutoff, + synth_param_resonance, + synth_param_attack, + synth_param_decay, + synth_param_release, + synth_param_amp_lfo_depth, + synth_param_amp_lfo_freq, + synth_param_pitch_lfo_depth, + synth_param_pitch_lfo_freq, + }; union { /// Note-on and note-off event specifics struct _Note { @@ -120,6 +193,8 @@ uint8_t Velocity; ///< Trigger or release velocity of note-on / note-off event. 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 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). + note_id_t ID; ///< Unique numeric ID of the @c Note object associated with this note event. + 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. void* pRegion; ///< Engine specific pointer to instrument region } Note; /// Control change event specifics @@ -149,18 +224,23 @@ uint8_t Key; ///< MIDI note number where key pressure (polyphonic aftertouch) changed. uint8_t Value; ///< New pressure value for note. } NotePressure; + ///< Note synthesis parameter change event's specifics (used for real-time instrument script built-in functions which may alter synthesis parameters on note level). + struct _NoteSynthParam { + 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). + } NoteSynthParam; } 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; 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() { + Param.Note.ID = 0; + Param.Note.ParentNoteID = 0; + Param.NoteSynthParam.NoteID = 0; + } inline int32_t FragmentPos() { if (iFragmentPos >= 0) return iFragmentPos; iFragmentPos = pEventGenerator->ToFragmentPos(TimeStamp); @@ -170,6 +250,13 @@ inline void ResetFragmentPos() { iFragmentPos = -1; } + inline void CopyTimeFrom(const Event& other) { + TimeStamp = other.TimeStamp; + iFragmentPos = other.iFragmentPos; + } + inline sched_time_t SchedTime() { + return pEventGenerator->schedTimeAtCurrentFragmentStart() + FragmentPos(); + } protected: typedef EventGenerator::time_stamp_t time_stamp_t; Event(EventGenerator* pGenerator, EventGenerator::time_stamp_t Time); @@ -188,6 +275,8 @@ */ class SchedulerNode : public RTAVLNode { public: + using RTAVLNode::reset; // make reset() method public + 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. /// Required operator implementation for RTAVLTree class. @@ -199,6 +288,9 @@ inline bool operator<(const SchedulerNode& other) const { return this->scheduleTime < other.scheduleTime; } + + /// This is actually just for code readability. + inline RTAVLTreeBase* currentSchedulerQueue() const { return rtavlTree(); } }; /** @@ -229,18 +321,20 @@ */ class ScriptEvent : public SchedulerNode { public: - Event cause; ///< Original external event that triggered this script event (i.e. MIDI note on event, MIDI CC event, etc.). - int id; ///< Unique ID of the external event that triggered this script event. + Event cause; ///< Copy of original external @c Event that triggered this script event (i.e. MIDI note on event, MIDI CC event, etc.). + 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. VMEventHandler** handlers; ///< The script's event handlers (callbacks) to be processed (NULL terminated list). VMExecContext* execCtx; ///< Script's current execution state (polyphonic variables and execution stack). int currentHandler; ///< Current index in 'handlers' list above. 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. }; /** * Insert given @a node into the supplied timing @a queue with a scheduled * timing position given by @a fragmentPosBase and @a microseconds, where - * @a microseconds reflects the amount microseconds in future from "now" + * @a microseconds reflects the amount of microseconds in future from "now" * where the node shall be scheduled, and @a fragmentPos identifies the * sample point within the current audio fragment cycle which shall be * interpreted by this method to be "now".