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

Legend:
Removed from v.2606  
changed lines
  Added in v.3303

  ViewVC Help
Powered by ViewVC