/[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 3205 by schoenebeck, Wed May 24 20:05:38 2017 UTC revision 3715 by schoenebeck, Sun Jan 12 17:07:34 2020 UTC
# Line 1  Line 1 
1  /*  /*
2   * Copyright (c) 2014-2017 Christian Schoenebeck   * Copyright (c) 2014-2020 Christian Schoenebeck
3   *   *
4   * http://www.linuxsampler.org   * http://www.linuxsampler.org
5   *   *
# Line 10  Line 10 
10  #include "InstrumentScriptVMFunctions.h"  #include "InstrumentScriptVMFunctions.h"
11  #include "InstrumentScriptVM.h"  #include "InstrumentScriptVM.h"
12  #include "../AbstractEngineChannel.h"  #include "../AbstractEngineChannel.h"
13    #include "../../common/global_private.h"
14    
15  namespace LinuxSampler {  namespace LinuxSampler {
16            
# Line 20  namespace LinuxSampler { Line 21  namespace LinuxSampler {
21      {      {
22      }      }
23    
24        bool InstrumentScriptVMFunction_play_note::acceptsArgType(vmint iArg, ExprType_t type) const {
25            if (iArg == 2 || iArg == 3)
26                return type == INT_EXPR || type == REAL_EXPR;
27            else
28                return type == INT_EXPR;
29        }
30    
31        bool InstrumentScriptVMFunction_play_note::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
32            if (iArg == 2 || iArg == 3)
33                return type == VM_NO_UNIT || type == VM_SECOND;
34            else
35                return type == VM_NO_UNIT;
36        }
37    
38        bool InstrumentScriptVMFunction_play_note::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
39            if (iArg == 2 || iArg == 3)
40                return type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
41            else
42                return false;
43        }
44    
45        void InstrumentScriptVMFunction_play_note::checkArgs(VMFnArgs* args,
46                                                             std::function<void(String)> err,
47                                                             std::function<void(String)> wrn)
48        {
49            // super class checks
50            Super::checkArgs(args, err, wrn);
51    
52            // own checks ...
53            if (args->arg(0)->isConstExpr()) {
54                vmint note = args->arg(0)->asNumber()->evalCastInt();
55                if (note < 0 || note > 127) {
56                    err("MIDI note number value for argument 1 must be between 0..127");
57                    return;
58                }
59            }
60            if (args->argsCount() >= 2 && args->arg(1)->isConstExpr()) {
61                vmint velocity = args->arg(1)->asNumber()->evalCastInt();
62                if (velocity < 0 || velocity > 127) {
63                    err("MIDI velocity value for argument 2 must be between 0..127");
64                    return;
65                }
66            }
67            if (args->argsCount() >= 3 && args->arg(2)->isConstExpr()) {
68                VMNumberExpr* argSampleOffset = args->arg(2)->asNumber();
69                vmint sampleoffset =
70                    (argSampleOffset->unitType()) ?
71                        argSampleOffset->evalCastInt(VM_MICRO) :
72                        argSampleOffset->evalCastInt();
73                if (sampleoffset < -1) {
74                    err("Sample offset of argument 3 may not be less than -1");
75                    return;
76                }
77            }
78            if (args->argsCount() >= 4 && args->arg(3)->isConstExpr()) {
79                VMNumberExpr* argDuration = args->arg(3)->asNumber();
80                vmint duration =
81                    (argDuration->unitType()) ?
82                        argDuration->evalCastInt(VM_MICRO) :
83                        argDuration->evalCastInt();
84                if (duration < -2) {
85                    err("Argument 4 must be a duration value of at least -2 or higher");
86                    return;
87                }
88            }
89        }
90    
91      VMFnResult* InstrumentScriptVMFunction_play_note::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_play_note::exec(VMFnArgs* args) {
92          int note = args->arg(0)->asInt()->evalInt();          vmint note = args->arg(0)->asInt()->evalInt();
93          int velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127;          vmint velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127;
94          int sampleoffset = (args->argsCount() >= 3) ? args->arg(2)->asInt()->evalInt() : 0;          VMNumberExpr* argDuration = (args->argsCount() >= 4) ? args->arg(3)->asNumber() : NULL;
95          int duration = (args->argsCount() >= 4) ? args->arg(3)->asInt()->evalInt() : 0; //TODO: -1 might be a better default value instead of 0          vmint duration =
96                (argDuration) ?
97                    (argDuration->unitType()) ?
98                        argDuration->evalCastInt(VM_MICRO) :
99                        argDuration->evalCastInt() : 0; //TODO: -1 might be a better default value instead of 0
100    
101          if (note < 0 || note > 127) {          if (note < 0 || note > 127) {
102              errMsg("play_note(): argument 1 is an invalid note number");              errMsg("play_note(): argument 1 is an invalid note number");
# Line 36  namespace LinuxSampler { Line 108  namespace LinuxSampler {
108              return errorResult(0);              return errorResult(0);
109          }          }
110    
111          if (sampleoffset < 0) {          if (duration < -2) {
112              errMsg("play_note(): argument 3 may not be a negative sample offset");              errMsg("play_note(): argument 4 must be a duration value of at least -2 or higher");
             return errorResult(0);  
         } else if (sampleoffset != 0) {  
             wrnMsg("play_note(): argument 3 does not support a sample offset other than 0 yet");  
         }  
   
         if (duration < -1) {  
             errMsg("play_note(): argument 4 must be a duration value of at least -1 or higher");  
113              return errorResult(0);              return errorResult(0);
114          }          }
115    
# Line 63  namespace LinuxSampler { Line 128  namespace LinuxSampler {
128                  return errorResult(0);                  return errorResult(0);
129              }              }
130              e.Param.Note.ParentNoteID = m_vm->m_event->cause.Param.Note.ID;              e.Param.Note.ParentNoteID = m_vm->m_event->cause.Param.Note.ID;
131                // check if that requested parent note is actually still alive
132                NoteBase* pParentNote =
133                    pEngineChannel->pEngine->NoteByID( e.Param.Note.ParentNoteID );
134                // if parent note is already gone then this new note is not required anymore
135                if (!pParentNote)
136                    return successResult(0);
137          }          }
138    
139          const note_id_t id = pEngineChannel->ScheduleNoteMicroSec(&e, 0);          const note_id_t id = pEngineChannel->ScheduleNoteMicroSec(&e, 0);
140    
141            // if a sample offset is supplied, assign the offset as override
142            // to the previously created Note object
143            if (args->argsCount() >= 3) {
144                VMNumberExpr* argSampleOffset = args->arg(2)->asNumber();
145                vmint sampleoffset =
146                    (argSampleOffset->unitType()) ?
147                        argSampleOffset->evalCastInt(VM_MICRO) :
148                        argSampleOffset->evalCastInt();
149                if (sampleoffset >= 0) {
150                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID(id);
151                    if (pNote) {
152                        pNote->Override.SampleOffset =
153                            (decltype(pNote->Override.SampleOffset)) sampleoffset;
154                    }
155                } else if (sampleoffset < -1) {
156                    errMsg("play_note(): sample offset of argument 3 may not be less than -1");
157                }
158            }
159    
160          // if a duration is supplied (and play-note event was scheduled          // if a duration is supplied (and play-note event was scheduled
161          // successfully above), then schedule a subsequent stop-note event          // successfully above), then schedule a subsequent stop-note event
162          if (id && duration > 0) {          if (id && duration > 0) {
# Line 90  namespace LinuxSampler { Line 180  namespace LinuxSampler {
180      }      }
181    
182      VMFnResult* InstrumentScriptVMFunction_set_controller::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_set_controller::exec(VMFnArgs* args) {
183          int controller = args->arg(0)->asInt()->evalInt();          vmint controller = args->arg(0)->asInt()->evalInt();
184          int value      = args->arg(1)->asInt()->evalInt();          vmint value      = args->arg(1)->asInt()->evalInt();
185    
186          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
187              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 121  namespace LinuxSampler { Line 211  namespace LinuxSampler {
211          return successResult( ScriptID::fromEventID(id) );          return successResult( ScriptID::fromEventID(id) );
212      }      }
213    
214        // set_rpn() function
215    
216        InstrumentScriptVMFunction_set_rpn::InstrumentScriptVMFunction_set_rpn(InstrumentScriptVM* parent)
217            : m_vm(parent)
218        {
219        }
220    
221        VMFnResult* InstrumentScriptVMFunction_set_rpn::exec(VMFnArgs* args) {
222            vmint parameter = args->arg(0)->asInt()->evalInt();
223            vmint value     = args->arg(1)->asInt()->evalInt();
224    
225            if (parameter < 0 || parameter > 16383) {
226                errMsg("set_rpn(): argument 1 exceeds RPN parameter number range");
227                return errorResult();
228            }
229            if (value < 0 || value > 16383) {
230                errMsg("set_rpn(): argument 2 exceeds RPN value range");
231                return errorResult();
232            }
233    
234            AbstractEngineChannel* pEngineChannel =
235                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
236    
237            Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
238            e.Init(); // clear IDs
239            e.Type = Event::type_rpn;
240            e.Param.RPN.Parameter = parameter;
241            e.Param.RPN.Value = value;
242    
243            const event_id_t id = pEngineChannel->ScheduleEventMicroSec(&e, 0);
244    
245            // even if id is null, don't return an errorResult() here, because that
246            // would abort the script, and under heavy load it may be considerable
247            // that ScheduleEventMicroSec() fails above, so simply ignore that
248            return successResult( ScriptID::fromEventID(id) );
249        }
250    
251        // set_nrpn() function
252    
253        InstrumentScriptVMFunction_set_nrpn::InstrumentScriptVMFunction_set_nrpn(InstrumentScriptVM* parent)
254            : m_vm(parent)
255        {
256        }
257    
258        VMFnResult* InstrumentScriptVMFunction_set_nrpn::exec(VMFnArgs* args) {
259            vmint parameter = args->arg(0)->asInt()->evalInt();
260            vmint value     = args->arg(1)->asInt()->evalInt();
261    
262            if (parameter < 0 || parameter > 16383) {
263                errMsg("set_nrpn(): argument 1 exceeds NRPN parameter number range");
264                return errorResult();
265            }
266            if (value < 0 || value > 16383) {
267                errMsg("set_nrpn(): argument 2 exceeds NRPN value range");
268                return errorResult();
269            }
270    
271            AbstractEngineChannel* pEngineChannel =
272                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
273    
274            Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
275            e.Init(); // clear IDs
276            e.Type = Event::type_nrpn;
277            e.Param.NRPN.Parameter = parameter;
278            e.Param.NRPN.Value = value;
279    
280            const event_id_t id = pEngineChannel->ScheduleEventMicroSec(&e, 0);
281    
282            // even if id is null, don't return an errorResult() here, because that
283            // would abort the script, and under heavy load it may be considerable
284            // that ScheduleEventMicroSec() fails above, so simply ignore that
285            return successResult( ScriptID::fromEventID(id) );
286        }
287    
288      // ignore_event() function      // ignore_event() function
289    
290      InstrumentScriptVMFunction_ignore_event::InstrumentScriptVMFunction_ignore_event(InstrumentScriptVM* parent)      InstrumentScriptVMFunction_ignore_event::InstrumentScriptVMFunction_ignore_event(InstrumentScriptVM* parent)
# Line 128  namespace LinuxSampler { Line 292  namespace LinuxSampler {
292      {      {
293      }      }
294    
295      bool InstrumentScriptVMFunction_ignore_event::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_ignore_event::acceptsArgType(vmint iArg, ExprType_t type) const {
296          return type == INT_EXPR || type == INT_ARR_EXPR;          return type == INT_EXPR || type == INT_ARR_EXPR;
297      }      }
298    
# Line 136  namespace LinuxSampler { Line 300  namespace LinuxSampler {
300          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
301                  static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);                  static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
302    
303          if (args->arg(0)->exprType() == INT_EXPR) {          if (args->argsCount() == 0 || args->arg(0)->exprType() == INT_EXPR) {
304              const ScriptID id = args->arg(0)->asInt()->evalInt();              const ScriptID id = (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : m_vm->m_event->id;
305              if (!id) {              if (!id && args->argsCount() >= 1) {
306                  wrnMsg("ignore_event(): event ID argument may not be zero");                  wrnMsg("ignore_event(): event ID argument may not be zero");
307                  // not errorResult(), because that would abort the script, not intentional in this case                  // not errorResult(), because that would abort the script, not intentional in this case
308                  return successResult();                  return successResult();
# Line 184  namespace LinuxSampler { Line 348  namespace LinuxSampler {
348      {      {
349      }      }
350    
351      bool InstrumentScriptVMFunction_note_off::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_note_off::acceptsArgType(vmint iArg, ExprType_t type) const {
352          return type == INT_EXPR || type == INT_ARR_EXPR;          return type == INT_EXPR || type == INT_ARR_EXPR;
353      }      }
354    
355        void InstrumentScriptVMFunction_note_off::checkArgs(VMFnArgs* args,
356                                                            std::function<void(String)> err,
357                                                            std::function<void(String)> wrn)
358        {
359            // super class checks
360            Super::checkArgs(args, err, wrn);
361    
362            // own checks ...
363            if (args->argsCount() >= 2 && args->arg(1)->isConstExpr() && args->arg(1)->exprType() == INT_EXPR) {
364                vmint velocity = args->arg(1)->asInt()->evalInt();
365                if (velocity < 0 || velocity > 127) {
366                    err("MIDI velocity value for argument 2 must be between 0..127");
367                    return;
368                }
369            }
370        }
371    
372      VMFnResult* InstrumentScriptVMFunction_note_off::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_note_off::exec(VMFnArgs* args) {
373          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
374              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
375    
376          int velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127;          vmint velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127;
377          if (velocity < 0 || velocity > 127) {          if (velocity < 0 || velocity > 127) {
378              errMsg("note_off(): argument 2 is an invalid velocity value");              errMsg("note_off(): argument 2 is an invalid velocity value");
379              return errorResult();              return errorResult();
# Line 223  namespace LinuxSampler { Line 404  namespace LinuxSampler {
404              pEngineChannel->ScheduleEventMicroSec(&e, 0);              pEngineChannel->ScheduleEventMicroSec(&e, 0);
405          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
406              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
407              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
408                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
409                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
410    
# Line 252  namespace LinuxSampler { Line 433  namespace LinuxSampler {
433      {      {
434      }      }
435    
436        void InstrumentScriptVMFunction_set_event_mark::checkArgs(VMFnArgs* args,
437                                                                  std::function<void(String)> err,
438                                                                  std::function<void(String)> wrn)
439        {
440            // super class checks
441            Super::checkArgs(args, err, wrn);
442    
443            // own checks ...
444            if (args->argsCount() >= 2 && args->arg(1)->isConstExpr()) {
445                const vmint groupID = args->arg(1)->asInt()->evalInt();
446                if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
447                    err("Argument 2 value is an invalid group id.");
448                    return;
449                }
450            }
451        }
452    
453      VMFnResult* InstrumentScriptVMFunction_set_event_mark::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_set_event_mark::exec(VMFnArgs* args) {
454          const ScriptID id = args->arg(0)->asInt()->evalInt();          const ScriptID id = args->arg(0)->asInt()->evalInt();
455          const int groupID = args->arg(1)->asInt()->evalInt();          const vmint groupID = args->arg(1)->asInt()->evalInt();
456    
457          if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {          if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
458              errMsg("set_event_mark(): argument 2 is an invalid group id");              errMsg("set_event_mark(): argument 2 is an invalid group id");
# Line 290  namespace LinuxSampler { Line 488  namespace LinuxSampler {
488      {      {
489      }      }
490    
491        void InstrumentScriptVMFunction_delete_event_mark::checkArgs(VMFnArgs* args,
492                                                                     std::function<void(String)> err,
493                                                                     std::function<void(String)> wrn)
494        {
495            // super class checks
496            Super::checkArgs(args, err, wrn);
497    
498            // own checks ...
499            if (args->argsCount() >= 2 && args->arg(1)->isConstExpr()) {
500                const vmint groupID = args->arg(1)->asInt()->evalInt();
501                if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
502                    err("Argument 2 value is an invalid group id.");
503                    return;
504                }
505            }
506        }
507    
508      VMFnResult* InstrumentScriptVMFunction_delete_event_mark::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_delete_event_mark::exec(VMFnArgs* args) {
509          const ScriptID id = args->arg(0)->asInt()->evalInt();          const ScriptID id = args->arg(0)->asInt()->evalInt();
510          const int groupID = args->arg(1)->asInt()->evalInt();          const vmint groupID = args->arg(1)->asInt()->evalInt();
511    
512          if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {          if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
513              errMsg("delete_event_mark(): argument 2 is an invalid group id");              errMsg("delete_event_mark(): argument 2 is an invalid group id");
# Line 314  namespace LinuxSampler { Line 529  namespace LinuxSampler {
529      {      {
530      }      }
531    
532      int InstrumentScriptVMFunction_by_marks::Result::arraySize() const {      vmint InstrumentScriptVMFunction_by_marks::Result::arraySize() const {
533          return eventGroup->size();          return eventGroup->size();
534      }      }
535    
536      int InstrumentScriptVMFunction_by_marks::Result::evalIntElement(uint i) {      vmint InstrumentScriptVMFunction_by_marks::Result::evalIntElement(vmuint i) {
537          return (*eventGroup)[i];          return (*eventGroup)[i];
538      }      }
539    
# Line 334  namespace LinuxSampler { Line 549  namespace LinuxSampler {
549          return &m_result;          return &m_result;
550      }      }
551    
552        void InstrumentScriptVMFunction_by_marks::checkArgs(VMFnArgs* args,
553                                                            std::function<void(String)> err,
554                                                            std::function<void(String)> wrn)
555        {
556            // super class checks
557            Super::checkArgs(args, err, wrn);
558    
559            // own checks ...
560            if (args->arg(0)->isConstExpr()) {
561                const vmint groupID = args->arg(0)->asInt()->evalInt();
562                if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
563                    err("Argument value is an invalid group id.");
564                    return;
565                }
566            }
567        }
568    
569      VMFnResult* InstrumentScriptVMFunction_by_marks::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_by_marks::exec(VMFnArgs* args) {
570          int groupID = args->arg(0)->asInt()->evalInt();          vmint groupID = args->arg(0)->asInt()->evalInt();
571    
572          if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {          if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
573              errMsg("by_marks(): argument is an invalid group id");              errMsg("by_marks(): argument is an invalid group id");
# Line 355  namespace LinuxSampler { Line 587  namespace LinuxSampler {
587      {      {
588      }      }
589    
590      bool InstrumentScriptVMFunction_change_vol::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_change_vol::acceptsArgType(vmint iArg, ExprType_t type) const {
591          if (iArg == 0)          if (iArg == 0)
592              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
593            else if (iArg == 1)
594                return type == INT_EXPR || type == REAL_EXPR;
595          else          else
596              return type == INT_EXPR;              return type == INT_EXPR;
597      }      }
598    
599        bool InstrumentScriptVMFunction_change_vol::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
600            if (iArg == 1)
601                return type == VM_NO_UNIT || type == VM_BEL;
602            else
603                return type == VM_NO_UNIT;
604        }
605    
606        bool InstrumentScriptVMFunction_change_vol::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
607            return iArg == 1 && type == VM_BEL; // only allow metric prefix(es) if 'Bel' is used as unit type
608        }
609    
610        bool InstrumentScriptVMFunction_change_vol::acceptsArgFinal(vmint iArg) const {
611            return iArg == 1;
612        }
613    
614      VMFnResult* InstrumentScriptVMFunction_change_vol::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_vol::exec(VMFnArgs* args) {
615          int volume = args->arg(1)->asInt()->evalInt(); // volume change in milli dB          StdUnit_t unit = args->arg(1)->asNumber()->unitType();
616            vmint volume =
617                (unit) ?
618                    args->arg(1)->asNumber()->evalCastInt(VM_MILLI,VM_DECI) :
619                    args->arg(1)->asNumber()->evalCastInt(); // volume change in milli dB
620            bool isFinal = args->arg(1)->asNumber()->isFinal();
621          bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;          bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
622          const float fVolumeLin = RTMath::DecibelToLinRatio(float(volume) / 1000.f);          const float fVolumeLin = RTMath::DecibelToLinRatio(float(volume) / 1000.f);
623    
# Line 385  namespace LinuxSampler { Line 639  namespace LinuxSampler {
639              if (!pNote) return successResult();              if (!pNote) return successResult();
640    
641              // if change_vol() was called immediately after note was triggered              // if change_vol() was called immediately after note was triggered
642              // then immediately apply the volume to note object              // then immediately apply the volume to note object, but only if
643              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              // change_vol_time() has not been called before
644                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
645                    pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S)
646                {
647                  if (relative)                  if (relative)
648                      pNote->Override.Volume *= fVolumeLin;                      pNote->Override.Volume.Value *= fVolumeLin;
649                  else                  else
650                      pNote->Override.Volume = fVolumeLin;                      pNote->Override.Volume.Value = fVolumeLin;
651                    pNote->Override.Volume.Final = isFinal;
652              } else { // otherwise schedule the volume change ...              } else { // otherwise schedule the volume change ...
653                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
654                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 398  namespace LinuxSampler { Line 656  namespace LinuxSampler {
656                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
657                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume;                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
658                  e.Param.NoteSynthParam.Delta    = fVolumeLin;                  e.Param.NoteSynthParam.Delta    = fVolumeLin;
659                  e.Param.NoteSynthParam.Relative = relative;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
660                        isFinal, relative, unit
661                    );
662                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
663              }              }
664          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
665              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
666              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
667                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
668                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
669    
# Line 412  namespace LinuxSampler { Line 671  namespace LinuxSampler {
671                  if (!pNote) continue;                  if (!pNote) continue;
672    
673                  // if change_vol() was called immediately after note was triggered                  // if change_vol() was called immediately after note was triggered
674                  // then immediately apply the volume to Note object                  // then immediately apply the volume to Note object, but only if
675                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  // change_vol_time() has not been called before
676                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
677                        pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S)
678                    {
679                      if (relative)                      if (relative)
680                          pNote->Override.Volume *= fVolumeLin;                          pNote->Override.Volume.Value *= fVolumeLin;
681                      else                      else
682                          pNote->Override.Volume = fVolumeLin;                          pNote->Override.Volume.Value = fVolumeLin;
683                        pNote->Override.Volume.Final = isFinal;
684                  } else { // otherwise schedule the volume change ...                  } else { // otherwise schedule the volume change ...
685                      Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                      Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
686                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 425  namespace LinuxSampler { Line 688  namespace LinuxSampler {
688                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
689                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume;                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
690                      e.Param.NoteSynthParam.Delta    = fVolumeLin;                      e.Param.NoteSynthParam.Delta    = fVolumeLin;
691                      e.Param.NoteSynthParam.Relative = relative;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
692                            isFinal, relative, unit
693                        );
694                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
695                  }                  }
696              }              }
# Line 442  namespace LinuxSampler { Line 706  namespace LinuxSampler {
706      {      {
707      }      }
708    
709      bool InstrumentScriptVMFunction_change_tune::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_change_tune::acceptsArgType(vmint iArg, ExprType_t type) const {
710          if (iArg == 0)          if (iArg == 0)
711              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
712            else if (iArg == 1)
713                return type == INT_EXPR || type == REAL_EXPR;
714          else          else
715              return type == INT_EXPR;              return type == INT_EXPR;
716      }      }
717    
718        bool InstrumentScriptVMFunction_change_tune::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
719            return iArg == 1;
720        }
721    
722        bool InstrumentScriptVMFunction_change_tune::acceptsArgFinal(vmint iArg) const {
723            return iArg == 1;
724        }
725    
726      VMFnResult* InstrumentScriptVMFunction_change_tune::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_tune::exec(VMFnArgs* args) {
727          int tune = args->arg(1)->asInt()->evalInt(); // tuning change in milli cents          vmint tune =
728                (args->arg(1)->asNumber()->hasUnitFactorNow())
729                    ? args->arg(1)->asNumber()->evalCastInt(VM_MILLI,VM_CENTI)
730                    : args->arg(1)->asNumber()->evalCastInt(); // tuning change in milli cents
731            bool isFinal = args->arg(1)->asNumber()->isFinal();
732            StdUnit_t unit = args->arg(1)->asNumber()->unitType();
733          bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;          bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
734          const float fFreqRatio = RTMath::CentsToFreqRatioUnlimited(float(tune) / 1000.f);          const float fFreqRatio = RTMath::CentsToFreqRatioUnlimited(float(tune) / 1000.f);
735    
# Line 472  namespace LinuxSampler { Line 751  namespace LinuxSampler {
751              if (!pNote) return successResult();              if (!pNote) return successResult();
752    
753              // if change_tune() was called immediately after note was triggered              // if change_tune() was called immediately after note was triggered
754              // then immediately apply the tuning to Note object              // then immediately apply the tuning to Note object, but only if
755              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              // change_tune_time() has not been called before
756                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
757                    pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S)
758                {
759                  if (relative)                  if (relative)
760                      pNote->Override.Pitch *= fFreqRatio;                      pNote->Override.Pitch.Value *= fFreqRatio;
761                  else                  else
762                      pNote->Override.Pitch = fFreqRatio;                      pNote->Override.Pitch.Value = fFreqRatio;
763                    pNote->Override.Pitch.Final = isFinal;
764              } else { // otherwise schedule tuning change ...              } else { // otherwise schedule tuning change ...
765                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
766                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 485  namespace LinuxSampler { Line 768  namespace LinuxSampler {
768                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
769                  e.Param.NoteSynthParam.Type     = Event::synth_param_pitch;                  e.Param.NoteSynthParam.Type     = Event::synth_param_pitch;
770                  e.Param.NoteSynthParam.Delta    = fFreqRatio;                  e.Param.NoteSynthParam.Delta    = fFreqRatio;
771                  e.Param.NoteSynthParam.Relative = relative;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
772                        isFinal, relative, unit
773                    );
774                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
775              }              }
776          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
777              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
778              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
779                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
780                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
781    
# Line 499  namespace LinuxSampler { Line 783  namespace LinuxSampler {
783                  if (!pNote) continue;                  if (!pNote) continue;
784    
785                  // if change_tune() was called immediately after note was triggered                  // if change_tune() was called immediately after note was triggered
786                  // then immediately apply the tuning to Note object                  // then immediately apply the tuning to Note object, but only if
787                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  // change_tune_time() has not been called before
788                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
789                        pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S)
790                    {
791                      if (relative)                      if (relative)
792                          pNote->Override.Pitch *= fFreqRatio;                          pNote->Override.Pitch.Value *= fFreqRatio;
793                      else                      else
794                          pNote->Override.Pitch = fFreqRatio;                          pNote->Override.Pitch.Value = fFreqRatio;
795                        pNote->Override.Pitch.Final = isFinal;
796                  } else { // otherwise schedule tuning change ...                  } else { // otherwise schedule tuning change ...
797                      Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                      Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
798                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 512  namespace LinuxSampler { Line 800  namespace LinuxSampler {
800                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
801                      e.Param.NoteSynthParam.Type     = Event::synth_param_pitch;                      e.Param.NoteSynthParam.Type     = Event::synth_param_pitch;
802                      e.Param.NoteSynthParam.Delta    = fFreqRatio;                      e.Param.NoteSynthParam.Delta    = fFreqRatio;
803                      e.Param.NoteSynthParam.Relative = relative;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
804                            isFinal, relative, unit
805                        );
806                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
807                  }                  }
808              }              }
# Line 529  namespace LinuxSampler { Line 818  namespace LinuxSampler {
818      {      {
819      }      }
820    
821      bool InstrumentScriptVMFunction_change_pan::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_change_pan::acceptsArgType(vmint iArg, ExprType_t type) const {
822          if (iArg == 0)          if (iArg == 0)
823              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
824          else          else
825              return type == INT_EXPR;              return type == INT_EXPR;
826      }      }
827    
828        bool InstrumentScriptVMFunction_change_pan::acceptsArgFinal(vmint iArg) const {
829            return iArg == 1;
830        }
831    
832      VMFnResult* InstrumentScriptVMFunction_change_pan::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_pan::exec(VMFnArgs* args) {
833          int pan = args->arg(1)->asInt()->evalInt();          vmint pan    = args->arg(1)->asInt()->evalInt();
834            bool isFinal = args->arg(1)->asInt()->isFinal();
835          bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;          bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
836    
837          if (pan > 1000) {          if (pan > 1000) {
# Line 570  namespace LinuxSampler { Line 864  namespace LinuxSampler {
864              // then immediately apply the panning to Note object              // then immediately apply the panning to Note object
865              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
866                  if (relative) {                  if (relative) {
867                      pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources);                      pNote->Override.Pan.Value = RTMath::RelativeSummedAvg(pNote->Override.Pan.Value, fPan, ++pNote->Override.Pan.Sources);
868                  } else {                  } else {
869                      pNote->Override.Pan = fPan;                      pNote->Override.Pan.Value = fPan;
870                      pNote->Override.PanSources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set                      pNote->Override.Pan.Sources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set
871                  }                  }
872                    pNote->Override.Pan.Final = isFinal;
873              } else { // otherwise schedule panning change ...              } else { // otherwise schedule panning change ...
874                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
875                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 582  namespace LinuxSampler { Line 877  namespace LinuxSampler {
877                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
878                  e.Param.NoteSynthParam.Type     = Event::synth_param_pan;                  e.Param.NoteSynthParam.Type     = Event::synth_param_pan;
879                  e.Param.NoteSynthParam.Delta    = fPan;                  e.Param.NoteSynthParam.Delta    = fPan;
880                  e.Param.NoteSynthParam.Relative = relative;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
881                        isFinal, relative, false
882                    );
883                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
884              }              }
885          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
886              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
887              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
888                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
889                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
890    
# Line 599  namespace LinuxSampler { Line 895  namespace LinuxSampler {
895                  // then immediately apply the panning to Note object                  // then immediately apply the panning to Note object
896                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
897                      if (relative) {                      if (relative) {
898                          pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources);                          pNote->Override.Pan.Value = RTMath::RelativeSummedAvg(pNote->Override.Pan.Value, fPan, ++pNote->Override.Pan.Sources);
899                      } else {                      } else {
900                          pNote->Override.Pan = fPan;                          pNote->Override.Pan.Value = fPan;
901                          pNote->Override.PanSources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set                          pNote->Override.Pan.Sources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set
902                      }                      }
903                        pNote->Override.Pan.Final = isFinal;
904                  } else { // otherwise schedule panning change ...                  } else { // otherwise schedule panning change ...
905                      Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                      Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
906                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 611  namespace LinuxSampler { Line 908  namespace LinuxSampler {
908                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
909                      e.Param.NoteSynthParam.Type     = Event::synth_param_pan;                      e.Param.NoteSynthParam.Type     = Event::synth_param_pan;
910                      e.Param.NoteSynthParam.Delta    = fPan;                      e.Param.NoteSynthParam.Delta    = fPan;
911                      e.Param.NoteSynthParam.Relative = relative;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
912                            isFinal, relative, false
913                        );
914                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
915                  }                  }
916              }              }
# Line 622  namespace LinuxSampler { Line 920  namespace LinuxSampler {
920      }      }
921    
922      #define VM_FILTER_PAR_MAX_VALUE 1000000      #define VM_FILTER_PAR_MAX_VALUE 1000000
923        #define VM_FILTER_PAR_MAX_HZ 30000
924      #define VM_EG_PAR_MAX_VALUE 1000000      #define VM_EG_PAR_MAX_VALUE 1000000
925    
926      // change_cutoff() function      // change_cutoff() function
# Line 631  namespace LinuxSampler { Line 930  namespace LinuxSampler {
930      {      {
931      }      }
932    
933      bool InstrumentScriptVMFunction_change_cutoff::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_change_cutoff::acceptsArgType(vmint iArg, ExprType_t type) const {
934          if (iArg == 0)          if (iArg == 0)
935              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
936            else if (iArg == 1)
937                return type == INT_EXPR || type == REAL_EXPR;
938          else          else
939              return type == INT_EXPR;              return type == INT_EXPR;
940      }      }
941    
942        bool InstrumentScriptVMFunction_change_cutoff::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
943            if (iArg == 1)
944                return type == VM_NO_UNIT || type == VM_HERTZ;
945            else
946                return type == VM_NO_UNIT;
947        }
948    
949        bool InstrumentScriptVMFunction_change_cutoff::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
950            return iArg == 1 && type == VM_HERTZ; // only allow metric prefix(es) if 'Hz' is used as unit type
951        }
952    
953        bool InstrumentScriptVMFunction_change_cutoff::acceptsArgFinal(vmint iArg) const {
954            return iArg == 1;
955        }
956    
957        void InstrumentScriptVMFunction_change_cutoff::checkArgs(VMFnArgs* args,
958                                                                 std::function<void(String)> err,
959                                                                 std::function<void(String)> wrn)
960        {
961            // super class checks
962            Super::checkArgs(args, err, wrn);
963    
964            // own checks ...
965            if (args->argsCount() >= 2) {
966                VMNumberExpr* argCutoff = args->arg(1)->asNumber();
967                if (argCutoff->unitType() && !argCutoff->isFinal()) {
968                    wrn("Argument 2 implies 'final' value when using Hz as unit for cutoff frequency.");
969                }
970            }
971        }
972    
973      VMFnResult* InstrumentScriptVMFunction_change_cutoff::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_cutoff::exec(VMFnArgs* args) {
974          int cutoff = args->arg(1)->asInt()->evalInt();          const StdUnit_t unit = args->arg(1)->asNumber()->unitType();
975          if (cutoff > VM_FILTER_PAR_MAX_VALUE) {          vmint cutoff =
976              wrnMsg("change_cutoff(): argument 2 may not be larger than 1000000");              (unit) ?
977              cutoff = VM_FILTER_PAR_MAX_VALUE;                  args->arg(1)->asNumber()->evalCastInt(VM_NO_PREFIX) :
978                    args->arg(1)->asNumber()->evalCastInt();
979            const bool isFinal =
980                (unit) ?
981                    true : // imply 'final' value if unit type is used
982                    args->arg(1)->asNumber()->isFinal();
983            // note: intentionally not checking against a max. value here if no unit!
984            // (to allow i.e. passing 2000000 for doubling cutoff frequency)
985            if (unit && cutoff > VM_FILTER_PAR_MAX_HZ) {
986                wrnMsg("change_cutoff(): argument 2 may not be larger than " strfy(VM_FILTER_PAR_MAX_HZ) " Hz");
987                cutoff = VM_FILTER_PAR_MAX_HZ;
988          } else if (cutoff < 0) {          } else if (cutoff < 0) {
989              wrnMsg("change_cutoff(): argument 2 may not be negative");              wrnMsg("change_cutoff(): argument 2 may not be negative");
990              cutoff = 0;              cutoff = 0;
991          }          }
992          const float fCutoff = float(cutoff) / float(VM_FILTER_PAR_MAX_VALUE);          const float fCutoff =
993                (unit) ? cutoff : float(cutoff) / float(VM_FILTER_PAR_MAX_VALUE);
994    
995          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
996              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 669  namespace LinuxSampler { Line 1012  namespace LinuxSampler {
1012              // if change_cutoff() was called immediately after note was triggered              // if change_cutoff() was called immediately after note was triggered
1013              // then immediately apply cutoff to Note object              // then immediately apply cutoff to Note object
1014              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1015                  pNote->Override.Cutoff = fCutoff;                  pNote->Override.Cutoff.Value = fCutoff;
1016                    pNote->Override.Cutoff.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1017              } else { // otherwise schedule cutoff change ...              } else { // otherwise schedule cutoff change ...
1018                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1019                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 677  namespace LinuxSampler { Line 1021  namespace LinuxSampler {
1021                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
1022                  e.Param.NoteSynthParam.Type     = Event::synth_param_cutoff;                  e.Param.NoteSynthParam.Type     = Event::synth_param_cutoff;
1023                  e.Param.NoteSynthParam.Delta    = fCutoff;                  e.Param.NoteSynthParam.Delta    = fCutoff;
1024                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1025                        isFinal, false, unit
1026                    );
1027                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
1028              }              }
1029          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1030              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1031              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
1032                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
1033                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
1034    
# Line 693  namespace LinuxSampler { Line 1038  namespace LinuxSampler {
1038                  // if change_cutoff() was called immediately after note was triggered                  // if change_cutoff() was called immediately after note was triggered
1039                  // then immediately apply cutoff to Note object                  // then immediately apply cutoff to Note object
1040                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1041                      pNote->Override.Cutoff = fCutoff;                      pNote->Override.Cutoff.Value = fCutoff;
1042                        pNote->Override.Cutoff.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1043                  } else { // otherwise schedule cutoff change ...                  } else { // otherwise schedule cutoff change ...
1044                      Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                      Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1045                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 701  namespace LinuxSampler { Line 1047  namespace LinuxSampler {
1047                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
1048                      e.Param.NoteSynthParam.Type     = Event::synth_param_cutoff;                      e.Param.NoteSynthParam.Type     = Event::synth_param_cutoff;
1049                      e.Param.NoteSynthParam.Delta    = fCutoff;                      e.Param.NoteSynthParam.Delta    = fCutoff;
1050                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1051                            isFinal, false, unit
1052                        );
1053                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
1054                  }                  }
1055              }              }
# Line 718  namespace LinuxSampler { Line 1065  namespace LinuxSampler {
1065      {      {
1066      }      }
1067    
1068      bool InstrumentScriptVMFunction_change_reso::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_change_reso::acceptsArgType(vmint iArg, ExprType_t type) const {
1069          if (iArg == 0)          if (iArg == 0)
1070              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
1071          else          else
1072              return type == INT_EXPR;              return type == INT_EXPR;
1073      }      }
1074    
1075        bool InstrumentScriptVMFunction_change_reso::acceptsArgFinal(vmint iArg) const {
1076            return iArg == 1;
1077        }
1078    
1079      VMFnResult* InstrumentScriptVMFunction_change_reso::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_reso::exec(VMFnArgs* args) {
1080          int resonance = args->arg(1)->asInt()->evalInt();          vmint resonance = args->arg(1)->asInt()->evalInt();
1081          if (resonance > VM_FILTER_PAR_MAX_VALUE) {          bool isFinal    = args->arg(1)->asInt()->isFinal();
1082              wrnMsg("change_reso(): argument 2 may not be larger than 1000000");          // note: intentionally not checking against a max. value here!
1083              resonance = VM_FILTER_PAR_MAX_VALUE;          // (to allow i.e. passing 2000000 for doubling the resonance)
1084          } else if (resonance < 0) {          if (resonance < 0) {
1085              wrnMsg("change_reso(): argument 2 may not be negative");              wrnMsg("change_reso(): argument 2 may not be negative");
1086              resonance = 0;              resonance = 0;
1087          }          }
# Line 756  namespace LinuxSampler { Line 1107  namespace LinuxSampler {
1107              // if change_reso() was called immediately after note was triggered              // if change_reso() was called immediately after note was triggered
1108              // then immediately apply resonance to Note object              // then immediately apply resonance to Note object
1109              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1110                  pNote->Override.Resonance = fResonance;                  pNote->Override.Resonance.Value = fResonance;
1111                    pNote->Override.Resonance.Final = isFinal;
1112              } else { // otherwise schedule resonance change ...              } else { // otherwise schedule resonance change ...
1113                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1114                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 764  namespace LinuxSampler { Line 1116  namespace LinuxSampler {
1116                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
1117                  e.Param.NoteSynthParam.Type     = Event::synth_param_resonance;                  e.Param.NoteSynthParam.Type     = Event::synth_param_resonance;
1118                  e.Param.NoteSynthParam.Delta    = fResonance;                  e.Param.NoteSynthParam.Delta    = fResonance;
1119                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1120                        isFinal, false, false
1121                    );
1122                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
1123              }              }
1124          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1125              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1126              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
1127                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
1128                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
1129    
# Line 780  namespace LinuxSampler { Line 1133  namespace LinuxSampler {
1133                  // if change_reso() was called immediately after note was triggered                  // if change_reso() was called immediately after note was triggered
1134                  // then immediately apply resonance to Note object                  // then immediately apply resonance to Note object
1135                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1136                      pNote->Override.Resonance = fResonance;                      pNote->Override.Resonance.Value = fResonance;
1137                        pNote->Override.Resonance.Final = isFinal;
1138                  } else { // otherwise schedule resonance change ...                  } else { // otherwise schedule resonance change ...
1139                      Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                      Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1140                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 788  namespace LinuxSampler { Line 1142  namespace LinuxSampler {
1142                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
1143                      e.Param.NoteSynthParam.Type     = Event::synth_param_resonance;                      e.Param.NoteSynthParam.Type     = Event::synth_param_resonance;
1144                      e.Param.NoteSynthParam.Delta    = fResonance;                      e.Param.NoteSynthParam.Delta    = fResonance;
1145                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1146                            isFinal, false, false
1147                        );
1148                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
1149                  }                  }
1150              }              }
# Line 805  namespace LinuxSampler { Line 1160  namespace LinuxSampler {
1160      {      {
1161      }      }
1162    
1163      bool InstrumentScriptVMFunction_change_attack::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_change_attack::acceptsArgType(vmint iArg, ExprType_t type) const {
1164          if (iArg == 0)          if (iArg == 0)
1165              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
1166          else          else
1167              return type == INT_EXPR;              return type == INT_EXPR || type == REAL_EXPR;
1168        }
1169    
1170        bool InstrumentScriptVMFunction_change_attack::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
1171            if (iArg == 1)
1172                return type == VM_NO_UNIT || type == VM_SECOND;
1173            else
1174                return type == VM_NO_UNIT;
1175        }
1176    
1177        bool InstrumentScriptVMFunction_change_attack::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
1178            return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
1179        }
1180    
1181        bool InstrumentScriptVMFunction_change_attack::acceptsArgFinal(vmint iArg) const {
1182            return iArg == 1;
1183        }
1184    
1185        void InstrumentScriptVMFunction_change_attack::checkArgs(VMFnArgs* args,
1186                                                                 std::function<void(String)> err,
1187                                                                 std::function<void(String)> wrn)
1188        {
1189            // super class checks
1190            Super::checkArgs(args, err, wrn);
1191    
1192            // own checks ...
1193            if (args->argsCount() >= 2) {
1194                VMNumberExpr* argTime = args->arg(1)->asNumber();
1195                if (argTime->unitType() && !argTime->isFinal()) {
1196                    wrn("Argument 2 implies 'final' value when using seconds as unit for attack time.");
1197                }
1198            }
1199      }      }
1200    
1201      VMFnResult* InstrumentScriptVMFunction_change_attack::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_attack::exec(VMFnArgs* args) {
1202          int attack = args->arg(1)->asInt()->evalInt();          const StdUnit_t unit = args->arg(1)->asNumber()->unitType();
1203          if (attack > VM_EG_PAR_MAX_VALUE) {          vmint attack =
1204              wrnMsg("change_attack(): argument 2 may not be larger than 1000000");              (unit) ?
1205              attack = VM_EG_PAR_MAX_VALUE;                  args->arg(1)->asNumber()->evalCastInt(VM_MICRO) :
1206          } else if (attack < 0) {                  args->arg(1)->asNumber()->evalCastInt();
1207            const bool isFinal =
1208                (unit) ?
1209                    true : // imply 'final' value if unit type is used
1210                    args->arg(1)->asNumber()->isFinal();
1211            // note: intentionally not checking against a max. value here!
1212            // (to allow i.e. passing 2000000 for doubling the attack time)
1213            if (attack < 0) {
1214              wrnMsg("change_attack(): argument 2 may not be negative");              wrnMsg("change_attack(): argument 2 may not be negative");
1215              attack = 0;              attack = 0;
1216          }          }
1217          const float fAttack = float(attack) / float(VM_EG_PAR_MAX_VALUE);          const float fAttack =
1218                (unit) ? attack : float(attack) / float(VM_EG_PAR_MAX_VALUE);
1219    
1220          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
1221              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 843  namespace LinuxSampler { Line 1237  namespace LinuxSampler {
1237              // if change_attack() was called immediately after note was triggered              // if change_attack() was called immediately after note was triggered
1238              // then immediately apply attack to Note object              // then immediately apply attack to Note object
1239              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1240                  pNote->Override.Attack = fAttack;                  pNote->Override.Attack.Value = fAttack;
1241                    pNote->Override.Attack.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1242              } else { // otherwise schedule attack change ...              } else { // otherwise schedule attack change ...
1243                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1244                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 851  namespace LinuxSampler { Line 1246  namespace LinuxSampler {
1246                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
1247                  e.Param.NoteSynthParam.Type     = Event::synth_param_attack;                  e.Param.NoteSynthParam.Type     = Event::synth_param_attack;
1248                  e.Param.NoteSynthParam.Delta    = fAttack;                  e.Param.NoteSynthParam.Delta    = fAttack;
1249                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1250                        isFinal, false, unit
1251                    );
1252                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
1253              }              }
1254          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1255              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1256              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
1257                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
1258                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
1259    
# Line 867  namespace LinuxSampler { Line 1263  namespace LinuxSampler {
1263                  // if change_attack() was called immediately after note was triggered                  // if change_attack() was called immediately after note was triggered
1264                  // then immediately apply attack to Note object                  // then immediately apply attack to Note object
1265                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1266                      pNote->Override.Attack = fAttack;                      pNote->Override.Attack.Value = fAttack;
1267                        pNote->Override.Attack.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1268                  } else { // otherwise schedule attack change ...                  } else { // otherwise schedule attack change ...
1269                      Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                      Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1270                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 875  namespace LinuxSampler { Line 1272  namespace LinuxSampler {
1272                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
1273                      e.Param.NoteSynthParam.Type     = Event::synth_param_attack;                      e.Param.NoteSynthParam.Type     = Event::synth_param_attack;
1274                      e.Param.NoteSynthParam.Delta    = fAttack;                      e.Param.NoteSynthParam.Delta    = fAttack;
1275                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1276                            isFinal, false, unit
1277                        );
1278                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
1279                  }                  }
1280              }              }
# Line 892  namespace LinuxSampler { Line 1290  namespace LinuxSampler {
1290      {      {
1291      }      }
1292    
1293      bool InstrumentScriptVMFunction_change_decay::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_change_decay::acceptsArgType(vmint iArg, ExprType_t type) const {
1294          if (iArg == 0)          if (iArg == 0)
1295              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
1296          else          else
1297              return type == INT_EXPR;              return type == INT_EXPR || type == REAL_EXPR;
1298        }
1299    
1300        bool InstrumentScriptVMFunction_change_decay::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
1301            if (iArg == 1)
1302                return type == VM_NO_UNIT || type == VM_SECOND;
1303            else
1304                return type == VM_NO_UNIT;
1305        }
1306    
1307        bool InstrumentScriptVMFunction_change_decay::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
1308            return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
1309        }
1310    
1311        bool InstrumentScriptVMFunction_change_decay::acceptsArgFinal(vmint iArg) const {
1312            return iArg == 1;
1313        }
1314    
1315        void InstrumentScriptVMFunction_change_decay::checkArgs(VMFnArgs* args,
1316                                                                std::function<void(String)> err,
1317                                                                std::function<void(String)> wrn)
1318        {
1319            // super class checks
1320            Super::checkArgs(args, err, wrn);
1321    
1322            // own checks ...
1323            if (args->argsCount() >= 2) {
1324                VMNumberExpr* argTime = args->arg(1)->asNumber();
1325                if (argTime->unitType() && !argTime->isFinal()) {
1326                    wrn("Argument 2 implies 'final' value when using seconds as unit for decay time.");
1327                }
1328            }
1329      }      }
1330    
1331      VMFnResult* InstrumentScriptVMFunction_change_decay::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_decay::exec(VMFnArgs* args) {
1332          int decay = args->arg(1)->asInt()->evalInt();          const StdUnit_t unit = args->arg(1)->asNumber()->unitType();
1333          if (decay > VM_EG_PAR_MAX_VALUE) {          vmint decay =
1334              wrnMsg("change_decay(): argument 2 may not be larger than 1000000");              (unit) ?
1335              decay = VM_EG_PAR_MAX_VALUE;                  args->arg(1)->asNumber()->evalCastInt(VM_MICRO) :
1336          } else if (decay < 0) {                  args->arg(1)->asNumber()->evalCastInt();
1337            const bool isFinal =
1338                (unit) ?
1339                    true : // imply 'final' value if unit type is used
1340                    args->arg(1)->asNumber()->isFinal();
1341            // note: intentionally not checking against a max. value here!
1342            // (to allow i.e. passing 2000000 for doubling the decay time)
1343            if (decay < 0) {
1344              wrnMsg("change_decay(): argument 2 may not be negative");              wrnMsg("change_decay(): argument 2 may not be negative");
1345              decay = 0;              decay = 0;
1346          }          }
1347          const float fDecay = float(decay) / float(VM_EG_PAR_MAX_VALUE);          const float fDecay =
1348                (unit) ? decay : float(decay) / float(VM_EG_PAR_MAX_VALUE);
1349    
1350          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
1351              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 930  namespace LinuxSampler { Line 1367  namespace LinuxSampler {
1367              // if change_decay() was called immediately after note was triggered              // if change_decay() was called immediately after note was triggered
1368              // then immediately apply decay to Note object              // then immediately apply decay to Note object
1369              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1370                  pNote->Override.Decay = fDecay;                  pNote->Override.Decay.Value = fDecay;
1371                    pNote->Override.Decay.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1372              } else { // otherwise schedule decay change ...              } else { // otherwise schedule decay change ...
1373                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1374                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 938  namespace LinuxSampler { Line 1376  namespace LinuxSampler {
1376                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
1377                  e.Param.NoteSynthParam.Type     = Event::synth_param_decay;                  e.Param.NoteSynthParam.Type     = Event::synth_param_decay;
1378                  e.Param.NoteSynthParam.Delta    = fDecay;                  e.Param.NoteSynthParam.Delta    = fDecay;
1379                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1380                        isFinal, false, unit
1381                    );
1382                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
1383              }              }
1384          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1385              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1386              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
1387                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
1388                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
1389    
# Line 954  namespace LinuxSampler { Line 1393  namespace LinuxSampler {
1393                  // if change_decay() was called immediately after note was triggered                  // if change_decay() was called immediately after note was triggered
1394                  // then immediately apply decay to Note object                  // then immediately apply decay to Note object
1395                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1396                      pNote->Override.Decay = fDecay;                      pNote->Override.Decay.Value = fDecay;
1397                        pNote->Override.Decay.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1398                  } else { // otherwise schedule decay change ...                  } else { // otherwise schedule decay change ...
1399                      Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                      Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1400                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 962  namespace LinuxSampler { Line 1402  namespace LinuxSampler {
1402                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
1403                      e.Param.NoteSynthParam.Type     = Event::synth_param_decay;                      e.Param.NoteSynthParam.Type     = Event::synth_param_decay;
1404                      e.Param.NoteSynthParam.Delta    = fDecay;                      e.Param.NoteSynthParam.Delta    = fDecay;
1405                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1406                            isFinal, false, unit
1407                        );
1408                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
1409                  }                  }
1410              }              }
# Line 979  namespace LinuxSampler { Line 1420  namespace LinuxSampler {
1420      {      {
1421      }      }
1422    
1423      bool InstrumentScriptVMFunction_change_release::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_change_release::acceptsArgType(vmint iArg, ExprType_t type) const {
1424          if (iArg == 0)          if (iArg == 0)
1425              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
1426          else          else
1427              return type == INT_EXPR;              return type == INT_EXPR || type == REAL_EXPR;
1428        }
1429    
1430        bool InstrumentScriptVMFunction_change_release::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
1431            if (iArg == 1)
1432                return type == VM_NO_UNIT || type == VM_SECOND;
1433            else
1434                return type == VM_NO_UNIT;
1435        }
1436    
1437        bool InstrumentScriptVMFunction_change_release::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
1438            return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
1439        }
1440    
1441        bool InstrumentScriptVMFunction_change_release::acceptsArgFinal(vmint iArg) const {
1442            return iArg == 1;
1443        }
1444    
1445        void InstrumentScriptVMFunction_change_release::checkArgs(VMFnArgs* args,
1446                                                                  std::function<void(String)> err,
1447                                                                  std::function<void(String)> wrn)
1448        {
1449            // super class checks
1450            Super::checkArgs(args, err, wrn);
1451    
1452            // own checks ...
1453            if (args->argsCount() >= 2) {
1454                VMNumberExpr* argTime = args->arg(1)->asNumber();
1455                if (argTime->unitType() && !argTime->isFinal()) {
1456                    wrn("Argument 2 implies 'final' value when using seconds as unit for release time.");
1457                }
1458            }
1459      }      }
1460    
1461      VMFnResult* InstrumentScriptVMFunction_change_release::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_release::exec(VMFnArgs* args) {
1462          int release = args->arg(1)->asInt()->evalInt();          const StdUnit_t unit = args->arg(1)->asNumber()->unitType();
1463          if (release > VM_EG_PAR_MAX_VALUE) {          vmint release =
1464              wrnMsg("change_release(): argument 2 may not be larger than 1000000");              (unit) ?
1465              release = VM_EG_PAR_MAX_VALUE;                  args->arg(1)->asNumber()->evalCastInt(VM_MICRO) :
1466          } else if (release < 0) {                  args->arg(1)->asNumber()->evalCastInt();
1467            const bool isFinal =
1468                (unit) ?
1469                    true : // imply 'final' value if unit type is used
1470                    args->arg(1)->asNumber()->isFinal();
1471            // note: intentionally not checking against a max. value here!
1472            // (to allow i.e. passing 2000000 for doubling the release time)
1473            if (release < 0) {
1474              wrnMsg("change_release(): argument 2 may not be negative");              wrnMsg("change_release(): argument 2 may not be negative");
1475              release = 0;              release = 0;
1476          }          }
1477          const float fRelease = float(release) / float(VM_EG_PAR_MAX_VALUE);          const float fRelease =
1478                (unit) ? release : float(release) / float(VM_EG_PAR_MAX_VALUE);
1479    
1480          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
1481              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 1017  namespace LinuxSampler { Line 1497  namespace LinuxSampler {
1497              // if change_release() was called immediately after note was triggered              // if change_release() was called immediately after note was triggered
1498              // then immediately apply relase to Note object              // then immediately apply relase to Note object
1499              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1500                  pNote->Override.Release = fRelease;                  pNote->Override.Release.Value = fRelease;
1501                    pNote->Override.Release.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1502              } else { // otherwise schedule release change ...              } else { // otherwise schedule release change ...
1503                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1504                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 1025  namespace LinuxSampler { Line 1506  namespace LinuxSampler {
1506                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
1507                  e.Param.NoteSynthParam.Type     = Event::synth_param_release;                  e.Param.NoteSynthParam.Type     = Event::synth_param_release;
1508                  e.Param.NoteSynthParam.Delta    = fRelease;                  e.Param.NoteSynthParam.Delta    = fRelease;
1509                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1510                        isFinal, false, unit
1511                    );
1512                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
1513              }              }
1514          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1515              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1516              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
1517                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
1518                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
1519    
# Line 1041  namespace LinuxSampler { Line 1523  namespace LinuxSampler {
1523                  // if change_release() was called immediately after note was triggered                  // if change_release() was called immediately after note was triggered
1524                  // then immediately apply relase to Note object                  // then immediately apply relase to Note object
1525                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1526                      pNote->Override.Release = fRelease;                      pNote->Override.Release.Value = fRelease;
1527                        pNote->Override.Release.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1528                  } else { // otherwise schedule release change ...                  } else { // otherwise schedule release change ...
1529                      Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                      Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1530                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 1049  namespace LinuxSampler { Line 1532  namespace LinuxSampler {
1532                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
1533                      e.Param.NoteSynthParam.Type     = Event::synth_param_release;                      e.Param.NoteSynthParam.Type     = Event::synth_param_release;
1534                      e.Param.NoteSynthParam.Delta    = fRelease;                      e.Param.NoteSynthParam.Delta    = fRelease;
1535                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1536                            isFinal, false, unit
1537                        );
1538                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
1539                  }                  }
1540              }              }
# Line 1059  namespace LinuxSampler { Line 1543  namespace LinuxSampler {
1543          return successResult();          return successResult();
1544      }      }
1545    
1546      bool VMChangeSynthParamFunction::acceptsArgType(int iArg, ExprType_t type) const {      // template for change_*() functions
1547    
1548        bool VMChangeSynthParamFunction::acceptsArgType(vmint iArg, ExprType_t type) const {
1549          if (iArg == 0)          if (iArg == 0)
1550              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
1551          else          else
1552              return type == INT_EXPR;              return type == INT_EXPR || (m_acceptReal && type == REAL_EXPR);
1553        }
1554    
1555        bool VMChangeSynthParamFunction::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
1556            if (iArg == 1)
1557                return type == VM_NO_UNIT || type == m_unit;
1558            else
1559                return type == VM_NO_UNIT;
1560        }
1561    
1562        bool VMChangeSynthParamFunction::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
1563            return m_acceptUnitPrefix && iArg == 1 && type == m_unit; // only allow metric prefix(es) if approprirate unit type is used (e.g. Hz)
1564        }
1565    
1566        bool VMChangeSynthParamFunction::acceptsArgFinal(vmint iArg) const {
1567            return (m_acceptFinal) ? (iArg == 1) : false;
1568        }
1569    
1570        inline static void setNoteParamScopeBy_FinalUnit(NoteBase::Param& param, const bool bFinal, const StdUnit_t unit) {
1571            param.Scope = NoteBase::scopeBy_FinalUnit(bFinal, unit);
1572        }
1573    
1574        inline static void setNoteParamScopeBy_FinalUnit(NoteBase::Norm& param, const bool bFinal, const StdUnit_t unit) {
1575            param.Final = bFinal;
1576        }
1577    
1578        inline static void setNoteParamScopeBy_FinalUnit(float& param, const bool bFinal, const StdUnit_t unit) {
1579            /* NOOP */
1580        }
1581    
1582        template<class T>
1583        inline static void setNoteParamValue(T& param, vmfloat value) {
1584            param.Value = value;
1585        }
1586    
1587        inline static void setNoteParamValue(float& param, vmfloat value) {
1588            param = value;
1589        }
1590    
1591        void VMChangeSynthParamFunction::checkArgs(VMFnArgs* args,
1592                                                   std::function<void(String)> err,
1593                                                   std::function<void(String)> wrn)
1594        {
1595            // super class checks
1596            Super::checkArgs(args, err, wrn);
1597    
1598            // own checks ...
1599            if (m_unit && m_unit != VM_BEL && args->argsCount() >= 2) {
1600                VMNumberExpr* arg = args->arg(1)->asNumber();
1601                if (arg && arg->unitType() && !arg->isFinal()) {
1602                    wrn("Argument 2 implies 'final' value when unit type " +
1603                        unitTypeStr(arg->unitType()) + " is used.");
1604                }
1605            }
1606      }      }
1607    
1608      // Arbitrarily chosen constant value symbolizing "no limit".      // Arbitrarily chosen constant value symbolizing "no limit".
1609      #define NO_LIMIT 1315916909      #define NO_LIMIT 1315916909
1610    
1611      template<float NoteBase::_Override::*T_noteParam, int T_synthParam,      template<class T_NoteParamType, T_NoteParamType NoteBase::_Override::*T_noteParam,
1612               bool T_isNormalizedParam, int T_maxValue, int T_minValue>               vmint T_synthParam,
1613      VMFnResult* VMChangeSynthParamFunction::execTemplate(VMFnArgs* args, const char* functionName) {               vmint T_minValueNorm, vmint T_maxValueNorm, bool T_normalizeNorm,
1614          int value = args->arg(1)->asInt()->evalInt();               vmint T_minValueUnit, vmint T_maxValueUnit,
1615          if (T_maxValue != NO_LIMIT && value > T_maxValue) {               MetricPrefix_t T_unitPrefix0, MetricPrefix_t ... T_unitPrefixN>
1616              wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValue));      VMFnResult* VMChangeSynthParamFunction::execTemplate(VMFnArgs* args, const char* functionName)
1617              value = T_maxValue;      {
1618          } else if (T_minValue != NO_LIMIT && value < T_minValue) {          const StdUnit_t unit = args->arg(1)->asNumber()->unitType();
1619              if (T_minValue == 0)          const bool isFinal =
1620                  wrnMsg(String(functionName) + "(): argument 2 may not be negative");              (m_unit && m_unit != VM_BEL && unit) ?
1621              else                  true : // imply 'final' value if unit type is used (except of 'Bel' which may be relative)
1622                  wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValue));                  args->arg(1)->asNumber()->isFinal();
1623              value = T_minValue;          vmint value =
1624          }              (m_acceptUnitPrefix && ((m_unit && unit) || (!m_unit && args->arg(1)->asNumber()->hasUnitFactorNow())))
1625          const float fValue = (T_isNormalizedParam) ?                  ? args->arg(1)->asNumber()->evalCastInt(T_unitPrefix0, T_unitPrefixN ...)
1626              float(value) / float(T_maxValue) : // convert to 0.0 .. 1.0 value range                  : args->arg(1)->asNumber()->evalCastInt();
1627              float(value) / 1000000.f; // assuming microseconds here, convert to seconds  
1628            // check if passed value is in allowed range
1629            if (unit && m_unit) {
1630                if (T_maxValueUnit != NO_LIMIT && value > T_maxValueUnit) {
1631                    wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValueUnit));
1632                    value = T_maxValueUnit;
1633                } else if (T_minValueUnit != NO_LIMIT && value < T_minValueUnit) {
1634                    if (T_minValueUnit == 0)
1635                        wrnMsg(String(functionName) + "(): argument 2 may not be negative");
1636                    else
1637                        wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValueUnit));
1638                    value = T_minValueUnit;
1639                }
1640            } else { // value was passed to this function without a unit ...
1641                if (T_maxValueNorm != NO_LIMIT && value > T_maxValueNorm) {
1642                    wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValueNorm));
1643                    value = T_maxValueNorm;
1644                } else if (T_minValueNorm != NO_LIMIT && value < T_minValueNorm) {
1645                    if (T_minValueNorm == 0)
1646                        wrnMsg(String(functionName) + "(): argument 2 may not be negative");
1647                    else
1648                        wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValueNorm));
1649                    value = T_minValueNorm;
1650                }
1651            }
1652    
1653            // convert passed argument value to engine internal expected value range (i.e. 0.0 .. 1.0)
1654            const float fValue =
1655                (unit && m_unit) ?
1656                    (unit == VM_BEL) ?
1657                        RTMath::DecibelToLinRatio(float(value) * float(T_unitPrefix0) /*i.e. mdB -> dB*/) :
1658                        float(value) * VMUnit::unitFactor(T_unitPrefix0, T_unitPrefixN ...) /*i.e. us -> s*/ :
1659                    (T_normalizeNorm) ?
1660                        float(value) / ((T_maxValueNorm != NO_LIMIT) ? float(T_maxValueNorm) : 1000000.f/* fallback: value range used for most */) :
1661                        float(value) /* as is */;
1662    
1663          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
1664              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 1108  namespace LinuxSampler { Line 1681  namespace LinuxSampler {
1681              // note was triggered then immediately apply the synth parameter              // note was triggered then immediately apply the synth parameter
1682              // change to Note object              // change to Note object
1683              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1684                  pNote->Override.*T_noteParam = fValue;                  setNoteParamValue(pNote->Override.*T_noteParam, fValue);
1685                    setNoteParamScopeBy_FinalUnit(
1686                        (pNote->Override.*T_noteParam),
1687                        isFinal, unit
1688                    );
1689              } else { // otherwise schedule this synth parameter change ...              } else { // otherwise schedule this synth parameter change ...
1690                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1691                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 1116  namespace LinuxSampler { Line 1693  namespace LinuxSampler {
1693                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
1694                  e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;                  e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1695                  e.Param.NoteSynthParam.Delta    = fValue;                  e.Param.NoteSynthParam.Delta    = fValue;
1696                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1697                        isFinal, false, unit
1698                    );
1699                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
1700              }              }
1701          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1702              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1703              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
1704                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
1705                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
1706    
# Line 1133  namespace LinuxSampler { Line 1711  namespace LinuxSampler {
1711                  // note was triggered then immediately apply the synth parameter                  // note was triggered then immediately apply the synth parameter
1712                  // change to Note object                  // change to Note object
1713                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1714                      pNote->Override.*T_noteParam = fValue;                      setNoteParamValue(pNote->Override.*T_noteParam, fValue);
1715                        setNoteParamScopeBy_FinalUnit(
1716                            (pNote->Override.*T_noteParam),
1717                            isFinal, unit
1718                        );
1719                  } else { // otherwise schedule this synth parameter change ...                  } else { // otherwise schedule this synth parameter change ...
1720                      Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                      Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1721                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 1141  namespace LinuxSampler { Line 1723  namespace LinuxSampler {
1723                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
1724                      e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;                      e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1725                      e.Param.NoteSynthParam.Delta    = fValue;                      e.Param.NoteSynthParam.Delta    = fValue;
1726                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1727                            isFinal, false, unit
1728                        );
1729                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
1730                  }                  }
1731              }              }
# Line 1151  namespace LinuxSampler { Line 1734  namespace LinuxSampler {
1734          return successResult();          return successResult();
1735      }      }
1736    
1737        // change_sustain() function
1738    
1739        VMFnResult* InstrumentScriptVMFunction_change_sustain::exec(VMFnArgs* args) {
1740            return VMChangeSynthParamFunction::execTemplate<
1741                        decltype(NoteBase::_Override::Sustain),
1742                        &NoteBase::_Override::Sustain,
1743                        Event::synth_param_sustain,
1744                        /* if value passed without unit */
1745                        0, NO_LIMIT, true,
1746                        /* if value passed WITH 'Bel' unit */
1747                        NO_LIMIT, NO_LIMIT, VM_MILLI, VM_DECI>( args, "change_sustain" );
1748        }
1749    
1750        // change_cutoff_attack() function
1751    
1752        VMFnResult* InstrumentScriptVMFunction_change_cutoff_attack::exec(VMFnArgs* args) {
1753            return VMChangeSynthParamFunction::execTemplate<
1754                        decltype(NoteBase::_Override::CutoffAttack),
1755                        &NoteBase::_Override::CutoffAttack,
1756                        Event::synth_param_cutoff_attack,
1757                        /* if value passed without unit */
1758                        0, NO_LIMIT, true,
1759                        /* if value passed with 'seconds' unit */
1760                        0, NO_LIMIT, VM_MICRO>( args, "change_cutoff_attack" );
1761        }
1762    
1763        // change_cutoff_decay() function
1764    
1765        VMFnResult* InstrumentScriptVMFunction_change_cutoff_decay::exec(VMFnArgs* args) {
1766            return VMChangeSynthParamFunction::execTemplate<
1767                        decltype(NoteBase::_Override::CutoffDecay),
1768                        &NoteBase::_Override::CutoffDecay,
1769                        Event::synth_param_cutoff_decay,
1770                        /* if value passed without unit */
1771                        0, NO_LIMIT, true,
1772                        /* if value passed with 'seconds' unit */
1773                        0, NO_LIMIT, VM_MICRO>( args, "change_cutoff_decay" );
1774        }
1775    
1776        // change_cutoff_sustain() function
1777    
1778        VMFnResult* InstrumentScriptVMFunction_change_cutoff_sustain::exec(VMFnArgs* args) {
1779            return VMChangeSynthParamFunction::execTemplate<
1780                        decltype(NoteBase::_Override::CutoffSustain),
1781                        &NoteBase::_Override::CutoffSustain,
1782                        Event::synth_param_cutoff_sustain,
1783                        /* if value passed without unit */
1784                        0, NO_LIMIT, true,
1785                        /* if value passed WITH 'Bel' unit */
1786                        NO_LIMIT, NO_LIMIT, VM_MILLI, VM_DECI>( args, "change_cutoff_sustain" );
1787        }
1788    
1789        // change_cutoff_release() function
1790    
1791        VMFnResult* InstrumentScriptVMFunction_change_cutoff_release::exec(VMFnArgs* args) {
1792            return VMChangeSynthParamFunction::execTemplate<
1793                        decltype(NoteBase::_Override::CutoffRelease),
1794                        &NoteBase::_Override::CutoffRelease,
1795                        Event::synth_param_cutoff_release,
1796                        /* if value passed without unit */
1797                        0, NO_LIMIT, true,
1798                        /* if value passed with 'seconds' unit */
1799                        0, NO_LIMIT, VM_MICRO>( args, "change_cutoff_release" );
1800        }
1801    
1802      // change_amp_lfo_depth() function      // change_amp_lfo_depth() function
1803    
1804      VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_depth::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_depth::exec(VMFnArgs* args) {
1805          return VMChangeSynthParamFunction::execTemplate<          return VMChangeSynthParamFunction::execTemplate<
1806                        decltype(NoteBase::_Override::AmpLFODepth),
1807                      &NoteBase::_Override::AmpLFODepth,                      &NoteBase::_Override::AmpLFODepth,
1808                      Event::synth_param_amp_lfo_depth,                      Event::synth_param_amp_lfo_depth,
1809                      true, 1000000, 0>( args, "change_amp_lfo_depth" );                      /* if value passed without unit */
1810                        0, NO_LIMIT, true,
1811                        /* not used (since this function does not accept unit) */
1812                        NO_LIMIT, NO_LIMIT, VM_NO_PREFIX>( args, "change_amp_lfo_depth" );
1813      }      }
1814    
1815      // change_amp_lfo_freq() function      // change_amp_lfo_freq() function
1816    
1817      VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_freq::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_freq::exec(VMFnArgs* args) {
1818          return VMChangeSynthParamFunction::execTemplate<          return VMChangeSynthParamFunction::execTemplate<
1819                        decltype(NoteBase::_Override::AmpLFOFreq),
1820                      &NoteBase::_Override::AmpLFOFreq,                      &NoteBase::_Override::AmpLFOFreq,
1821                      Event::synth_param_amp_lfo_freq,                      Event::synth_param_amp_lfo_freq,
1822                      true, 1000000, 0>( args, "change_amp_lfo_freq" );                      /* if value passed without unit */
1823                        0, NO_LIMIT, true,
1824                        /* if value passed with 'Hz' unit */
1825                        0, 30000, VM_NO_PREFIX>( args, "change_amp_lfo_freq" );
1826        }
1827    
1828        // change_cutoff_lfo_depth() function
1829    
1830        VMFnResult* InstrumentScriptVMFunction_change_cutoff_lfo_depth::exec(VMFnArgs* args) {
1831            return VMChangeSynthParamFunction::execTemplate<
1832                        decltype(NoteBase::_Override::CutoffLFODepth),
1833                        &NoteBase::_Override::CutoffLFODepth,
1834                        Event::synth_param_cutoff_lfo_depth,
1835                        /* if value passed without unit */
1836                        0, NO_LIMIT, true,
1837                        /* not used (since this function does not accept unit) */
1838                        NO_LIMIT, NO_LIMIT, VM_NO_PREFIX>( args, "change_cutoff_lfo_depth" );
1839        }
1840    
1841        // change_cutoff_lfo_freq() function
1842    
1843        VMFnResult* InstrumentScriptVMFunction_change_cutoff_lfo_freq::exec(VMFnArgs* args) {
1844            return VMChangeSynthParamFunction::execTemplate<
1845                        decltype(NoteBase::_Override::CutoffLFOFreq),
1846                        &NoteBase::_Override::CutoffLFOFreq,
1847                        Event::synth_param_cutoff_lfo_freq,
1848                        /* if value passed without unit */
1849                        0, NO_LIMIT, true,
1850                        /* if value passed with 'Hz' unit */
1851                        0, 30000, VM_NO_PREFIX>( args, "change_cutoff_lfo_freq" );
1852      }      }
1853    
1854      // change_pitch_lfo_depth() function      // change_pitch_lfo_depth() function
1855    
1856      VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_depth::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_depth::exec(VMFnArgs* args) {
1857          return VMChangeSynthParamFunction::execTemplate<          return VMChangeSynthParamFunction::execTemplate<
1858                        decltype(NoteBase::_Override::PitchLFODepth),
1859                      &NoteBase::_Override::PitchLFODepth,                      &NoteBase::_Override::PitchLFODepth,
1860                      Event::synth_param_pitch_lfo_depth,                      Event::synth_param_pitch_lfo_depth,
1861                      true, 1000000, 0>( args, "change_pitch_lfo_depth" );                      /* if value passed without unit */
1862                        0, NO_LIMIT, true,
1863                        /* not used (since this function does not accept unit) */
1864                        NO_LIMIT, NO_LIMIT, VM_NO_PREFIX>( args, "change_pitch_lfo_depth" );
1865      }      }
1866    
1867      // change_pitch_lfo_freq() function      // change_pitch_lfo_freq() function
1868    
1869      VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_freq::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_freq::exec(VMFnArgs* args) {
1870          return VMChangeSynthParamFunction::execTemplate<          return VMChangeSynthParamFunction::execTemplate<
1871                        decltype(NoteBase::_Override::PitchLFOFreq),
1872                      &NoteBase::_Override::PitchLFOFreq,                      &NoteBase::_Override::PitchLFOFreq,
1873                      Event::synth_param_pitch_lfo_freq,                      Event::synth_param_pitch_lfo_freq,
1874                      true, 1000000, 0>( args, "change_pitch_lfo_freq" );                      /* if value passed without unit */
1875                        0, NO_LIMIT, true,
1876                        /* if value passed with 'Hz' unit */
1877                        0, 30000, VM_NO_PREFIX>( args, "change_pitch_lfo_freq" );
1878      }      }
1879    
1880      // change_vol_time() function      // change_vol_time() function
1881    
1882      VMFnResult* InstrumentScriptVMFunction_change_vol_time::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_vol_time::exec(VMFnArgs* args) {
1883          return VMChangeSynthParamFunction::execTemplate<          return VMChangeSynthParamFunction::execTemplate<
1884                        decltype(NoteBase::_Override::VolumeTime),
1885                      &NoteBase::_Override::VolumeTime,                      &NoteBase::_Override::VolumeTime,
1886                      Event::synth_param_volume_time,                      Event::synth_param_volume_time,
1887                      false, NO_LIMIT, 0>( args, "change_vol_time" );                      /* if value passed without unit (implying 'us' unit) */
1888                        0, NO_LIMIT, true,
1889                        /* if value passed with 'seconds' unit */
1890                        0, NO_LIMIT, VM_MICRO>( args, "change_vol_time" );
1891      }      }
1892    
1893      // change_tune_time() function      // change_tune_time() function
1894    
1895      VMFnResult* InstrumentScriptVMFunction_change_tune_time::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_tune_time::exec(VMFnArgs* args) {
1896          return VMChangeSynthParamFunction::execTemplate<          return VMChangeSynthParamFunction::execTemplate<
1897                        decltype(NoteBase::_Override::PitchTime),
1898                      &NoteBase::_Override::PitchTime,                      &NoteBase::_Override::PitchTime,
1899                      Event::synth_param_pitch_time,                      Event::synth_param_pitch_time,
1900                      false, NO_LIMIT, 0>( args, "change_tune_time" );                      /* if value passed without unit (implying 'us' unit) */
1901                        0, NO_LIMIT, true,
1902                        /* if value passed with 'seconds' unit */
1903                        0, NO_LIMIT, VM_MICRO>( args, "change_tune_time" );
1904        }
1905    
1906        // change_pan_time() function
1907    
1908        VMFnResult* InstrumentScriptVMFunction_change_pan_time::exec(VMFnArgs* args) {
1909            return VMChangeSynthParamFunction::execTemplate<
1910                        decltype(NoteBase::_Override::PanTime),
1911                        &NoteBase::_Override::PanTime,
1912                        Event::synth_param_pan_time,
1913                        /* if value passed without unit (implying 'us' unit) */
1914                        0, NO_LIMIT, true,
1915                        /* if value passed with 'seconds' unit */
1916                        0, NO_LIMIT, VM_MICRO>( args, "change_pan_time" );
1917        }
1918    
1919        // template for change_*_curve() functions
1920    
1921        bool VMChangeFadeCurveFunction::acceptsArgType(vmint iArg, ExprType_t type) const {
1922            if (iArg == 0)
1923                return type == INT_EXPR || type == INT_ARR_EXPR;
1924            else
1925                return type == INT_EXPR;
1926        }
1927    
1928        template<fade_curve_t NoteBase::_Override::*T_noteParam, vmint T_synthParam>
1929        VMFnResult* VMChangeFadeCurveFunction::execTemplate(VMFnArgs* args, const char* functionName) {
1930            vmint value = args->arg(1)->asInt()->evalInt();
1931            switch (value) {
1932                case FADE_CURVE_LINEAR:
1933                case FADE_CURVE_EASE_IN_EASE_OUT:
1934                    break;
1935                default:
1936                    wrnMsg(String(functionName) + "(): invalid curve type passed as argument 2");
1937                    return successResult();
1938            }
1939    
1940            AbstractEngineChannel* pEngineChannel =
1941                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1942    
1943            if (args->arg(0)->exprType() == INT_EXPR) {
1944                const ScriptID id = args->arg(0)->asInt()->evalInt();
1945                if (!id) {
1946                    wrnMsg(String(functionName) + "(): note ID for argument 1 may not be zero");
1947                    return successResult();
1948                }
1949                if (!id.isNoteID()) {
1950                    wrnMsg(String(functionName) + "(): argument 1 is not a note ID");
1951                    return successResult();
1952                }
1953    
1954                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1955                if (!pNote) return successResult();
1956    
1957                // if this change_*_curve() script function was called immediately after
1958                // note was triggered then immediately apply the synth parameter
1959                // change to Note object
1960                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1961                    pNote->Override.*T_noteParam = (fade_curve_t) value;
1962                } else { // otherwise schedule this synth parameter change ...
1963                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1964                    e.Init(); // clear IDs
1965                    e.Type = Event::type_note_synth_param;
1966                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1967                    e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1968                    e.Param.NoteSynthParam.Delta    = value;
1969                    e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
1970    
1971                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
1972                }
1973            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1974                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1975                for (vmint i = 0; i < ids->arraySize(); ++i) {
1976                    const ScriptID id = ids->evalIntElement(i);
1977                    if (!id || !id.isNoteID()) continue;
1978    
1979                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1980                    if (!pNote) continue;
1981    
1982                    // if this change_*_curve() script function was called immediately after
1983                    // note was triggered then immediately apply the synth parameter
1984                    // change to Note object
1985                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1986                        pNote->Override.*T_noteParam = (fade_curve_t) value;
1987                    } else { // otherwise schedule this synth parameter change ...
1988                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1989                        e.Init(); // clear IDs
1990                        e.Type = Event::type_note_synth_param;
1991                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1992                        e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1993                        e.Param.NoteSynthParam.Delta    = value;
1994                        e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
1995    
1996                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
1997                    }
1998                }
1999            }
2000    
2001            return successResult();
2002        }
2003    
2004        // change_vol_curve() function
2005    
2006        VMFnResult* InstrumentScriptVMFunction_change_vol_curve::exec(VMFnArgs* args) {
2007            return VMChangeFadeCurveFunction::execTemplate<
2008                        &NoteBase::_Override::VolumeCurve,
2009                        Event::synth_param_volume_curve>( args, "change_vol_curve" );
2010        }
2011    
2012        // change_tune_curve() function
2013    
2014        VMFnResult* InstrumentScriptVMFunction_change_tune_curve::exec(VMFnArgs* args) {
2015            return VMChangeFadeCurveFunction::execTemplate<
2016                        &NoteBase::_Override::PitchCurve,
2017                        Event::synth_param_pitch_curve>( args, "change_tune_curve" );
2018        }
2019    
2020        // change_pan_curve() function
2021    
2022        VMFnResult* InstrumentScriptVMFunction_change_pan_curve::exec(VMFnArgs* args) {
2023            return VMChangeFadeCurveFunction::execTemplate<
2024            &NoteBase::_Override::PanCurve,
2025            Event::synth_param_pan_curve>( args, "change_pan_curve" );
2026      }      }
2027    
2028      // fade_in() function      // fade_in() function
# Line 1212  namespace LinuxSampler { Line 2032  namespace LinuxSampler {
2032      {      {
2033      }      }
2034    
2035      bool InstrumentScriptVMFunction_fade_in::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_fade_in::acceptsArgType(vmint iArg, ExprType_t type) const {
2036          if (iArg == 0)          if (iArg == 0)
2037              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
2038          else          else
2039              return type == INT_EXPR;              return type == INT_EXPR || type == REAL_EXPR;
2040        }
2041    
2042        bool InstrumentScriptVMFunction_fade_in::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
2043            if (iArg == 1)
2044                return type == VM_NO_UNIT || type == VM_SECOND;
2045            else
2046                return type == VM_NO_UNIT;
2047        }
2048    
2049        bool InstrumentScriptVMFunction_fade_in::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
2050            return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
2051      }      }
2052    
2053      VMFnResult* InstrumentScriptVMFunction_fade_in::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_fade_in::exec(VMFnArgs* args) {
2054          int duration = args->arg(1)->asInt()->evalInt();          StdUnit_t unit = args->arg(1)->asNumber()->unitType();
2055            vmint duration =
2056                (unit) ?
2057                    args->arg(1)->asNumber()->evalCastInt(VM_MICRO) :
2058                    args->arg(1)->asNumber()->evalCastInt();
2059          if (duration < 0) {          if (duration < 0) {
2060              wrnMsg("fade_in(): argument 2 may not be negative");              wrnMsg("fade_in(): argument 2 may not be negative");
2061              duration = 0;              duration = 0;
# Line 1248  namespace LinuxSampler { Line 2083  namespace LinuxSampler {
2083              // then immediately apply a start volume of zero to Note object,              // then immediately apply a start volume of zero to Note object,
2084              // as well as the fade in duration              // as well as the fade in duration
2085              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
2086                  pNote->Override.Volume = 0.f;                  pNote->Override.Volume.Value = 0.f;
2087                  pNote->Override.VolumeTime = fDuration;                  pNote->Override.VolumeTime = fDuration;
2088              } else { // otherwise schedule a "volume time" change with the requested fade in duration ...              } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
2089                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
# Line 1257  namespace LinuxSampler { Line 2092  namespace LinuxSampler {
2092                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
2093                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
2094                  e.Param.NoteSynthParam.Delta    = fDuration;                  e.Param.NoteSynthParam.Delta    = fDuration;
2095                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2096    
2097                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
2098              }              }
# Line 1270  namespace LinuxSampler { Line 2105  namespace LinuxSampler {
2105                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
2106                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume;                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
2107                  e.Param.NoteSynthParam.Delta    = 1.f;                  e.Param.NoteSynthParam.Delta    = 1.f;
2108                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2109    
2110                  // scheduling with 0 delay would also work here, but +1 is more                  // scheduling with 0 delay would also work here, but +1 is more
2111                  // safe regarding potential future implementation changes of the                  // safe regarding potential future implementation changes of the
# Line 1279  namespace LinuxSampler { Line 2114  namespace LinuxSampler {
2114              }              }
2115          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
2116              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
2117              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
2118                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
2119                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
2120    
# Line 1290  namespace LinuxSampler { Line 2125  namespace LinuxSampler {
2125                  // then immediately apply a start volume of zero to Note object,                  // then immediately apply a start volume of zero to Note object,
2126                  // as well as the fade in duration                  // as well as the fade in duration
2127                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
2128                      pNote->Override.Volume = 0.f;                      pNote->Override.Volume.Value = 0.f;
2129                      pNote->Override.VolumeTime = fDuration;                      pNote->Override.VolumeTime = fDuration;
2130                  } else { // otherwise schedule a "volume time" change with the requested fade in duration ...                  } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
2131                      Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                      Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
# Line 1299  namespace LinuxSampler { Line 2134  namespace LinuxSampler {
2134                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
2135                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
2136                      e.Param.NoteSynthParam.Delta    = fDuration;                      e.Param.NoteSynthParam.Delta    = fDuration;
2137                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2138    
2139                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
2140                  }                  }
# Line 1312  namespace LinuxSampler { Line 2147  namespace LinuxSampler {
2147                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
2148                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume;                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
2149                      e.Param.NoteSynthParam.Delta    = 1.f;                      e.Param.NoteSynthParam.Delta    = 1.f;
2150                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2151    
2152                      // scheduling with 0 delay would also work here, but +1 is more                      // scheduling with 0 delay would also work here, but +1 is more
2153                      // safe regarding potential future implementation changes of the                      // safe regarding potential future implementation changes of the
# Line 1332  namespace LinuxSampler { Line 2167  namespace LinuxSampler {
2167      {      {
2168      }      }
2169    
2170      bool InstrumentScriptVMFunction_fade_out::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_fade_out::acceptsArgType(vmint iArg, ExprType_t type) const {
2171          if (iArg == 0)          if (iArg == 0)
2172              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
2173          else          else
2174              return type == INT_EXPR;              return type == INT_EXPR || type == REAL_EXPR;
2175        }
2176    
2177        bool InstrumentScriptVMFunction_fade_out::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
2178            if (iArg == 1)
2179                return type == VM_NO_UNIT || type == VM_SECOND;
2180            else
2181                return type == VM_NO_UNIT;
2182        }
2183    
2184        bool InstrumentScriptVMFunction_fade_out::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
2185            return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
2186      }      }
2187    
2188      VMFnResult* InstrumentScriptVMFunction_fade_out::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_fade_out::exec(VMFnArgs* args) {
2189          int duration = args->arg(1)->asInt()->evalInt();          StdUnit_t unit = args->arg(1)->asNumber()->unitType();
2190            vmint duration =
2191                (unit) ?
2192                    args->arg(1)->asNumber()->evalCastInt(VM_MICRO) :
2193                    args->arg(1)->asNumber()->evalCastInt();
2194          if (duration < 0) {          if (duration < 0) {
2195              wrnMsg("fade_out(): argument 2 may not be negative");              wrnMsg("fade_out(): argument 2 may not be negative");
2196              duration = 0;              duration = 0;
# Line 1377  namespace LinuxSampler { Line 2227  namespace LinuxSampler {
2227                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
2228                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
2229                  e.Param.NoteSynthParam.Delta    = fDuration;                  e.Param.NoteSynthParam.Delta    = fDuration;
2230                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2231    
2232                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
2233              }              }
# Line 1390  namespace LinuxSampler { Line 2240  namespace LinuxSampler {
2240                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
2241                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume;                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
2242                  e.Param.NoteSynthParam.Delta    = 0.f;                  e.Param.NoteSynthParam.Delta    = 0.f;
2243                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2244    
2245                  // scheduling with 0 delay would also work here, but +1 is more                  // scheduling with 0 delay would also work here, but +1 is more
2246                  // safe regarding potential future implementation changes of the                  // safe regarding potential future implementation changes of the
# Line 1411  namespace LinuxSampler { Line 2261  namespace LinuxSampler {
2261              }              }
2262          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
2263              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
2264              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
2265                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
2266                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
2267    
# Line 1429  namespace LinuxSampler { Line 2279  namespace LinuxSampler {
2279                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
2280                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
2281                      e.Param.NoteSynthParam.Delta    = fDuration;                      e.Param.NoteSynthParam.Delta    = fDuration;
2282                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2283    
2284                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
2285                  }                  }
# Line 1442  namespace LinuxSampler { Line 2292  namespace LinuxSampler {
2292                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
2293                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume;                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
2294                      e.Param.NoteSynthParam.Delta    = 0.f;                      e.Param.NoteSynthParam.Delta    = 0.f;
2295                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2296    
2297                      // scheduling with 0 delay would also work here, but +1 is more                      // scheduling with 0 delay would also work here, but +1 is more
2298                      // safe regarding potential future implementation changes of the                      // safe regarding potential future implementation changes of the
# Line 1494  namespace LinuxSampler { Line 2344  namespace LinuxSampler {
2344              return successResult(0);              return successResult(0);
2345          }          }
2346    
2347          const int parameter = args->arg(1)->asInt()->evalInt();          const vmint parameter = args->arg(1)->asInt()->evalInt();
2348          switch (parameter) {          switch (parameter) {
2349              case EVENT_PAR_NOTE:              case EVENT_PAR_NOTE:
2350                  return successResult(pNote->cause.Param.Note.Key);                  return successResult(pNote->cause.Param.Note.Key);
# Line 1502  namespace LinuxSampler { Line 2352  namespace LinuxSampler {
2352                  return successResult(pNote->cause.Param.Note.Velocity);                  return successResult(pNote->cause.Param.Note.Velocity);
2353              case EVENT_PAR_VOLUME:              case EVENT_PAR_VOLUME:
2354                  return successResult(                  return successResult(
2355                      RTMath::LinRatioToDecibel(pNote->Override.Volume) * 1000.f                      RTMath::LinRatioToDecibel(pNote->Override.Volume.Value) * 1000.f
2356                  );                  );
2357              case EVENT_PAR_TUNE:              case EVENT_PAR_TUNE:
2358                  return successResult(                  return successResult(
2359                       RTMath::FreqRatioToCents(pNote->Override.Pitch) * 1000.f                       RTMath::FreqRatioToCents(pNote->Override.Pitch.Value) * 1000.f
2360                  );                  );
2361              case EVENT_PAR_0:              case EVENT_PAR_0:
2362                  return successResult(pNote->userPar[0]);                  return successResult(pNote->userPar[0]);
# Line 1546  namespace LinuxSampler { Line 2396  namespace LinuxSampler {
2396          NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );          NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2397          if (!pNote) return successResult();          if (!pNote) return successResult();
2398    
2399          const int parameter = args->arg(1)->asInt()->evalInt();          const vmint parameter = args->arg(1)->asInt()->evalInt();
2400          const int value     = args->arg(2)->asInt()->evalInt();          const vmint value     = args->arg(2)->asInt()->evalInt();
2401    
2402          switch (parameter) {          switch (parameter) {
2403              case EVENT_PAR_NOTE:              case EVENT_PAR_NOTE:
# Line 1555  namespace LinuxSampler { Line 2405  namespace LinuxSampler {
2405                      wrnMsg("set_event_par(): note number of argument 3 is out of range");                      wrnMsg("set_event_par(): note number of argument 3 is out of range");
2406                      return successResult();                      return successResult();
2407                  }                  }
2408                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime)                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
2409                      pNote->cause.Param.Note.Key = value;                      pNote->cause.Param.Note.Key = value;
2410                  else                      m_vm->m_event->cause.Param.Note.Key = value;
2411                    } else {
2412                      wrnMsg("set_event_par(): note number can only be changed when note is new");                      wrnMsg("set_event_par(): note number can only be changed when note is new");
2413                    }
2414                  return successResult();                  return successResult();
2415              case EVENT_PAR_VELOCITY:              case EVENT_PAR_VELOCITY:
2416                  if (value < 0 || value > 127) {                  if (value < 0 || value > 127) {
2417                      wrnMsg("set_event_par(): velocity of argument 3 is out of range");                      wrnMsg("set_event_par(): velocity of argument 3 is out of range");
2418                      return successResult();                      return successResult();
2419                  }                  }
2420                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime)                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
2421                      pNote->cause.Param.Note.Velocity = value;                      pNote->cause.Param.Note.Velocity = value;
2422                  else                      m_vm->m_event->cause.Param.Note.Velocity = value;
2423                    } else {
2424                      wrnMsg("set_event_par(): velocity can only be changed when note is new");                      wrnMsg("set_event_par(): velocity can only be changed when note is new");
2425                    }
2426                  return successResult();                  return successResult();
2427              case EVENT_PAR_VOLUME:              case EVENT_PAR_VOLUME:
2428                  wrnMsg("set_event_par(): changing volume by this function is currently not supported, use change_vol() instead");                  wrnMsg("set_event_par(): changing volume by this function is currently not supported, use change_vol() instead");
# Line 1594  namespace LinuxSampler { Line 2448  namespace LinuxSampler {
2448          return successResult();          return successResult();
2449      }      }
2450    
2451        // change_note() function
2452    
2453        InstrumentScriptVMFunction_change_note::InstrumentScriptVMFunction_change_note(InstrumentScriptVM* parent)
2454        : m_vm(parent)
2455        {
2456        }
2457    
2458        VMFnResult* InstrumentScriptVMFunction_change_note::exec(VMFnArgs* args) {
2459            AbstractEngineChannel* pEngineChannel =
2460                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2461    
2462            const ScriptID id = args->arg(0)->asInt()->evalInt();
2463            if (!id) {
2464                wrnMsg("change_note(): note ID for argument 1 may not be zero");
2465                return successResult();
2466            }
2467            if (!id.isNoteID()) {
2468                wrnMsg("change_note(): argument 1 is not a note ID");
2469                return successResult();
2470            }
2471    
2472            NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2473            if (!pNote) return successResult();
2474    
2475            const vmint value = args->arg(1)->asInt()->evalInt();
2476            if (value < 0 || value > 127) {
2477                wrnMsg("change_note(): note number of argument 2 is out of range");
2478                return successResult();
2479            }
2480    
2481            if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
2482                pNote->cause.Param.Note.Key = value;
2483                m_vm->m_event->cause.Param.Note.Key = value;
2484            } else {
2485                wrnMsg("change_note(): note number can only be changed when note is new");
2486            }
2487    
2488            return successResult();
2489        }
2490    
2491        // change_velo() function
2492    
2493        InstrumentScriptVMFunction_change_velo::InstrumentScriptVMFunction_change_velo(InstrumentScriptVM* parent)
2494        : m_vm(parent)
2495        {
2496        }
2497    
2498        VMFnResult* InstrumentScriptVMFunction_change_velo::exec(VMFnArgs* args) {
2499            AbstractEngineChannel* pEngineChannel =
2500                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2501    
2502            const ScriptID id = args->arg(0)->asInt()->evalInt();
2503            if (!id) {
2504                wrnMsg("change_velo(): note ID for argument 1 may not be zero");
2505                return successResult();
2506            }
2507            if (!id.isNoteID()) {
2508                wrnMsg("change_velo(): argument 1 is not a note ID");
2509                return successResult();
2510            }
2511    
2512            NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2513            if (!pNote) return successResult();
2514    
2515            const vmint value = args->arg(1)->asInt()->evalInt();
2516            if (value < 0 || value > 127) {
2517                wrnMsg("change_velo(): velocity of argument 2 is out of range");
2518                return successResult();
2519            }
2520    
2521            if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
2522                pNote->cause.Param.Note.Velocity = value;
2523                m_vm->m_event->cause.Param.Note.Velocity = value;
2524            } else {
2525                wrnMsg("change_velo(): velocity can only be changed when note is new");
2526            }
2527    
2528            return successResult();
2529        }
2530    
2531        // change_play_pos() function
2532    
2533        InstrumentScriptVMFunction_change_play_pos::InstrumentScriptVMFunction_change_play_pos(InstrumentScriptVM* parent)
2534            : m_vm(parent)
2535        {
2536        }
2537    
2538        bool InstrumentScriptVMFunction_change_play_pos::acceptsArgType(vmint iArg, ExprType_t type) const {
2539            if (iArg == 0)
2540                return type == INT_EXPR;
2541            else
2542                return type == INT_EXPR || type == REAL_EXPR;
2543        }
2544    
2545        bool InstrumentScriptVMFunction_change_play_pos::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
2546            if (iArg == 1)
2547                return type == VM_NO_UNIT || type == VM_SECOND;
2548            else
2549                return type == VM_NO_UNIT;
2550        }
2551    
2552        bool InstrumentScriptVMFunction_change_play_pos::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
2553            return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
2554        }
2555    
2556        VMFnResult* InstrumentScriptVMFunction_change_play_pos::exec(VMFnArgs* args) {
2557            const ScriptID id = args->arg(0)->asInt()->evalInt();
2558            if (!id) {
2559                wrnMsg("change_play_pos(): note ID for argument 1 may not be zero");
2560                return successResult();
2561            }
2562            if (!id.isNoteID()) {
2563                wrnMsg("change_play_pos(): argument 1 is not a note ID");
2564                return successResult();
2565            }
2566    
2567            StdUnit_t unit = args->arg(1)->asNumber()->unitType();
2568            const vmint pos =
2569                (unit) ?
2570                    args->arg(1)->asNumber()->evalCastInt(VM_MICRO) :
2571                    args->arg(1)->asNumber()->evalCastInt();
2572            if (pos < 0) {
2573                wrnMsg("change_play_pos(): playback position of argument 2 may not be negative");
2574                return successResult();
2575            }
2576    
2577            AbstractEngineChannel* pEngineChannel =
2578                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2579    
2580            NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2581            if (!pNote) return successResult();
2582    
2583            pNote->Override.SampleOffset =
2584                (decltype(pNote->Override.SampleOffset)) pos;
2585    
2586            return successResult();
2587        }
2588    
2589      // event_status() function      // event_status() function
2590    
2591      InstrumentScriptVMFunction_event_status::InstrumentScriptVMFunction_event_status(InstrumentScriptVM* parent)      InstrumentScriptVMFunction_event_status::InstrumentScriptVMFunction_event_status(InstrumentScriptVM* parent)
# Line 1619  namespace LinuxSampler { Line 2611  namespace LinuxSampler {
2611          return successResult(pNote ? EVENT_STATUS_NOTE_QUEUE : EVENT_STATUS_INACTIVE);          return successResult(pNote ? EVENT_STATUS_NOTE_QUEUE : EVENT_STATUS_INACTIVE);
2612      }      }
2613    
2614        // callback_status() function
2615    
2616        InstrumentScriptVMFunction_callback_status::InstrumentScriptVMFunction_callback_status(InstrumentScriptVM* parent)
2617            : m_vm(parent)
2618        {
2619        }
2620    
2621        VMFnResult* InstrumentScriptVMFunction_callback_status::exec(VMFnArgs* args) {
2622            const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
2623            if (!id) {
2624                wrnMsg("callback_status(): callback ID for argument 1 may not be zero");
2625                return successResult();
2626            }
2627    
2628            AbstractEngineChannel* pEngineChannel =
2629                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2630    
2631            RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
2632            if (!itCallback)
2633                return successResult(CALLBACK_STATUS_TERMINATED);
2634    
2635            return successResult(
2636                (m_vm->m_event->execCtx == itCallback->execCtx) ?
2637                    CALLBACK_STATUS_RUNNING : CALLBACK_STATUS_QUEUE
2638            );
2639        }
2640    
2641      // wait() function (overrides core wait() implementation)      // wait() function (overrides core wait() implementation)
2642    
2643      InstrumentScriptVMFunction_wait::InstrumentScriptVMFunction_wait(InstrumentScriptVM* parent)      InstrumentScriptVMFunction_wait::InstrumentScriptVMFunction_wait(InstrumentScriptVM* parent)
# Line 1665  namespace LinuxSampler { Line 2684  namespace LinuxSampler {
2684          return successResult();          return successResult();
2685      }      }
2686    
2687        // abort() function
2688    
2689        InstrumentScriptVMFunction_abort::InstrumentScriptVMFunction_abort(InstrumentScriptVM* parent)
2690            : m_vm(parent)
2691        {
2692        }
2693    
2694        VMFnResult* InstrumentScriptVMFunction_abort::exec(VMFnArgs* args) {
2695            const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
2696            if (!id) {
2697                wrnMsg("abort(): callback ID for argument 1 may not be zero");
2698                return successResult();
2699            }
2700    
2701            AbstractEngineChannel* pEngineChannel =
2702                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2703    
2704            RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
2705            if (!itCallback) return successResult(); // ignore if callback is i.e. not alive anymore
2706    
2707            itCallback->execCtx->signalAbort();
2708    
2709            return successResult();
2710        }
2711    
2712        // fork() function
2713    
2714        InstrumentScriptVMFunction_fork::InstrumentScriptVMFunction_fork(InstrumentScriptVM* parent)
2715            : m_vm(parent)
2716        {
2717        }
2718    
2719        VMFnResult* InstrumentScriptVMFunction_fork::exec(VMFnArgs* args) {
2720            // check if this is actually the parent going to fork, or rather one of
2721            // the children which is already forked
2722            if (m_vm->m_event->forkIndex != 0) { // this is the entry point for a child ...
2723                int forkResult = m_vm->m_event->forkIndex;
2724                // reset so that this child may i.e. also call fork() later on
2725                m_vm->m_event->forkIndex = 0;
2726                return successResult(forkResult);
2727            }
2728    
2729            // if we are here, then this is the parent, so we must fork this parent
2730    
2731            const vmint n =
2732                (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : 1;
2733            const bool bAutoAbort =
2734                (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : true;
2735    
2736            if (m_vm->m_event->countChildHandlers() + n > MAX_FORK_PER_SCRIPT_HANDLER) {
2737                wrnMsg("fork(): requested amount would exceed allowed limit per event handler");
2738                return successResult(-1);
2739            }
2740    
2741            AbstractEngineChannel* pEngineChannel =
2742                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2743    
2744            if (!pEngineChannel->hasFreeScriptCallbacks(n)) {
2745                wrnMsg("fork(): global limit of event handlers exceeded");
2746                return successResult(-1);
2747            }
2748    
2749            for (int iChild = 0; iChild < n; ++iChild) {
2750                RTList<ScriptEvent>::Iterator itChild =
2751                    pEngineChannel->forkScriptCallback(m_vm->m_event, bAutoAbort);
2752                if (!itChild) { // should never happen, otherwise its a bug ...
2753                    errMsg("fork(): internal error while allocating child");
2754                    return errorResult(-1); // terminate script
2755                }
2756                // since both parent, as well all child script execution instances
2757                // all land in this exec() method, the following is (more or less)
2758                // the only feature that lets us distinguish the parent and
2759                // respective children from each other in this exec() method
2760                itChild->forkIndex = iChild + 1;
2761            }
2762    
2763            return successResult(0);
2764        }
2765    
2766  } // namespace LinuxSampler  } // namespace LinuxSampler

Legend:
Removed from v.3205  
changed lines
  Added in v.3715

  ViewVC Help
Powered by ViewVC