/[svn]/linuxsampler/trunk/src/engines/common/InstrumentScriptVMFunctions.cpp
ViewVC logotype

Diff of /linuxsampler/trunk/src/engines/common/InstrumentScriptVMFunctions.cpp

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 2879 by schoenebeck, Tue Apr 19 14:07:53 2016 UTC revision 3557 by schoenebeck, Sun Aug 18 00:06:04 2019 UTC
# Line 1  Line 1 
1  /*  /*
2   * Copyright (c) 2014-2016 Christian Schoenebeck   * Copyright (c) 2014-2019 Christian Schoenebeck
3   *   *
4   * http://www.linuxsampler.org   * http://www.linuxsampler.org
5   *   *
# Line 12  Line 12 
12  #include "../AbstractEngineChannel.h"  #include "../AbstractEngineChannel.h"
13    
14  namespace LinuxSampler {  namespace LinuxSampler {
15        
16        // play_note() function
17    
18      InstrumentScriptVMFunction_play_note::InstrumentScriptVMFunction_play_note(InstrumentScriptVM* parent)      InstrumentScriptVMFunction_play_note::InstrumentScriptVMFunction_play_note(InstrumentScriptVM* parent)
19          : m_vm(parent)          : m_vm(parent)
# Line 19  namespace LinuxSampler { Line 21  namespace LinuxSampler {
21      }      }
22    
23      VMFnResult* InstrumentScriptVMFunction_play_note::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_play_note::exec(VMFnArgs* args) {
24          int note = args->arg(0)->asInt()->evalInt();          vmint note = args->arg(0)->asInt()->evalInt();
25          int velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127;          vmint velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127;
26          int sampleoffset = (args->argsCount() >= 3) ? args->arg(2)->asInt()->evalInt() : 0;          vmint duration = (args->argsCount() >= 4) ? args->arg(3)->asInt()->evalInt() : 0; //TODO: -1 might be a better default value instead of 0
         int duration = (args->argsCount() >= 4) ? args->arg(3)->asInt()->evalInt() : 0; //TODO: -1 might be a better default value instead of 0  
27    
28          if (note < 0 || note > 127) {          if (note < 0 || note > 127) {
29              errMsg("play_note(): argument 1 is an invalid note number");              errMsg("play_note(): argument 1 is an invalid note number");
# Line 34  namespace LinuxSampler { Line 35  namespace LinuxSampler {
35              return errorResult(0);              return errorResult(0);
36          }          }
37    
38          if (sampleoffset < 0) {          if (duration < -2) {
39              errMsg("play_note(): argument 3 may not be a negative sample offset");              errMsg("play_note(): argument 4 must be a duration value of at least -2 or higher");
             return errorResult(0);  
         } else if (sampleoffset != 0) {  
             wrnMsg("play_note(): argument 3 does not support a sample offset other than 0 yet");  
         }  
   
         if (duration < -1) {  
             errMsg("play_note(): argument 4 must be a duration value of at least -1 or higher");  
40              return errorResult(0);              return errorResult(0);
41          }          }
42    
# Line 51  namespace LinuxSampler { Line 45  namespace LinuxSampler {
45    
46          Event e = m_vm->m_event->cause; // copy to get fragment time for "now"          Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
47          e.Init(); // clear IDs          e.Init(); // clear IDs
48          e.Type = Event::type_note_on;          e.Type = Event::type_play_note;
49          e.Param.Note.Key = note;          e.Param.Note.Key = note;
50          e.Param.Note.Velocity = velocity;          e.Param.Note.Velocity = velocity;
51          // make this new note dependent to the life time of the original note          // make this new note dependent to the life time of the original note
# Line 61  namespace LinuxSampler { Line 55  namespace LinuxSampler {
55                  return errorResult(0);                  return errorResult(0);
56              }              }
57              e.Param.Note.ParentNoteID = m_vm->m_event->cause.Param.Note.ID;              e.Param.Note.ParentNoteID = m_vm->m_event->cause.Param.Note.ID;
58                // check if that requested parent note is actually still alive
59                NoteBase* pParentNote =
60                    pEngineChannel->pEngine->NoteByID( e.Param.Note.ParentNoteID );
61                // if parent note is already gone then this new note is not required anymore
62                if (!pParentNote)
63                    return successResult(0);
64          }          }
65    
66          const note_id_t id = pEngineChannel->ScheduleNoteMicroSec(&e, 0);          const note_id_t id = pEngineChannel->ScheduleNoteMicroSec(&e, 0);
67    
68          // if a duration is supplied (and note-on event was scheduled          // if a sample offset is supplied, assign the offset as override
69          // successfully above), then schedule a subsequent note-off event          // to the previously created Note object
70            if (args->argsCount() >= 3) {
71                vmint sampleoffset = args->arg(2)->asInt()->evalInt();
72                if (sampleoffset >= 0) {
73                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID(id);
74                    if (pNote) {
75                        pNote->Override.SampleOffset =
76                            (decltype(pNote->Override.SampleOffset)) sampleoffset;
77                    }
78                } else if (sampleoffset < -1) {
79                    errMsg("play_note(): sample offset of argument 3 may not be less than -1");
80                }
81            }
82    
83            // if a duration is supplied (and play-note event was scheduled
84            // successfully above), then schedule a subsequent stop-note event
85          if (id && duration > 0) {          if (id && duration > 0) {
86              e.Type = Event::type_note_off;              e.Type = Event::type_stop_note;
87                e.Param.Note.ID = id;
88              e.Param.Note.Velocity = 127;              e.Param.Note.Velocity = 127;
89              pEngineChannel->ScheduleEventMicroSec(&e, duration);              pEngineChannel->ScheduleEventMicroSec(&e, duration);
90          }          }
# Line 79  namespace LinuxSampler { Line 95  namespace LinuxSampler {
95          return successResult( ScriptID::fromNoteID(id) );          return successResult( ScriptID::fromNoteID(id) );
96      }      }
97    
98        // set_controller() function
99    
100      InstrumentScriptVMFunction_set_controller::InstrumentScriptVMFunction_set_controller(InstrumentScriptVM* parent)      InstrumentScriptVMFunction_set_controller::InstrumentScriptVMFunction_set_controller(InstrumentScriptVM* parent)
101          : m_vm(parent)          : m_vm(parent)
102      {      {
103      }      }
104    
105      VMFnResult* InstrumentScriptVMFunction_set_controller::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_set_controller::exec(VMFnArgs* args) {
106          int controller = args->arg(0)->asInt()->evalInt();          vmint controller = args->arg(0)->asInt()->evalInt();
107          int value      = args->arg(1)->asInt()->evalInt();          vmint value      = args->arg(1)->asInt()->evalInt();
108    
109          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
110              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 114  namespace LinuxSampler { Line 132  namespace LinuxSampler {
132          // would abort the script, and under heavy load it may be considerable          // would abort the script, and under heavy load it may be considerable
133          // that ScheduleEventMicroSec() fails above, so simply ignore that          // that ScheduleEventMicroSec() fails above, so simply ignore that
134          return successResult( ScriptID::fromEventID(id) );          return successResult( ScriptID::fromEventID(id) );
135      }          }
136    
137        // ignore_event() function
138    
139      InstrumentScriptVMFunction_ignore_event::InstrumentScriptVMFunction_ignore_event(InstrumentScriptVM* parent)      InstrumentScriptVMFunction_ignore_event::InstrumentScriptVMFunction_ignore_event(InstrumentScriptVM* parent)
140          : m_vm(parent)          : m_vm(parent)
141      {      {
142      }      }
143    
144      bool InstrumentScriptVMFunction_ignore_event::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_ignore_event::acceptsArgType(vmint iArg, ExprType_t type) const {
145          return type == INT_EXPR || type == INT_ARR_EXPR;          return type == INT_EXPR || type == INT_ARR_EXPR;
146      }      }
147    
# Line 129  namespace LinuxSampler { Line 149  namespace LinuxSampler {
149          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
150                  static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);                  static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
151    
152          if (args->arg(0)->exprType() == INT_EXPR) {          if (args->argsCount() == 0 || args->arg(0)->exprType() == INT_EXPR) {
153              const ScriptID id = args->arg(0)->asInt()->evalInt();              const ScriptID id = (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : m_vm->m_event->id;
154              if (!id) {              if (!id && args->argsCount() >= 1) {
155                  wrnMsg("ignore_event(): event ID argument may not be zero");                  wrnMsg("ignore_event(): event ID argument may not be zero");
156                  // not errorResult(), because that would abort the script, not intentional in this case                  // not errorResult(), because that would abort the script, not intentional in this case
157                  return successResult();                  return successResult();
# Line 148  namespace LinuxSampler { Line 168  namespace LinuxSampler {
168          return successResult();          return successResult();
169      }      }
170    
171        // ignore_controller() function
172    
173      InstrumentScriptVMFunction_ignore_controller::InstrumentScriptVMFunction_ignore_controller(InstrumentScriptVM* parent)      InstrumentScriptVMFunction_ignore_controller::InstrumentScriptVMFunction_ignore_controller(InstrumentScriptVM* parent)
174          : m_vm(parent)          : m_vm(parent)
175      {      {
# Line 168  namespace LinuxSampler { Line 190  namespace LinuxSampler {
190          return successResult();          return successResult();
191      }      }
192    
193        // note_off() function
194    
195      InstrumentScriptVMFunction_note_off::InstrumentScriptVMFunction_note_off(InstrumentScriptVM* parent)      InstrumentScriptVMFunction_note_off::InstrumentScriptVMFunction_note_off(InstrumentScriptVM* parent)
196          : m_vm(parent)          : m_vm(parent)
197      {      {
198      }      }
199    
200      bool InstrumentScriptVMFunction_note_off::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_note_off::acceptsArgType(vmint iArg, ExprType_t type) const {
201          return type == INT_EXPR || type == INT_ARR_EXPR;          return type == INT_EXPR || type == INT_ARR_EXPR;
202      }      }
203    
# Line 181  namespace LinuxSampler { Line 205  namespace LinuxSampler {
205          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
206              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
207    
208          int velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127;          vmint velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127;
209          if (velocity < 0 || velocity > 127) {          if (velocity < 0 || velocity > 127) {
210              errMsg("note_off(): argument 2 is an invalid velocity value");              errMsg("note_off(): argument 2 is an invalid velocity value");
211              return errorResult();              return errorResult();
# Line 204  namespace LinuxSampler { Line 228  namespace LinuxSampler {
228              Event e = pNote->cause;              Event e = pNote->cause;
229              e.Init(); // clear IDs              e.Init(); // clear IDs
230              e.CopyTimeFrom(m_vm->m_event->cause); // set fragment time for "now"              e.CopyTimeFrom(m_vm->m_event->cause); // set fragment time for "now"
231              e.Type = Event::type_note_off;              e.Type = Event::type_stop_note;
232                e.Param.Note.ID = id.noteID();
233                e.Param.Note.Key = pNote->hostKey;
234              e.Param.Note.Velocity = velocity;              e.Param.Note.Velocity = velocity;
235    
236              pEngineChannel->ScheduleEventMicroSec(&e, 0);              pEngineChannel->ScheduleEventMicroSec(&e, 0);
237          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
238              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
239              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
240                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
241                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
242    
# Line 220  namespace LinuxSampler { Line 246  namespace LinuxSampler {
246                  Event e = pNote->cause;                  Event e = pNote->cause;
247                  e.Init(); // clear IDs                  e.Init(); // clear IDs
248                  e.CopyTimeFrom(m_vm->m_event->cause); // set fragment time for "now"                  e.CopyTimeFrom(m_vm->m_event->cause); // set fragment time for "now"
249                  e.Type = Event::type_note_off;                  e.Type = Event::type_stop_note;
250                    e.Param.Note.ID = id.noteID();
251                    e.Param.Note.Key = pNote->hostKey;
252                  e.Param.Note.Velocity = velocity;                  e.Param.Note.Velocity = velocity;
253    
254                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
# Line 230  namespace LinuxSampler { Line 258  namespace LinuxSampler {
258          return successResult();          return successResult();
259      }      }
260    
261        // set_event_mark() function
262    
263      InstrumentScriptVMFunction_set_event_mark::InstrumentScriptVMFunction_set_event_mark(InstrumentScriptVM* parent)      InstrumentScriptVMFunction_set_event_mark::InstrumentScriptVMFunction_set_event_mark(InstrumentScriptVM* parent)
264          : m_vm(parent)          : m_vm(parent)
265      {      {
# Line 237  namespace LinuxSampler { Line 267  namespace LinuxSampler {
267    
268      VMFnResult* InstrumentScriptVMFunction_set_event_mark::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_set_event_mark::exec(VMFnArgs* args) {
269          const ScriptID id = args->arg(0)->asInt()->evalInt();          const ScriptID id = args->arg(0)->asInt()->evalInt();
270          const int groupID = args->arg(1)->asInt()->evalInt();          const vmint groupID = args->arg(1)->asInt()->evalInt();
271    
272          if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {          if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
273              errMsg("set_event_mark(): argument 2 is an invalid group id");              errMsg("set_event_mark(): argument 2 is an invalid group id");
# Line 266  namespace LinuxSampler { Line 296  namespace LinuxSampler {
296          return successResult();          return successResult();
297      }      }
298    
299        // delete_event_mark() function
300    
301      InstrumentScriptVMFunction_delete_event_mark::InstrumentScriptVMFunction_delete_event_mark(InstrumentScriptVM* parent)      InstrumentScriptVMFunction_delete_event_mark::InstrumentScriptVMFunction_delete_event_mark(InstrumentScriptVM* parent)
302          : m_vm(parent)          : m_vm(parent)
303      {      {
# Line 273  namespace LinuxSampler { Line 305  namespace LinuxSampler {
305    
306      VMFnResult* InstrumentScriptVMFunction_delete_event_mark::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_delete_event_mark::exec(VMFnArgs* args) {
307          const ScriptID id = args->arg(0)->asInt()->evalInt();          const ScriptID id = args->arg(0)->asInt()->evalInt();
308          const int groupID = args->arg(1)->asInt()->evalInt();          const vmint groupID = args->arg(1)->asInt()->evalInt();
309    
310          if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {          if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
311              errMsg("delete_event_mark(): argument 2 is an invalid group id");              errMsg("delete_event_mark(): argument 2 is an invalid group id");
# Line 288  namespace LinuxSampler { Line 320  namespace LinuxSampler {
320          return successResult();          return successResult();
321      }      }
322    
323        // by_marks() function
324    
325      InstrumentScriptVMFunction_by_marks::InstrumentScriptVMFunction_by_marks(InstrumentScriptVM* parent)      InstrumentScriptVMFunction_by_marks::InstrumentScriptVMFunction_by_marks(InstrumentScriptVM* parent)
326          : m_vm(parent)          : m_vm(parent)
327      {      {
328      }      }
329    
330      int InstrumentScriptVMFunction_by_marks::Result::arraySize() const {      vmint InstrumentScriptVMFunction_by_marks::Result::arraySize() const {
331          return eventGroup->size();          return eventGroup->size();
332      }      }
333    
334      int InstrumentScriptVMFunction_by_marks::Result::evalIntElement(uint i) {      vmint InstrumentScriptVMFunction_by_marks::Result::evalIntElement(vmuint i) {
335          return (*eventGroup)[i];          return (*eventGroup)[i];
336      }      }
337    
# Line 314  namespace LinuxSampler { Line 348  namespace LinuxSampler {
348      }      }
349    
350      VMFnResult* InstrumentScriptVMFunction_by_marks::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_by_marks::exec(VMFnArgs* args) {
351          int groupID = args->arg(0)->asInt()->evalInt();          vmint groupID = args->arg(0)->asInt()->evalInt();
352    
353          if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {          if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
354              errMsg("by_marks(): argument is an invalid group id");              errMsg("by_marks(): argument is an invalid group id");
# Line 327  namespace LinuxSampler { Line 361  namespace LinuxSampler {
361          return successResult( &pEngineChannel->pScript->eventGroups[groupID] );          return successResult( &pEngineChannel->pScript->eventGroups[groupID] );
362      }      }
363    
364        // change_vol() function
365    
366        InstrumentScriptVMFunction_change_vol::InstrumentScriptVMFunction_change_vol(InstrumentScriptVM* parent)
367            : m_vm(parent)
368        {
369        }
370    
371        bool InstrumentScriptVMFunction_change_vol::acceptsArgType(vmint iArg, ExprType_t type) const {
372            if (iArg == 0)
373                return type == INT_EXPR || type == INT_ARR_EXPR;
374            else
375                return type == INT_EXPR;
376        }
377    
378        VMFnResult* InstrumentScriptVMFunction_change_vol::exec(VMFnArgs* args) {
379            vmint volume = args->arg(1)->asInt()->evalInt(); // volume change in milli dB
380            bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
381            const float fVolumeLin = RTMath::DecibelToLinRatio(float(volume) / 1000.f);
382    
383            AbstractEngineChannel* pEngineChannel =
384                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
385    
386            if (args->arg(0)->exprType() == INT_EXPR) {
387                const ScriptID id = args->arg(0)->asInt()->evalInt();
388                if (!id) {
389                    wrnMsg("change_vol(): note ID for argument 1 may not be zero");
390                    return successResult();
391                }
392                if (!id.isNoteID()) {
393                    wrnMsg("change_vol(): argument 1 is not a note ID");
394                    return successResult();
395                }
396    
397                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
398                if (!pNote) return successResult();
399    
400                // if change_vol() was called immediately after note was triggered
401                // then immediately apply the volume to note object, but only if
402                // change_vol_time() has not been called before
403                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
404                    pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S)
405                {
406                    if (relative)
407                        pNote->Override.Volume *= fVolumeLin;
408                    else
409                        pNote->Override.Volume = fVolumeLin;
410                } else { // otherwise schedule the volume change ...
411                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
412                    e.Init(); // clear IDs
413                    e.Type = Event::type_note_synth_param;
414                    e.Param.NoteSynthParam.NoteID   = id.noteID();
415                    e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
416                    e.Param.NoteSynthParam.Delta    = fVolumeLin;
417                    e.Param.NoteSynthParam.Relative = relative;
418    
419                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
420                }
421            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
422                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
423                for (vmint i = 0; i < ids->arraySize(); ++i) {
424                    const ScriptID id = ids->evalIntElement(i);
425                    if (!id || !id.isNoteID()) continue;
426    
427                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
428                    if (!pNote) continue;
429    
430                    // if change_vol() was called immediately after note was triggered
431                    // then immediately apply the volume to Note object, but only if
432                    // change_vol_time() has not been called before
433                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
434                        pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S)
435                    {
436                        if (relative)
437                            pNote->Override.Volume *= fVolumeLin;
438                        else
439                            pNote->Override.Volume = fVolumeLin;
440                    } else { // otherwise schedule the volume change ...
441                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
442                        e.Init(); // clear IDs
443                        e.Type = Event::type_note_synth_param;
444                        e.Param.NoteSynthParam.NoteID   = id.noteID();
445                        e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
446                        e.Param.NoteSynthParam.Delta    = fVolumeLin;
447                        e.Param.NoteSynthParam.Relative = relative;
448    
449                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
450                    }
451                }
452            }
453    
454            return successResult();
455        }
456    
457        // change_tune() function
458    
459        InstrumentScriptVMFunction_change_tune::InstrumentScriptVMFunction_change_tune(InstrumentScriptVM* parent)
460            : m_vm(parent)
461        {
462        }
463    
464        bool InstrumentScriptVMFunction_change_tune::acceptsArgType(vmint iArg, ExprType_t type) const {
465            if (iArg == 0)
466                return type == INT_EXPR || type == INT_ARR_EXPR;
467            else
468                return type == INT_EXPR;
469        }
470    
471        VMFnResult* InstrumentScriptVMFunction_change_tune::exec(VMFnArgs* args) {
472            vmint tune = args->arg(1)->asInt()->evalInt(); // tuning change in milli cents
473            bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
474            const float fFreqRatio = RTMath::CentsToFreqRatioUnlimited(float(tune) / 1000.f);
475    
476            AbstractEngineChannel* pEngineChannel =
477                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
478    
479            if (args->arg(0)->exprType() == INT_EXPR) {
480                const ScriptID id = args->arg(0)->asInt()->evalInt();
481                if (!id) {
482                    wrnMsg("change_tune(): note ID for argument 1 may not be zero");
483                    return successResult();
484                }
485                if (!id.isNoteID()) {
486                    wrnMsg("change_tune(): argument 1 is not a note ID");
487                    return successResult();
488                }
489    
490                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
491                if (!pNote) return successResult();
492    
493                // if change_tune() was called immediately after note was triggered
494                // then immediately apply the tuning to Note object, but only if
495                // change_tune_time() has not been called before
496                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
497                    pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S)
498                {
499                    if (relative)
500                        pNote->Override.Pitch *= fFreqRatio;
501                    else
502                        pNote->Override.Pitch = fFreqRatio;
503                } else { // otherwise schedule tuning change ...
504                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
505                    e.Init(); // clear IDs
506                    e.Type = Event::type_note_synth_param;
507                    e.Param.NoteSynthParam.NoteID   = id.noteID();
508                    e.Param.NoteSynthParam.Type     = Event::synth_param_pitch;
509                    e.Param.NoteSynthParam.Delta    = fFreqRatio;
510                    e.Param.NoteSynthParam.Relative = relative;
511    
512                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
513                }
514            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
515                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
516                for (vmint i = 0; i < ids->arraySize(); ++i) {
517                    const ScriptID id = ids->evalIntElement(i);
518                    if (!id || !id.isNoteID()) continue;
519    
520                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
521                    if (!pNote) continue;
522    
523                    // if change_tune() was called immediately after note was triggered
524                    // then immediately apply the tuning to Note object, but only if
525                    // change_tune_time() has not been called before
526                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
527                        pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S)
528                    {
529                        if (relative)
530                            pNote->Override.Pitch *= fFreqRatio;
531                        else
532                            pNote->Override.Pitch = fFreqRatio;
533                    } else { // otherwise schedule tuning change ...
534                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
535                        e.Init(); // clear IDs
536                        e.Type = Event::type_note_synth_param;
537                        e.Param.NoteSynthParam.NoteID   = id.noteID();
538                        e.Param.NoteSynthParam.Type     = Event::synth_param_pitch;
539                        e.Param.NoteSynthParam.Delta    = fFreqRatio;
540                        e.Param.NoteSynthParam.Relative = relative;
541    
542                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
543                    }
544                }
545            }
546    
547            return successResult();
548        }
549    
550        // change_pan() function
551    
552        InstrumentScriptVMFunction_change_pan::InstrumentScriptVMFunction_change_pan(InstrumentScriptVM* parent)
553            : m_vm(parent)
554        {
555        }
556    
557        bool InstrumentScriptVMFunction_change_pan::acceptsArgType(vmint iArg, ExprType_t type) const {
558            if (iArg == 0)
559                return type == INT_EXPR || type == INT_ARR_EXPR;
560            else
561                return type == INT_EXPR;
562        }
563    
564        VMFnResult* InstrumentScriptVMFunction_change_pan::exec(VMFnArgs* args) {
565            vmint pan = args->arg(1)->asInt()->evalInt();
566            bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
567    
568            if (pan > 1000) {
569                wrnMsg("change_pan(): argument 2 may not be larger than 1000");
570                pan = 1000;
571            } else if (pan < -1000) {
572                wrnMsg("change_pan(): argument 2 may not be smaller than -1000");
573                pan = -1000;
574            }
575            const float fPan = float(pan) / 1000.f;
576    
577            AbstractEngineChannel* pEngineChannel =
578                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
579    
580            if (args->arg(0)->exprType() == INT_EXPR) {
581                const ScriptID id = args->arg(0)->asInt()->evalInt();
582                if (!id) {
583                    wrnMsg("change_pan(): note ID for argument 1 may not be zero");
584                    return successResult();
585                }
586                if (!id.isNoteID()) {
587                    wrnMsg("change_pan(): argument 1 is not a note ID");
588                    return successResult();
589                }
590    
591                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
592                if (!pNote) return successResult();
593    
594                // if change_pan() was called immediately after note was triggered
595                // then immediately apply the panning to Note object
596                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
597                    if (relative) {
598                        pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources);
599                    } else {
600                        pNote->Override.Pan = fPan;
601                        pNote->Override.PanSources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set
602                    }
603                } else { // otherwise schedule panning change ...
604                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
605                    e.Init(); // clear IDs
606                    e.Type = Event::type_note_synth_param;
607                    e.Param.NoteSynthParam.NoteID   = id.noteID();
608                    e.Param.NoteSynthParam.Type     = Event::synth_param_pan;
609                    e.Param.NoteSynthParam.Delta    = fPan;
610                    e.Param.NoteSynthParam.Relative = relative;
611    
612                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
613                }
614            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
615                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
616                for (vmint i = 0; i < ids->arraySize(); ++i) {
617                    const ScriptID id = ids->evalIntElement(i);
618                    if (!id || !id.isNoteID()) continue;
619    
620                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
621                    if (!pNote) continue;
622    
623                    // if change_pan() was called immediately after note was triggered
624                    // then immediately apply the panning to Note object
625                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
626                        if (relative) {
627                            pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources);
628                        } else {
629                            pNote->Override.Pan = fPan;
630                            pNote->Override.PanSources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set
631                        }
632                    } else { // otherwise schedule panning change ...
633                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
634                        e.Init(); // clear IDs
635                        e.Type = Event::type_note_synth_param;
636                        e.Param.NoteSynthParam.NoteID   = id.noteID();
637                        e.Param.NoteSynthParam.Type     = Event::synth_param_pan;
638                        e.Param.NoteSynthParam.Delta    = fPan;
639                        e.Param.NoteSynthParam.Relative = relative;
640    
641                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
642                    }
643                }
644            }
645    
646            return successResult();
647        }
648    
649        #define VM_FILTER_PAR_MAX_VALUE 1000000
650        #define VM_EG_PAR_MAX_VALUE 1000000
651    
652        // change_cutoff() function
653    
654        InstrumentScriptVMFunction_change_cutoff::InstrumentScriptVMFunction_change_cutoff(InstrumentScriptVM* parent)
655            : m_vm(parent)
656        {
657        }
658    
659        bool InstrumentScriptVMFunction_change_cutoff::acceptsArgType(vmint iArg, ExprType_t type) const {
660            if (iArg == 0)
661                return type == INT_EXPR || type == INT_ARR_EXPR;
662            else
663                return type == INT_EXPR;
664        }
665    
666        VMFnResult* InstrumentScriptVMFunction_change_cutoff::exec(VMFnArgs* args) {
667            vmint cutoff = args->arg(1)->asInt()->evalInt();
668            if (cutoff > VM_FILTER_PAR_MAX_VALUE) {
669                wrnMsg("change_cutoff(): argument 2 may not be larger than 1000000");
670                cutoff = VM_FILTER_PAR_MAX_VALUE;
671            } else if (cutoff < 0) {
672                wrnMsg("change_cutoff(): argument 2 may not be negative");
673                cutoff = 0;
674            }
675            const float fCutoff = float(cutoff) / float(VM_FILTER_PAR_MAX_VALUE);
676    
677            AbstractEngineChannel* pEngineChannel =
678                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
679    
680            if (args->arg(0)->exprType() == INT_EXPR) {
681                const ScriptID id = args->arg(0)->asInt()->evalInt();
682                if (!id) {
683                    wrnMsg("change_cutoff(): note ID for argument 1 may not be zero");
684                    return successResult();
685                }
686                if (!id.isNoteID()) {
687                    wrnMsg("change_cutoff(): argument 1 is not a note ID");
688                    return successResult();
689                }
690    
691                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
692                if (!pNote) return successResult();
693    
694                // if change_cutoff() was called immediately after note was triggered
695                // then immediately apply cutoff to Note object
696                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
697                    pNote->Override.Cutoff = fCutoff;
698                } else { // otherwise schedule cutoff change ...
699                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
700                    e.Init(); // clear IDs
701                    e.Type = Event::type_note_synth_param;
702                    e.Param.NoteSynthParam.NoteID   = id.noteID();
703                    e.Param.NoteSynthParam.Type     = Event::synth_param_cutoff;
704                    e.Param.NoteSynthParam.Delta    = fCutoff;
705                    e.Param.NoteSynthParam.Relative = false;
706    
707                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
708                }
709            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
710                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
711                for (vmint i = 0; i < ids->arraySize(); ++i) {
712                    const ScriptID id = ids->evalIntElement(i);
713                    if (!id || !id.isNoteID()) continue;
714    
715                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
716                    if (!pNote) continue;
717    
718                    // if change_cutoff() was called immediately after note was triggered
719                    // then immediately apply cutoff to Note object
720                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
721                        pNote->Override.Cutoff = fCutoff;
722                    } else { // otherwise schedule cutoff change ...
723                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
724                        e.Init(); // clear IDs
725                        e.Type = Event::type_note_synth_param;
726                        e.Param.NoteSynthParam.NoteID   = id.noteID();
727                        e.Param.NoteSynthParam.Type     = Event::synth_param_cutoff;
728                        e.Param.NoteSynthParam.Delta    = fCutoff;
729                        e.Param.NoteSynthParam.Relative = false;
730    
731                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
732                    }
733                }
734            }
735    
736            return successResult();
737        }
738    
739        // change_reso() function
740        
741        InstrumentScriptVMFunction_change_reso::InstrumentScriptVMFunction_change_reso(InstrumentScriptVM* parent)
742            : m_vm(parent)
743        {
744        }
745    
746        bool InstrumentScriptVMFunction_change_reso::acceptsArgType(vmint iArg, ExprType_t type) const {
747            if (iArg == 0)
748                return type == INT_EXPR || type == INT_ARR_EXPR;
749            else
750                return type == INT_EXPR;
751        }
752    
753        VMFnResult* InstrumentScriptVMFunction_change_reso::exec(VMFnArgs* args) {
754            vmint resonance = args->arg(1)->asInt()->evalInt();
755            if (resonance > VM_FILTER_PAR_MAX_VALUE) {
756                wrnMsg("change_reso(): argument 2 may not be larger than 1000000");
757                resonance = VM_FILTER_PAR_MAX_VALUE;
758            } else if (resonance < 0) {
759                wrnMsg("change_reso(): argument 2 may not be negative");
760                resonance = 0;
761            }
762            const float fResonance = float(resonance) / float(VM_FILTER_PAR_MAX_VALUE);
763    
764            AbstractEngineChannel* pEngineChannel =
765                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
766    
767            if (args->arg(0)->exprType() == INT_EXPR) {
768                const ScriptID id = args->arg(0)->asInt()->evalInt();
769                if (!id) {
770                    wrnMsg("change_reso(): note ID for argument 1 may not be zero");
771                    return successResult();
772                }
773                if (!id.isNoteID()) {
774                    wrnMsg("change_reso(): argument 1 is not a note ID");
775                    return successResult();
776                }
777    
778                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
779                if (!pNote) return successResult();
780    
781                // if change_reso() was called immediately after note was triggered
782                // then immediately apply resonance to Note object
783                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
784                    pNote->Override.Resonance = fResonance;
785                } else { // otherwise schedule resonance change ...
786                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
787                    e.Init(); // clear IDs
788                    e.Type = Event::type_note_synth_param;
789                    e.Param.NoteSynthParam.NoteID   = id.noteID();
790                    e.Param.NoteSynthParam.Type     = Event::synth_param_resonance;
791                    e.Param.NoteSynthParam.Delta    = fResonance;
792                    e.Param.NoteSynthParam.Relative = false;
793    
794                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
795                }
796            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
797                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
798                for (vmint i = 0; i < ids->arraySize(); ++i) {
799                    const ScriptID id = ids->evalIntElement(i);
800                    if (!id || !id.isNoteID()) continue;
801    
802                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
803                    if (!pNote) continue;
804    
805                    // if change_reso() was called immediately after note was triggered
806                    // then immediately apply resonance to Note object
807                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
808                        pNote->Override.Resonance = fResonance;
809                    } else { // otherwise schedule resonance change ...
810                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
811                        e.Init(); // clear IDs
812                        e.Type = Event::type_note_synth_param;
813                        e.Param.NoteSynthParam.NoteID   = id.noteID();
814                        e.Param.NoteSynthParam.Type     = Event::synth_param_resonance;
815                        e.Param.NoteSynthParam.Delta    = fResonance;
816                        e.Param.NoteSynthParam.Relative = false;
817    
818                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
819                    }
820                }
821            }
822    
823            return successResult();
824        }
825        
826        // change_attack() function
827    
828        InstrumentScriptVMFunction_change_attack::InstrumentScriptVMFunction_change_attack(InstrumentScriptVM* parent)
829            : m_vm(parent)
830        {
831        }
832    
833        bool InstrumentScriptVMFunction_change_attack::acceptsArgType(vmint iArg, ExprType_t type) const {
834            if (iArg == 0)
835                return type == INT_EXPR || type == INT_ARR_EXPR;
836            else
837                return type == INT_EXPR;
838        }
839    
840        VMFnResult* InstrumentScriptVMFunction_change_attack::exec(VMFnArgs* args) {
841            vmint attack = args->arg(1)->asInt()->evalInt();
842            // note: intentionally not checking against a max. value here!
843            // (to allow i.e. passing 2000000 for doubling the attack time)
844            if (attack < 0) {
845                wrnMsg("change_attack(): argument 2 may not be negative");
846                attack = 0;
847            }
848            const float fAttack = float(attack) / float(VM_EG_PAR_MAX_VALUE);
849    
850            AbstractEngineChannel* pEngineChannel =
851                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
852    
853            if (args->arg(0)->exprType() == INT_EXPR) {
854                const ScriptID id = args->arg(0)->asInt()->evalInt();
855                if (!id) {
856                    wrnMsg("change_attack(): note ID for argument 1 may not be zero");
857                    return successResult();
858                }
859                if (!id.isNoteID()) {
860                    wrnMsg("change_attack(): argument 1 is not a note ID");
861                    return successResult();
862                }
863    
864                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
865                if (!pNote) return successResult();
866    
867                // if change_attack() was called immediately after note was triggered
868                // then immediately apply attack to Note object
869                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
870                    pNote->Override.Attack = fAttack;
871                } else { // otherwise schedule attack change ...
872                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
873                    e.Init(); // clear IDs
874                    e.Type = Event::type_note_synth_param;
875                    e.Param.NoteSynthParam.NoteID   = id.noteID();
876                    e.Param.NoteSynthParam.Type     = Event::synth_param_attack;
877                    e.Param.NoteSynthParam.Delta    = fAttack;
878                    e.Param.NoteSynthParam.Relative = false;
879    
880                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
881                }
882            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
883                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
884                for (vmint i = 0; i < ids->arraySize(); ++i) {
885                    const ScriptID id = ids->evalIntElement(i);
886                    if (!id || !id.isNoteID()) continue;
887    
888                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
889                    if (!pNote) continue;
890    
891                    // if change_attack() was called immediately after note was triggered
892                    // then immediately apply attack to Note object
893                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
894                        pNote->Override.Attack = fAttack;
895                    } else { // otherwise schedule attack change ...
896                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
897                        e.Init(); // clear IDs
898                        e.Type = Event::type_note_synth_param;
899                        e.Param.NoteSynthParam.NoteID   = id.noteID();
900                        e.Param.NoteSynthParam.Type     = Event::synth_param_attack;
901                        e.Param.NoteSynthParam.Delta    = fAttack;
902                        e.Param.NoteSynthParam.Relative = false;
903    
904                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
905                    }
906                }
907            }
908    
909            return successResult();
910        }
911    
912        // change_decay() function
913        
914        InstrumentScriptVMFunction_change_decay::InstrumentScriptVMFunction_change_decay(InstrumentScriptVM* parent)
915            : m_vm(parent)
916        {
917        }
918    
919        bool InstrumentScriptVMFunction_change_decay::acceptsArgType(vmint iArg, ExprType_t type) const {
920            if (iArg == 0)
921                return type == INT_EXPR || type == INT_ARR_EXPR;
922            else
923                return type == INT_EXPR;
924        }
925    
926        VMFnResult* InstrumentScriptVMFunction_change_decay::exec(VMFnArgs* args) {
927            vmint decay = args->arg(1)->asInt()->evalInt();
928            // note: intentionally not checking against a max. value here!
929            // (to allow i.e. passing 2000000 for doubling the decay time)
930            if (decay < 0) {
931                wrnMsg("change_decay(): argument 2 may not be negative");
932                decay = 0;
933            }
934            const float fDecay = float(decay) / float(VM_EG_PAR_MAX_VALUE);
935    
936            AbstractEngineChannel* pEngineChannel =
937                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
938    
939            if (args->arg(0)->exprType() == INT_EXPR) {
940                const ScriptID id = args->arg(0)->asInt()->evalInt();
941                if (!id) {
942                    wrnMsg("change_decay(): note ID for argument 1 may not be zero");
943                    return successResult();
944                }
945                if (!id.isNoteID()) {
946                    wrnMsg("change_decay(): argument 1 is not a note ID");
947                    return successResult();
948                }
949    
950                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
951                if (!pNote) return successResult();
952    
953                // if change_decay() was called immediately after note was triggered
954                // then immediately apply decay to Note object
955                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
956                    pNote->Override.Decay = fDecay;
957                } else { // otherwise schedule decay change ...
958                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
959                    e.Init(); // clear IDs
960                    e.Type = Event::type_note_synth_param;
961                    e.Param.NoteSynthParam.NoteID   = id.noteID();
962                    e.Param.NoteSynthParam.Type     = Event::synth_param_decay;
963                    e.Param.NoteSynthParam.Delta    = fDecay;
964                    e.Param.NoteSynthParam.Relative = false;
965    
966                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
967                }
968            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
969                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
970                for (vmint i = 0; i < ids->arraySize(); ++i) {
971                    const ScriptID id = ids->evalIntElement(i);
972                    if (!id || !id.isNoteID()) continue;
973    
974                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
975                    if (!pNote) continue;
976    
977                    // if change_decay() was called immediately after note was triggered
978                    // then immediately apply decay to Note object
979                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
980                        pNote->Override.Decay = fDecay;
981                    } else { // otherwise schedule decay change ...
982                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
983                        e.Init(); // clear IDs
984                        e.Type = Event::type_note_synth_param;
985                        e.Param.NoteSynthParam.NoteID   = id.noteID();
986                        e.Param.NoteSynthParam.Type     = Event::synth_param_decay;
987                        e.Param.NoteSynthParam.Delta    = fDecay;
988                        e.Param.NoteSynthParam.Relative = false;
989    
990                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
991                    }
992                }
993            }
994    
995            return successResult();
996        }
997    
998        // change_release() function
999        
1000        InstrumentScriptVMFunction_change_release::InstrumentScriptVMFunction_change_release(InstrumentScriptVM* parent)
1001            : m_vm(parent)
1002        {
1003        }
1004    
1005        bool InstrumentScriptVMFunction_change_release::acceptsArgType(vmint iArg, ExprType_t type) const {
1006            if (iArg == 0)
1007                return type == INT_EXPR || type == INT_ARR_EXPR;
1008            else
1009                return type == INT_EXPR;
1010        }
1011    
1012        VMFnResult* InstrumentScriptVMFunction_change_release::exec(VMFnArgs* args) {
1013            vmint release = args->arg(1)->asInt()->evalInt();
1014            // note: intentionally not checking against a max. value here!
1015            // (to allow i.e. passing 2000000 for doubling the release time)
1016            if (release < 0) {
1017                wrnMsg("change_release(): argument 2 may not be negative");
1018                release = 0;
1019            }
1020            const float fRelease = float(release) / float(VM_EG_PAR_MAX_VALUE);
1021    
1022            AbstractEngineChannel* pEngineChannel =
1023                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1024    
1025            if (args->arg(0)->exprType() == INT_EXPR) {
1026                const ScriptID id = args->arg(0)->asInt()->evalInt();
1027                if (!id) {
1028                    wrnMsg("change_release(): note ID for argument 1 may not be zero");
1029                    return successResult();
1030                }
1031                if (!id.isNoteID()) {
1032                    wrnMsg("change_release(): argument 1 is not a note ID");
1033                    return successResult();
1034                }
1035    
1036                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1037                if (!pNote) return successResult();
1038    
1039                // if change_release() was called immediately after note was triggered
1040                // then immediately apply relase to Note object
1041                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1042                    pNote->Override.Release = fRelease;
1043                } else { // otherwise schedule release change ...
1044                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1045                    e.Init(); // clear IDs
1046                    e.Type = Event::type_note_synth_param;
1047                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1048                    e.Param.NoteSynthParam.Type     = Event::synth_param_release;
1049                    e.Param.NoteSynthParam.Delta    = fRelease;
1050                    e.Param.NoteSynthParam.Relative = false;
1051    
1052                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
1053                }
1054            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1055                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1056                for (vmint i = 0; i < ids->arraySize(); ++i) {
1057                    const ScriptID id = ids->evalIntElement(i);
1058                    if (!id || !id.isNoteID()) continue;
1059    
1060                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1061                    if (!pNote) continue;
1062    
1063                    // if change_release() was called immediately after note was triggered
1064                    // then immediately apply relase to Note object
1065                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1066                        pNote->Override.Release = fRelease;
1067                    } else { // otherwise schedule release change ...
1068                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1069                        e.Init(); // clear IDs
1070                        e.Type = Event::type_note_synth_param;
1071                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1072                        e.Param.NoteSynthParam.Type     = Event::synth_param_release;
1073                        e.Param.NoteSynthParam.Delta    = fRelease;
1074                        e.Param.NoteSynthParam.Relative = false;
1075    
1076                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
1077                    }
1078                }
1079            }
1080    
1081            return successResult();
1082        }
1083    
1084        // template for change_*() functions
1085    
1086        bool VMChangeSynthParamFunction::acceptsArgType(vmint iArg, ExprType_t type) const {
1087            if (iArg == 0)
1088                return type == INT_EXPR || type == INT_ARR_EXPR;
1089            else
1090                return type == INT_EXPR;
1091        }
1092    
1093        // Arbitrarily chosen constant value symbolizing "no limit".
1094        #define NO_LIMIT 1315916909
1095    
1096        template<float NoteBase::_Override::*T_noteParam, vmint T_synthParam,
1097                 bool T_isNormalizedParam, vmint T_maxValue, vmint T_minValue>
1098        VMFnResult* VMChangeSynthParamFunction::execTemplate(VMFnArgs* args, const char* functionName) {
1099            vmint value = args->arg(1)->asInt()->evalInt();
1100            if (T_maxValue != NO_LIMIT && value > T_maxValue) {
1101                wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValue));
1102                value = T_maxValue;
1103            } else if (T_minValue != NO_LIMIT && value < T_minValue) {
1104                if (T_minValue == 0)
1105                    wrnMsg(String(functionName) + "(): argument 2 may not be negative");
1106                else
1107                    wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValue));
1108                value = T_minValue;
1109            }
1110            const float fValue = (T_isNormalizedParam) ?
1111                float(value) / float(T_maxValue) : // convert to 0.0 .. 1.0 value range
1112                float(value) / 1000000.f; // assuming microseconds here, convert to seconds
1113    
1114            AbstractEngineChannel* pEngineChannel =
1115                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1116    
1117            if (args->arg(0)->exprType() == INT_EXPR) {
1118                const ScriptID id = args->arg(0)->asInt()->evalInt();
1119                if (!id) {
1120                    wrnMsg(String(functionName) + "(): note ID for argument 1 may not be zero");
1121                    return successResult();
1122                }
1123                if (!id.isNoteID()) {
1124                    wrnMsg(String(functionName) + "(): argument 1 is not a note ID");
1125                    return successResult();
1126                }
1127    
1128                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1129                if (!pNote) return successResult();
1130    
1131                // if this change_*() script function was called immediately after
1132                // note was triggered then immediately apply the synth parameter
1133                // change to Note object
1134                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1135                    pNote->Override.*T_noteParam = fValue;
1136                } else { // otherwise schedule this synth parameter change ...
1137                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1138                    e.Init(); // clear IDs
1139                    e.Type = Event::type_note_synth_param;
1140                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1141                    e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1142                    e.Param.NoteSynthParam.Delta    = fValue;
1143                    e.Param.NoteSynthParam.Relative = false;
1144    
1145                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
1146                }
1147            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1148                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1149                for (vmint i = 0; i < ids->arraySize(); ++i) {
1150                    const ScriptID id = ids->evalIntElement(i);
1151                    if (!id || !id.isNoteID()) continue;
1152    
1153                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1154                    if (!pNote) continue;
1155    
1156                    // if this change_*() script function was called immediately after
1157                    // note was triggered then immediately apply the synth parameter
1158                    // change to Note object
1159                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1160                        pNote->Override.*T_noteParam = fValue;
1161                    } else { // otherwise schedule this synth parameter change ...
1162                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1163                        e.Init(); // clear IDs
1164                        e.Type = Event::type_note_synth_param;
1165                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1166                        e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1167                        e.Param.NoteSynthParam.Delta    = fValue;
1168                        e.Param.NoteSynthParam.Relative = false;
1169    
1170                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
1171                    }
1172                }
1173            }
1174    
1175            return successResult();
1176        }
1177    
1178        // change_sustain() function
1179    
1180        VMFnResult* InstrumentScriptVMFunction_change_sustain::exec(VMFnArgs* args) {
1181            return VMChangeSynthParamFunction::execTemplate<
1182                        &NoteBase::_Override::Sustain,
1183                        Event::synth_param_sustain,
1184                        false, NO_LIMIT, 0>( args, "change_sustain" );
1185        }
1186    
1187        // change_cutoff_attack() function
1188    
1189        VMFnResult* InstrumentScriptVMFunction_change_cutoff_attack::exec(VMFnArgs* args) {
1190            return VMChangeSynthParamFunction::execTemplate<
1191                        &NoteBase::_Override::CutoffAttack,
1192                        Event::synth_param_cutoff_attack,
1193                        false, NO_LIMIT, 0>( args, "change_cutoff_attack" );
1194        }
1195    
1196        // change_cutoff_decay() function
1197    
1198        VMFnResult* InstrumentScriptVMFunction_change_cutoff_decay::exec(VMFnArgs* args) {
1199            return VMChangeSynthParamFunction::execTemplate<
1200                        &NoteBase::_Override::CutoffDecay,
1201                        Event::synth_param_cutoff_decay,
1202                        false, NO_LIMIT, 0>( args, "change_cutoff_decay" );
1203        }
1204    
1205        // change_cutoff_sustain() function
1206    
1207        VMFnResult* InstrumentScriptVMFunction_change_cutoff_sustain::exec(VMFnArgs* args) {
1208            return VMChangeSynthParamFunction::execTemplate<
1209                        &NoteBase::_Override::CutoffSustain,
1210                        Event::synth_param_cutoff_sustain,
1211                        false, NO_LIMIT, 0>( args, "change_cutoff_sustain" );
1212        }
1213    
1214        // change_cutoff_release() function
1215    
1216        VMFnResult* InstrumentScriptVMFunction_change_cutoff_release::exec(VMFnArgs* args) {
1217            return VMChangeSynthParamFunction::execTemplate<
1218                        &NoteBase::_Override::CutoffRelease,
1219                        Event::synth_param_cutoff_release,
1220                        false, NO_LIMIT, 0>( args, "change_cutoff_release" );
1221        }
1222    
1223        // change_amp_lfo_depth() function
1224    
1225        VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_depth::exec(VMFnArgs* args) {
1226            return VMChangeSynthParamFunction::execTemplate<
1227                        &NoteBase::_Override::AmpLFODepth,
1228                        Event::synth_param_amp_lfo_depth,
1229                        true, 1000000, 0>( args, "change_amp_lfo_depth" );
1230        }
1231    
1232        // change_amp_lfo_freq() function
1233    
1234        VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_freq::exec(VMFnArgs* args) {
1235            return VMChangeSynthParamFunction::execTemplate<
1236                        &NoteBase::_Override::AmpLFOFreq,
1237                        Event::synth_param_amp_lfo_freq,
1238                        true, 1000000, 0>( args, "change_amp_lfo_freq" );
1239        }
1240    
1241        // change_cutoff_lfo_depth() function
1242    
1243        VMFnResult* InstrumentScriptVMFunction_change_cutoff_lfo_depth::exec(VMFnArgs* args) {
1244            return VMChangeSynthParamFunction::execTemplate<
1245                        &NoteBase::_Override::CutoffLFODepth,
1246                        Event::synth_param_cutoff_lfo_depth,
1247                        true, 1000000, 0>( args, "change_cutoff_lfo_depth" );
1248        }
1249    
1250        // change_cutoff_lfo_freq() function
1251    
1252        VMFnResult* InstrumentScriptVMFunction_change_cutoff_lfo_freq::exec(VMFnArgs* args) {
1253            return VMChangeSynthParamFunction::execTemplate<
1254                        &NoteBase::_Override::CutoffLFOFreq,
1255                        Event::synth_param_cutoff_lfo_freq,
1256                        true, 1000000, 0>( args, "change_cutoff_lfo_freq" );
1257        }
1258    
1259        // change_pitch_lfo_depth() function
1260    
1261        VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_depth::exec(VMFnArgs* args) {
1262            return VMChangeSynthParamFunction::execTemplate<
1263                        &NoteBase::_Override::PitchLFODepth,
1264                        Event::synth_param_pitch_lfo_depth,
1265                        true, 1000000, 0>( args, "change_pitch_lfo_depth" );
1266        }
1267    
1268        // change_pitch_lfo_freq() function
1269    
1270        VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_freq::exec(VMFnArgs* args) {
1271            return VMChangeSynthParamFunction::execTemplate<
1272                        &NoteBase::_Override::PitchLFOFreq,
1273                        Event::synth_param_pitch_lfo_freq,
1274                        true, 1000000, 0>( args, "change_pitch_lfo_freq" );
1275        }
1276    
1277        // change_vol_time() function
1278    
1279        VMFnResult* InstrumentScriptVMFunction_change_vol_time::exec(VMFnArgs* args) {
1280            return VMChangeSynthParamFunction::execTemplate<
1281                        &NoteBase::_Override::VolumeTime,
1282                        Event::synth_param_volume_time,
1283                        false, NO_LIMIT, 0>( args, "change_vol_time" );
1284        }
1285    
1286        // change_tune_time() function
1287    
1288        VMFnResult* InstrumentScriptVMFunction_change_tune_time::exec(VMFnArgs* args) {
1289            return VMChangeSynthParamFunction::execTemplate<
1290                        &NoteBase::_Override::PitchTime,
1291                        Event::synth_param_pitch_time,
1292                        false, NO_LIMIT, 0>( args, "change_tune_time" );
1293        }
1294    
1295        // change_pan_time() function
1296    
1297        VMFnResult* InstrumentScriptVMFunction_change_pan_time::exec(VMFnArgs* args) {
1298            return VMChangeSynthParamFunction::execTemplate<
1299            &NoteBase::_Override::PanTime,
1300            Event::synth_param_pan_time,
1301            false, NO_LIMIT, 0>( args, "change_pan_time" );
1302        }
1303    
1304        // template for change_*_curve() functions
1305    
1306        bool VMChangeFadeCurveFunction::acceptsArgType(vmint iArg, ExprType_t type) const {
1307            if (iArg == 0)
1308                return type == INT_EXPR || type == INT_ARR_EXPR;
1309            else
1310                return type == INT_EXPR;
1311        }
1312    
1313        template<fade_curve_t NoteBase::_Override::*T_noteParam, vmint T_synthParam>
1314        VMFnResult* VMChangeFadeCurveFunction::execTemplate(VMFnArgs* args, const char* functionName) {
1315            vmint value = args->arg(1)->asInt()->evalInt();
1316            switch (value) {
1317                case FADE_CURVE_LINEAR:
1318                case FADE_CURVE_EASE_IN_EASE_OUT:
1319                    break;
1320                default:
1321                    wrnMsg(String(functionName) + "(): invalid curve type passed as argument 2");
1322                    return successResult();
1323            }
1324    
1325            AbstractEngineChannel* pEngineChannel =
1326                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1327    
1328            if (args->arg(0)->exprType() == INT_EXPR) {
1329                const ScriptID id = args->arg(0)->asInt()->evalInt();
1330                if (!id) {
1331                    wrnMsg(String(functionName) + "(): note ID for argument 1 may not be zero");
1332                    return successResult();
1333                }
1334                if (!id.isNoteID()) {
1335                    wrnMsg(String(functionName) + "(): argument 1 is not a note ID");
1336                    return successResult();
1337                }
1338    
1339                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1340                if (!pNote) return successResult();
1341    
1342                // if this change_*_curve() script function was called immediately after
1343                // note was triggered then immediately apply the synth parameter
1344                // change to Note object
1345                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1346                    pNote->Override.*T_noteParam = (fade_curve_t) value;
1347                } else { // otherwise schedule this synth parameter change ...
1348                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1349                    e.Init(); // clear IDs
1350                    e.Type = Event::type_note_synth_param;
1351                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1352                    e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1353                    e.Param.NoteSynthParam.Delta    = value;
1354                    e.Param.NoteSynthParam.Relative = false;
1355    
1356                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
1357                }
1358            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1359                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1360                for (vmint i = 0; i < ids->arraySize(); ++i) {
1361                    const ScriptID id = ids->evalIntElement(i);
1362                    if (!id || !id.isNoteID()) continue;
1363    
1364                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1365                    if (!pNote) continue;
1366    
1367                    // if this change_*_curve() script function was called immediately after
1368                    // note was triggered then immediately apply the synth parameter
1369                    // change to Note object
1370                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1371                        pNote->Override.*T_noteParam = (fade_curve_t) value;
1372                    } else { // otherwise schedule this synth parameter change ...
1373                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1374                        e.Init(); // clear IDs
1375                        e.Type = Event::type_note_synth_param;
1376                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1377                        e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1378                        e.Param.NoteSynthParam.Delta    = value;
1379                        e.Param.NoteSynthParam.Relative = false;
1380    
1381                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
1382                    }
1383                }
1384            }
1385    
1386            return successResult();
1387        }
1388    
1389        // change_vol_curve() function
1390    
1391        VMFnResult* InstrumentScriptVMFunction_change_vol_curve::exec(VMFnArgs* args) {
1392            return VMChangeFadeCurveFunction::execTemplate<
1393                        &NoteBase::_Override::VolumeCurve,
1394                        Event::synth_param_volume_curve>( args, "change_vol_curve" );
1395        }
1396    
1397        // change_tune_curve() function
1398    
1399        VMFnResult* InstrumentScriptVMFunction_change_tune_curve::exec(VMFnArgs* args) {
1400            return VMChangeFadeCurveFunction::execTemplate<
1401                        &NoteBase::_Override::PitchCurve,
1402                        Event::synth_param_pitch_curve>( args, "change_tune_curve" );
1403        }
1404    
1405        // change_pan_curve() function
1406    
1407        VMFnResult* InstrumentScriptVMFunction_change_pan_curve::exec(VMFnArgs* args) {
1408            return VMChangeFadeCurveFunction::execTemplate<
1409            &NoteBase::_Override::PanCurve,
1410            Event::synth_param_pan_curve>( args, "change_pan_curve" );
1411        }
1412    
1413        // fade_in() function
1414    
1415        InstrumentScriptVMFunction_fade_in::InstrumentScriptVMFunction_fade_in(InstrumentScriptVM* parent)
1416            : m_vm(parent)
1417        {
1418        }
1419    
1420        bool InstrumentScriptVMFunction_fade_in::acceptsArgType(vmint iArg, ExprType_t type) const {
1421            if (iArg == 0)
1422                return type == INT_EXPR || type == INT_ARR_EXPR;
1423            else
1424                return type == INT_EXPR;
1425        }
1426    
1427        VMFnResult* InstrumentScriptVMFunction_fade_in::exec(VMFnArgs* args) {
1428            vmint duration = args->arg(1)->asInt()->evalInt();
1429            if (duration < 0) {
1430                wrnMsg("fade_in(): argument 2 may not be negative");
1431                duration = 0;
1432            }
1433            const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
1434    
1435            AbstractEngineChannel* pEngineChannel =
1436                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1437    
1438            if (args->arg(0)->exprType() == INT_EXPR) {
1439                const ScriptID id = args->arg(0)->asInt()->evalInt();
1440                if (!id) {
1441                    wrnMsg("fade_in(): note ID for argument 1 may not be zero");
1442                    return successResult();
1443                }
1444                if (!id.isNoteID()) {
1445                    wrnMsg("fade_in(): argument 1 is not a note ID");
1446                    return successResult();
1447                }
1448    
1449                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1450                if (!pNote) return successResult();
1451    
1452                // if fade_in() was called immediately after note was triggered
1453                // then immediately apply a start volume of zero to Note object,
1454                // as well as the fade in duration
1455                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1456                    pNote->Override.Volume = 0.f;
1457                    pNote->Override.VolumeTime = fDuration;
1458                } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
1459                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1460                    e.Init(); // clear IDs
1461                    e.Type = Event::type_note_synth_param;
1462                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1463                    e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1464                    e.Param.NoteSynthParam.Delta    = fDuration;
1465                    e.Param.NoteSynthParam.Relative = false;
1466    
1467                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
1468                }
1469                // and finally schedule a "volume" change, simply one time slice
1470                // ahead, with the final fade in volume (1.0)
1471                {
1472                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1473                    e.Init(); // clear IDs
1474                    e.Type = Event::type_note_synth_param;
1475                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1476                    e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1477                    e.Param.NoteSynthParam.Delta    = 1.f;
1478                    e.Param.NoteSynthParam.Relative = false;
1479    
1480                    // scheduling with 0 delay would also work here, but +1 is more
1481                    // safe regarding potential future implementation changes of the
1482                    // scheduler (see API comments of RTAVLTree::insert())
1483                    pEngineChannel->ScheduleEventMicroSec(&e, 1);
1484                }
1485            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1486                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1487                for (vmint i = 0; i < ids->arraySize(); ++i) {
1488                    const ScriptID id = ids->evalIntElement(i);
1489                    if (!id || !id.isNoteID()) continue;
1490    
1491                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1492                    if (!pNote) continue;
1493    
1494                    // if fade_in() was called immediately after note was triggered
1495                    // then immediately apply a start volume of zero to Note object,
1496                    // as well as the fade in duration
1497                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1498                        pNote->Override.Volume = 0.f;
1499                        pNote->Override.VolumeTime = fDuration;
1500                    } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
1501                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1502                        e.Init(); // clear IDs
1503                        e.Type = Event::type_note_synth_param;
1504                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1505                        e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1506                        e.Param.NoteSynthParam.Delta    = fDuration;
1507                        e.Param.NoteSynthParam.Relative = false;
1508    
1509                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
1510                    }
1511                    // and finally schedule a "volume" change, simply one time slice
1512                    // ahead, with the final fade in volume (1.0)
1513                    {
1514                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1515                        e.Init(); // clear IDs
1516                        e.Type = Event::type_note_synth_param;
1517                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1518                        e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1519                        e.Param.NoteSynthParam.Delta    = 1.f;
1520                        e.Param.NoteSynthParam.Relative = false;
1521    
1522                        // scheduling with 0 delay would also work here, but +1 is more
1523                        // safe regarding potential future implementation changes of the
1524                        // scheduler (see API comments of RTAVLTree::insert())
1525                        pEngineChannel->ScheduleEventMicroSec(&e, 1);
1526                    }
1527                }
1528            }
1529    
1530            return successResult();
1531        }
1532    
1533        // fade_out() function
1534    
1535        InstrumentScriptVMFunction_fade_out::InstrumentScriptVMFunction_fade_out(InstrumentScriptVM* parent)
1536            : m_vm(parent)
1537        {
1538        }
1539    
1540        bool InstrumentScriptVMFunction_fade_out::acceptsArgType(vmint iArg, ExprType_t type) const {
1541            if (iArg == 0)
1542                return type == INT_EXPR || type == INT_ARR_EXPR;
1543            else
1544                return type == INT_EXPR;
1545        }
1546    
1547        VMFnResult* InstrumentScriptVMFunction_fade_out::exec(VMFnArgs* args) {
1548            vmint duration = args->arg(1)->asInt()->evalInt();
1549            if (duration < 0) {
1550                wrnMsg("fade_out(): argument 2 may not be negative");
1551                duration = 0;
1552            }
1553            const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
1554    
1555            bool stop = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : true;
1556    
1557            AbstractEngineChannel* pEngineChannel =
1558                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1559    
1560            if (args->arg(0)->exprType() == INT_EXPR) {
1561                const ScriptID id = args->arg(0)->asInt()->evalInt();
1562                if (!id) {
1563                    wrnMsg("fade_out(): note ID for argument 1 may not be zero");
1564                    return successResult();
1565                }
1566                if (!id.isNoteID()) {
1567                    wrnMsg("fade_out(): argument 1 is not a note ID");
1568                    return successResult();
1569                }
1570    
1571                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1572                if (!pNote) return successResult();
1573    
1574                // if fade_out() was called immediately after note was triggered
1575                // then immediately apply fade out duration to Note object
1576                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1577                    pNote->Override.VolumeTime = fDuration;
1578                } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
1579                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1580                    e.Init(); // clear IDs
1581                    e.Type = Event::type_note_synth_param;
1582                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1583                    e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1584                    e.Param.NoteSynthParam.Delta    = fDuration;
1585                    e.Param.NoteSynthParam.Relative = false;
1586    
1587                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
1588                }
1589                // now schedule a "volume" change, simply one time slice ahead, with
1590                // the final fade out volume (0.0)
1591                {
1592                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1593                    e.Init(); // clear IDs
1594                    e.Type = Event::type_note_synth_param;
1595                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1596                    e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1597                    e.Param.NoteSynthParam.Delta    = 0.f;
1598                    e.Param.NoteSynthParam.Relative = false;
1599    
1600                    // scheduling with 0 delay would also work here, but +1 is more
1601                    // safe regarding potential future implementation changes of the
1602                    // scheduler (see API comments of RTAVLTree::insert())
1603                    pEngineChannel->ScheduleEventMicroSec(&e, 1);
1604                }
1605                // and finally if stopping the note was requested after the fade out
1606                // completed, then schedule to kill the voice after the requested
1607                // duration
1608                if (stop) {
1609                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1610                    e.Init(); // clear IDs
1611                    e.Type = Event::type_kill_note;
1612                    e.Param.Note.ID = id.noteID();
1613                    e.Param.Note.Key = pNote->hostKey;
1614    
1615                    pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
1616                }
1617            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1618                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1619                for (vmint i = 0; i < ids->arraySize(); ++i) {
1620                    const ScriptID id = ids->evalIntElement(i);
1621                    if (!id || !id.isNoteID()) continue;
1622    
1623                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1624                    if (!pNote) continue;
1625    
1626                    // if fade_out() was called immediately after note was triggered
1627                    // then immediately apply fade out duration to Note object
1628                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1629                        pNote->Override.VolumeTime = fDuration;
1630                    } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
1631                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1632                        e.Init(); // clear IDs
1633                        e.Type = Event::type_note_synth_param;
1634                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1635                        e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1636                        e.Param.NoteSynthParam.Delta    = fDuration;
1637                        e.Param.NoteSynthParam.Relative = false;
1638    
1639                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
1640                    }
1641                    // now schedule a "volume" change, simply one time slice ahead, with
1642                    // the final fade out volume (0.0)
1643                    {
1644                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1645                        e.Init(); // clear IDs
1646                        e.Type = Event::type_note_synth_param;
1647                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1648                        e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1649                        e.Param.NoteSynthParam.Delta    = 0.f;
1650                        e.Param.NoteSynthParam.Relative = false;
1651    
1652                        // scheduling with 0 delay would also work here, but +1 is more
1653                        // safe regarding potential future implementation changes of the
1654                        // scheduler (see API comments of RTAVLTree::insert())
1655                        pEngineChannel->ScheduleEventMicroSec(&e, 1);
1656                    }
1657                    // and finally if stopping the note was requested after the fade out
1658                    // completed, then schedule to kill the voice after the requested
1659                    // duration
1660                    if (stop) {
1661                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1662                        e.Init(); // clear IDs
1663                        e.Type = Event::type_kill_note;
1664                        e.Param.Note.ID = id.noteID();
1665                        e.Param.Note.Key = pNote->hostKey;
1666                        
1667                        pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
1668                    }
1669                }
1670            }
1671    
1672            return successResult();
1673        }
1674    
1675        // get_event_par() function
1676    
1677        InstrumentScriptVMFunction_get_event_par::InstrumentScriptVMFunction_get_event_par(InstrumentScriptVM* parent)
1678            : m_vm(parent)
1679        {
1680        }
1681    
1682        VMFnResult* InstrumentScriptVMFunction_get_event_par::exec(VMFnArgs* args) {
1683            AbstractEngineChannel* pEngineChannel =
1684                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1685    
1686            const ScriptID id = args->arg(0)->asInt()->evalInt();
1687            if (!id) {
1688                wrnMsg("get_event_par(): note ID for argument 1 may not be zero");
1689                return successResult(0);
1690            }
1691            if (!id.isNoteID()) {
1692                wrnMsg("get_event_par(): argument 1 is not a note ID");
1693                return successResult(0);
1694            }
1695    
1696            NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1697            if (!pNote) {
1698                wrnMsg("get_event_par(): no note alive with that note ID of argument 1");
1699                return successResult(0);
1700            }
1701    
1702            const vmint parameter = args->arg(1)->asInt()->evalInt();
1703            switch (parameter) {
1704                case EVENT_PAR_NOTE:
1705                    return successResult(pNote->cause.Param.Note.Key);
1706                case EVENT_PAR_VELOCITY:
1707                    return successResult(pNote->cause.Param.Note.Velocity);
1708                case EVENT_PAR_VOLUME:
1709                    return successResult(
1710                        RTMath::LinRatioToDecibel(pNote->Override.Volume) * 1000.f
1711                    );
1712                case EVENT_PAR_TUNE:
1713                    return successResult(
1714                         RTMath::FreqRatioToCents(pNote->Override.Pitch) * 1000.f
1715                    );
1716                case EVENT_PAR_0:
1717                    return successResult(pNote->userPar[0]);
1718                case EVENT_PAR_1:
1719                    return successResult(pNote->userPar[1]);
1720                case EVENT_PAR_2:
1721                    return successResult(pNote->userPar[2]);
1722                case EVENT_PAR_3:
1723                    return successResult(pNote->userPar[3]);
1724            }
1725    
1726            wrnMsg("get_event_par(): argument 2 is an invalid event parameter");
1727            return successResult(0);
1728        }
1729    
1730        // set_event_par() function
1731    
1732        InstrumentScriptVMFunction_set_event_par::InstrumentScriptVMFunction_set_event_par(InstrumentScriptVM* parent)
1733            : m_vm(parent)
1734        {
1735        }
1736    
1737        VMFnResult* InstrumentScriptVMFunction_set_event_par::exec(VMFnArgs* args) {
1738            AbstractEngineChannel* pEngineChannel =
1739                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1740    
1741            const ScriptID id = args->arg(0)->asInt()->evalInt();
1742            if (!id) {
1743                wrnMsg("set_event_par(): note ID for argument 1 may not be zero");
1744                return successResult();
1745            }
1746            if (!id.isNoteID()) {
1747                wrnMsg("set_event_par(): argument 1 is not a note ID");
1748                return successResult();
1749            }
1750    
1751            NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1752            if (!pNote) return successResult();
1753    
1754            const vmint parameter = args->arg(1)->asInt()->evalInt();
1755            const vmint value     = args->arg(2)->asInt()->evalInt();
1756    
1757            switch (parameter) {
1758                case EVENT_PAR_NOTE:
1759                    if (value < 0 || value > 127) {
1760                        wrnMsg("set_event_par(): note number of argument 3 is out of range");
1761                        return successResult();
1762                    }
1763                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1764                        pNote->cause.Param.Note.Key = value;
1765                        m_vm->m_event->cause.Param.Note.Key = value;
1766                    } else {
1767                        wrnMsg("set_event_par(): note number can only be changed when note is new");
1768                    }
1769                    return successResult();
1770                case EVENT_PAR_VELOCITY:
1771                    if (value < 0 || value > 127) {
1772                        wrnMsg("set_event_par(): velocity of argument 3 is out of range");
1773                        return successResult();
1774                    }
1775                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1776                        pNote->cause.Param.Note.Velocity = value;
1777                        m_vm->m_event->cause.Param.Note.Velocity = value;
1778                    } else {
1779                        wrnMsg("set_event_par(): velocity can only be changed when note is new");
1780                    }
1781                    return successResult();
1782                case EVENT_PAR_VOLUME:
1783                    wrnMsg("set_event_par(): changing volume by this function is currently not supported, use change_vol() instead");
1784                    return successResult();
1785                case EVENT_PAR_TUNE:
1786                    wrnMsg("set_event_par(): changing tune by this function is currently not supported, use change_tune() instead");
1787                    return successResult();
1788                case EVENT_PAR_0:
1789                    pNote->userPar[0] = value;
1790                    return successResult();
1791                case EVENT_PAR_1:
1792                    pNote->userPar[1] = value;
1793                    return successResult();
1794                case EVENT_PAR_2:
1795                    pNote->userPar[2] = value;
1796                    return successResult();
1797                case EVENT_PAR_3:
1798                    pNote->userPar[3] = value;
1799                    return successResult();
1800            }
1801    
1802            wrnMsg("set_event_par(): argument 2 is an invalid event parameter");
1803            return successResult();
1804        }
1805    
1806        // change_note() function
1807    
1808        InstrumentScriptVMFunction_change_note::InstrumentScriptVMFunction_change_note(InstrumentScriptVM* parent)
1809        : m_vm(parent)
1810        {
1811        }
1812    
1813        VMFnResult* InstrumentScriptVMFunction_change_note::exec(VMFnArgs* args) {
1814            AbstractEngineChannel* pEngineChannel =
1815                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1816    
1817            const ScriptID id = args->arg(0)->asInt()->evalInt();
1818            if (!id) {
1819                wrnMsg("change_note(): note ID for argument 1 may not be zero");
1820                return successResult();
1821            }
1822            if (!id.isNoteID()) {
1823                wrnMsg("change_note(): argument 1 is not a note ID");
1824                return successResult();
1825            }
1826    
1827            NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1828            if (!pNote) return successResult();
1829    
1830            const vmint value = args->arg(1)->asInt()->evalInt();
1831            if (value < 0 || value > 127) {
1832                wrnMsg("change_note(): note number of argument 2 is out of range");
1833                return successResult();
1834            }
1835    
1836            if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1837                pNote->cause.Param.Note.Key = value;
1838                m_vm->m_event->cause.Param.Note.Key = value;
1839            } else {
1840                wrnMsg("change_note(): note number can only be changed when note is new");
1841            }
1842    
1843            return successResult();
1844        }
1845    
1846        // change_velo() function
1847    
1848        InstrumentScriptVMFunction_change_velo::InstrumentScriptVMFunction_change_velo(InstrumentScriptVM* parent)
1849        : m_vm(parent)
1850        {
1851        }
1852    
1853        VMFnResult* InstrumentScriptVMFunction_change_velo::exec(VMFnArgs* args) {
1854            AbstractEngineChannel* pEngineChannel =
1855                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1856    
1857            const ScriptID id = args->arg(0)->asInt()->evalInt();
1858            if (!id) {
1859                wrnMsg("change_velo(): note ID for argument 1 may not be zero");
1860                return successResult();
1861            }
1862            if (!id.isNoteID()) {
1863                wrnMsg("change_velo(): argument 1 is not a note ID");
1864                return successResult();
1865            }
1866    
1867            NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1868            if (!pNote) return successResult();
1869    
1870            const vmint value = args->arg(1)->asInt()->evalInt();
1871            if (value < 0 || value > 127) {
1872                wrnMsg("change_velo(): velocity of argument 2 is out of range");
1873                return successResult();
1874            }
1875    
1876            if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1877                pNote->cause.Param.Note.Velocity = value;
1878                m_vm->m_event->cause.Param.Note.Velocity = value;
1879            } else {
1880                wrnMsg("change_velo(): velocity can only be changed when note is new");
1881            }
1882    
1883            return successResult();
1884        }
1885    
1886        // change_play_pos() function
1887    
1888        InstrumentScriptVMFunction_change_play_pos::InstrumentScriptVMFunction_change_play_pos(InstrumentScriptVM* parent)
1889        : m_vm(parent)
1890        {
1891        }
1892    
1893        VMFnResult* InstrumentScriptVMFunction_change_play_pos::exec(VMFnArgs* args) {
1894            const ScriptID id = args->arg(0)->asInt()->evalInt();
1895            if (!id) {
1896                wrnMsg("change_play_pos(): note ID for argument 1 may not be zero");
1897                return successResult();
1898            }
1899            if (!id.isNoteID()) {
1900                wrnMsg("change_play_pos(): argument 1 is not a note ID");
1901                return successResult();
1902            }
1903    
1904            const vmint pos = args->arg(1)->asInt()->evalInt();
1905            if (pos < 0) {
1906                wrnMsg("change_play_pos(): playback position of argument 2 may not be negative");
1907                return successResult();
1908            }
1909    
1910            AbstractEngineChannel* pEngineChannel =
1911                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1912    
1913            NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1914            if (!pNote) return successResult();
1915    
1916            pNote->Override.SampleOffset =
1917                (decltype(pNote->Override.SampleOffset)) pos;
1918    
1919            return successResult();
1920        }
1921    
1922        // event_status() function
1923    
1924        InstrumentScriptVMFunction_event_status::InstrumentScriptVMFunction_event_status(InstrumentScriptVM* parent)
1925            : m_vm(parent)
1926        {
1927        }
1928    
1929        VMFnResult* InstrumentScriptVMFunction_event_status::exec(VMFnArgs* args) {
1930            AbstractEngineChannel* pEngineChannel =
1931                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1932    
1933            const ScriptID id = args->arg(0)->asInt()->evalInt();
1934            if (!id) {
1935                wrnMsg("event_status(): note ID for argument 1 may not be zero");
1936                return successResult(EVENT_STATUS_INACTIVE);
1937            }
1938            if (!id.isNoteID()) {
1939                wrnMsg("event_status(): argument 1 is not a note ID");
1940                return successResult(EVENT_STATUS_INACTIVE);
1941            }
1942    
1943            NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1944            return successResult(pNote ? EVENT_STATUS_NOTE_QUEUE : EVENT_STATUS_INACTIVE);
1945        }
1946    
1947        // callback_status() function
1948    
1949        InstrumentScriptVMFunction_callback_status::InstrumentScriptVMFunction_callback_status(InstrumentScriptVM* parent)
1950            : m_vm(parent)
1951        {
1952        }
1953    
1954        VMFnResult* InstrumentScriptVMFunction_callback_status::exec(VMFnArgs* args) {
1955            const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
1956            if (!id) {
1957                wrnMsg("callback_status(): callback ID for argument 1 may not be zero");
1958                return successResult();
1959            }
1960    
1961            AbstractEngineChannel* pEngineChannel =
1962                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1963    
1964            RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
1965            if (!itCallback)
1966                return successResult(CALLBACK_STATUS_TERMINATED);
1967    
1968            return successResult(
1969                (m_vm->m_event->execCtx == itCallback->execCtx) ?
1970                    CALLBACK_STATUS_RUNNING : CALLBACK_STATUS_QUEUE
1971            );
1972        }
1973    
1974        // wait() function (overrides core wait() implementation)
1975    
1976        InstrumentScriptVMFunction_wait::InstrumentScriptVMFunction_wait(InstrumentScriptVM* parent)
1977            : CoreVMFunction_wait(parent)
1978        {    
1979        }
1980    
1981        VMFnResult* InstrumentScriptVMFunction_wait::exec(VMFnArgs* args) {
1982            InstrumentScriptVM* m_vm = (InstrumentScriptVM*) vm;
1983    
1984            // this might be set by passing 1 with the 2nd argument of built-in stop_wait() function
1985            if (m_vm->m_event->ignoreAllWaitCalls) return successResult();
1986    
1987            return CoreVMFunction_wait::exec(args);
1988        }
1989    
1990        // stop_wait() function
1991    
1992        InstrumentScriptVMFunction_stop_wait::InstrumentScriptVMFunction_stop_wait(InstrumentScriptVM* parent)
1993            : m_vm(parent)
1994        {    
1995        }
1996    
1997        VMFnResult* InstrumentScriptVMFunction_stop_wait::exec(VMFnArgs* args) {
1998            AbstractEngineChannel* pEngineChannel =
1999                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2000    
2001            const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
2002            if (!id) {
2003                wrnMsg("stop_wait(): callback ID for argument 1 may not be zero");
2004                return successResult();
2005            }
2006    
2007            RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
2008            if (!itCallback) return successResult(); // ignore if callback is i.e. not alive anymore
2009    
2010            const bool disableWaitForever =
2011                (args->argsCount() >= 2) ? (args->arg(1)->asInt()->evalInt() == 1) : false;
2012    
2013            pEngineChannel->ScheduleResumeOfScriptCallback(
2014                itCallback, m_vm->m_event->scheduleTime, disableWaitForever
2015            );
2016    
2017            return successResult();
2018        }
2019    
2020        // abort() function
2021    
2022        InstrumentScriptVMFunction_abort::InstrumentScriptVMFunction_abort(InstrumentScriptVM* parent)
2023            : m_vm(parent)
2024        {
2025        }
2026    
2027        VMFnResult* InstrumentScriptVMFunction_abort::exec(VMFnArgs* args) {
2028            const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
2029            if (!id) {
2030                wrnMsg("abort(): callback ID for argument 1 may not be zero");
2031                return successResult();
2032            }
2033    
2034            AbstractEngineChannel* pEngineChannel =
2035                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2036    
2037            RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
2038            if (!itCallback) return successResult(); // ignore if callback is i.e. not alive anymore
2039    
2040            itCallback->execCtx->signalAbort();
2041    
2042            return successResult();
2043        }
2044    
2045        // fork() function
2046    
2047        InstrumentScriptVMFunction_fork::InstrumentScriptVMFunction_fork(InstrumentScriptVM* parent)
2048            : m_vm(parent)
2049        {
2050        }
2051    
2052        VMFnResult* InstrumentScriptVMFunction_fork::exec(VMFnArgs* args) {
2053            // check if this is actually the parent going to fork, or rather one of
2054            // the children which is already forked
2055            if (m_vm->m_event->forkIndex != 0) { // this is the entry point for a child ...
2056                int forkResult = m_vm->m_event->forkIndex;
2057                // reset so that this child may i.e. also call fork() later on
2058                m_vm->m_event->forkIndex = 0;
2059                return successResult(forkResult);
2060            }
2061    
2062            // if we are here, then this is the parent, so we must fork this parent
2063    
2064            const vmint n =
2065                (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : 1;
2066            const bool bAutoAbort =
2067                (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : true;
2068    
2069            if (m_vm->m_event->countChildHandlers() + n > MAX_FORK_PER_SCRIPT_HANDLER) {
2070                wrnMsg("fork(): requested amount would exceed allowed limit per event handler");
2071                return successResult(-1);
2072            }
2073    
2074            AbstractEngineChannel* pEngineChannel =
2075                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2076    
2077            if (!pEngineChannel->hasFreeScriptCallbacks(n)) {
2078                wrnMsg("fork(): global limit of event handlers exceeded");
2079                return successResult(-1);
2080            }
2081    
2082            for (int iChild = 0; iChild < n; ++iChild) {
2083                RTList<ScriptEvent>::Iterator itChild =
2084                    pEngineChannel->forkScriptCallback(m_vm->m_event, bAutoAbort);
2085                if (!itChild) { // should never happen, otherwise its a bug ...
2086                    errMsg("fork(): internal error while allocating child");
2087                    return errorResult(-1); // terminate script
2088                }
2089                // since both parent, as well all child script execution instances
2090                // all land in this exec() method, the following is (more or less)
2091                // the only feature that lets us distinguish the parent and
2092                // respective children from each other in this exec() method
2093                itChild->forkIndex = iChild + 1;
2094            }
2095    
2096            return successResult(0);
2097        }
2098    
2099  } // namespace LinuxSampler  } // namespace LinuxSampler

Legend:
Removed from v.2879  
changed lines
  Added in v.3557

  ViewVC Help
Powered by ViewVC