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

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

  ViewVC Help
Powered by ViewVC