1 /*************************************************************************** 2 * * 3 * LinuxSampler - modular, streaming capable sampler * 4 * * 5 * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * 6 * Copyright (C) 2005 - 2021 Christian Schoenebeck * 7 * * 8 * 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 * 10 * the Free Software Foundation; either version 2 of the License, or * 11 * (at your option) any later version. * 12 * * 13 * This program is distributed in the hope that it will be useful, * 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 16 * GNU General Public License for more details. * 17 * * 18 * You should have received a copy of the GNU General Public License * 19 * along with this program; if not, write to the Free Software * 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * 21 * MA 02111-1307 USA * 22 ***************************************************************************/ 23 24 #ifndef __LS_EVENT_H__ 25 #define __LS_EVENT_H__ 26 27 #include "../../common/global.h" 28 #include "../../common/RTMath.h" 29 #include "../../common/RTAVLTree.h" 30 #include "../../common/Pool.h" 31 #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 { 43 44 // just symbol prototyping 45 class Event; 46 class SchedulerNode; 47 class ScriptEvent; 48 class ScheduledEvent; 49 50 /** 51 * Data type used to schedule events sample point accurately both within, as 52 * well as beyond the scope of the current audio fragment cycle. The timing 53 * reflected by this data type is consecutively running for a very long 54 * time. Even with a sample rate of 96 kHz a scheduler time of this data 55 * type will not wrap before 6 million years. So in practice such time 56 * stamps are unique and will not repeat (unless the EventGenerator is 57 * reset). 58 */ 59 typedef uint64_t sched_time_t; 60 61 /** 62 * Generates Event objects and is responsible for resolving the position 63 * in the current audio fragment each Event actually belongs to. 64 */ 65 class EventGenerator { 66 public: 67 EventGenerator(uint SampleRate); 68 void UpdateFragmentTime(uint SamplesToProcess); 69 void SetSampleRate(uint SampleRate); 70 Event CreateEvent(); 71 Event CreateEvent(int32_t FragmentPos); 72 73 template<typename T> 74 void scheduleAheadMicroSec(RTAVLTree<T>& queue, T& node, int32_t fragmentPosBase, uint64_t microseconds); 75 76 RTList<ScheduledEvent>::Iterator popNextScheduledEvent(RTAVLTree<ScheduledEvent>& queue, Pool<ScheduledEvent>& pool, sched_time_t end); 77 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 */ schedTimeAtCurrentFragmentStart()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 89 * audio fragment cycle. 90 */ schedTimeAtCurrentFragmentEnd()91 sched_time_t schedTimeAtCurrentFragmentEnd() const { 92 return uiTotalSamplesProcessed + uiSamplesProcessed; 93 } 94 95 protected: 96 typedef RTMath::time_stamp_t time_stamp_t; ToFragmentPos(time_stamp_t TimeStamp)97 inline int32_t ToFragmentPos(time_stamp_t TimeStamp) { 98 return int32_t (int32_t(TimeStamp - FragmentTime.begin) * FragmentTime.sample_ratio); 99 } 100 friend class Event; 101 private: 102 uint uiSampleRate; 103 uint uiSamplesProcessed; 104 struct __FragmentTime__ { 105 time_stamp_t begin; ///< Real time stamp of the beginning of this audio fragment cycle. 106 time_stamp_t end; ///< Real time stamp of the end of this audio fragment cycle. 107 float sample_ratio; ///< (Samples per cycle) / (Real time duration of cycle) 108 } FragmentTime; 109 sched_time_t uiTotalSamplesProcessed; ///< Total amount of sample points that have been processed since this EventGenerator object has been created. This is used to schedule instrument script events long time ahead in future (that is beyond the scope of the current audio fragment). 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 one 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 157 * controller like LFO or EG. An event should only be created by an 158 * EventGenerator! 159 * 160 * @see EventGenerator, ScriptEvent 161 */ 162 class Event { 163 public: Event()164 Event(){} 165 enum type_t { 166 type_note_on, ///< (real) MIDI note-on event 167 type_note_off, ///< (real) MIDI note-off event 168 type_pitchbend, ///< MIDI pitch bend wheel change event 169 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 173 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 175 type_release_note, ///< transformed from a type_stop_note event 176 type_channel_pressure, ///< a.k.a. aftertouch 177 type_note_pressure, ///< polyphonic key pressure (aftertouch) 178 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() 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.) 182 } Type; 183 enum synth_param_t { 184 synth_param_volume, 185 synth_param_volume_time, 186 synth_param_volume_curve, 187 synth_param_pitch, 188 synth_param_pitch_time, 189 synth_param_pitch_curve, 190 synth_param_pan, 191 synth_param_pan_time, 192 synth_param_pan_curve, 193 synth_param_cutoff, 194 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 { 246 /// Note-on and note-off event specifics 247 struct _Note { 248 uint8_t Channel; ///< MIDI channel (0..15) 249 uint8_t Key; ///< MIDI key number of note-on / note-off event. 250 uint8_t Velocity; ///< Trigger or release velocity of note-on / note-off event. 251 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). 252 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). 253 note_id_t ID; ///< Unique numeric ID of the @c Note object associated with this note event. 254 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. 255 void* pRegion; ///< Engine specific pointer to instrument region 256 } Note; 257 /// Control change event specifics 258 struct _CC { 259 uint8_t Channel; ///< MIDI channel (0..15) 260 uint8_t Controller; ///< MIDI controller number of control change event. 261 uint8_t Value; ///< Controller Value of control change event. 262 } 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). ParameterMSB__anon8b9b8e2d010a::_RPN268 uint8_t ParameterMSB() const { return Parameter >> 7; } ParameterLSB__anon8b9b8e2d010a::_RPN269 uint8_t ParameterLSB() const { return Parameter & 127; } ValueMSB__anon8b9b8e2d010a::_RPN270 uint8_t ValueMSB() const { return Value >> 7; } ValueLSB__anon8b9b8e2d010a::_RPN271 uint8_t ValueLSB() const { return Value & 127; } 272 } RPN, NRPN; 273 /// Pitchbend event specifics 274 struct _Pitch { 275 uint8_t Channel; ///< MIDI channel (0..15) 276 int16_t Pitch; ///< Pitch value of pitchbend event. 277 } Pitch; 278 /// MIDI system exclusive event specifics 279 struct _Sysex { 280 uint Size; ///< Data length (in bytes) of MIDI system exclusive message. 281 } Sysex; 282 /// Channel Pressure (aftertouch) event specifics 283 struct _ChannelPressure { 284 uint8_t Channel; ///< MIDI channel (0..15) 285 uint8_t Controller; ///< Should always be assigned to CTRL_TABLE_IDX_AFTERTOUCH. 286 uint8_t Value; ///< New aftertouch / pressure value for keys on that channel. 287 } ChannelPressure; 288 /// Polyphonic Note Pressure (aftertouch) event specifics 289 struct _NotePressure { 290 uint8_t Channel; ///< MIDI channel (0..15) 291 uint8_t Key; ///< MIDI note number where key pressure (polyphonic aftertouch) changed. 292 uint8_t Value; ///< New pressure value for note. 293 } NotePressure; 294 ///< Note synthesis parameter change event's specifics (used for real-time instrument script built-in functions which may alter synthesis parameters on note level). 295 struct _NoteSynthParam { 296 note_id_t NoteID; ///< ID of Note whose voices shall be modified. 297 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. 299 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 isFinal__anon8b9b8e2d010a::_NoteSynthParam302 inline bool isFinal() const { return Scope >= ValueScope::FINAL_SELF_RELATIVE; } 303 } NoteSynthParam; 304 } Param; 305 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) 307 Init()308 inline void Init() { 309 memset(&Param, 0, sizeof(Param)); 310 } FragmentPos()311 inline int32_t FragmentPos() { 312 if (iFragmentPos >= 0) return iFragmentPos; 313 iFragmentPos = pEventGenerator->ToFragmentPos(TimeStamp); 314 if (iFragmentPos < 0) iFragmentPos = 0; // if event arrived shortly before the beginning of current fragment 315 return iFragmentPos; 316 } ResetFragmentPos()317 inline void ResetFragmentPos() { 318 iFragmentPos = -1; 319 } CopyTimeFrom(const Event & other)320 inline void CopyTimeFrom(const Event& other) { 321 TimeStamp = other.TimeStamp; 322 iFragmentPos = other.iFragmentPos; 323 } SchedTime()324 inline sched_time_t SchedTime() { 325 return pEventGenerator->schedTimeAtCurrentFragmentStart() + FragmentPos(); 326 } scopeBy_FinalRelativeUnit(bool bFinal,bool bRelative,bool bNativeUnit)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: 339 typedef EventGenerator::time_stamp_t time_stamp_t; 340 Event(EventGenerator* pGenerator, EventGenerator::time_stamp_t Time); 341 Event(EventGenerator* pGenerator, int32_t FragmentPos); 342 friend class EventGenerator; 343 private: 344 EventGenerator* pEventGenerator; ///< Creator of the event. 345 time_stamp_t TimeStamp; ///< Time stamp of the event's occurence. 346 int32_t iFragmentPos; ///< Position in the current fragment this event refers to. 347 }; 348 prevEventOf(const Pool<Event>::Iterator & itEvent)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 nextEventOf(const Pool<Event>::Iterator & itEvent)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 isPrevEventCCNr(const Pool<Event>::Iterator & itEvent,uint8_t CCNr)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 isNextEventCCNr(const Pool<Event>::Iterator & itEvent,uint8_t CCNr)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 377 * queue. This class is just intended as base class and should be derived 378 * for its actual purpose (for the precise data type being scheduled). 379 */ 380 class SchedulerNode : public RTAVLNode { 381 public: 382 using RTAVLNode::reset; // make reset() method public 383 384 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. 385 386 /// Required operator implementation for RTAVLTree class. 387 inline bool operator==(const SchedulerNode& other) const { 388 return this->scheduleTime == other.scheduleTime; 389 } 390 391 /// Required operator implementation for RTAVLTree class. 392 inline bool operator<(const SchedulerNode& other) const { 393 return this->scheduleTime < other.scheduleTime; 394 } 395 396 /// This is actually just for code readability. currentSchedulerQueue()397 inline RTAVLTreeBase* currentSchedulerQueue() const { return rtavlTree(); } 398 }; 399 400 /** 401 * Used to sort delayed MIDI events into a timing/scheduler queue. This 402 * object just contains the timing informations, the actual MIDI event is 403 * pointed by member variable @c itEvent. 404 */ 405 class ScheduledEvent : public SchedulerNode { 406 public: 407 Pool<Event>::Iterator itEvent; ///< Points to the actual Event object being scheduled. 408 }; 409 410 class VMEventHandler; 411 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. 420 * 421 * Encapsulates one execution instance of a real-time instrument script for 422 * exactly one script event handler (script event callback). 423 * 424 * This class derives from SchedulerNode for being able to be sorted efficiently 425 * by the script scheduler if the script was either a) calling the wait() 426 * script function or b) the script was auto suspended by the ScriptVM 427 * because the script was executing for too long. In both cases the 428 * scheduler has to sort the ScriptEvents in its execution queue according 429 * to the precise time the respective script execution instance needs to be 430 * resumed. 431 */ 432 class ScriptEvent : public SchedulerNode { 433 public: 434 Event cause; ///< Copy of original external @c Event that triggered this script event (i.e. MIDI note on event, MIDI CC event, etc.). 435 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. 436 VMEventHandler** handlers; ///< The script's event handlers (callbacks) to be processed (NULL terminated list). 437 VMExecContext* execCtx; ///< Script's current execution state (polyphonic variables and execution stack). 438 int currentHandler; ///< Current index in 'handlers' list above. 439 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()). 441 bool releaseMatched; ///< Only for note handlers with polyphonic data: whether a corresponding release handler has already been triggered subsequently for this note handler. 442 VMEventHandlerType_t handlerType; ///< Native representation of built-in script variable $NI_CALLBACK_TYPE, reflecting the script event type of this script event. 443 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). 444 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). 445 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. 446 int forkIndex; ///< Only for fork() calls: distinguishment feature which is 0 for parent, 1 for 1st child, 2 for 2nd child, etc. 447 448 void forkTo(ScriptEvent* e, bool bAutoAbort) const; 449 int countChildHandlers() const; 450 void addChildHandlerID(script_callback_id_t childID); 451 }; 452 453 /** 454 * Insert given @a node into the supplied timing @a queue with a scheduled 455 * timing position given by @a fragmentPosBase and @a microseconds, where 456 * @a microseconds reflects the amount of microseconds in future from "now" 457 * where the node shall be scheduled, and @a fragmentPos identifies the 458 * sample point within the current audio fragment cycle which shall be 459 * interpreted by this method to be "now". 460 * 461 * The meaning of @a fragmentPosBase becomes more important the larger 462 * the audio fragment size, and vice versa it becomes less important the 463 * smaller the audio fragment size. 464 * 465 * @param queue - destination scheduler queue 466 * @param node - node (i.e. event) to be inserted into the queue 467 * @param fragmentPosBase - sample point in current audio fragment to be "now" 468 * @param microseconds - timing of node from "now" (in microseconds) 469 */ 470 template<typename T> scheduleAheadMicroSec(RTAVLTree<T> & queue,T & node,int32_t fragmentPosBase,uint64_t microseconds)471 void EventGenerator::scheduleAheadMicroSec(RTAVLTree<T>& queue, T& node, int32_t fragmentPosBase, uint64_t microseconds) { 472 // round up (+1) if microseconds is not zero (i.e. because 44.1 kHz and 473 // 1 us would yield in < 1 and thus would be offset == 0) 474 const sched_time_t offset = 475 (microseconds != 0LL) ? 476 1.f + (float(uiSampleRate) * (float(microseconds) / 1000000.f)) 477 : 0.f; 478 node.scheduleTime = uiTotalSamplesProcessed + fragmentPosBase + offset; 479 queue.insert(node); 480 } 481 482 } // namespace LinuxSampler 483 484 #endif // __LS_EVENT_H__ 485