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

Legend:
Removed from v.2598  
changed lines
  Added in v.3216

  ViewVC Help
Powered by ViewVC