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

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

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

revision 2606 by persson, Sun Jun 8 05:42:56 2014 UTC revision 3218 by schoenebeck, Thu May 25 16:32:17 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          memset(&e.Format, 0, sizeof(e.Format)); // init format speific stuff with zero          // make this new note dependent to the life time of the original note
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          int id = pEngineChannel->ScheduleEvent(&e, duration);          // 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          return successResult(id);          // 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)      InstrumentScriptVMFunction_set_controller::InstrumentScriptVMFunction_set_controller(InstrumentScriptVM* parent)
88          : m_vm(parent)          : m_vm(parent)
89      {      {
# Line 76  namespace LinuxSampler { Line 96  namespace LinuxSampler {
96          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
97              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
98    
99          Event e = m_vm->m_event->cause;          Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
100          memset(&e.Format, 0, sizeof(e.Format)); // init format speific stuff with zero          e.Init(); // clear IDs
101          if (controller == CTRL_TABLE_IDX_AFTERTOUCH) {          if (controller == CTRL_TABLE_IDX_AFTERTOUCH) {
102              e.Type = Event::type_channel_pressure;              e.Type = Event::type_channel_pressure;
103              e.Param.ChannelPressure.Value = value & 127;              e.Param.ChannelPressure.Value = value & 127;
# Line 93  namespace LinuxSampler { Line 113  namespace LinuxSampler {
113              return errorResult();              return errorResult();
114          }          }
115    
116          int id = pEngineChannel->ScheduleEvent(&e, 0);          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, but only if
389                // change_vol_time() has not been called before
390                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
391                    pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S)
392                {
393                    if (relative)
394                        pNote->Override.Volume *= fVolumeLin;
395                    else
396                        pNote->Override.Volume = fVolumeLin;
397                } else { // otherwise schedule the volume change ...
398                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
399                    e.Init(); // clear IDs
400                    e.Type = Event::type_note_synth_param;
401                    e.Param.NoteSynthParam.NoteID   = id.noteID();
402                    e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
403                    e.Param.NoteSynthParam.Delta    = fVolumeLin;
404                    e.Param.NoteSynthParam.Relative = relative;
405    
406                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
407                }
408            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
409                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
410                for (int i = 0; i < ids->arraySize(); ++i) {
411                    const ScriptID id = ids->evalIntElement(i);
412                    if (!id || !id.isNoteID()) continue;
413    
414                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
415                    if (!pNote) continue;
416    
417                    // if change_vol() was called immediately after note was triggered
418                    // then immediately apply the volume to Note object, but only if
419                    // change_vol_time() has not been called before
420                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
421                        pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S)
422                    {
423                        if (relative)
424                            pNote->Override.Volume *= fVolumeLin;
425                        else
426                            pNote->Override.Volume = fVolumeLin;
427                    } else { // otherwise schedule the volume change ...
428                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
429                        e.Init(); // clear IDs
430                        e.Type = Event::type_note_synth_param;
431                        e.Param.NoteSynthParam.NoteID   = id.noteID();
432                        e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
433                        e.Param.NoteSynthParam.Delta    = fVolumeLin;
434                        e.Param.NoteSynthParam.Relative = relative;
435    
436                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
437                    }
438                }
439            }
440    
441            return successResult();
442        }
443    
444        // change_tune() function
445    
446        InstrumentScriptVMFunction_change_tune::InstrumentScriptVMFunction_change_tune(InstrumentScriptVM* parent)
447            : m_vm(parent)
448        {
449        }
450    
451        bool InstrumentScriptVMFunction_change_tune::acceptsArgType(int iArg, ExprType_t type) const {
452            if (iArg == 0)
453                return type == INT_EXPR || type == INT_ARR_EXPR;
454            else
455                return type == INT_EXPR;
456        }
457    
458        VMFnResult* InstrumentScriptVMFunction_change_tune::exec(VMFnArgs* args) {
459            int tune = args->arg(1)->asInt()->evalInt(); // tuning change in milli cents
460            bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
461            const float fFreqRatio = RTMath::CentsToFreqRatioUnlimited(float(tune) / 1000.f);
462    
463            AbstractEngineChannel* pEngineChannel =
464                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
465    
466            if (args->arg(0)->exprType() == INT_EXPR) {
467                const ScriptID id = args->arg(0)->asInt()->evalInt();
468                if (!id) {
469                    wrnMsg("change_tune(): note ID for argument 1 may not be zero");
470                    return successResult();
471                }
472                if (!id.isNoteID()) {
473                    wrnMsg("change_tune(): argument 1 is not a note ID");
474                    return successResult();
475                }
476    
477                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
478                if (!pNote) return successResult();
479    
480                // if change_tune() was called immediately after note was triggered
481                // then immediately apply the tuning to Note object, but only if
482                // change_tune_time() has not been called before
483                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
484                    pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S)
485                {
486                    if (relative)
487                        pNote->Override.Pitch *= fFreqRatio;
488                    else
489                        pNote->Override.Pitch = fFreqRatio;
490                } else { // otherwise schedule tuning change ...
491                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
492                    e.Init(); // clear IDs
493                    e.Type = Event::type_note_synth_param;
494                    e.Param.NoteSynthParam.NoteID   = id.noteID();
495                    e.Param.NoteSynthParam.Type     = Event::synth_param_pitch;
496                    e.Param.NoteSynthParam.Delta    = fFreqRatio;
497                    e.Param.NoteSynthParam.Relative = relative;
498    
499                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
500                }
501            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
502                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
503                for (int i = 0; i < ids->arraySize(); ++i) {
504                    const ScriptID id = ids->evalIntElement(i);
505                    if (!id || !id.isNoteID()) continue;
506    
507                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
508                    if (!pNote) continue;
509    
510                    // if change_tune() was called immediately after note was triggered
511                    // then immediately apply the tuning to Note object, but only if
512                    // change_tune_time() has not been called before
513                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
514                        pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S)
515                    {
516                        if (relative)
517                            pNote->Override.Pitch *= fFreqRatio;
518                        else
519                            pNote->Override.Pitch = fFreqRatio;
520                    } else { // otherwise schedule tuning change ...
521                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
522                        e.Init(); // clear IDs
523                        e.Type = Event::type_note_synth_param;
524                        e.Param.NoteSynthParam.NoteID   = id.noteID();
525                        e.Param.NoteSynthParam.Type     = Event::synth_param_pitch;
526                        e.Param.NoteSynthParam.Delta    = fFreqRatio;
527                        e.Param.NoteSynthParam.Relative = relative;
528    
529                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
530                    }
531                }
532            }
533    
534            return successResult();
535        }
536    
537        // change_pan() function
538    
539        InstrumentScriptVMFunction_change_pan::InstrumentScriptVMFunction_change_pan(InstrumentScriptVM* parent)
540            : m_vm(parent)
541        {
542        }
543    
544        bool InstrumentScriptVMFunction_change_pan::acceptsArgType(int iArg, ExprType_t type) const {
545            if (iArg == 0)
546                return type == INT_EXPR || type == INT_ARR_EXPR;
547            else
548                return type == INT_EXPR;
549        }
550    
551        VMFnResult* InstrumentScriptVMFunction_change_pan::exec(VMFnArgs* args) {
552            int pan = args->arg(1)->asInt()->evalInt();
553            bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
554    
555            if (pan > 1000) {
556                wrnMsg("change_pan(): argument 2 may not be larger than 1000");
557                pan = 1000;
558            } else if (pan < -1000) {
559                wrnMsg("change_pan(): argument 2 may not be smaller than -1000");
560                pan = -1000;
561            }
562            const float fPan = float(pan) / 1000.f;
563    
564            AbstractEngineChannel* pEngineChannel =
565                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
566    
567            if (args->arg(0)->exprType() == INT_EXPR) {
568                const ScriptID id = args->arg(0)->asInt()->evalInt();
569                if (!id) {
570                    wrnMsg("change_pan(): note ID for argument 1 may not be zero");
571                    return successResult();
572                }
573                if (!id.isNoteID()) {
574                    wrnMsg("change_pan(): argument 1 is not a note ID");
575                    return successResult();
576                }
577    
578                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
579                if (!pNote) return successResult();
580    
581                // if change_pan() was called immediately after note was triggered
582                // then immediately apply the panning to Note object
583                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
584                    if (relative) {
585                        pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources);
586                    } else {
587                        pNote->Override.Pan = fPan;
588                        pNote->Override.PanSources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set
589                    }
590                } else { // otherwise schedule panning change ...
591                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
592                    e.Init(); // clear IDs
593                    e.Type = Event::type_note_synth_param;
594                    e.Param.NoteSynthParam.NoteID   = id.noteID();
595                    e.Param.NoteSynthParam.Type     = Event::synth_param_pan;
596                    e.Param.NoteSynthParam.Delta    = fPan;
597                    e.Param.NoteSynthParam.Relative = relative;
598    
599                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
600                }
601            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
602                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
603                for (int i = 0; i < ids->arraySize(); ++i) {
604                    const ScriptID id = ids->evalIntElement(i);
605                    if (!id || !id.isNoteID()) continue;
606    
607                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
608                    if (!pNote) continue;
609    
610                    // if change_pan() was called immediately after note was triggered
611                    // then immediately apply the panning to Note object
612                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
613                        if (relative) {
614                            pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources);
615                        } else {
616                            pNote->Override.Pan = fPan;
617                            pNote->Override.PanSources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set
618                        }
619                    } else { // otherwise schedule panning change ...
620                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
621                        e.Init(); // clear IDs
622                        e.Type = Event::type_note_synth_param;
623                        e.Param.NoteSynthParam.NoteID   = id.noteID();
624                        e.Param.NoteSynthParam.Type     = Event::synth_param_pan;
625                        e.Param.NoteSynthParam.Delta    = fPan;
626                        e.Param.NoteSynthParam.Relative = relative;
627    
628                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
629                    }
630                }
631            }
632    
633            return successResult();
634        }
635    
636        #define VM_FILTER_PAR_MAX_VALUE 1000000
637        #define VM_EG_PAR_MAX_VALUE 1000000
638    
639        // change_cutoff() function
640    
641        InstrumentScriptVMFunction_change_cutoff::InstrumentScriptVMFunction_change_cutoff(InstrumentScriptVM* parent)
642            : m_vm(parent)
643        {
644        }
645    
646        bool InstrumentScriptVMFunction_change_cutoff::acceptsArgType(int iArg, ExprType_t type) const {
647            if (iArg == 0)
648                return type == INT_EXPR || type == INT_ARR_EXPR;
649            else
650                return type == INT_EXPR;
651        }
652    
653        VMFnResult* InstrumentScriptVMFunction_change_cutoff::exec(VMFnArgs* args) {
654            int cutoff = args->arg(1)->asInt()->evalInt();
655            if (cutoff > VM_FILTER_PAR_MAX_VALUE) {
656                wrnMsg("change_cutoff(): argument 2 may not be larger than 1000000");
657                cutoff = VM_FILTER_PAR_MAX_VALUE;
658            } else if (cutoff < 0) {
659                wrnMsg("change_cutoff(): argument 2 may not be negative");
660                cutoff = 0;
661            }
662            const float fCutoff = float(cutoff) / float(VM_FILTER_PAR_MAX_VALUE);
663    
664            AbstractEngineChannel* pEngineChannel =
665                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
666    
667            if (args->arg(0)->exprType() == INT_EXPR) {
668                const ScriptID id = args->arg(0)->asInt()->evalInt();
669                if (!id) {
670                    wrnMsg("change_cutoff(): note ID for argument 1 may not be zero");
671                    return successResult();
672                }
673                if (!id.isNoteID()) {
674                    wrnMsg("change_cutoff(): argument 1 is not a note ID");
675                    return successResult();
676                }
677    
678                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
679                if (!pNote) return successResult();
680    
681                // if change_cutoff() was called immediately after note was triggered
682                // then immediately apply cutoff to Note object
683                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
684                    pNote->Override.Cutoff = fCutoff;
685                } else { // otherwise schedule cutoff change ...
686                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
687                    e.Init(); // clear IDs
688                    e.Type = Event::type_note_synth_param;
689                    e.Param.NoteSynthParam.NoteID   = id.noteID();
690                    e.Param.NoteSynthParam.Type     = Event::synth_param_cutoff;
691                    e.Param.NoteSynthParam.Delta    = fCutoff;
692                    e.Param.NoteSynthParam.Relative = false;
693    
694                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
695                }
696            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
697                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
698                for (int i = 0; i < ids->arraySize(); ++i) {
699                    const ScriptID id = ids->evalIntElement(i);
700                    if (!id || !id.isNoteID()) continue;
701    
702                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
703                    if (!pNote) continue;
704    
705                    // if change_cutoff() was called immediately after note was triggered
706                    // then immediately apply cutoff to Note object
707                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
708                        pNote->Override.Cutoff = fCutoff;
709                    } else { // otherwise schedule cutoff change ...
710                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
711                        e.Init(); // clear IDs
712                        e.Type = Event::type_note_synth_param;
713                        e.Param.NoteSynthParam.NoteID   = id.noteID();
714                        e.Param.NoteSynthParam.Type     = Event::synth_param_cutoff;
715                        e.Param.NoteSynthParam.Delta    = fCutoff;
716                        e.Param.NoteSynthParam.Relative = false;
717    
718                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
719                    }
720                }
721            }
722    
723            return successResult();
724        }
725    
726        // change_reso() function
727        
728        InstrumentScriptVMFunction_change_reso::InstrumentScriptVMFunction_change_reso(InstrumentScriptVM* parent)
729            : m_vm(parent)
730        {
731        }
732    
733        bool InstrumentScriptVMFunction_change_reso::acceptsArgType(int iArg, ExprType_t type) const {
734            if (iArg == 0)
735                return type == INT_EXPR || type == INT_ARR_EXPR;
736            else
737                return type == INT_EXPR;
738        }
739    
740        VMFnResult* InstrumentScriptVMFunction_change_reso::exec(VMFnArgs* args) {
741            int resonance = args->arg(1)->asInt()->evalInt();
742            if (resonance > VM_FILTER_PAR_MAX_VALUE) {
743                wrnMsg("change_reso(): argument 2 may not be larger than 1000000");
744                resonance = VM_FILTER_PAR_MAX_VALUE;
745            } else if (resonance < 0) {
746                wrnMsg("change_reso(): argument 2 may not be negative");
747                resonance = 0;
748            }
749            const float fResonance = float(resonance) / float(VM_FILTER_PAR_MAX_VALUE);
750    
751            AbstractEngineChannel* pEngineChannel =
752                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
753    
754            if (args->arg(0)->exprType() == INT_EXPR) {
755                const ScriptID id = args->arg(0)->asInt()->evalInt();
756                if (!id) {
757                    wrnMsg("change_reso(): note ID for argument 1 may not be zero");
758                    return successResult();
759                }
760                if (!id.isNoteID()) {
761                    wrnMsg("change_reso(): argument 1 is not a note ID");
762                    return successResult();
763                }
764    
765                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
766                if (!pNote) return successResult();
767    
768                // if change_reso() was called immediately after note was triggered
769                // then immediately apply resonance to Note object
770                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
771                    pNote->Override.Resonance = fResonance;
772                } else { // otherwise schedule resonance change ...
773                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
774                    e.Init(); // clear IDs
775                    e.Type = Event::type_note_synth_param;
776                    e.Param.NoteSynthParam.NoteID   = id.noteID();
777                    e.Param.NoteSynthParam.Type     = Event::synth_param_resonance;
778                    e.Param.NoteSynthParam.Delta    = fResonance;
779                    e.Param.NoteSynthParam.Relative = false;
780    
781                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
782                }
783            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
784                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
785                for (int i = 0; i < ids->arraySize(); ++i) {
786                    const ScriptID id = ids->evalIntElement(i);
787                    if (!id || !id.isNoteID()) continue;
788    
789                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
790                    if (!pNote) continue;
791    
792                    // if change_reso() was called immediately after note was triggered
793                    // then immediately apply resonance to Note object
794                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
795                        pNote->Override.Resonance = fResonance;
796                    } else { // otherwise schedule resonance change ...
797                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
798                        e.Init(); // clear IDs
799                        e.Type = Event::type_note_synth_param;
800                        e.Param.NoteSynthParam.NoteID   = id.noteID();
801                        e.Param.NoteSynthParam.Type     = Event::synth_param_resonance;
802                        e.Param.NoteSynthParam.Delta    = fResonance;
803                        e.Param.NoteSynthParam.Relative = false;
804    
805                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
806                    }
807                }
808            }
809    
810            return successResult();
811        }
812        
813        // change_attack() function
814    
815        InstrumentScriptVMFunction_change_attack::InstrumentScriptVMFunction_change_attack(InstrumentScriptVM* parent)
816            : m_vm(parent)
817        {
818        }
819    
820        bool InstrumentScriptVMFunction_change_attack::acceptsArgType(int iArg, ExprType_t type) const {
821            if (iArg == 0)
822                return type == INT_EXPR || type == INT_ARR_EXPR;
823            else
824                return type == INT_EXPR;
825        }
826    
827        VMFnResult* InstrumentScriptVMFunction_change_attack::exec(VMFnArgs* args) {
828            int attack = args->arg(1)->asInt()->evalInt();
829            if (attack > VM_EG_PAR_MAX_VALUE) {
830                wrnMsg("change_attack(): argument 2 may not be larger than 1000000");
831                attack = VM_EG_PAR_MAX_VALUE;
832            } else if (attack < 0) {
833                wrnMsg("change_attack(): argument 2 may not be negative");
834                attack = 0;
835            }
836            const float fAttack = float(attack) / float(VM_EG_PAR_MAX_VALUE);
837    
838            AbstractEngineChannel* pEngineChannel =
839                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
840    
841            if (args->arg(0)->exprType() == INT_EXPR) {
842                const ScriptID id = args->arg(0)->asInt()->evalInt();
843                if (!id) {
844                    wrnMsg("change_attack(): note ID for argument 1 may not be zero");
845                    return successResult();
846                }
847                if (!id.isNoteID()) {
848                    wrnMsg("change_attack(): argument 1 is not a note ID");
849                    return successResult();
850                }
851    
852                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
853                if (!pNote) return successResult();
854    
855                // if change_attack() was called immediately after note was triggered
856                // then immediately apply attack to Note object
857                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
858                    pNote->Override.Attack = fAttack;
859                } else { // otherwise schedule attack change ...
860                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
861                    e.Init(); // clear IDs
862                    e.Type = Event::type_note_synth_param;
863                    e.Param.NoteSynthParam.NoteID   = id.noteID();
864                    e.Param.NoteSynthParam.Type     = Event::synth_param_attack;
865                    e.Param.NoteSynthParam.Delta    = fAttack;
866                    e.Param.NoteSynthParam.Relative = false;
867    
868                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
869                }
870            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
871                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
872                for (int i = 0; i < ids->arraySize(); ++i) {
873                    const ScriptID id = ids->evalIntElement(i);
874                    if (!id || !id.isNoteID()) continue;
875    
876                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
877                    if (!pNote) continue;
878    
879                    // if change_attack() was called immediately after note was triggered
880                    // then immediately apply attack to Note object
881                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
882                        pNote->Override.Attack = fAttack;
883                    } else { // otherwise schedule attack change ...
884                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
885                        e.Init(); // clear IDs
886                        e.Type = Event::type_note_synth_param;
887                        e.Param.NoteSynthParam.NoteID   = id.noteID();
888                        e.Param.NoteSynthParam.Type     = Event::synth_param_attack;
889                        e.Param.NoteSynthParam.Delta    = fAttack;
890                        e.Param.NoteSynthParam.Relative = false;
891    
892                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
893                    }
894                }
895            }
896    
897            return successResult();
898        }
899    
900        // change_decay() function
901        
902        InstrumentScriptVMFunction_change_decay::InstrumentScriptVMFunction_change_decay(InstrumentScriptVM* parent)
903            : m_vm(parent)
904        {
905        }
906    
907        bool InstrumentScriptVMFunction_change_decay::acceptsArgType(int iArg, ExprType_t type) const {
908            if (iArg == 0)
909                return type == INT_EXPR || type == INT_ARR_EXPR;
910            else
911                return type == INT_EXPR;
912        }
913    
914        VMFnResult* InstrumentScriptVMFunction_change_decay::exec(VMFnArgs* args) {
915            int decay = args->arg(1)->asInt()->evalInt();
916            if (decay > VM_EG_PAR_MAX_VALUE) {
917                wrnMsg("change_decay(): argument 2 may not be larger than 1000000");
918                decay = VM_EG_PAR_MAX_VALUE;
919            } else if (decay < 0) {
920                wrnMsg("change_decay(): argument 2 may not be negative");
921                decay = 0;
922            }
923            const float fDecay = float(decay) / float(VM_EG_PAR_MAX_VALUE);
924    
925            AbstractEngineChannel* pEngineChannel =
926                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
927    
928            if (args->arg(0)->exprType() == INT_EXPR) {
929                const ScriptID id = args->arg(0)->asInt()->evalInt();
930                if (!id) {
931                    wrnMsg("change_decay(): note ID for argument 1 may not be zero");
932                    return successResult();
933                }
934                if (!id.isNoteID()) {
935                    wrnMsg("change_decay(): argument 1 is not a note ID");
936                    return successResult();
937                }
938    
939                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
940                if (!pNote) return successResult();
941    
942                // if change_decay() was called immediately after note was triggered
943                // then immediately apply decay to Note object
944                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
945                    pNote->Override.Decay = fDecay;
946                } else { // otherwise schedule decay change ...
947                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
948                    e.Init(); // clear IDs
949                    e.Type = Event::type_note_synth_param;
950                    e.Param.NoteSynthParam.NoteID   = id.noteID();
951                    e.Param.NoteSynthParam.Type     = Event::synth_param_decay;
952                    e.Param.NoteSynthParam.Delta    = fDecay;
953                    e.Param.NoteSynthParam.Relative = false;
954    
955                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
956                }
957            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
958                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
959                for (int i = 0; i < ids->arraySize(); ++i) {
960                    const ScriptID id = ids->evalIntElement(i);
961                    if (!id || !id.isNoteID()) continue;
962    
963                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
964                    if (!pNote) continue;
965    
966                    // if change_decay() was called immediately after note was triggered
967                    // then immediately apply decay to Note object
968                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
969                        pNote->Override.Decay = fDecay;
970                    } else { // otherwise schedule decay change ...
971                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
972                        e.Init(); // clear IDs
973                        e.Type = Event::type_note_synth_param;
974                        e.Param.NoteSynthParam.NoteID   = id.noteID();
975                        e.Param.NoteSynthParam.Type     = Event::synth_param_decay;
976                        e.Param.NoteSynthParam.Delta    = fDecay;
977                        e.Param.NoteSynthParam.Relative = false;
978    
979                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
980                    }
981                }
982            }
983    
984            return successResult();
985        }
986    
987        // change_release() function
988        
989        InstrumentScriptVMFunction_change_release::InstrumentScriptVMFunction_change_release(InstrumentScriptVM* parent)
990            : m_vm(parent)
991        {
992        }
993    
994        bool InstrumentScriptVMFunction_change_release::acceptsArgType(int iArg, ExprType_t type) const {
995            if (iArg == 0)
996                return type == INT_EXPR || type == INT_ARR_EXPR;
997            else
998                return type == INT_EXPR;
999        }
1000    
1001        VMFnResult* InstrumentScriptVMFunction_change_release::exec(VMFnArgs* args) {
1002            int release = args->arg(1)->asInt()->evalInt();
1003            if (release > VM_EG_PAR_MAX_VALUE) {
1004                wrnMsg("change_release(): argument 2 may not be larger than 1000000");
1005                release = VM_EG_PAR_MAX_VALUE;
1006            } else if (release < 0) {
1007                wrnMsg("change_release(): argument 2 may not be negative");
1008                release = 0;
1009            }
1010            const float fRelease = float(release) / float(VM_EG_PAR_MAX_VALUE);
1011    
1012            AbstractEngineChannel* pEngineChannel =
1013                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1014    
1015            if (args->arg(0)->exprType() == INT_EXPR) {
1016                const ScriptID id = args->arg(0)->asInt()->evalInt();
1017                if (!id) {
1018                    wrnMsg("change_release(): note ID for argument 1 may not be zero");
1019                    return successResult();
1020                }
1021                if (!id.isNoteID()) {
1022                    wrnMsg("change_release(): argument 1 is not a note ID");
1023                    return successResult();
1024                }
1025    
1026                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1027                if (!pNote) return successResult();
1028    
1029                // if change_release() was called immediately after note was triggered
1030                // then immediately apply relase to Note object
1031                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1032                    pNote->Override.Release = fRelease;
1033                } else { // otherwise schedule release change ...
1034                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1035                    e.Init(); // clear IDs
1036                    e.Type = Event::type_note_synth_param;
1037                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1038                    e.Param.NoteSynthParam.Type     = Event::synth_param_release;
1039                    e.Param.NoteSynthParam.Delta    = fRelease;
1040                    e.Param.NoteSynthParam.Relative = false;
1041    
1042                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
1043                }
1044            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1045                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1046                for (int i = 0; i < ids->arraySize(); ++i) {
1047                    const ScriptID id = ids->evalIntElement(i);
1048                    if (!id || !id.isNoteID()) continue;
1049    
1050                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1051                    if (!pNote) continue;
1052    
1053                    // if change_release() was called immediately after note was triggered
1054                    // then immediately apply relase to Note object
1055                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1056                        pNote->Override.Release = fRelease;
1057                    } else { // otherwise schedule release change ...
1058                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1059                        e.Init(); // clear IDs
1060                        e.Type = Event::type_note_synth_param;
1061                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1062                        e.Param.NoteSynthParam.Type     = Event::synth_param_release;
1063                        e.Param.NoteSynthParam.Delta    = fRelease;
1064                        e.Param.NoteSynthParam.Relative = false;
1065    
1066                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
1067                    }
1068                }
1069            }
1070    
1071            return successResult();
1072        }
1073    
1074        bool VMChangeSynthParamFunction::acceptsArgType(int iArg, ExprType_t type) const {
1075            if (iArg == 0)
1076                return type == INT_EXPR || type == INT_ARR_EXPR;
1077            else
1078                return type == INT_EXPR;
1079        }
1080    
1081        // Arbitrarily chosen constant value symbolizing "no limit".
1082        #define NO_LIMIT 1315916909
1083    
1084        template<float NoteBase::_Override::*T_noteParam, int T_synthParam,
1085                 bool T_isNormalizedParam, int T_maxValue, int T_minValue>
1086        VMFnResult* VMChangeSynthParamFunction::execTemplate(VMFnArgs* args, const char* functionName) {
1087            int value = args->arg(1)->asInt()->evalInt();
1088            if (T_maxValue != NO_LIMIT && value > T_maxValue) {
1089                wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValue));
1090                value = T_maxValue;
1091            } else if (T_minValue != NO_LIMIT && value < T_minValue) {
1092                if (T_minValue == 0)
1093                    wrnMsg(String(functionName) + "(): argument 2 may not be negative");
1094                else
1095                    wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValue));
1096                value = T_minValue;
1097            }
1098            const float fValue = (T_isNormalizedParam) ?
1099                float(value) / float(T_maxValue) : // convert to 0.0 .. 1.0 value range
1100                float(value) / 1000000.f; // assuming microseconds here, convert to seconds
1101    
1102            AbstractEngineChannel* pEngineChannel =
1103                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1104    
1105            if (args->arg(0)->exprType() == INT_EXPR) {
1106                const ScriptID id = args->arg(0)->asInt()->evalInt();
1107                if (!id) {
1108                    wrnMsg(String(functionName) + "(): note ID for argument 1 may not be zero");
1109                    return successResult();
1110                }
1111                if (!id.isNoteID()) {
1112                    wrnMsg(String(functionName) + "(): argument 1 is not a note ID");
1113                    return successResult();
1114                }
1115    
1116                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1117                if (!pNote) return successResult();
1118    
1119                // if this change_*() script function was called immediately after
1120                // note was triggered then immediately apply the synth parameter
1121                // change to Note object
1122                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1123                    pNote->Override.*T_noteParam = fValue;
1124                } else { // otherwise schedule this synth parameter change ...
1125                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1126                    e.Init(); // clear IDs
1127                    e.Type = Event::type_note_synth_param;
1128                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1129                    e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1130                    e.Param.NoteSynthParam.Delta    = fValue;
1131                    e.Param.NoteSynthParam.Relative = false;
1132    
1133                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
1134                }
1135            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1136                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1137                for (int i = 0; i < ids->arraySize(); ++i) {
1138                    const ScriptID id = ids->evalIntElement(i);
1139                    if (!id || !id.isNoteID()) continue;
1140    
1141                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1142                    if (!pNote) continue;
1143    
1144                    // if this change_*() script function was called immediately after
1145                    // note was triggered then immediately apply the synth parameter
1146                    // change to Note object
1147                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1148                        pNote->Override.*T_noteParam = fValue;
1149                    } else { // otherwise schedule this synth parameter change ...
1150                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1151                        e.Init(); // clear IDs
1152                        e.Type = Event::type_note_synth_param;
1153                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1154                        e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1155                        e.Param.NoteSynthParam.Delta    = fValue;
1156                        e.Param.NoteSynthParam.Relative = false;
1157    
1158                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
1159                    }
1160                }
1161            }
1162    
1163            return successResult();
1164        }
1165    
1166        // change_amp_lfo_depth() function
1167    
1168        VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_depth::exec(VMFnArgs* args) {
1169            return VMChangeSynthParamFunction::execTemplate<
1170                        &NoteBase::_Override::AmpLFODepth,
1171                        Event::synth_param_amp_lfo_depth,
1172                        true, 1000000, 0>( args, "change_amp_lfo_depth" );
1173        }
1174    
1175        // change_amp_lfo_freq() function
1176    
1177        VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_freq::exec(VMFnArgs* args) {
1178            return VMChangeSynthParamFunction::execTemplate<
1179                        &NoteBase::_Override::AmpLFOFreq,
1180                        Event::synth_param_amp_lfo_freq,
1181                        true, 1000000, 0>( args, "change_amp_lfo_freq" );
1182        }
1183    
1184        // change_pitch_lfo_depth() function
1185    
1186        VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_depth::exec(VMFnArgs* args) {
1187            return VMChangeSynthParamFunction::execTemplate<
1188                        &NoteBase::_Override::PitchLFODepth,
1189                        Event::synth_param_pitch_lfo_depth,
1190                        true, 1000000, 0>( args, "change_pitch_lfo_depth" );
1191        }
1192    
1193        // change_pitch_lfo_freq() function
1194    
1195        VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_freq::exec(VMFnArgs* args) {
1196            return VMChangeSynthParamFunction::execTemplate<
1197                        &NoteBase::_Override::PitchLFOFreq,
1198                        Event::synth_param_pitch_lfo_freq,
1199                        true, 1000000, 0>( args, "change_pitch_lfo_freq" );
1200        }
1201    
1202        // change_vol_time() function
1203    
1204        VMFnResult* InstrumentScriptVMFunction_change_vol_time::exec(VMFnArgs* args) {
1205            return VMChangeSynthParamFunction::execTemplate<
1206                        &NoteBase::_Override::VolumeTime,
1207                        Event::synth_param_volume_time,
1208                        false, NO_LIMIT, 0>( args, "change_vol_time" );
1209        }
1210    
1211        // change_tune_time() function
1212    
1213        VMFnResult* InstrumentScriptVMFunction_change_tune_time::exec(VMFnArgs* args) {
1214            return VMChangeSynthParamFunction::execTemplate<
1215                        &NoteBase::_Override::PitchTime,
1216                        Event::synth_param_pitch_time,
1217                        false, NO_LIMIT, 0>( args, "change_tune_time" );
1218        }
1219    
1220        // fade_in() function
1221    
1222        InstrumentScriptVMFunction_fade_in::InstrumentScriptVMFunction_fade_in(InstrumentScriptVM* parent)
1223            : m_vm(parent)
1224        {
1225        }
1226    
1227        bool InstrumentScriptVMFunction_fade_in::acceptsArgType(int iArg, ExprType_t type) const {
1228            if (iArg == 0)
1229                return type == INT_EXPR || type == INT_ARR_EXPR;
1230            else
1231                return type == INT_EXPR;
1232        }
1233    
1234        VMFnResult* InstrumentScriptVMFunction_fade_in::exec(VMFnArgs* args) {
1235            int duration = args->arg(1)->asInt()->evalInt();
1236            if (duration < 0) {
1237                wrnMsg("fade_in(): argument 2 may not be negative");
1238                duration = 0;
1239            }
1240            const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
1241    
1242            AbstractEngineChannel* pEngineChannel =
1243                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1244    
1245            if (args->arg(0)->exprType() == INT_EXPR) {
1246                const ScriptID id = args->arg(0)->asInt()->evalInt();
1247                if (!id) {
1248                    wrnMsg("fade_in(): note ID for argument 1 may not be zero");
1249                    return successResult();
1250                }
1251                if (!id.isNoteID()) {
1252                    wrnMsg("fade_in(): argument 1 is not a note ID");
1253                    return successResult();
1254                }
1255    
1256                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1257                if (!pNote) return successResult();
1258    
1259                // if fade_in() was called immediately after note was triggered
1260                // then immediately apply a start volume of zero to Note object,
1261                // as well as the fade in duration
1262                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1263                    pNote->Override.Volume = 0.f;
1264                    pNote->Override.VolumeTime = fDuration;
1265                } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
1266                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1267                    e.Init(); // clear IDs
1268                    e.Type = Event::type_note_synth_param;
1269                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1270                    e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1271                    e.Param.NoteSynthParam.Delta    = fDuration;
1272                    e.Param.NoteSynthParam.Relative = false;
1273    
1274                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
1275                }
1276                // and finally schedule a "volume" change, simply one time slice
1277                // ahead, with the final fade in volume (1.0)
1278                {
1279                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1280                    e.Init(); // clear IDs
1281                    e.Type = Event::type_note_synth_param;
1282                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1283                    e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1284                    e.Param.NoteSynthParam.Delta    = 1.f;
1285                    e.Param.NoteSynthParam.Relative = false;
1286    
1287                    // scheduling with 0 delay would also work here, but +1 is more
1288                    // safe regarding potential future implementation changes of the
1289                    // scheduler (see API comments of RTAVLTree::insert())
1290                    pEngineChannel->ScheduleEventMicroSec(&e, 1);
1291                }
1292            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1293                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1294                for (int i = 0; i < ids->arraySize(); ++i) {
1295                    const ScriptID id = ids->evalIntElement(i);
1296                    if (!id || !id.isNoteID()) continue;
1297    
1298                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1299                    if (!pNote) continue;
1300    
1301                    // if fade_in() was called immediately after note was triggered
1302                    // then immediately apply a start volume of zero to Note object,
1303                    // as well as the fade in duration
1304                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1305                        pNote->Override.Volume = 0.f;
1306                        pNote->Override.VolumeTime = fDuration;
1307                    } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
1308                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1309                        e.Init(); // clear IDs
1310                        e.Type = Event::type_note_synth_param;
1311                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1312                        e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1313                        e.Param.NoteSynthParam.Delta    = fDuration;
1314                        e.Param.NoteSynthParam.Relative = false;
1315    
1316                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
1317                    }
1318                    // and finally schedule a "volume" change, simply one time slice
1319                    // ahead, with the final fade in volume (1.0)
1320                    {
1321                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1322                        e.Init(); // clear IDs
1323                        e.Type = Event::type_note_synth_param;
1324                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1325                        e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1326                        e.Param.NoteSynthParam.Delta    = 1.f;
1327                        e.Param.NoteSynthParam.Relative = false;
1328    
1329                        // scheduling with 0 delay would also work here, but +1 is more
1330                        // safe regarding potential future implementation changes of the
1331                        // scheduler (see API comments of RTAVLTree::insert())
1332                        pEngineChannel->ScheduleEventMicroSec(&e, 1);
1333                    }
1334                }
1335            }
1336    
1337            return successResult();
1338        }
1339    
1340        // fade_out() function
1341    
1342        InstrumentScriptVMFunction_fade_out::InstrumentScriptVMFunction_fade_out(InstrumentScriptVM* parent)
1343            : m_vm(parent)
1344        {
1345        }
1346    
1347        bool InstrumentScriptVMFunction_fade_out::acceptsArgType(int iArg, ExprType_t type) const {
1348            if (iArg == 0)
1349                return type == INT_EXPR || type == INT_ARR_EXPR;
1350            else
1351                return type == INT_EXPR;
1352        }
1353    
1354        VMFnResult* InstrumentScriptVMFunction_fade_out::exec(VMFnArgs* args) {
1355            int duration = args->arg(1)->asInt()->evalInt();
1356            if (duration < 0) {
1357                wrnMsg("fade_out(): argument 2 may not be negative");
1358                duration = 0;
1359            }
1360            const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
1361    
1362            bool stop = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : true;
1363    
1364            AbstractEngineChannel* pEngineChannel =
1365                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1366    
1367            if (args->arg(0)->exprType() == INT_EXPR) {
1368                const ScriptID id = args->arg(0)->asInt()->evalInt();
1369                if (!id) {
1370                    wrnMsg("fade_out(): note ID for argument 1 may not be zero");
1371                    return successResult();
1372                }
1373                if (!id.isNoteID()) {
1374                    wrnMsg("fade_out(): argument 1 is not a note ID");
1375                    return successResult();
1376                }
1377    
1378                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1379                if (!pNote) return successResult();
1380    
1381                // if fade_out() was called immediately after note was triggered
1382                // then immediately apply fade out duration to Note object
1383                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1384                    pNote->Override.VolumeTime = fDuration;
1385                } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
1386                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1387                    e.Init(); // clear IDs
1388                    e.Type = Event::type_note_synth_param;
1389                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1390                    e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1391                    e.Param.NoteSynthParam.Delta    = fDuration;
1392                    e.Param.NoteSynthParam.Relative = false;
1393    
1394                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
1395                }
1396                // now schedule a "volume" change, simply one time slice ahead, with
1397                // the final fade out volume (0.0)
1398                {
1399                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1400                    e.Init(); // clear IDs
1401                    e.Type = Event::type_note_synth_param;
1402                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1403                    e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1404                    e.Param.NoteSynthParam.Delta    = 0.f;
1405                    e.Param.NoteSynthParam.Relative = false;
1406    
1407                    // scheduling with 0 delay would also work here, but +1 is more
1408                    // safe regarding potential future implementation changes of the
1409                    // scheduler (see API comments of RTAVLTree::insert())
1410                    pEngineChannel->ScheduleEventMicroSec(&e, 1);
1411                }
1412                // and finally if stopping the note was requested after the fade out
1413                // completed, then schedule to kill the voice after the requested
1414                // duration
1415                if (stop) {
1416                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1417                    e.Init(); // clear IDs
1418                    e.Type = Event::type_kill_note;
1419                    e.Param.Note.ID = id.noteID();
1420                    e.Param.Note.Key = pNote->hostKey;
1421    
1422                    pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
1423                }
1424            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1425                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1426                for (int i = 0; i < ids->arraySize(); ++i) {
1427                    const ScriptID id = ids->evalIntElement(i);
1428                    if (!id || !id.isNoteID()) continue;
1429    
1430                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1431                    if (!pNote) continue;
1432    
1433                    // if fade_out() was called immediately after note was triggered
1434                    // then immediately apply fade out duration to Note object
1435                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1436                        pNote->Override.VolumeTime = fDuration;
1437                    } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
1438                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1439                        e.Init(); // clear IDs
1440                        e.Type = Event::type_note_synth_param;
1441                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1442                        e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1443                        e.Param.NoteSynthParam.Delta    = fDuration;
1444                        e.Param.NoteSynthParam.Relative = false;
1445    
1446                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
1447                    }
1448                    // now schedule a "volume" change, simply one time slice ahead, with
1449                    // the final fade out volume (0.0)
1450                    {
1451                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1452                        e.Init(); // clear IDs
1453                        e.Type = Event::type_note_synth_param;
1454                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1455                        e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1456                        e.Param.NoteSynthParam.Delta    = 0.f;
1457                        e.Param.NoteSynthParam.Relative = false;
1458    
1459                        // scheduling with 0 delay would also work here, but +1 is more
1460                        // safe regarding potential future implementation changes of the
1461                        // scheduler (see API comments of RTAVLTree::insert())
1462                        pEngineChannel->ScheduleEventMicroSec(&e, 1);
1463                    }
1464                    // and finally if stopping the note was requested after the fade out
1465                    // completed, then schedule to kill the voice after the requested
1466                    // duration
1467                    if (stop) {
1468                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1469                        e.Init(); // clear IDs
1470                        e.Type = Event::type_kill_note;
1471                        e.Param.Note.ID = id.noteID();
1472                        e.Param.Note.Key = pNote->hostKey;
1473                        
1474                        pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
1475                    }
1476                }
1477            }
1478    
1479            return successResult();
1480        }
1481    
1482        // get_event_par() function
1483    
1484        InstrumentScriptVMFunction_get_event_par::InstrumentScriptVMFunction_get_event_par(InstrumentScriptVM* parent)
1485            : m_vm(parent)
1486        {
1487        }
1488    
1489        VMFnResult* InstrumentScriptVMFunction_get_event_par::exec(VMFnArgs* args) {
1490            AbstractEngineChannel* pEngineChannel =
1491                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1492    
1493            const ScriptID id = args->arg(0)->asInt()->evalInt();
1494            if (!id) {
1495                wrnMsg("get_event_par(): note ID for argument 1 may not be zero");
1496                return successResult(0);
1497            }
1498            if (!id.isNoteID()) {
1499                wrnMsg("get_event_par(): argument 1 is not a note ID");
1500                return successResult(0);
1501            }
1502    
1503            NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1504            if (!pNote) {
1505                wrnMsg("get_event_par(): no note alive with that note ID of argument 1");
1506                return successResult(0);
1507            }
1508    
1509            const int parameter = args->arg(1)->asInt()->evalInt();
1510            switch (parameter) {
1511                case EVENT_PAR_NOTE:
1512                    return successResult(pNote->cause.Param.Note.Key);
1513                case EVENT_PAR_VELOCITY:
1514                    return successResult(pNote->cause.Param.Note.Velocity);
1515                case EVENT_PAR_VOLUME:
1516                    return successResult(
1517                        RTMath::LinRatioToDecibel(pNote->Override.Volume) * 1000.f
1518                    );
1519                case EVENT_PAR_TUNE:
1520                    return successResult(
1521                         RTMath::FreqRatioToCents(pNote->Override.Pitch) * 1000.f
1522                    );
1523                case EVENT_PAR_0:
1524                    return successResult(pNote->userPar[0]);
1525                case EVENT_PAR_1:
1526                    return successResult(pNote->userPar[1]);
1527                case EVENT_PAR_2:
1528                    return successResult(pNote->userPar[2]);
1529                case EVENT_PAR_3:
1530                    return successResult(pNote->userPar[3]);
1531            }
1532    
1533            wrnMsg("get_event_par(): argument 2 is an invalid event parameter");
1534            return successResult(0);
1535        }
1536    
1537        // set_event_par() function
1538    
1539        InstrumentScriptVMFunction_set_event_par::InstrumentScriptVMFunction_set_event_par(InstrumentScriptVM* parent)
1540            : m_vm(parent)
1541        {
1542        }
1543    
1544        VMFnResult* InstrumentScriptVMFunction_set_event_par::exec(VMFnArgs* args) {
1545            AbstractEngineChannel* pEngineChannel =
1546                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1547    
1548            const ScriptID id = args->arg(0)->asInt()->evalInt();
1549            if (!id) {
1550                wrnMsg("set_event_par(): note ID for argument 1 may not be zero");
1551                return successResult();
1552            }
1553            if (!id.isNoteID()) {
1554                wrnMsg("set_event_par(): argument 1 is not a note ID");
1555                return successResult();
1556            }
1557    
1558            NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1559            if (!pNote) return successResult();
1560    
1561            const int parameter = args->arg(1)->asInt()->evalInt();
1562            const int value     = args->arg(2)->asInt()->evalInt();
1563    
1564            switch (parameter) {
1565                case EVENT_PAR_NOTE:
1566                    if (value < 0 || value > 127) {
1567                        wrnMsg("set_event_par(): note number 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.Key = value;
1572                        m_vm->m_event->cause.Param.Note.Key = value;
1573                    } else {
1574                        wrnMsg("set_event_par(): note number can only be changed when note is new");
1575                    }
1576                    return successResult();
1577                case EVENT_PAR_VELOCITY:
1578                    if (value < 0 || value > 127) {
1579                        wrnMsg("set_event_par(): velocity of argument 3 is out of range");
1580                        return successResult();
1581                    }
1582                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1583                        pNote->cause.Param.Note.Velocity = value;
1584                        m_vm->m_event->cause.Param.Note.Velocity = value;
1585                    } else {
1586                        wrnMsg("set_event_par(): velocity can only be changed when note is new");
1587                    }
1588                    return successResult();
1589                case EVENT_PAR_VOLUME:
1590                    wrnMsg("set_event_par(): changing volume by this function is currently not supported, use change_vol() instead");
1591                    return successResult();
1592                case EVENT_PAR_TUNE:
1593                    wrnMsg("set_event_par(): changing tune by this function is currently not supported, use change_tune() instead");
1594                    return successResult();
1595                case EVENT_PAR_0:
1596                    pNote->userPar[0] = value;
1597                    return successResult();
1598                case EVENT_PAR_1:
1599                    pNote->userPar[1] = value;
1600                    return successResult();
1601                case EVENT_PAR_2:
1602                    pNote->userPar[2] = value;
1603                    return successResult();
1604                case EVENT_PAR_3:
1605                    pNote->userPar[3] = value;
1606                    return successResult();
1607            }
1608    
1609            wrnMsg("set_event_par(): argument 2 is an invalid event parameter");
1610            return successResult();
1611        }
1612    
1613        // change_note() function
1614    
1615        InstrumentScriptVMFunction_change_note::InstrumentScriptVMFunction_change_note(InstrumentScriptVM* parent)
1616        : m_vm(parent)
1617        {
1618        }
1619    
1620        VMFnResult* InstrumentScriptVMFunction_change_note::exec(VMFnArgs* args) {
1621            AbstractEngineChannel* pEngineChannel =
1622                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1623    
1624            const ScriptID id = args->arg(0)->asInt()->evalInt();
1625            if (!id) {
1626                wrnMsg("change_note(): note ID for argument 1 may not be zero");
1627                return successResult();
1628            }
1629            if (!id.isNoteID()) {
1630                wrnMsg("change_note(): argument 1 is not a note ID");
1631                return successResult();
1632            }
1633    
1634            NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1635            if (!pNote) return successResult();
1636    
1637            const int value = args->arg(1)->asInt()->evalInt();
1638            if (value < 0 || value > 127) {
1639                wrnMsg("change_note(): note number of argument 2 is out of range");
1640                return successResult();
1641            }
1642    
1643            if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1644                pNote->cause.Param.Note.Key = value;
1645                m_vm->m_event->cause.Param.Note.Key = value;
1646            } else {
1647                wrnMsg("change_note(): note number can only be changed when note is new");
1648            }
1649    
1650            return successResult();
1651        }
1652    
1653        // change_velo() function
1654    
1655        InstrumentScriptVMFunction_change_velo::InstrumentScriptVMFunction_change_velo(InstrumentScriptVM* parent)
1656        : m_vm(parent)
1657        {
1658        }
1659    
1660        VMFnResult* InstrumentScriptVMFunction_change_velo::exec(VMFnArgs* args) {
1661            AbstractEngineChannel* pEngineChannel =
1662                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1663    
1664            const ScriptID id = args->arg(0)->asInt()->evalInt();
1665            if (!id) {
1666                wrnMsg("change_velo(): note ID for argument 1 may not be zero");
1667                return successResult();
1668            }
1669            if (!id.isNoteID()) {
1670                wrnMsg("change_velo(): argument 1 is not a note ID");
1671              return successResult();              return successResult();
1672          }          }
1673    
1674            NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1675            if (!pNote) return successResult();
1676    
1677            const int value = args->arg(1)->asInt()->evalInt();
1678            if (value < 0 || value > 127) {
1679                wrnMsg("change_velo(): velocity of argument 2 is out of range");
1680                return successResult();
1681            }
1682    
1683            if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1684                pNote->cause.Param.Note.Velocity = value;
1685                m_vm->m_event->cause.Param.Note.Velocity = value;
1686            } else {
1687                wrnMsg("change_velo(): velocity can only be changed when note is new");
1688            }
1689    
1690            return successResult();
1691        }
1692    
1693        // event_status() function
1694    
1695        InstrumentScriptVMFunction_event_status::InstrumentScriptVMFunction_event_status(InstrumentScriptVM* parent)
1696            : m_vm(parent)
1697        {
1698        }
1699    
1700        VMFnResult* InstrumentScriptVMFunction_event_status::exec(VMFnArgs* args) {
1701            AbstractEngineChannel* pEngineChannel =
1702                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1703    
1704            const ScriptID id = args->arg(0)->asInt()->evalInt();
1705            if (!id) {
1706                wrnMsg("event_status(): note ID for argument 1 may not be zero");
1707                return successResult(EVENT_STATUS_INACTIVE);
1708            }
1709            if (!id.isNoteID()) {
1710                wrnMsg("event_status(): argument 1 is not a note ID");
1711                return successResult(EVENT_STATUS_INACTIVE);
1712            }
1713    
1714            NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1715            return successResult(pNote ? EVENT_STATUS_NOTE_QUEUE : EVENT_STATUS_INACTIVE);
1716        }
1717    
1718        // wait() function (overrides core wait() implementation)
1719    
1720        InstrumentScriptVMFunction_wait::InstrumentScriptVMFunction_wait(InstrumentScriptVM* parent)
1721            : CoreVMFunction_wait(parent)
1722        {    
1723        }
1724    
1725        VMFnResult* InstrumentScriptVMFunction_wait::exec(VMFnArgs* args) {
1726            InstrumentScriptVM* m_vm = (InstrumentScriptVM*) vm;
1727    
1728            // this might be set by passing 1 with the 2nd argument of built-in stop_wait() function
1729            if (m_vm->m_event->ignoreAllWaitCalls) return successResult();
1730    
1731            return CoreVMFunction_wait::exec(args);
1732        }
1733    
1734        // stop_wait() function
1735    
1736        InstrumentScriptVMFunction_stop_wait::InstrumentScriptVMFunction_stop_wait(InstrumentScriptVM* parent)
1737            : m_vm(parent)
1738        {    
1739        }
1740    
1741        VMFnResult* InstrumentScriptVMFunction_stop_wait::exec(VMFnArgs* args) {
1742          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
1743              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1744    
1745          pEngineChannel->IgnoreEvent(id);          const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
1746            if (!id) {
1747                wrnMsg("stop_wait(): callback ID for argument 1 may not be zero");
1748                return successResult();
1749            }
1750    
1751            RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
1752            if (!itCallback) return successResult(); // ignore if callback is i.e. not alive anymore
1753    
1754            const bool disableWaitForever =
1755                (args->argsCount() >= 2) ? (args->arg(1)->asInt()->evalInt() == 1) : false;
1756    
1757            pEngineChannel->ScheduleResumeOfScriptCallback(
1758                itCallback, m_vm->m_event->scheduleTime, disableWaitForever
1759            );
1760    
1761          return successResult();          return successResult();
1762      }      }

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

  ViewVC Help
Powered by ViewVC