/[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 3296 by schoenebeck, Wed Jun 28 09:45:56 2017 UTC revision 3588 by schoenebeck, Sun Sep 1 16:06:48 2019 UTC
# Line 1  Line 1 
1  /*  /*
2   * Copyright (c) 2014-2017 Christian Schoenebeck   * Copyright (c) 2014-2019 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 duration = (args->argsCount() >= 4) ? args->arg(3)->asInt()->evalInt() : 0; //TODO: -1 might be a better default value instead of 0          VMNumberExpr* argDuration = (args->argsCount() >= 4) ? args->arg(3)->asNumber() : NULL;
95            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 68  namespace LinuxSampler { Line 141  namespace LinuxSampler {
141          // if a sample offset is supplied, assign the offset as override          // if a sample offset is supplied, assign the offset as override
142          // to the previously created Note object          // to the previously created Note object
143          if (args->argsCount() >= 3) {          if (args->argsCount() >= 3) {
144              int sampleoffset = args->arg(2)->asInt()->evalInt();              VMNumberExpr* argSampleOffset = args->arg(2)->asNumber();
145                vmint sampleoffset =
146                    (argSampleOffset->unitType()) ?
147                        argSampleOffset->evalCastInt(VM_MICRO) :
148                        argSampleOffset->evalCastInt();
149              if (sampleoffset >= 0) {              if (sampleoffset >= 0) {
150                  NoteBase* pNote = pEngineChannel->pEngine->NoteByID(id);                  NoteBase* pNote = pEngineChannel->pEngine->NoteByID(id);
151                  if (pNote) {                  if (pNote) {
152                      pNote->Override.SampleOffset = sampleoffset;                      pNote->Override.SampleOffset =
153                            (decltype(pNote->Override.SampleOffset)) sampleoffset;
154                  }                  }
155              } else if (sampleoffset < -1) {              } else if (sampleoffset < -1) {
156                  errMsg("play_note(): sample offset of argument 3 may not be less than -1");                  errMsg("play_note(): sample offset of argument 3 may not be less than -1");
# Line 102  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 140  namespace LinuxSampler { Line 218  namespace LinuxSampler {
218      {      {
219      }      }
220    
221      bool InstrumentScriptVMFunction_ignore_event::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_ignore_event::acceptsArgType(vmint iArg, ExprType_t type) const {
222          return type == INT_EXPR || type == INT_ARR_EXPR;          return type == INT_EXPR || type == INT_ARR_EXPR;
223      }      }
224    
# Line 196  namespace LinuxSampler { Line 274  namespace LinuxSampler {
274      {      {
275      }      }
276    
277      bool InstrumentScriptVMFunction_note_off::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_note_off::acceptsArgType(vmint iArg, ExprType_t type) const {
278          return type == INT_EXPR || type == INT_ARR_EXPR;          return type == INT_EXPR || type == INT_ARR_EXPR;
279      }      }
280    
281        void InstrumentScriptVMFunction_note_off::checkArgs(VMFnArgs* args,
282                                                            std::function<void(String)> err,
283                                                            std::function<void(String)> wrn)
284        {
285            // super class checks
286            Super::checkArgs(args, err, wrn);
287    
288            // own checks ...
289            if (args->argsCount() >= 2 && args->arg(1)->isConstExpr() && args->arg(1)->exprType() == INT_EXPR) {
290                vmint velocity = args->arg(1)->asInt()->evalInt();
291                if (velocity < 0 || velocity > 127) {
292                    err("MIDI velocity value for argument 2 must be between 0..127");
293                    return;
294                }
295            }
296        }
297    
298      VMFnResult* InstrumentScriptVMFunction_note_off::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_note_off::exec(VMFnArgs* args) {
299          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
300              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
301    
302          int velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127;          vmint velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127;
303          if (velocity < 0 || velocity > 127) {          if (velocity < 0 || velocity > 127) {
304              errMsg("note_off(): argument 2 is an invalid velocity value");              errMsg("note_off(): argument 2 is an invalid velocity value");
305              return errorResult();              return errorResult();
# Line 235  namespace LinuxSampler { Line 330  namespace LinuxSampler {
330              pEngineChannel->ScheduleEventMicroSec(&e, 0);              pEngineChannel->ScheduleEventMicroSec(&e, 0);
331          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
332              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
333              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
334                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
335                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
336    
# Line 264  namespace LinuxSampler { Line 359  namespace LinuxSampler {
359      {      {
360      }      }
361    
362        void InstrumentScriptVMFunction_set_event_mark::checkArgs(VMFnArgs* args,
363                                                                  std::function<void(String)> err,
364                                                                  std::function<void(String)> wrn)
365        {
366            // super class checks
367            Super::checkArgs(args, err, wrn);
368    
369            // own checks ...
370            if (args->argsCount() >= 2 && args->arg(1)->isConstExpr()) {
371                const vmint groupID = args->arg(1)->asInt()->evalInt();
372                if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
373                    err("Argument 2 value is an invalid group id.");
374                    return;
375                }
376            }
377        }
378    
379      VMFnResult* InstrumentScriptVMFunction_set_event_mark::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_set_event_mark::exec(VMFnArgs* args) {
380          const ScriptID id = args->arg(0)->asInt()->evalInt();          const ScriptID id = args->arg(0)->asInt()->evalInt();
381          const int groupID = args->arg(1)->asInt()->evalInt();          const vmint groupID = args->arg(1)->asInt()->evalInt();
382    
383          if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {          if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
384              errMsg("set_event_mark(): argument 2 is an invalid group id");              errMsg("set_event_mark(): argument 2 is an invalid group id");
# Line 302  namespace LinuxSampler { Line 414  namespace LinuxSampler {
414      {      {
415      }      }
416    
417        void InstrumentScriptVMFunction_delete_event_mark::checkArgs(VMFnArgs* args,
418                                                                     std::function<void(String)> err,
419                                                                     std::function<void(String)> wrn)
420        {
421            // super class checks
422            Super::checkArgs(args, err, wrn);
423    
424            // own checks ...
425            if (args->argsCount() >= 2 && args->arg(1)->isConstExpr()) {
426                const vmint groupID = args->arg(1)->asInt()->evalInt();
427                if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
428                    err("Argument 2 value is an invalid group id.");
429                    return;
430                }
431            }
432        }
433    
434      VMFnResult* InstrumentScriptVMFunction_delete_event_mark::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_delete_event_mark::exec(VMFnArgs* args) {
435          const ScriptID id = args->arg(0)->asInt()->evalInt();          const ScriptID id = args->arg(0)->asInt()->evalInt();
436          const int groupID = args->arg(1)->asInt()->evalInt();          const vmint groupID = args->arg(1)->asInt()->evalInt();
437    
438          if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {          if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
439              errMsg("delete_event_mark(): argument 2 is an invalid group id");              errMsg("delete_event_mark(): argument 2 is an invalid group id");
# Line 326  namespace LinuxSampler { Line 455  namespace LinuxSampler {
455      {      {
456      }      }
457    
458      int InstrumentScriptVMFunction_by_marks::Result::arraySize() const {      vmint InstrumentScriptVMFunction_by_marks::Result::arraySize() const {
459          return eventGroup->size();          return eventGroup->size();
460      }      }
461    
462      int InstrumentScriptVMFunction_by_marks::Result::evalIntElement(uint i) {      vmint InstrumentScriptVMFunction_by_marks::Result::evalIntElement(vmuint i) {
463          return (*eventGroup)[i];          return (*eventGroup)[i];
464      }      }
465    
# Line 346  namespace LinuxSampler { Line 475  namespace LinuxSampler {
475          return &m_result;          return &m_result;
476      }      }
477    
478        void InstrumentScriptVMFunction_by_marks::checkArgs(VMFnArgs* args,
479                                                            std::function<void(String)> err,
480                                                            std::function<void(String)> wrn)
481        {
482            // super class checks
483            Super::checkArgs(args, err, wrn);
484    
485            // own checks ...
486            if (args->arg(0)->isConstExpr()) {
487                const vmint groupID = args->arg(0)->asInt()->evalInt();
488                if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
489                    err("Argument value is an invalid group id.");
490                    return;
491                }
492            }
493        }
494    
495      VMFnResult* InstrumentScriptVMFunction_by_marks::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_by_marks::exec(VMFnArgs* args) {
496          int groupID = args->arg(0)->asInt()->evalInt();          vmint groupID = args->arg(0)->asInt()->evalInt();
497    
498          if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {          if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
499              errMsg("by_marks(): argument is an invalid group id");              errMsg("by_marks(): argument is an invalid group id");
# Line 367  namespace LinuxSampler { Line 513  namespace LinuxSampler {
513      {      {
514      }      }
515    
516      bool InstrumentScriptVMFunction_change_vol::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_change_vol::acceptsArgType(vmint iArg, ExprType_t type) const {
517          if (iArg == 0)          if (iArg == 0)
518              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
519            else if (iArg == 1)
520                return type == INT_EXPR || type == REAL_EXPR;
521          else          else
522              return type == INT_EXPR;              return type == INT_EXPR;
523      }      }
524    
525        bool InstrumentScriptVMFunction_change_vol::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
526            if (iArg == 1)
527                return type == VM_NO_UNIT || type == VM_BEL;
528            else
529                return type == VM_NO_UNIT;
530        }
531    
532        bool InstrumentScriptVMFunction_change_vol::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
533            return iArg == 1 && type == VM_BEL; // only allow metric prefix(es) if 'Bel' is used as unit type
534        }
535    
536        bool InstrumentScriptVMFunction_change_vol::acceptsArgFinal(vmint iArg) const {
537            return iArg == 1;
538        }
539    
540      VMFnResult* InstrumentScriptVMFunction_change_vol::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_vol::exec(VMFnArgs* args) {
541          int volume = args->arg(1)->asInt()->evalInt(); // volume change in milli dB          StdUnit_t unit = args->arg(1)->asNumber()->unitType();
542            vmint volume =
543                (unit) ?
544                    args->arg(1)->asNumber()->evalCastInt(VM_MILLI,VM_DECI) :
545                    args->arg(1)->asNumber()->evalCastInt(); // volume change in milli dB
546            bool isFinal = args->arg(1)->asNumber()->isFinal();
547          bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;          bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
548          const float fVolumeLin = RTMath::DecibelToLinRatio(float(volume) / 1000.f);          const float fVolumeLin = RTMath::DecibelToLinRatio(float(volume) / 1000.f);
549    
# Line 403  namespace LinuxSampler { Line 571  namespace LinuxSampler {
571                  pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S)                  pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S)
572              {              {
573                  if (relative)                  if (relative)
574                      pNote->Override.Volume *= fVolumeLin;                      pNote->Override.Volume.Value *= fVolumeLin;
575                  else                  else
576                      pNote->Override.Volume = fVolumeLin;                      pNote->Override.Volume.Value = fVolumeLin;
577                    pNote->Override.Volume.Final = isFinal;
578              } else { // otherwise schedule the volume change ...              } else { // otherwise schedule the volume change ...
579                  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"
580                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 413  namespace LinuxSampler { Line 582  namespace LinuxSampler {
582                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
583                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume;                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
584                  e.Param.NoteSynthParam.Delta    = fVolumeLin;                  e.Param.NoteSynthParam.Delta    = fVolumeLin;
585                  e.Param.NoteSynthParam.Relative = relative;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
586                        isFinal, relative, unit
587                    );
588                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
589              }              }
590          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
591              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
592              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
593                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
594                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
595    
# Line 433  namespace LinuxSampler { Line 603  namespace LinuxSampler {
603                      pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S)                      pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S)
604                  {                  {
605                      if (relative)                      if (relative)
606                          pNote->Override.Volume *= fVolumeLin;                          pNote->Override.Volume.Value *= fVolumeLin;
607                      else                      else
608                          pNote->Override.Volume = fVolumeLin;                          pNote->Override.Volume.Value = fVolumeLin;
609                        pNote->Override.Volume.Final = isFinal;
610                  } else { // otherwise schedule the volume change ...                  } else { // otherwise schedule the volume change ...
611                      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"
612                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 443  namespace LinuxSampler { Line 614  namespace LinuxSampler {
614                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
615                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume;                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
616                      e.Param.NoteSynthParam.Delta    = fVolumeLin;                      e.Param.NoteSynthParam.Delta    = fVolumeLin;
617                      e.Param.NoteSynthParam.Relative = relative;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
618                            isFinal, relative, unit
619                        );
620                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
621                  }                  }
622              }              }
# Line 460  namespace LinuxSampler { Line 632  namespace LinuxSampler {
632      {      {
633      }      }
634    
635      bool InstrumentScriptVMFunction_change_tune::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_change_tune::acceptsArgType(vmint iArg, ExprType_t type) const {
636          if (iArg == 0)          if (iArg == 0)
637              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
638            else if (iArg == 1)
639                return type == INT_EXPR || type == REAL_EXPR;
640          else          else
641              return type == INT_EXPR;              return type == INT_EXPR;
642      }      }
643    
644        bool InstrumentScriptVMFunction_change_tune::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
645            return iArg == 1;
646        }
647    
648        bool InstrumentScriptVMFunction_change_tune::acceptsArgFinal(vmint iArg) const {
649            return iArg == 1;
650        }
651    
652      VMFnResult* InstrumentScriptVMFunction_change_tune::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_tune::exec(VMFnArgs* args) {
653          int tune = args->arg(1)->asInt()->evalInt(); // tuning change in milli cents          vmint tune =
654                (args->arg(1)->asNumber()->hasUnitFactorNow())
655                    ? args->arg(1)->asNumber()->evalCastInt(VM_MILLI,VM_CENTI)
656                    : args->arg(1)->asNumber()->evalCastInt(); // tuning change in milli cents
657            bool isFinal = args->arg(1)->asNumber()->isFinal();
658            StdUnit_t unit = args->arg(1)->asNumber()->unitType();
659          bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;          bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
660          const float fFreqRatio = RTMath::CentsToFreqRatioUnlimited(float(tune) / 1000.f);          const float fFreqRatio = RTMath::CentsToFreqRatioUnlimited(float(tune) / 1000.f);
661    
# Line 496  namespace LinuxSampler { Line 683  namespace LinuxSampler {
683                  pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S)                  pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S)
684              {              {
685                  if (relative)                  if (relative)
686                      pNote->Override.Pitch *= fFreqRatio;                      pNote->Override.Pitch.Value *= fFreqRatio;
687                  else                  else
688                      pNote->Override.Pitch = fFreqRatio;                      pNote->Override.Pitch.Value = fFreqRatio;
689                    pNote->Override.Pitch.Final = isFinal;
690              } else { // otherwise schedule tuning change ...              } else { // otherwise schedule tuning change ...
691                  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"
692                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 506  namespace LinuxSampler { Line 694  namespace LinuxSampler {
694                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
695                  e.Param.NoteSynthParam.Type     = Event::synth_param_pitch;                  e.Param.NoteSynthParam.Type     = Event::synth_param_pitch;
696                  e.Param.NoteSynthParam.Delta    = fFreqRatio;                  e.Param.NoteSynthParam.Delta    = fFreqRatio;
697                  e.Param.NoteSynthParam.Relative = relative;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
698                        isFinal, relative, unit
699                    );
700                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
701              }              }
702          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
703              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
704              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
705                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
706                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
707    
# Line 526  namespace LinuxSampler { Line 715  namespace LinuxSampler {
715                      pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S)                      pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S)
716                  {                  {
717                      if (relative)                      if (relative)
718                          pNote->Override.Pitch *= fFreqRatio;                          pNote->Override.Pitch.Value *= fFreqRatio;
719                      else                      else
720                          pNote->Override.Pitch = fFreqRatio;                          pNote->Override.Pitch.Value = fFreqRatio;
721                        pNote->Override.Pitch.Final = isFinal;
722                  } else { // otherwise schedule tuning change ...                  } else { // otherwise schedule tuning change ...
723                      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"
724                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 536  namespace LinuxSampler { Line 726  namespace LinuxSampler {
726                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
727                      e.Param.NoteSynthParam.Type     = Event::synth_param_pitch;                      e.Param.NoteSynthParam.Type     = Event::synth_param_pitch;
728                      e.Param.NoteSynthParam.Delta    = fFreqRatio;                      e.Param.NoteSynthParam.Delta    = fFreqRatio;
729                      e.Param.NoteSynthParam.Relative = relative;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
730                            isFinal, relative, unit
731                        );
732                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
733                  }                  }
734              }              }
# Line 553  namespace LinuxSampler { Line 744  namespace LinuxSampler {
744      {      {
745      }      }
746    
747      bool InstrumentScriptVMFunction_change_pan::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_change_pan::acceptsArgType(vmint iArg, ExprType_t type) const {
748          if (iArg == 0)          if (iArg == 0)
749              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
750          else          else
751              return type == INT_EXPR;              return type == INT_EXPR;
752      }      }
753    
754        bool InstrumentScriptVMFunction_change_pan::acceptsArgFinal(vmint iArg) const {
755            return iArg == 1;
756        }
757    
758      VMFnResult* InstrumentScriptVMFunction_change_pan::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_pan::exec(VMFnArgs* args) {
759          int pan = args->arg(1)->asInt()->evalInt();          vmint pan    = args->arg(1)->asInt()->evalInt();
760            bool isFinal = args->arg(1)->asInt()->isFinal();
761          bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;          bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
762    
763          if (pan > 1000) {          if (pan > 1000) {
# Line 594  namespace LinuxSampler { Line 790  namespace LinuxSampler {
790              // then immediately apply the panning to Note object              // then immediately apply the panning to Note object
791              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
792                  if (relative) {                  if (relative) {
793                      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);
794                  } else {                  } else {
795                      pNote->Override.Pan = fPan;                      pNote->Override.Pan.Value = fPan;
796                      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
797                  }                  }
798                    pNote->Override.Pan.Final = isFinal;
799              } else { // otherwise schedule panning change ...              } else { // otherwise schedule panning change ...
800                  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"
801                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 606  namespace LinuxSampler { Line 803  namespace LinuxSampler {
803                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
804                  e.Param.NoteSynthParam.Type     = Event::synth_param_pan;                  e.Param.NoteSynthParam.Type     = Event::synth_param_pan;
805                  e.Param.NoteSynthParam.Delta    = fPan;                  e.Param.NoteSynthParam.Delta    = fPan;
806                  e.Param.NoteSynthParam.Relative = relative;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
807                        isFinal, relative, false
808                    );
809                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
810              }              }
811          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
812              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
813              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
814                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
815                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
816    
# Line 623  namespace LinuxSampler { Line 821  namespace LinuxSampler {
821                  // then immediately apply the panning to Note object                  // then immediately apply the panning to Note object
822                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
823                      if (relative) {                      if (relative) {
824                          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);
825                      } else {                      } else {
826                          pNote->Override.Pan = fPan;                          pNote->Override.Pan.Value = fPan;
827                          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
828                      }                      }
829                        pNote->Override.Pan.Final = isFinal;
830                  } else { // otherwise schedule panning change ...                  } else { // otherwise schedule panning change ...
831                      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"
832                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 635  namespace LinuxSampler { Line 834  namespace LinuxSampler {
834                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
835                      e.Param.NoteSynthParam.Type     = Event::synth_param_pan;                      e.Param.NoteSynthParam.Type     = Event::synth_param_pan;
836                      e.Param.NoteSynthParam.Delta    = fPan;                      e.Param.NoteSynthParam.Delta    = fPan;
837                      e.Param.NoteSynthParam.Relative = relative;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
838                            isFinal, relative, false
839                        );
840                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
841                  }                  }
842              }              }
# Line 646  namespace LinuxSampler { Line 846  namespace LinuxSampler {
846      }      }
847    
848      #define VM_FILTER_PAR_MAX_VALUE 1000000      #define VM_FILTER_PAR_MAX_VALUE 1000000
849        #define VM_FILTER_PAR_MAX_HZ 30000
850      #define VM_EG_PAR_MAX_VALUE 1000000      #define VM_EG_PAR_MAX_VALUE 1000000
851    
852      // change_cutoff() function      // change_cutoff() function
# Line 655  namespace LinuxSampler { Line 856  namespace LinuxSampler {
856      {      {
857      }      }
858    
859      bool InstrumentScriptVMFunction_change_cutoff::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_change_cutoff::acceptsArgType(vmint iArg, ExprType_t type) const {
860          if (iArg == 0)          if (iArg == 0)
861              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
862            else if (iArg == 1)
863                return type == INT_EXPR || type == REAL_EXPR;
864          else          else
865              return type == INT_EXPR;              return type == INT_EXPR;
866      }      }
867    
868        bool InstrumentScriptVMFunction_change_cutoff::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
869            if (iArg == 1)
870                return type == VM_NO_UNIT || type == VM_HERTZ;
871            else
872                return type == VM_NO_UNIT;
873        }
874    
875        bool InstrumentScriptVMFunction_change_cutoff::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
876            return iArg == 1 && type == VM_HERTZ; // only allow metric prefix(es) if 'Hz' is used as unit type
877        }
878    
879        bool InstrumentScriptVMFunction_change_cutoff::acceptsArgFinal(vmint iArg) const {
880            return iArg == 1;
881        }
882    
883        void InstrumentScriptVMFunction_change_cutoff::checkArgs(VMFnArgs* args,
884                                                                 std::function<void(String)> err,
885                                                                 std::function<void(String)> wrn)
886        {
887            // super class checks
888            Super::checkArgs(args, err, wrn);
889    
890            // own checks ...
891            if (args->argsCount() >= 2) {
892                VMNumberExpr* argCutoff = args->arg(1)->asNumber();
893                if (argCutoff->unitType() && !argCutoff->isFinal()) {
894                    wrn("Argument 2 implies 'final' value when using Hz as unit for cutoff frequency.");
895                }
896            }
897        }
898    
899      VMFnResult* InstrumentScriptVMFunction_change_cutoff::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_cutoff::exec(VMFnArgs* args) {
900          int cutoff = args->arg(1)->asInt()->evalInt();          const StdUnit_t unit = args->arg(1)->asNumber()->unitType();
901          if (cutoff > VM_FILTER_PAR_MAX_VALUE) {          vmint cutoff =
902              wrnMsg("change_cutoff(): argument 2 may not be larger than 1000000");              (unit) ?
903                    args->arg(1)->asNumber()->evalCastInt(VM_NO_PREFIX) :
904                    args->arg(1)->asNumber()->evalCastInt();
905            const bool isFinal =
906                (unit) ?
907                    true : // imply 'final' value if unit type is used
908                    args->arg(1)->asNumber()->isFinal();
909            if (!unit && cutoff > VM_FILTER_PAR_MAX_VALUE) {
910                wrnMsg("change_cutoff(): argument 2 may not be larger than " strfy(VM_FILTER_PAR_MAX_VALUE));
911              cutoff = VM_FILTER_PAR_MAX_VALUE;              cutoff = VM_FILTER_PAR_MAX_VALUE;
912            } else if (unit && cutoff > VM_FILTER_PAR_MAX_HZ) {
913                wrnMsg("change_cutoff(): argument 2 may not be larger than " strfy(VM_FILTER_PAR_MAX_HZ) " Hz");
914                cutoff = VM_FILTER_PAR_MAX_HZ;
915          } else if (cutoff < 0) {          } else if (cutoff < 0) {
916              wrnMsg("change_cutoff(): argument 2 may not be negative");              wrnMsg("change_cutoff(): argument 2 may not be negative");
917              cutoff = 0;              cutoff = 0;
918          }          }
919          const float fCutoff = float(cutoff) / float(VM_FILTER_PAR_MAX_VALUE);          const float fCutoff =
920                (unit) ? cutoff : float(cutoff) / float(VM_FILTER_PAR_MAX_VALUE);
921    
922          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
923              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 693  namespace LinuxSampler { Line 939  namespace LinuxSampler {
939              // if change_cutoff() was called immediately after note was triggered              // if change_cutoff() was called immediately after note was triggered
940              // then immediately apply cutoff to Note object              // then immediately apply cutoff to Note object
941              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
942                  pNote->Override.Cutoff = fCutoff;                  pNote->Override.Cutoff.Value = fCutoff;
943                    pNote->Override.Cutoff.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
944              } else { // otherwise schedule cutoff change ...              } else { // otherwise schedule cutoff change ...
945                  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"
946                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 701  namespace LinuxSampler { Line 948  namespace LinuxSampler {
948                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
949                  e.Param.NoteSynthParam.Type     = Event::synth_param_cutoff;                  e.Param.NoteSynthParam.Type     = Event::synth_param_cutoff;
950                  e.Param.NoteSynthParam.Delta    = fCutoff;                  e.Param.NoteSynthParam.Delta    = fCutoff;
951                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
952                        isFinal, false, unit
953                    );
954                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
955              }              }
956          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
957              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
958              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
959                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
960                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
961    
# Line 717  namespace LinuxSampler { Line 965  namespace LinuxSampler {
965                  // if change_cutoff() was called immediately after note was triggered                  // if change_cutoff() was called immediately after note was triggered
966                  // then immediately apply cutoff to Note object                  // then immediately apply cutoff to Note object
967                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
968                      pNote->Override.Cutoff = fCutoff;                      pNote->Override.Cutoff.Value = fCutoff;
969                        pNote->Override.Cutoff.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
970                  } else { // otherwise schedule cutoff change ...                  } else { // otherwise schedule cutoff change ...
971                      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"
972                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 725  namespace LinuxSampler { Line 974  namespace LinuxSampler {
974                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
975                      e.Param.NoteSynthParam.Type     = Event::synth_param_cutoff;                      e.Param.NoteSynthParam.Type     = Event::synth_param_cutoff;
976                      e.Param.NoteSynthParam.Delta    = fCutoff;                      e.Param.NoteSynthParam.Delta    = fCutoff;
977                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
978                            isFinal, false, unit
979                        );
980                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
981                  }                  }
982              }              }
# Line 742  namespace LinuxSampler { Line 992  namespace LinuxSampler {
992      {      {
993      }      }
994    
995      bool InstrumentScriptVMFunction_change_reso::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_change_reso::acceptsArgType(vmint iArg, ExprType_t type) const {
996          if (iArg == 0)          if (iArg == 0)
997              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
998          else          else
999              return type == INT_EXPR;              return type == INT_EXPR;
1000      }      }
1001    
1002        bool InstrumentScriptVMFunction_change_reso::acceptsArgFinal(vmint iArg) const {
1003            return iArg == 1;
1004        }
1005    
1006      VMFnResult* InstrumentScriptVMFunction_change_reso::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_reso::exec(VMFnArgs* args) {
1007          int resonance = args->arg(1)->asInt()->evalInt();          vmint resonance = args->arg(1)->asInt()->evalInt();
1008            bool isFinal    = args->arg(1)->asInt()->isFinal();
1009          if (resonance > VM_FILTER_PAR_MAX_VALUE) {          if (resonance > VM_FILTER_PAR_MAX_VALUE) {
1010              wrnMsg("change_reso(): argument 2 may not be larger than 1000000");              wrnMsg("change_reso(): argument 2 may not be larger than 1000000");
1011              resonance = VM_FILTER_PAR_MAX_VALUE;              resonance = VM_FILTER_PAR_MAX_VALUE;
# Line 780  namespace LinuxSampler { Line 1035  namespace LinuxSampler {
1035              // if change_reso() was called immediately after note was triggered              // if change_reso() was called immediately after note was triggered
1036              // then immediately apply resonance to Note object              // then immediately apply resonance to Note object
1037              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1038                  pNote->Override.Resonance = fResonance;                  pNote->Override.Resonance.Value = fResonance;
1039                    pNote->Override.Resonance.Final = isFinal;
1040              } else { // otherwise schedule resonance change ...              } else { // otherwise schedule resonance change ...
1041                  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"
1042                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 788  namespace LinuxSampler { Line 1044  namespace LinuxSampler {
1044                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
1045                  e.Param.NoteSynthParam.Type     = Event::synth_param_resonance;                  e.Param.NoteSynthParam.Type     = Event::synth_param_resonance;
1046                  e.Param.NoteSynthParam.Delta    = fResonance;                  e.Param.NoteSynthParam.Delta    = fResonance;
1047                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1048                        isFinal, false, false
1049                    );
1050                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
1051              }              }
1052          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1053              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1054              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
1055                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
1056                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
1057    
# Line 804  namespace LinuxSampler { Line 1061  namespace LinuxSampler {
1061                  // if change_reso() was called immediately after note was triggered                  // if change_reso() was called immediately after note was triggered
1062                  // then immediately apply resonance to Note object                  // then immediately apply resonance to Note object
1063                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1064                      pNote->Override.Resonance = fResonance;                      pNote->Override.Resonance.Value = fResonance;
1065                        pNote->Override.Resonance.Final = isFinal;
1066                  } else { // otherwise schedule resonance change ...                  } else { // otherwise schedule resonance change ...
1067                      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"
1068                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 812  namespace LinuxSampler { Line 1070  namespace LinuxSampler {
1070                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
1071                      e.Param.NoteSynthParam.Type     = Event::synth_param_resonance;                      e.Param.NoteSynthParam.Type     = Event::synth_param_resonance;
1072                      e.Param.NoteSynthParam.Delta    = fResonance;                      e.Param.NoteSynthParam.Delta    = fResonance;
1073                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1074                            isFinal, false, false
1075                        );
1076                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
1077                  }                  }
1078              }              }
# Line 829  namespace LinuxSampler { Line 1088  namespace LinuxSampler {
1088      {      {
1089      }      }
1090    
1091      bool InstrumentScriptVMFunction_change_attack::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_change_attack::acceptsArgType(vmint iArg, ExprType_t type) const {
1092          if (iArg == 0)          if (iArg == 0)
1093              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
1094          else          else
1095              return type == INT_EXPR;              return type == INT_EXPR || type == REAL_EXPR;
1096        }
1097    
1098        bool InstrumentScriptVMFunction_change_attack::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
1099            if (iArg == 1)
1100                return type == VM_NO_UNIT || type == VM_SECOND;
1101            else
1102                return type == VM_NO_UNIT;
1103        }
1104    
1105        bool InstrumentScriptVMFunction_change_attack::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
1106            return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
1107        }
1108    
1109        bool InstrumentScriptVMFunction_change_attack::acceptsArgFinal(vmint iArg) const {
1110            return iArg == 1;
1111        }
1112    
1113        void InstrumentScriptVMFunction_change_attack::checkArgs(VMFnArgs* args,
1114                                                                 std::function<void(String)> err,
1115                                                                 std::function<void(String)> wrn)
1116        {
1117            // super class checks
1118            Super::checkArgs(args, err, wrn);
1119    
1120            // own checks ...
1121            if (args->argsCount() >= 2) {
1122                VMNumberExpr* argTime = args->arg(1)->asNumber();
1123                if (argTime->unitType() && !argTime->isFinal()) {
1124                    wrn("Argument 2 implies 'final' value when using seconds as unit for attack time.");
1125                }
1126            }
1127      }      }
1128    
1129      VMFnResult* InstrumentScriptVMFunction_change_attack::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_attack::exec(VMFnArgs* args) {
1130          int attack = args->arg(1)->asInt()->evalInt();          const StdUnit_t unit = args->arg(1)->asNumber()->unitType();
1131          if (attack > VM_EG_PAR_MAX_VALUE) {          vmint attack =
1132              wrnMsg("change_attack(): argument 2 may not be larger than 1000000");              (unit) ?
1133              attack = VM_EG_PAR_MAX_VALUE;                  args->arg(1)->asNumber()->evalCastInt(VM_MICRO) :
1134          } else if (attack < 0) {                  args->arg(1)->asNumber()->evalCastInt();
1135            const bool isFinal =
1136                (unit) ?
1137                    true : // imply 'final' value if unit type is used
1138                    args->arg(1)->asNumber()->isFinal();
1139            // note: intentionally not checking against a max. value here!
1140            // (to allow i.e. passing 2000000 for doubling the attack time)
1141            if (attack < 0) {
1142              wrnMsg("change_attack(): argument 2 may not be negative");              wrnMsg("change_attack(): argument 2 may not be negative");
1143              attack = 0;              attack = 0;
1144          }          }
1145          const float fAttack = float(attack) / float(VM_EG_PAR_MAX_VALUE);          const float fAttack =
1146                (unit) ? attack : float(attack) / float(VM_EG_PAR_MAX_VALUE);
1147    
1148          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
1149              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 867  namespace LinuxSampler { Line 1165  namespace LinuxSampler {
1165              // if change_attack() was called immediately after note was triggered              // if change_attack() was called immediately after note was triggered
1166              // then immediately apply attack to Note object              // then immediately apply attack to Note object
1167              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1168                  pNote->Override.Attack = fAttack;                  pNote->Override.Attack.Value = fAttack;
1169                    pNote->Override.Attack.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1170              } else { // otherwise schedule attack change ...              } else { // otherwise schedule attack change ...
1171                  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"
1172                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 875  namespace LinuxSampler { Line 1174  namespace LinuxSampler {
1174                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
1175                  e.Param.NoteSynthParam.Type     = Event::synth_param_attack;                  e.Param.NoteSynthParam.Type     = Event::synth_param_attack;
1176                  e.Param.NoteSynthParam.Delta    = fAttack;                  e.Param.NoteSynthParam.Delta    = fAttack;
1177                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1178                        isFinal, false, unit
1179                    );
1180                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
1181              }              }
1182          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1183              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1184              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
1185                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
1186                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
1187    
# Line 891  namespace LinuxSampler { Line 1191  namespace LinuxSampler {
1191                  // if change_attack() was called immediately after note was triggered                  // if change_attack() was called immediately after note was triggered
1192                  // then immediately apply attack to Note object                  // then immediately apply attack to Note object
1193                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1194                      pNote->Override.Attack = fAttack;                      pNote->Override.Attack.Value = fAttack;
1195                        pNote->Override.Attack.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1196                  } else { // otherwise schedule attack change ...                  } else { // otherwise schedule attack change ...
1197                      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"
1198                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 899  namespace LinuxSampler { Line 1200  namespace LinuxSampler {
1200                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
1201                      e.Param.NoteSynthParam.Type     = Event::synth_param_attack;                      e.Param.NoteSynthParam.Type     = Event::synth_param_attack;
1202                      e.Param.NoteSynthParam.Delta    = fAttack;                      e.Param.NoteSynthParam.Delta    = fAttack;
1203                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1204                            isFinal, false, unit
1205                        );
1206                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
1207                  }                  }
1208              }              }
# Line 916  namespace LinuxSampler { Line 1218  namespace LinuxSampler {
1218      {      {
1219      }      }
1220    
1221      bool InstrumentScriptVMFunction_change_decay::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_change_decay::acceptsArgType(vmint iArg, ExprType_t type) const {
1222          if (iArg == 0)          if (iArg == 0)
1223              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
1224          else          else
1225              return type == INT_EXPR;              return type == INT_EXPR || type == REAL_EXPR;
1226        }
1227    
1228        bool InstrumentScriptVMFunction_change_decay::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
1229            if (iArg == 1)
1230                return type == VM_NO_UNIT || type == VM_SECOND;
1231            else
1232                return type == VM_NO_UNIT;
1233        }
1234    
1235        bool InstrumentScriptVMFunction_change_decay::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
1236            return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
1237        }
1238    
1239        bool InstrumentScriptVMFunction_change_decay::acceptsArgFinal(vmint iArg) const {
1240            return iArg == 1;
1241        }
1242    
1243        void InstrumentScriptVMFunction_change_decay::checkArgs(VMFnArgs* args,
1244                                                                std::function<void(String)> err,
1245                                                                std::function<void(String)> wrn)
1246        {
1247            // super class checks
1248            Super::checkArgs(args, err, wrn);
1249    
1250            // own checks ...
1251            if (args->argsCount() >= 2) {
1252                VMNumberExpr* argTime = args->arg(1)->asNumber();
1253                if (argTime->unitType() && !argTime->isFinal()) {
1254                    wrn("Argument 2 implies 'final' value when using seconds as unit for decay time.");
1255                }
1256            }
1257      }      }
1258    
1259      VMFnResult* InstrumentScriptVMFunction_change_decay::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_decay::exec(VMFnArgs* args) {
1260          int decay = args->arg(1)->asInt()->evalInt();          const StdUnit_t unit = args->arg(1)->asNumber()->unitType();
1261          if (decay > VM_EG_PAR_MAX_VALUE) {          vmint decay =
1262              wrnMsg("change_decay(): argument 2 may not be larger than 1000000");              (unit) ?
1263              decay = VM_EG_PAR_MAX_VALUE;                  args->arg(1)->asNumber()->evalCastInt(VM_MICRO) :
1264          } else if (decay < 0) {                  args->arg(1)->asNumber()->evalCastInt();
1265            const bool isFinal =
1266                (unit) ?
1267                    true : // imply 'final' value if unit type is used
1268                    args->arg(1)->asNumber()->isFinal();
1269            // note: intentionally not checking against a max. value here!
1270            // (to allow i.e. passing 2000000 for doubling the decay time)
1271            if (decay < 0) {
1272              wrnMsg("change_decay(): argument 2 may not be negative");              wrnMsg("change_decay(): argument 2 may not be negative");
1273              decay = 0;              decay = 0;
1274          }          }
1275          const float fDecay = float(decay) / float(VM_EG_PAR_MAX_VALUE);          const float fDecay =
1276                (unit) ? decay : float(decay) / float(VM_EG_PAR_MAX_VALUE);
1277    
1278          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
1279              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 954  namespace LinuxSampler { Line 1295  namespace LinuxSampler {
1295              // if change_decay() was called immediately after note was triggered              // if change_decay() was called immediately after note was triggered
1296              // then immediately apply decay to Note object              // then immediately apply decay to Note object
1297              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1298                  pNote->Override.Decay = fDecay;                  pNote->Override.Decay.Value = fDecay;
1299                    pNote->Override.Decay.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1300              } else { // otherwise schedule decay change ...              } else { // otherwise schedule decay change ...
1301                  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"
1302                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 962  namespace LinuxSampler { Line 1304  namespace LinuxSampler {
1304                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
1305                  e.Param.NoteSynthParam.Type     = Event::synth_param_decay;                  e.Param.NoteSynthParam.Type     = Event::synth_param_decay;
1306                  e.Param.NoteSynthParam.Delta    = fDecay;                  e.Param.NoteSynthParam.Delta    = fDecay;
1307                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1308                        isFinal, false, unit
1309                    );
1310                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
1311              }              }
1312          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1313              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1314              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
1315                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
1316                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
1317    
# Line 978  namespace LinuxSampler { Line 1321  namespace LinuxSampler {
1321                  // if change_decay() was called immediately after note was triggered                  // if change_decay() was called immediately after note was triggered
1322                  // then immediately apply decay to Note object                  // then immediately apply decay to Note object
1323                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1324                      pNote->Override.Decay = fDecay;                      pNote->Override.Decay.Value = fDecay;
1325                        pNote->Override.Decay.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1326                  } else { // otherwise schedule decay change ...                  } else { // otherwise schedule decay change ...
1327                      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"
1328                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 986  namespace LinuxSampler { Line 1330  namespace LinuxSampler {
1330                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
1331                      e.Param.NoteSynthParam.Type     = Event::synth_param_decay;                      e.Param.NoteSynthParam.Type     = Event::synth_param_decay;
1332                      e.Param.NoteSynthParam.Delta    = fDecay;                      e.Param.NoteSynthParam.Delta    = fDecay;
1333                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1334                            isFinal, false, unit
1335                        );
1336                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
1337                  }                  }
1338              }              }
# Line 1003  namespace LinuxSampler { Line 1348  namespace LinuxSampler {
1348      {      {
1349      }      }
1350    
1351      bool InstrumentScriptVMFunction_change_release::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_change_release::acceptsArgType(vmint iArg, ExprType_t type) const {
1352          if (iArg == 0)          if (iArg == 0)
1353              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
1354          else          else
1355              return type == INT_EXPR;              return type == INT_EXPR || type == REAL_EXPR;
1356        }
1357    
1358        bool InstrumentScriptVMFunction_change_release::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
1359            if (iArg == 1)
1360                return type == VM_NO_UNIT || type == VM_SECOND;
1361            else
1362                return type == VM_NO_UNIT;
1363        }
1364    
1365        bool InstrumentScriptVMFunction_change_release::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
1366            return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
1367        }
1368    
1369        bool InstrumentScriptVMFunction_change_release::acceptsArgFinal(vmint iArg) const {
1370            return iArg == 1;
1371        }
1372    
1373        void InstrumentScriptVMFunction_change_release::checkArgs(VMFnArgs* args,
1374                                                                  std::function<void(String)> err,
1375                                                                  std::function<void(String)> wrn)
1376        {
1377            // super class checks
1378            Super::checkArgs(args, err, wrn);
1379    
1380            // own checks ...
1381            if (args->argsCount() >= 2) {
1382                VMNumberExpr* argTime = args->arg(1)->asNumber();
1383                if (argTime->unitType() && !argTime->isFinal()) {
1384                    wrn("Argument 2 implies 'final' value when using seconds as unit for release time.");
1385                }
1386            }
1387      }      }
1388    
1389      VMFnResult* InstrumentScriptVMFunction_change_release::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_release::exec(VMFnArgs* args) {
1390          int release = args->arg(1)->asInt()->evalInt();          const StdUnit_t unit = args->arg(1)->asNumber()->unitType();
1391          if (release > VM_EG_PAR_MAX_VALUE) {          vmint release =
1392              wrnMsg("change_release(): argument 2 may not be larger than 1000000");              (unit) ?
1393              release = VM_EG_PAR_MAX_VALUE;                  args->arg(1)->asNumber()->evalCastInt(VM_MICRO) :
1394          } else if (release < 0) {                  args->arg(1)->asNumber()->evalCastInt();
1395            const bool isFinal =
1396                (unit) ?
1397                    true : // imply 'final' value if unit type is used
1398                    args->arg(1)->asNumber()->isFinal();
1399            // note: intentionally not checking against a max. value here!
1400            // (to allow i.e. passing 2000000 for doubling the release time)
1401            if (release < 0) {
1402              wrnMsg("change_release(): argument 2 may not be negative");              wrnMsg("change_release(): argument 2 may not be negative");
1403              release = 0;              release = 0;
1404          }          }
1405          const float fRelease = float(release) / float(VM_EG_PAR_MAX_VALUE);          const float fRelease =
1406                (unit) ? release : float(release) / float(VM_EG_PAR_MAX_VALUE);
1407    
1408          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
1409              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 1041  namespace LinuxSampler { Line 1425  namespace LinuxSampler {
1425              // if change_release() was called immediately after note was triggered              // if change_release() was called immediately after note was triggered
1426              // then immediately apply relase to Note object              // then immediately apply relase to Note object
1427              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1428                  pNote->Override.Release = fRelease;                  pNote->Override.Release.Value = fRelease;
1429                    pNote->Override.Release.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1430              } else { // otherwise schedule release change ...              } else { // otherwise schedule release change ...
1431                  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"
1432                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 1049  namespace LinuxSampler { Line 1434  namespace LinuxSampler {
1434                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
1435                  e.Param.NoteSynthParam.Type     = Event::synth_param_release;                  e.Param.NoteSynthParam.Type     = Event::synth_param_release;
1436                  e.Param.NoteSynthParam.Delta    = fRelease;                  e.Param.NoteSynthParam.Delta    = fRelease;
1437                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1438                        isFinal, false, unit
1439                    );
1440                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
1441              }              }
1442          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1443              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1444              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
1445                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
1446                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
1447    
# Line 1065  namespace LinuxSampler { Line 1451  namespace LinuxSampler {
1451                  // if change_release() was called immediately after note was triggered                  // if change_release() was called immediately after note was triggered
1452                  // then immediately apply relase to Note object                  // then immediately apply relase to Note object
1453                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1454                      pNote->Override.Release = fRelease;                      pNote->Override.Release.Value = fRelease;
1455                        pNote->Override.Release.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1456                  } else { // otherwise schedule release change ...                  } else { // otherwise schedule release change ...
1457                      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"
1458                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 1073  namespace LinuxSampler { Line 1460  namespace LinuxSampler {
1460                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
1461                      e.Param.NoteSynthParam.Type     = Event::synth_param_release;                      e.Param.NoteSynthParam.Type     = Event::synth_param_release;
1462                      e.Param.NoteSynthParam.Delta    = fRelease;                      e.Param.NoteSynthParam.Delta    = fRelease;
1463                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1464                            isFinal, false, unit
1465                        );
1466                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
1467                  }                  }
1468              }              }
# Line 1085  namespace LinuxSampler { Line 1473  namespace LinuxSampler {
1473    
1474      // template for change_*() functions      // template for change_*() functions
1475    
1476      bool VMChangeSynthParamFunction::acceptsArgType(int iArg, ExprType_t type) const {      bool VMChangeSynthParamFunction::acceptsArgType(vmint iArg, ExprType_t type) const {
1477          if (iArg == 0)          if (iArg == 0)
1478              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
1479          else          else
1480              return type == INT_EXPR;              return type == INT_EXPR || (m_acceptReal && type == REAL_EXPR);
1481        }
1482    
1483        bool VMChangeSynthParamFunction::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
1484            if (iArg == 1)
1485                return type == VM_NO_UNIT || type == m_unit;
1486            else
1487                return type == VM_NO_UNIT;
1488        }
1489    
1490        bool VMChangeSynthParamFunction::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
1491            return m_acceptUnitPrefix && iArg == 1 && type == m_unit; // only allow metric prefix(es) if approprirate unit type is used (e.g. Hz)
1492        }
1493    
1494        bool VMChangeSynthParamFunction::acceptsArgFinal(vmint iArg) const {
1495            return (m_acceptFinal) ? (iArg == 1) : false;
1496        }
1497    
1498        inline static void setNoteParamScopeBy_FinalUnit(NoteBase::Param& param, const bool bFinal, const StdUnit_t unit) {
1499            param.Scope = NoteBase::scopeBy_FinalUnit(bFinal, unit);
1500        }
1501    
1502        inline static void setNoteParamScopeBy_FinalUnit(NoteBase::Norm& param, const bool bFinal, const StdUnit_t unit) {
1503            param.Final = bFinal;
1504        }
1505    
1506        inline static void setNoteParamScopeBy_FinalUnit(float& param, const bool bFinal, const StdUnit_t unit) {
1507            /* NOOP */
1508        }
1509    
1510        template<class T>
1511        inline static void setNoteParamValue(T& param, vmfloat value) {
1512            param.Value = value;
1513        }
1514    
1515        inline static void setNoteParamValue(float& param, vmfloat value) {
1516            param = value;
1517        }
1518    
1519        void VMChangeSynthParamFunction::checkArgs(VMFnArgs* args,
1520                                                   std::function<void(String)> err,
1521                                                   std::function<void(String)> wrn)
1522        {
1523            // super class checks
1524            Super::checkArgs(args, err, wrn);
1525    
1526            // own checks ...
1527            if (m_unit && m_unit != VM_BEL && args->argsCount() >= 2) {
1528                VMNumberExpr* arg = args->arg(1)->asNumber();
1529                if (arg && arg->unitType() && !arg->isFinal()) {
1530                    wrn("Argument 2 implies 'final' value when unit type " +
1531                        unitTypeStr(arg->unitType()) + " is used.");
1532                }
1533            }
1534      }      }
1535    
1536      // Arbitrarily chosen constant value symbolizing "no limit".      // Arbitrarily chosen constant value symbolizing "no limit".
1537      #define NO_LIMIT 1315916909      #define NO_LIMIT 1315916909
1538    
1539      template<float NoteBase::_Override::*T_noteParam, int T_synthParam,      template<class T_NoteParamType, T_NoteParamType NoteBase::_Override::*T_noteParam,
1540               bool T_isNormalizedParam, int T_maxValue, int T_minValue>               vmint T_synthParam,
1541      VMFnResult* VMChangeSynthParamFunction::execTemplate(VMFnArgs* args, const char* functionName) {               vmint T_minValueNorm, vmint T_maxValueNorm, bool T_normalizeNorm,
1542          int value = args->arg(1)->asInt()->evalInt();               vmint T_minValueUnit, vmint T_maxValueUnit,
1543          if (T_maxValue != NO_LIMIT && value > T_maxValue) {               MetricPrefix_t T_unitPrefix0, MetricPrefix_t ... T_unitPrefixN>
1544              wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValue));      VMFnResult* VMChangeSynthParamFunction::execTemplate(VMFnArgs* args, const char* functionName)
1545              value = T_maxValue;      {
1546          } else if (T_minValue != NO_LIMIT && value < T_minValue) {          const StdUnit_t unit = args->arg(1)->asNumber()->unitType();
1547              if (T_minValue == 0)          const bool isFinal =
1548                  wrnMsg(String(functionName) + "(): argument 2 may not be negative");              (m_unit && m_unit != VM_BEL && unit) ?
1549              else                  true : // imply 'final' value if unit type is used (except of 'Bel' which may be relative)
1550                  wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValue));                  args->arg(1)->asNumber()->isFinal();
1551              value = T_minValue;          vmint value =
1552          }              (m_acceptUnitPrefix && ((m_unit && unit) || (!m_unit && args->arg(1)->asNumber()->hasUnitFactorNow())))
1553          const float fValue = (T_isNormalizedParam) ?                  ? args->arg(1)->asNumber()->evalCastInt(T_unitPrefix0, T_unitPrefixN ...)
1554              float(value) / float(T_maxValue) : // convert to 0.0 .. 1.0 value range                  : args->arg(1)->asNumber()->evalCastInt();
1555              float(value) / 1000000.f; // assuming microseconds here, convert to seconds  
1556            // check if passed value is in allowed range
1557            if (unit && m_unit) {
1558                if (T_maxValueUnit != NO_LIMIT && value > T_maxValueUnit) {
1559                    wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValueUnit));
1560                    value = T_maxValueUnit;
1561                } else if (T_minValueUnit != NO_LIMIT && value < T_minValueUnit) {
1562                    if (T_minValueUnit == 0)
1563                        wrnMsg(String(functionName) + "(): argument 2 may not be negative");
1564                    else
1565                        wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValueUnit));
1566                    value = T_minValueUnit;
1567                }
1568            } else { // value was passed to this function without a unit ...
1569                if (T_maxValueNorm != NO_LIMIT && value > T_maxValueNorm) {
1570                    wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValueNorm));
1571                    value = T_maxValueNorm;
1572                } else if (T_minValueNorm != NO_LIMIT && value < T_minValueNorm) {
1573                    if (T_minValueNorm == 0)
1574                        wrnMsg(String(functionName) + "(): argument 2 may not be negative");
1575                    else
1576                        wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValueNorm));
1577                    value = T_minValueNorm;
1578                }
1579            }
1580    
1581            // convert passed argument value to engine internal expected value range (i.e. 0.0 .. 1.0)
1582            const float fValue =
1583                (unit && m_unit) ?
1584                    (unit == VM_BEL) ?
1585                        RTMath::DecibelToLinRatio(float(value) * float(T_unitPrefix0) /*i.e. mdB -> dB*/) :
1586                        float(value) * VMUnit::unitFactor(T_unitPrefix0, T_unitPrefixN ...) /*i.e. us -> s*/ :
1587                    (T_normalizeNorm) ?
1588                        float(value) / ((T_maxValueNorm != NO_LIMIT) ? float(T_maxValueNorm) : 1000000.f/* fallback: value range used for most */) :
1589                        float(value) /* as is */;
1590    
1591          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
1592              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 1134  namespace LinuxSampler { Line 1609  namespace LinuxSampler {
1609              // note was triggered then immediately apply the synth parameter              // note was triggered then immediately apply the synth parameter
1610              // change to Note object              // change to Note object
1611              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1612                  pNote->Override.*T_noteParam = fValue;                  setNoteParamValue(pNote->Override.*T_noteParam, fValue);
1613                    setNoteParamScopeBy_FinalUnit(
1614                        (pNote->Override.*T_noteParam),
1615                        isFinal, unit
1616                    );
1617              } else { // otherwise schedule this synth parameter change ...              } else { // otherwise schedule this synth parameter change ...
1618                  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"
1619                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 1142  namespace LinuxSampler { Line 1621  namespace LinuxSampler {
1621                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
1622                  e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;                  e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1623                  e.Param.NoteSynthParam.Delta    = fValue;                  e.Param.NoteSynthParam.Delta    = fValue;
1624                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1625                        isFinal, false, unit
1626                    );
1627                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
1628              }              }
1629          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1630              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1631              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
1632                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
1633                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
1634    
# Line 1159  namespace LinuxSampler { Line 1639  namespace LinuxSampler {
1639                  // note was triggered then immediately apply the synth parameter                  // note was triggered then immediately apply the synth parameter
1640                  // change to Note object                  // change to Note object
1641                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1642                      pNote->Override.*T_noteParam = fValue;                      setNoteParamValue(pNote->Override.*T_noteParam, fValue);
1643                        setNoteParamScopeBy_FinalUnit(
1644                            (pNote->Override.*T_noteParam),
1645                            isFinal, unit
1646                        );
1647                  } else { // otherwise schedule this synth parameter change ...                  } else { // otherwise schedule this synth parameter change ...
1648                      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"
1649                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 1167  namespace LinuxSampler { Line 1651  namespace LinuxSampler {
1651                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
1652                      e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;                      e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1653                      e.Param.NoteSynthParam.Delta    = fValue;                      e.Param.NoteSynthParam.Delta    = fValue;
1654                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1655                            isFinal, false, unit
1656                        );
1657                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
1658                  }                  }
1659              }              }
# Line 1177  namespace LinuxSampler { Line 1662  namespace LinuxSampler {
1662          return successResult();          return successResult();
1663      }      }
1664    
1665        // change_sustain() function
1666    
1667        VMFnResult* InstrumentScriptVMFunction_change_sustain::exec(VMFnArgs* args) {
1668            return VMChangeSynthParamFunction::execTemplate<
1669                        decltype(NoteBase::_Override::Sustain),
1670                        &NoteBase::_Override::Sustain,
1671                        Event::synth_param_sustain,
1672                        /* if value passed without unit */
1673                        0, NO_LIMIT, true,
1674                        /* if value passed WITH 'Bel' unit */
1675                        NO_LIMIT, NO_LIMIT, VM_MILLI, VM_DECI>( args, "change_sustain" );
1676        }
1677    
1678        // change_cutoff_attack() function
1679    
1680        VMFnResult* InstrumentScriptVMFunction_change_cutoff_attack::exec(VMFnArgs* args) {
1681            return VMChangeSynthParamFunction::execTemplate<
1682                        decltype(NoteBase::_Override::CutoffAttack),
1683                        &NoteBase::_Override::CutoffAttack,
1684                        Event::synth_param_cutoff_attack,
1685                        /* if value passed without unit */
1686                        0, NO_LIMIT, true,
1687                        /* if value passed with 'seconds' unit */
1688                        0, NO_LIMIT, VM_MICRO>( args, "change_cutoff_attack" );
1689        }
1690    
1691        // change_cutoff_decay() function
1692    
1693        VMFnResult* InstrumentScriptVMFunction_change_cutoff_decay::exec(VMFnArgs* args) {
1694            return VMChangeSynthParamFunction::execTemplate<
1695                        decltype(NoteBase::_Override::CutoffDecay),
1696                        &NoteBase::_Override::CutoffDecay,
1697                        Event::synth_param_cutoff_decay,
1698                        /* if value passed without unit */
1699                        0, NO_LIMIT, true,
1700                        /* if value passed with 'seconds' unit */
1701                        0, NO_LIMIT, VM_MICRO>( args, "change_cutoff_decay" );
1702        }
1703    
1704        // change_cutoff_sustain() function
1705    
1706        VMFnResult* InstrumentScriptVMFunction_change_cutoff_sustain::exec(VMFnArgs* args) {
1707            return VMChangeSynthParamFunction::execTemplate<
1708                        decltype(NoteBase::_Override::CutoffSustain),
1709                        &NoteBase::_Override::CutoffSustain,
1710                        Event::synth_param_cutoff_sustain,
1711                        /* if value passed without unit */
1712                        0, NO_LIMIT, true,
1713                        /* if value passed WITH 'Bel' unit */
1714                        NO_LIMIT, NO_LIMIT, VM_MILLI, VM_DECI>( args, "change_cutoff_sustain" );
1715        }
1716    
1717        // change_cutoff_release() function
1718    
1719        VMFnResult* InstrumentScriptVMFunction_change_cutoff_release::exec(VMFnArgs* args) {
1720            return VMChangeSynthParamFunction::execTemplate<
1721                        decltype(NoteBase::_Override::CutoffRelease),
1722                        &NoteBase::_Override::CutoffRelease,
1723                        Event::synth_param_cutoff_release,
1724                        /* if value passed without unit */
1725                        0, NO_LIMIT, true,
1726                        /* if value passed with 'seconds' unit */
1727                        0, NO_LIMIT, VM_MICRO>( args, "change_cutoff_release" );
1728        }
1729    
1730      // change_amp_lfo_depth() function      // change_amp_lfo_depth() function
1731    
1732      VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_depth::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_depth::exec(VMFnArgs* args) {
1733          return VMChangeSynthParamFunction::execTemplate<          return VMChangeSynthParamFunction::execTemplate<
1734                        decltype(NoteBase::_Override::AmpLFODepth),
1735                      &NoteBase::_Override::AmpLFODepth,                      &NoteBase::_Override::AmpLFODepth,
1736                      Event::synth_param_amp_lfo_depth,                      Event::synth_param_amp_lfo_depth,
1737                      true, 1000000, 0>( args, "change_amp_lfo_depth" );                      /* if value passed without unit */
1738                        0, 1000000, true,
1739                        /* not used (since this function does not accept unit) */
1740                        NO_LIMIT, NO_LIMIT, VM_NO_PREFIX>( args, "change_amp_lfo_depth" );
1741      }      }
1742    
1743      // change_amp_lfo_freq() function      // change_amp_lfo_freq() function
1744    
1745      VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_freq::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_freq::exec(VMFnArgs* args) {
1746          return VMChangeSynthParamFunction::execTemplate<          return VMChangeSynthParamFunction::execTemplate<
1747                        decltype(NoteBase::_Override::AmpLFOFreq),
1748                      &NoteBase::_Override::AmpLFOFreq,                      &NoteBase::_Override::AmpLFOFreq,
1749                      Event::synth_param_amp_lfo_freq,                      Event::synth_param_amp_lfo_freq,
1750                      true, 1000000, 0>( args, "change_amp_lfo_freq" );                      /* if value passed without unit */
1751                        0, 1000000, true,
1752                        /* if value passed with 'Hz' unit */
1753                        0, 30000, VM_NO_PREFIX>( args, "change_amp_lfo_freq" );
1754        }
1755    
1756        // change_cutoff_lfo_depth() function
1757    
1758        VMFnResult* InstrumentScriptVMFunction_change_cutoff_lfo_depth::exec(VMFnArgs* args) {
1759            return VMChangeSynthParamFunction::execTemplate<
1760                        decltype(NoteBase::_Override::CutoffLFODepth),
1761                        &NoteBase::_Override::CutoffLFODepth,
1762                        Event::synth_param_cutoff_lfo_depth,
1763                        /* if value passed without unit */
1764                        0, 1000000, true,
1765                        /* not used (since this function does not accept unit) */
1766                        NO_LIMIT, NO_LIMIT, VM_NO_PREFIX>( args, "change_cutoff_lfo_depth" );
1767        }
1768    
1769        // change_cutoff_lfo_freq() function
1770    
1771        VMFnResult* InstrumentScriptVMFunction_change_cutoff_lfo_freq::exec(VMFnArgs* args) {
1772            return VMChangeSynthParamFunction::execTemplate<
1773                        decltype(NoteBase::_Override::CutoffLFOFreq),
1774                        &NoteBase::_Override::CutoffLFOFreq,
1775                        Event::synth_param_cutoff_lfo_freq,
1776                        /* if value passed without unit */
1777                        0, 1000000, true,
1778                        /* if value passed with 'Hz' unit */
1779                        0, 30000, VM_NO_PREFIX>( args, "change_cutoff_lfo_freq" );
1780      }      }
1781    
1782      // change_pitch_lfo_depth() function      // change_pitch_lfo_depth() function
1783    
1784      VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_depth::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_depth::exec(VMFnArgs* args) {
1785          return VMChangeSynthParamFunction::execTemplate<          return VMChangeSynthParamFunction::execTemplate<
1786                        decltype(NoteBase::_Override::PitchLFODepth),
1787                      &NoteBase::_Override::PitchLFODepth,                      &NoteBase::_Override::PitchLFODepth,
1788                      Event::synth_param_pitch_lfo_depth,                      Event::synth_param_pitch_lfo_depth,
1789                      true, 1000000, 0>( args, "change_pitch_lfo_depth" );                      /* if value passed without unit */
1790                        0, 1000000, true,
1791                        /* not used (since this function does not accept unit) */
1792                        NO_LIMIT, NO_LIMIT, VM_NO_PREFIX>( args, "change_pitch_lfo_depth" );
1793      }      }
1794    
1795      // change_pitch_lfo_freq() function      // change_pitch_lfo_freq() function
1796    
1797      VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_freq::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_freq::exec(VMFnArgs* args) {
1798          return VMChangeSynthParamFunction::execTemplate<          return VMChangeSynthParamFunction::execTemplate<
1799                        decltype(NoteBase::_Override::PitchLFOFreq),
1800                      &NoteBase::_Override::PitchLFOFreq,                      &NoteBase::_Override::PitchLFOFreq,
1801                      Event::synth_param_pitch_lfo_freq,                      Event::synth_param_pitch_lfo_freq,
1802                      true, 1000000, 0>( args, "change_pitch_lfo_freq" );                      /* if value passed without unit */
1803                        0, 1000000, true,
1804                        /* if value passed with 'Hz' unit */
1805                        0, 30000, VM_NO_PREFIX>( args, "change_pitch_lfo_freq" );
1806      }      }
1807    
1808      // change_vol_time() function      // change_vol_time() function
1809    
1810      VMFnResult* InstrumentScriptVMFunction_change_vol_time::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_vol_time::exec(VMFnArgs* args) {
1811          return VMChangeSynthParamFunction::execTemplate<          return VMChangeSynthParamFunction::execTemplate<
1812                        decltype(NoteBase::_Override::VolumeTime),
1813                      &NoteBase::_Override::VolumeTime,                      &NoteBase::_Override::VolumeTime,
1814                      Event::synth_param_volume_time,                      Event::synth_param_volume_time,
1815                      false, NO_LIMIT, 0>( args, "change_vol_time" );                      /* if value passed without unit (implying 'us' unit) */
1816                        0, NO_LIMIT, true,
1817                        /* if value passed with 'seconds' unit */
1818                        0, NO_LIMIT, VM_MICRO>( args, "change_vol_time" );
1819      }      }
1820    
1821      // change_tune_time() function      // change_tune_time() function
1822    
1823      VMFnResult* InstrumentScriptVMFunction_change_tune_time::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_tune_time::exec(VMFnArgs* args) {
1824          return VMChangeSynthParamFunction::execTemplate<          return VMChangeSynthParamFunction::execTemplate<
1825                        decltype(NoteBase::_Override::PitchTime),
1826                      &NoteBase::_Override::PitchTime,                      &NoteBase::_Override::PitchTime,
1827                      Event::synth_param_pitch_time,                      Event::synth_param_pitch_time,
1828                      false, NO_LIMIT, 0>( args, "change_tune_time" );                      /* if value passed without unit (implying 'us' unit) */
1829                        0, NO_LIMIT, true,
1830                        /* if value passed with 'seconds' unit */
1831                        0, NO_LIMIT, VM_MICRO>( args, "change_tune_time" );
1832        }
1833    
1834        // change_pan_time() function
1835    
1836        VMFnResult* InstrumentScriptVMFunction_change_pan_time::exec(VMFnArgs* args) {
1837            return VMChangeSynthParamFunction::execTemplate<
1838                        decltype(NoteBase::_Override::PanTime),
1839                        &NoteBase::_Override::PanTime,
1840                        Event::synth_param_pan_time,
1841                        /* if value passed without unit (implying 'us' unit) */
1842                        0, NO_LIMIT, true,
1843                        /* if value passed with 'seconds' unit */
1844                        0, NO_LIMIT, VM_MICRO>( args, "change_pan_time" );
1845      }      }
1846    
1847      // template for change_*_curve() functions      // template for change_*_curve() functions
1848    
1849      bool VMChangeFadeCurveFunction::acceptsArgType(int iArg, ExprType_t type) const {      bool VMChangeFadeCurveFunction::acceptsArgType(vmint iArg, ExprType_t type) const {
1850          if (iArg == 0)          if (iArg == 0)
1851              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
1852          else          else
1853              return type == INT_EXPR;              return type == INT_EXPR;
1854      }      }
1855    
1856      template<fade_curve_t NoteBase::_Override::*T_noteParam, int T_synthParam>      template<fade_curve_t NoteBase::_Override::*T_noteParam, vmint T_synthParam>
1857      VMFnResult* VMChangeFadeCurveFunction::execTemplate(VMFnArgs* args, const char* functionName) {      VMFnResult* VMChangeFadeCurveFunction::execTemplate(VMFnArgs* args, const char* functionName) {
1858          int value = args->arg(1)->asInt()->evalInt();          vmint value = args->arg(1)->asInt()->evalInt();
1859          switch (value) {          switch (value) {
1860              case FADE_CURVE_LINEAR:              case FADE_CURVE_LINEAR:
1861              case FADE_CURVE_EASE_IN_EASE_OUT:              case FADE_CURVE_EASE_IN_EASE_OUT:
# Line 1281  namespace LinuxSampler { Line 1894  namespace LinuxSampler {
1894                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
1895                  e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;                  e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1896                  e.Param.NoteSynthParam.Delta    = value;                  e.Param.NoteSynthParam.Delta    = value;
1897                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
1898    
1899                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
1900              }              }
1901          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1902              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1903              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
1904                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
1905                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
1906    
# Line 1306  namespace LinuxSampler { Line 1919  namespace LinuxSampler {
1919                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
1920                      e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;                      e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1921                      e.Param.NoteSynthParam.Delta    = value;                      e.Param.NoteSynthParam.Delta    = value;
1922                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
1923    
1924                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
1925                  }                  }
# Line 1332  namespace LinuxSampler { Line 1945  namespace LinuxSampler {
1945                      Event::synth_param_pitch_curve>( args, "change_tune_curve" );                      Event::synth_param_pitch_curve>( args, "change_tune_curve" );
1946      }      }
1947    
1948        // change_pan_curve() function
1949    
1950        VMFnResult* InstrumentScriptVMFunction_change_pan_curve::exec(VMFnArgs* args) {
1951            return VMChangeFadeCurveFunction::execTemplate<
1952            &NoteBase::_Override::PanCurve,
1953            Event::synth_param_pan_curve>( args, "change_pan_curve" );
1954        }
1955    
1956      // fade_in() function      // fade_in() function
1957    
1958      InstrumentScriptVMFunction_fade_in::InstrumentScriptVMFunction_fade_in(InstrumentScriptVM* parent)      InstrumentScriptVMFunction_fade_in::InstrumentScriptVMFunction_fade_in(InstrumentScriptVM* parent)
# Line 1339  namespace LinuxSampler { Line 1960  namespace LinuxSampler {
1960      {      {
1961      }      }
1962    
1963      bool InstrumentScriptVMFunction_fade_in::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_fade_in::acceptsArgType(vmint iArg, ExprType_t type) const {
1964          if (iArg == 0)          if (iArg == 0)
1965              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
1966          else          else
1967              return type == INT_EXPR;              return type == INT_EXPR || type == REAL_EXPR;
1968        }
1969    
1970        bool InstrumentScriptVMFunction_fade_in::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
1971            if (iArg == 1)
1972                return type == VM_NO_UNIT || type == VM_SECOND;
1973            else
1974                return type == VM_NO_UNIT;
1975        }
1976    
1977        bool InstrumentScriptVMFunction_fade_in::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
1978            return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
1979      }      }
1980    
1981      VMFnResult* InstrumentScriptVMFunction_fade_in::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_fade_in::exec(VMFnArgs* args) {
1982          int duration = args->arg(1)->asInt()->evalInt();          StdUnit_t unit = args->arg(1)->asNumber()->unitType();
1983            vmint duration =
1984                (unit) ?
1985                    args->arg(1)->asNumber()->evalCastInt(VM_MICRO) :
1986                    args->arg(1)->asNumber()->evalCastInt();
1987          if (duration < 0) {          if (duration < 0) {
1988              wrnMsg("fade_in(): argument 2 may not be negative");              wrnMsg("fade_in(): argument 2 may not be negative");
1989              duration = 0;              duration = 0;
# Line 1375  namespace LinuxSampler { Line 2011  namespace LinuxSampler {
2011              // then immediately apply a start volume of zero to Note object,              // then immediately apply a start volume of zero to Note object,
2012              // as well as the fade in duration              // as well as the fade in duration
2013              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
2014                  pNote->Override.Volume = 0.f;                  pNote->Override.Volume.Value = 0.f;
2015                  pNote->Override.VolumeTime = fDuration;                  pNote->Override.VolumeTime = fDuration;
2016              } 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 ...
2017                  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 1384  namespace LinuxSampler { Line 2020  namespace LinuxSampler {
2020                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
2021                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
2022                  e.Param.NoteSynthParam.Delta    = fDuration;                  e.Param.NoteSynthParam.Delta    = fDuration;
2023                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2024    
2025                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
2026              }              }
# Line 1397  namespace LinuxSampler { Line 2033  namespace LinuxSampler {
2033                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
2034                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume;                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
2035                  e.Param.NoteSynthParam.Delta    = 1.f;                  e.Param.NoteSynthParam.Delta    = 1.f;
2036                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2037    
2038                  // scheduling with 0 delay would also work here, but +1 is more                  // scheduling with 0 delay would also work here, but +1 is more
2039                  // safe regarding potential future implementation changes of the                  // safe regarding potential future implementation changes of the
# Line 1406  namespace LinuxSampler { Line 2042  namespace LinuxSampler {
2042              }              }
2043          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
2044              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
2045              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
2046                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
2047                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
2048    
# Line 1417  namespace LinuxSampler { Line 2053  namespace LinuxSampler {
2053                  // then immediately apply a start volume of zero to Note object,                  // then immediately apply a start volume of zero to Note object,
2054                  // as well as the fade in duration                  // as well as the fade in duration
2055                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
2056                      pNote->Override.Volume = 0.f;                      pNote->Override.Volume.Value = 0.f;
2057                      pNote->Override.VolumeTime = fDuration;                      pNote->Override.VolumeTime = fDuration;
2058                  } 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 ...
2059                      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 1426  namespace LinuxSampler { Line 2062  namespace LinuxSampler {
2062                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
2063                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
2064                      e.Param.NoteSynthParam.Delta    = fDuration;                      e.Param.NoteSynthParam.Delta    = fDuration;
2065                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2066    
2067                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
2068                  }                  }
# Line 1439  namespace LinuxSampler { Line 2075  namespace LinuxSampler {
2075                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
2076                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume;                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
2077                      e.Param.NoteSynthParam.Delta    = 1.f;                      e.Param.NoteSynthParam.Delta    = 1.f;
2078                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2079    
2080                      // scheduling with 0 delay would also work here, but +1 is more                      // scheduling with 0 delay would also work here, but +1 is more
2081                      // safe regarding potential future implementation changes of the                      // safe regarding potential future implementation changes of the
# Line 1459  namespace LinuxSampler { Line 2095  namespace LinuxSampler {
2095      {      {
2096      }      }
2097    
2098      bool InstrumentScriptVMFunction_fade_out::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_fade_out::acceptsArgType(vmint iArg, ExprType_t type) const {
2099          if (iArg == 0)          if (iArg == 0)
2100              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
2101          else          else
2102              return type == INT_EXPR;              return type == INT_EXPR || type == REAL_EXPR;
2103        }
2104    
2105        bool InstrumentScriptVMFunction_fade_out::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
2106            if (iArg == 1)
2107                return type == VM_NO_UNIT || type == VM_SECOND;
2108            else
2109                return type == VM_NO_UNIT;
2110        }
2111    
2112        bool InstrumentScriptVMFunction_fade_out::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
2113            return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
2114      }      }
2115    
2116      VMFnResult* InstrumentScriptVMFunction_fade_out::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_fade_out::exec(VMFnArgs* args) {
2117          int duration = args->arg(1)->asInt()->evalInt();          StdUnit_t unit = args->arg(1)->asNumber()->unitType();
2118            vmint duration =
2119                (unit) ?
2120                    args->arg(1)->asNumber()->evalCastInt(VM_MICRO) :
2121                    args->arg(1)->asNumber()->evalCastInt();
2122          if (duration < 0) {          if (duration < 0) {
2123              wrnMsg("fade_out(): argument 2 may not be negative");              wrnMsg("fade_out(): argument 2 may not be negative");
2124              duration = 0;              duration = 0;
# Line 1504  namespace LinuxSampler { Line 2155  namespace LinuxSampler {
2155                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
2156                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
2157                  e.Param.NoteSynthParam.Delta    = fDuration;                  e.Param.NoteSynthParam.Delta    = fDuration;
2158                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2159    
2160                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
2161              }              }
# Line 1517  namespace LinuxSampler { Line 2168  namespace LinuxSampler {
2168                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
2169                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume;                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
2170                  e.Param.NoteSynthParam.Delta    = 0.f;                  e.Param.NoteSynthParam.Delta    = 0.f;
2171                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2172    
2173                  // scheduling with 0 delay would also work here, but +1 is more                  // scheduling with 0 delay would also work here, but +1 is more
2174                  // safe regarding potential future implementation changes of the                  // safe regarding potential future implementation changes of the
# Line 1538  namespace LinuxSampler { Line 2189  namespace LinuxSampler {
2189              }              }
2190          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
2191              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
2192              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
2193                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
2194                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
2195    
# Line 1556  namespace LinuxSampler { Line 2207  namespace LinuxSampler {
2207                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
2208                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
2209                      e.Param.NoteSynthParam.Delta    = fDuration;                      e.Param.NoteSynthParam.Delta    = fDuration;
2210                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2211    
2212                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
2213                  }                  }
# Line 1569  namespace LinuxSampler { Line 2220  namespace LinuxSampler {
2220                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
2221                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume;                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
2222                      e.Param.NoteSynthParam.Delta    = 0.f;                      e.Param.NoteSynthParam.Delta    = 0.f;
2223                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2224    
2225                      // scheduling with 0 delay would also work here, but +1 is more                      // scheduling with 0 delay would also work here, but +1 is more
2226                      // safe regarding potential future implementation changes of the                      // safe regarding potential future implementation changes of the
# Line 1621  namespace LinuxSampler { Line 2272  namespace LinuxSampler {
2272              return successResult(0);              return successResult(0);
2273          }          }
2274    
2275          const int parameter = args->arg(1)->asInt()->evalInt();          const vmint parameter = args->arg(1)->asInt()->evalInt();
2276          switch (parameter) {          switch (parameter) {
2277              case EVENT_PAR_NOTE:              case EVENT_PAR_NOTE:
2278                  return successResult(pNote->cause.Param.Note.Key);                  return successResult(pNote->cause.Param.Note.Key);
# Line 1629  namespace LinuxSampler { Line 2280  namespace LinuxSampler {
2280                  return successResult(pNote->cause.Param.Note.Velocity);                  return successResult(pNote->cause.Param.Note.Velocity);
2281              case EVENT_PAR_VOLUME:              case EVENT_PAR_VOLUME:
2282                  return successResult(                  return successResult(
2283                      RTMath::LinRatioToDecibel(pNote->Override.Volume) * 1000.f                      RTMath::LinRatioToDecibel(pNote->Override.Volume.Value) * 1000.f
2284                  );                  );
2285              case EVENT_PAR_TUNE:              case EVENT_PAR_TUNE:
2286                  return successResult(                  return successResult(
2287                       RTMath::FreqRatioToCents(pNote->Override.Pitch) * 1000.f                       RTMath::FreqRatioToCents(pNote->Override.Pitch.Value) * 1000.f
2288                  );                  );
2289              case EVENT_PAR_0:              case EVENT_PAR_0:
2290                  return successResult(pNote->userPar[0]);                  return successResult(pNote->userPar[0]);
# Line 1673  namespace LinuxSampler { Line 2324  namespace LinuxSampler {
2324          NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );          NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2325          if (!pNote) return successResult();          if (!pNote) return successResult();
2326    
2327          const int parameter = args->arg(1)->asInt()->evalInt();          const vmint parameter = args->arg(1)->asInt()->evalInt();
2328          const int value     = args->arg(2)->asInt()->evalInt();          const vmint value     = args->arg(2)->asInt()->evalInt();
2329    
2330          switch (parameter) {          switch (parameter) {
2331              case EVENT_PAR_NOTE:              case EVENT_PAR_NOTE:
# Line 1749  namespace LinuxSampler { Line 2400  namespace LinuxSampler {
2400          NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );          NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2401          if (!pNote) return successResult();          if (!pNote) return successResult();
2402    
2403          const int value = args->arg(1)->asInt()->evalInt();          const vmint value = args->arg(1)->asInt()->evalInt();
2404          if (value < 0 || value > 127) {          if (value < 0 || value > 127) {
2405              wrnMsg("change_note(): note number of argument 2 is out of range");              wrnMsg("change_note(): note number of argument 2 is out of range");
2406              return successResult();              return successResult();
# Line 1789  namespace LinuxSampler { Line 2440  namespace LinuxSampler {
2440          NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );          NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2441          if (!pNote) return successResult();          if (!pNote) return successResult();
2442    
2443          const int value = args->arg(1)->asInt()->evalInt();          const vmint value = args->arg(1)->asInt()->evalInt();
2444          if (value < 0 || value > 127) {          if (value < 0 || value > 127) {
2445              wrnMsg("change_velo(): velocity of argument 2 is out of range");              wrnMsg("change_velo(): velocity of argument 2 is out of range");
2446              return successResult();              return successResult();
# Line 1808  namespace LinuxSampler { Line 2459  namespace LinuxSampler {
2459      // change_play_pos() function      // change_play_pos() function
2460    
2461      InstrumentScriptVMFunction_change_play_pos::InstrumentScriptVMFunction_change_play_pos(InstrumentScriptVM* parent)      InstrumentScriptVMFunction_change_play_pos::InstrumentScriptVMFunction_change_play_pos(InstrumentScriptVM* parent)
2462      : m_vm(parent)          : m_vm(parent)
2463      {      {
2464      }      }
2465    
2466        bool InstrumentScriptVMFunction_change_play_pos::acceptsArgType(vmint iArg, ExprType_t type) const {
2467            if (iArg == 0)
2468                return type == INT_EXPR;
2469            else
2470                return type == INT_EXPR || type == REAL_EXPR;
2471        }
2472    
2473        bool InstrumentScriptVMFunction_change_play_pos::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
2474            if (iArg == 1)
2475                return type == VM_NO_UNIT || type == VM_SECOND;
2476            else
2477                return type == VM_NO_UNIT;
2478        }
2479    
2480        bool InstrumentScriptVMFunction_change_play_pos::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
2481            return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
2482        }
2483    
2484      VMFnResult* InstrumentScriptVMFunction_change_play_pos::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_play_pos::exec(VMFnArgs* args) {
2485          const ScriptID id = args->arg(0)->asInt()->evalInt();          const ScriptID id = args->arg(0)->asInt()->evalInt();
2486          if (!id) {          if (!id) {
# Line 1823  namespace LinuxSampler { Line 2492  namespace LinuxSampler {
2492              return successResult();              return successResult();
2493          }          }
2494    
2495          const int pos = args->arg(1)->asInt()->evalInt();          StdUnit_t unit = args->arg(1)->asNumber()->unitType();
2496            const vmint pos =
2497                (unit) ?
2498                    args->arg(1)->asNumber()->evalCastInt(VM_MICRO) :
2499                    args->arg(1)->asNumber()->evalCastInt();
2500          if (pos < 0) {          if (pos < 0) {
2501              wrnMsg("change_play_pos(): playback position of argument 2 may not be negative");              wrnMsg("change_play_pos(): playback position of argument 2 may not be negative");
2502              return successResult();              return successResult();
# Line 1835  namespace LinuxSampler { Line 2508  namespace LinuxSampler {
2508          NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );          NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2509          if (!pNote) return successResult();          if (!pNote) return successResult();
2510    
2511          pNote->Override.SampleOffset = pos;          pNote->Override.SampleOffset =
2512                (decltype(pNote->Override.SampleOffset)) pos;
2513    
2514          return successResult();          return successResult();
2515      }      }
# Line 1982  namespace LinuxSampler { Line 2656  namespace LinuxSampler {
2656    
2657          // if we are here, then this is the parent, so we must fork this parent          // if we are here, then this is the parent, so we must fork this parent
2658    
2659          const int n =          const vmint n =
2660              (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : 1;              (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : 1;
2661          const bool bAutoAbort =          const bool bAutoAbort =
2662              (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : true;              (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : true;
# Line 2008  namespace LinuxSampler { Line 2682  namespace LinuxSampler {
2682                  return errorResult(-1); // terminate script                  return errorResult(-1); // terminate script
2683              }              }
2684              // since both parent, as well all child script execution instances              // since both parent, as well all child script execution instances
2685              // all land in this exect() method, the following is (more or less)              // all land in this exec() method, the following is (more or less)
2686              // the only feature that lets us distinguish the parent and              // the only feature that lets us distinguish the parent and
2687              // respective children from each other in this exect() method              // respective children from each other in this exec() method
2688              itChild->forkIndex = iChild + 1;              itChild->forkIndex = iChild + 1;
2689          }          }
2690    

Legend:
Removed from v.3296  
changed lines
  Added in v.3588

  ViewVC Help
Powered by ViewVC