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

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

  ViewVC Help
Powered by ViewVC