/[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 3335 by schoenebeck, Sun Jul 30 14:33:15 2017 UTC revision 3742 by schoenebeck, Fri Feb 14 15:15:13 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            vmint attack =
1204                (unit) ?
1205                    args->arg(1)->asNumber()->evalCastInt(VM_MICRO) :
1206                    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!          // note: intentionally not checking against a max. value here!
1212          // (to allow i.e. passing 2000000 for doubling the attack time)          // (to allow i.e. passing 2000000 for doubling the attack time)
1213          if (attack < 0) {          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) ?
1219                    float(attack) / 1000000.f /* us -> s */ :
1220                    float(attack) / float(VM_EG_PAR_MAX_VALUE);
1221    
1222          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
1223              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 866  namespace LinuxSampler { Line 1239  namespace LinuxSampler {
1239              // if change_attack() was called immediately after note was triggered              // if change_attack() was called immediately after note was triggered
1240              // then immediately apply attack to Note object              // then immediately apply attack to Note object
1241              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1242                  pNote->Override.Attack = fAttack;                  pNote->Override.Attack.Value = fAttack;
1243                    pNote->Override.Attack.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1244              } else { // otherwise schedule attack change ...              } else { // otherwise schedule attack change ...
1245                  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"
1246                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 874  namespace LinuxSampler { Line 1248  namespace LinuxSampler {
1248                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
1249                  e.Param.NoteSynthParam.Type     = Event::synth_param_attack;                  e.Param.NoteSynthParam.Type     = Event::synth_param_attack;
1250                  e.Param.NoteSynthParam.Delta    = fAttack;                  e.Param.NoteSynthParam.Delta    = fAttack;
1251                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1252                        isFinal, false, unit
1253                    );
1254                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
1255              }              }
1256          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1257              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1258              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
1259                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
1260                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
1261    
# Line 890  namespace LinuxSampler { Line 1265  namespace LinuxSampler {
1265                  // if change_attack() was called immediately after note was triggered                  // if change_attack() was called immediately after note was triggered
1266                  // then immediately apply attack to Note object                  // then immediately apply attack to Note object
1267                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1268                      pNote->Override.Attack = fAttack;                      pNote->Override.Attack.Value = fAttack;
1269                        pNote->Override.Attack.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1270                  } else { // otherwise schedule attack change ...                  } else { // otherwise schedule attack change ...
1271                      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"
1272                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 898  namespace LinuxSampler { Line 1274  namespace LinuxSampler {
1274                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
1275                      e.Param.NoteSynthParam.Type     = Event::synth_param_attack;                      e.Param.NoteSynthParam.Type     = Event::synth_param_attack;
1276                      e.Param.NoteSynthParam.Delta    = fAttack;                      e.Param.NoteSynthParam.Delta    = fAttack;
1277                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1278                            isFinal, false, unit
1279                        );
1280                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
1281                  }                  }
1282              }              }
# Line 915  namespace LinuxSampler { Line 1292  namespace LinuxSampler {
1292      {      {
1293      }      }
1294    
1295      bool InstrumentScriptVMFunction_change_decay::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_change_decay::acceptsArgType(vmint iArg, ExprType_t type) const {
1296          if (iArg == 0)          if (iArg == 0)
1297              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
1298          else          else
1299              return type == INT_EXPR;              return type == INT_EXPR || type == REAL_EXPR;
1300        }
1301    
1302        bool InstrumentScriptVMFunction_change_decay::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
1303            if (iArg == 1)
1304                return type == VM_NO_UNIT || type == VM_SECOND;
1305            else
1306                return type == VM_NO_UNIT;
1307        }
1308    
1309        bool InstrumentScriptVMFunction_change_decay::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
1310            return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
1311        }
1312    
1313        bool InstrumentScriptVMFunction_change_decay::acceptsArgFinal(vmint iArg) const {
1314            return iArg == 1;
1315        }
1316    
1317        void InstrumentScriptVMFunction_change_decay::checkArgs(VMFnArgs* args,
1318                                                                std::function<void(String)> err,
1319                                                                std::function<void(String)> wrn)
1320        {
1321            // super class checks
1322            Super::checkArgs(args, err, wrn);
1323    
1324            // own checks ...
1325            if (args->argsCount() >= 2) {
1326                VMNumberExpr* argTime = args->arg(1)->asNumber();
1327                if (argTime->unitType() && !argTime->isFinal()) {
1328                    wrn("Argument 2 implies 'final' value when using seconds as unit for decay time.");
1329                }
1330            }
1331      }      }
1332    
1333      VMFnResult* InstrumentScriptVMFunction_change_decay::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_decay::exec(VMFnArgs* args) {
1334          int decay = args->arg(1)->asInt()->evalInt();          const StdUnit_t unit = args->arg(1)->asNumber()->unitType();
1335            vmint decay =
1336                (unit) ?
1337                    args->arg(1)->asNumber()->evalCastInt(VM_MICRO) :
1338                    args->arg(1)->asNumber()->evalCastInt();
1339            const bool isFinal =
1340                (unit) ?
1341                    true : // imply 'final' value if unit type is used
1342                    args->arg(1)->asNumber()->isFinal();
1343          // note: intentionally not checking against a max. value here!          // note: intentionally not checking against a max. value here!
1344          // (to allow i.e. passing 2000000 for doubling the decay time)          // (to allow i.e. passing 2000000 for doubling the decay time)
1345          if (decay < 0) {          if (decay < 0) {
1346              wrnMsg("change_decay(): argument 2 may not be negative");              wrnMsg("change_decay(): argument 2 may not be negative");
1347              decay = 0;              decay = 0;
1348          }          }
1349          const float fDecay = float(decay) / float(VM_EG_PAR_MAX_VALUE);          const float fDecay =
1350                (unit) ?
1351                    float(decay) / 1000000.f /* us -> s */ :
1352                    float(decay) / float(VM_EG_PAR_MAX_VALUE);
1353    
1354          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
1355              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 952  namespace LinuxSampler { Line 1371  namespace LinuxSampler {
1371              // if change_decay() was called immediately after note was triggered              // if change_decay() was called immediately after note was triggered
1372              // then immediately apply decay to Note object              // then immediately apply decay to Note object
1373              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1374                  pNote->Override.Decay = fDecay;                  pNote->Override.Decay.Value = fDecay;
1375                    pNote->Override.Decay.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1376              } else { // otherwise schedule decay change ...              } else { // otherwise schedule decay change ...
1377                  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"
1378                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 960  namespace LinuxSampler { Line 1380  namespace LinuxSampler {
1380                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
1381                  e.Param.NoteSynthParam.Type     = Event::synth_param_decay;                  e.Param.NoteSynthParam.Type     = Event::synth_param_decay;
1382                  e.Param.NoteSynthParam.Delta    = fDecay;                  e.Param.NoteSynthParam.Delta    = fDecay;
1383                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1384                        isFinal, false, unit
1385                    );
1386                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
1387              }              }
1388          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1389              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1390              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
1391                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
1392                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
1393    
# Line 976  namespace LinuxSampler { Line 1397  namespace LinuxSampler {
1397                  // if change_decay() was called immediately after note was triggered                  // if change_decay() was called immediately after note was triggered
1398                  // then immediately apply decay to Note object                  // then immediately apply decay to Note object
1399                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1400                      pNote->Override.Decay = fDecay;                      pNote->Override.Decay.Value = fDecay;
1401                        pNote->Override.Decay.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1402                  } else { // otherwise schedule decay change ...                  } else { // otherwise schedule decay change ...
1403                      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"
1404                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 984  namespace LinuxSampler { Line 1406  namespace LinuxSampler {
1406                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
1407                      e.Param.NoteSynthParam.Type     = Event::synth_param_decay;                      e.Param.NoteSynthParam.Type     = Event::synth_param_decay;
1408                      e.Param.NoteSynthParam.Delta    = fDecay;                      e.Param.NoteSynthParam.Delta    = fDecay;
1409                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1410                            isFinal, false, unit
1411                        );
1412                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
1413                  }                  }
1414              }              }
# Line 1001  namespace LinuxSampler { Line 1424  namespace LinuxSampler {
1424      {      {
1425      }      }
1426    
1427      bool InstrumentScriptVMFunction_change_release::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_change_release::acceptsArgType(vmint iArg, ExprType_t type) const {
1428          if (iArg == 0)          if (iArg == 0)
1429              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
1430          else          else
1431              return type == INT_EXPR;              return type == INT_EXPR || type == REAL_EXPR;
1432        }
1433    
1434        bool InstrumentScriptVMFunction_change_release::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
1435            if (iArg == 1)
1436                return type == VM_NO_UNIT || type == VM_SECOND;
1437            else
1438                return type == VM_NO_UNIT;
1439        }
1440    
1441        bool InstrumentScriptVMFunction_change_release::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
1442            return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
1443        }
1444    
1445        bool InstrumentScriptVMFunction_change_release::acceptsArgFinal(vmint iArg) const {
1446            return iArg == 1;
1447        }
1448    
1449        void InstrumentScriptVMFunction_change_release::checkArgs(VMFnArgs* args,
1450                                                                  std::function<void(String)> err,
1451                                                                  std::function<void(String)> wrn)
1452        {
1453            // super class checks
1454            Super::checkArgs(args, err, wrn);
1455    
1456            // own checks ...
1457            if (args->argsCount() >= 2) {
1458                VMNumberExpr* argTime = args->arg(1)->asNumber();
1459                if (argTime->unitType() && !argTime->isFinal()) {
1460                    wrn("Argument 2 implies 'final' value when using seconds as unit for release time.");
1461                }
1462            }
1463      }      }
1464    
1465      VMFnResult* InstrumentScriptVMFunction_change_release::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_release::exec(VMFnArgs* args) {
1466          int release = args->arg(1)->asInt()->evalInt();          const StdUnit_t unit = args->arg(1)->asNumber()->unitType();
1467            vmint release =
1468                (unit) ?
1469                    args->arg(1)->asNumber()->evalCastInt(VM_MICRO) :
1470                    args->arg(1)->asNumber()->evalCastInt();
1471            const bool isFinal =
1472                (unit) ?
1473                    true : // imply 'final' value if unit type is used
1474                    args->arg(1)->asNumber()->isFinal();
1475          // note: intentionally not checking against a max. value here!          // note: intentionally not checking against a max. value here!
1476          // (to allow i.e. passing 2000000 for doubling the release time)          // (to allow i.e. passing 2000000 for doubling the release time)
1477          if (release < 0) {          if (release < 0) {
1478              wrnMsg("change_release(): argument 2 may not be negative");              wrnMsg("change_release(): argument 2 may not be negative");
1479              release = 0;              release = 0;
1480          }          }
1481          const float fRelease = float(release) / float(VM_EG_PAR_MAX_VALUE);          const float fRelease =
1482                (unit) ?
1483                    float(release) / 1000000.f /* us -> s */ :
1484                    float(release) / float(VM_EG_PAR_MAX_VALUE);
1485    
1486          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
1487              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 1038  namespace LinuxSampler { Line 1503  namespace LinuxSampler {
1503              // if change_release() was called immediately after note was triggered              // if change_release() was called immediately after note was triggered
1504              // then immediately apply relase to Note object              // then immediately apply relase to Note object
1505              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1506                  pNote->Override.Release = fRelease;                  pNote->Override.Release.Value = fRelease;
1507                    pNote->Override.Release.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1508              } else { // otherwise schedule release change ...              } else { // otherwise schedule release change ...
1509                  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"
1510                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 1046  namespace LinuxSampler { Line 1512  namespace LinuxSampler {
1512                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
1513                  e.Param.NoteSynthParam.Type     = Event::synth_param_release;                  e.Param.NoteSynthParam.Type     = Event::synth_param_release;
1514                  e.Param.NoteSynthParam.Delta    = fRelease;                  e.Param.NoteSynthParam.Delta    = fRelease;
1515                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1516                        isFinal, false, unit
1517                    );
1518                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
1519              }              }
1520          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1521              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1522              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
1523                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
1524                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
1525    
# Line 1062  namespace LinuxSampler { Line 1529  namespace LinuxSampler {
1529                  // if change_release() was called immediately after note was triggered                  // if change_release() was called immediately after note was triggered
1530                  // then immediately apply relase to Note object                  // then immediately apply relase to Note object
1531                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1532                      pNote->Override.Release = fRelease;                      pNote->Override.Release.Value = fRelease;
1533                        pNote->Override.Release.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1534                  } else { // otherwise schedule release change ...                  } else { // otherwise schedule release change ...
1535                      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"
1536                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 1070  namespace LinuxSampler { Line 1538  namespace LinuxSampler {
1538                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
1539                      e.Param.NoteSynthParam.Type     = Event::synth_param_release;                      e.Param.NoteSynthParam.Type     = Event::synth_param_release;
1540                      e.Param.NoteSynthParam.Delta    = fRelease;                      e.Param.NoteSynthParam.Delta    = fRelease;
1541                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1542                            isFinal, false, unit
1543                        );
1544                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
1545                  }                  }
1546              }              }
# Line 1082  namespace LinuxSampler { Line 1551  namespace LinuxSampler {
1551    
1552      // template for change_*() functions      // template for change_*() functions
1553    
1554      bool VMChangeSynthParamFunction::acceptsArgType(int iArg, ExprType_t type) const {      bool VMChangeSynthParamFunction::acceptsArgType(vmint iArg, ExprType_t type) const {
1555          if (iArg == 0)          if (iArg == 0)
1556              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
1557          else          else
1558              return type == INT_EXPR;              return type == INT_EXPR || (m_acceptReal && type == REAL_EXPR);
1559        }
1560    
1561        bool VMChangeSynthParamFunction::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
1562            if (iArg == 1)
1563                return type == VM_NO_UNIT || type == m_unit;
1564            else
1565                return type == VM_NO_UNIT;
1566        }
1567    
1568        bool VMChangeSynthParamFunction::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
1569            return m_acceptUnitPrefix && iArg == 1 && type == m_unit; // only allow metric prefix(es) if approprirate unit type is used (e.g. Hz)
1570        }
1571    
1572        bool VMChangeSynthParamFunction::acceptsArgFinal(vmint iArg) const {
1573            return (m_acceptFinal) ? (iArg == 1) : false;
1574        }
1575    
1576        inline static void setNoteParamScopeBy_FinalUnit(NoteBase::Param& param, const bool bFinal, const StdUnit_t unit) {
1577            param.Scope = NoteBase::scopeBy_FinalUnit(bFinal, unit);
1578        }
1579    
1580        inline static void setNoteParamScopeBy_FinalUnit(NoteBase::Norm& param, const bool bFinal, const StdUnit_t unit) {
1581            param.Final = bFinal;
1582        }
1583    
1584        inline static void setNoteParamScopeBy_FinalUnit(float& param, const bool bFinal, const StdUnit_t unit) {
1585            /* NOOP */
1586        }
1587    
1588        template<class T>
1589        inline static void setNoteParamValue(T& param, vmfloat value) {
1590            param.Value = value;
1591        }
1592    
1593        inline static void setNoteParamValue(float& param, vmfloat value) {
1594            param = value;
1595        }
1596    
1597        void VMChangeSynthParamFunction::checkArgs(VMFnArgs* args,
1598                                                   std::function<void(String)> err,
1599                                                   std::function<void(String)> wrn)
1600        {
1601            // super class checks
1602            Super::checkArgs(args, err, wrn);
1603    
1604            // own checks ...
1605            if (m_unit && m_unit != VM_BEL && args->argsCount() >= 2) {
1606                VMNumberExpr* arg = args->arg(1)->asNumber();
1607                if (arg && arg->unitType() && !arg->isFinal()) {
1608                    wrn("Argument 2 implies 'final' value when unit type " +
1609                        unitTypeStr(arg->unitType()) + " is used.");
1610                }
1611            }
1612      }      }
1613    
1614      // Arbitrarily chosen constant value symbolizing "no limit".      // Arbitrarily chosen constant value symbolizing "no limit".
1615      #define NO_LIMIT 1315916909      #define NO_LIMIT 1315916909
1616    
1617      template<float NoteBase::_Override::*T_noteParam, int T_synthParam,      template<class T_NoteParamType, T_NoteParamType NoteBase::_Override::*T_noteParam,
1618               bool T_isNormalizedParam, int T_maxValue, int T_minValue>               vmint T_synthParam,
1619      VMFnResult* VMChangeSynthParamFunction::execTemplate(VMFnArgs* args, const char* functionName) {               vmint T_minValueNorm, vmint T_maxValueNorm, bool T_normalizeNorm,
1620          int value = args->arg(1)->asInt()->evalInt();               vmint T_minValueUnit, vmint T_maxValueUnit,
1621          if (T_maxValue != NO_LIMIT && value > T_maxValue) {               MetricPrefix_t T_unitPrefix0, MetricPrefix_t ... T_unitPrefixN>
1622              wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValue));      VMFnResult* VMChangeSynthParamFunction::execTemplate(VMFnArgs* args, const char* functionName)
1623              value = T_maxValue;      {
1624          } else if (T_minValue != NO_LIMIT && value < T_minValue) {          const StdUnit_t unit = args->arg(1)->asNumber()->unitType();
1625              if (T_minValue == 0)          const bool isFinal =
1626                  wrnMsg(String(functionName) + "(): argument 2 may not be negative");              (m_unit && m_unit != VM_BEL && unit) ?
1627              else                  true : // imply 'final' value if unit type is used (except of 'Bel' which may be relative)
1628                  wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValue));                  args->arg(1)->asNumber()->isFinal();
1629              value = T_minValue;          vmint value =
1630          }              (m_acceptUnitPrefix && ((m_unit && unit) || (!m_unit && args->arg(1)->asNumber()->hasUnitFactorNow())))
1631          const float fValue = (T_isNormalizedParam) ?                  ? args->arg(1)->asNumber()->evalCastInt(T_unitPrefix0, T_unitPrefixN ...)
1632              float(value) / float(T_maxValue) : // convert to 0.0 .. 1.0 value range                  : args->arg(1)->asNumber()->evalCastInt();
1633              float(value) / 1000000.f; // assuming microseconds here, convert to seconds  
1634            // check if passed value is in allowed range
1635            if (unit && m_unit) {
1636                if (T_maxValueUnit != NO_LIMIT && value > T_maxValueUnit) {
1637                    wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValueUnit));
1638                    value = T_maxValueUnit;
1639                } else if (T_minValueUnit != NO_LIMIT && value < T_minValueUnit) {
1640                    if (T_minValueUnit == 0)
1641                        wrnMsg(String(functionName) + "(): argument 2 may not be negative");
1642                    else
1643                        wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValueUnit));
1644                    value = T_minValueUnit;
1645                }
1646            } else { // value was passed to this function without a unit ...
1647                if (T_maxValueNorm != NO_LIMIT && value > T_maxValueNorm) {
1648                    wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValueNorm));
1649                    value = T_maxValueNorm;
1650                } else if (T_minValueNorm != NO_LIMIT && value < T_minValueNorm) {
1651                    if (T_minValueNorm == 0)
1652                        wrnMsg(String(functionName) + "(): argument 2 may not be negative");
1653                    else
1654                        wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValueNorm));
1655                    value = T_minValueNorm;
1656                }
1657            }
1658    
1659            // convert passed argument value to engine internal expected value range (i.e. 0.0 .. 1.0)
1660            const float fValue =
1661                (unit && m_unit) ?
1662                    (unit == VM_BEL) ?
1663                        RTMath::DecibelToLinRatio(float(value) * float(T_unitPrefix0) /*i.e. mdB -> dB*/) :
1664                        float(value) * VMUnit::unitFactor(T_unitPrefix0, T_unitPrefixN ...) /*i.e. us -> s*/ :
1665                    (T_normalizeNorm) ?
1666                        float(value) / ((T_maxValueNorm != NO_LIMIT) ? float(T_maxValueNorm) : 1000000.f/* fallback: value range used for most */) :
1667                        float(value) /* as is */;
1668    
1669          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
1670              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 1131  namespace LinuxSampler { Line 1687  namespace LinuxSampler {
1687              // note was triggered then immediately apply the synth parameter              // note was triggered then immediately apply the synth parameter
1688              // change to Note object              // change to Note object
1689              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1690                  pNote->Override.*T_noteParam = fValue;                  setNoteParamValue(pNote->Override.*T_noteParam, fValue);
1691                    setNoteParamScopeBy_FinalUnit(
1692                        (pNote->Override.*T_noteParam),
1693                        isFinal, unit
1694                    );
1695              } else { // otherwise schedule this synth parameter change ...              } else { // otherwise schedule this synth parameter change ...
1696                  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"
1697                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 1139  namespace LinuxSampler { Line 1699  namespace LinuxSampler {
1699                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
1700                  e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;                  e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1701                  e.Param.NoteSynthParam.Delta    = fValue;                  e.Param.NoteSynthParam.Delta    = fValue;
1702                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1703                        isFinal, false, unit
1704                    );
1705                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
1706              }              }
1707          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1708              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1709              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
1710                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
1711                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
1712    
# Line 1156  namespace LinuxSampler { Line 1717  namespace LinuxSampler {
1717                  // note was triggered then immediately apply the synth parameter                  // note was triggered then immediately apply the synth parameter
1718                  // change to Note object                  // change to Note object
1719                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1720                      pNote->Override.*T_noteParam = fValue;                      setNoteParamValue(pNote->Override.*T_noteParam, fValue);
1721                        setNoteParamScopeBy_FinalUnit(
1722                            (pNote->Override.*T_noteParam),
1723                            isFinal, unit
1724                        );
1725                  } else { // otherwise schedule this synth parameter change ...                  } else { // otherwise schedule this synth parameter change ...
1726                      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"
1727                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 1164  namespace LinuxSampler { Line 1729  namespace LinuxSampler {
1729                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
1730                      e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;                      e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1731                      e.Param.NoteSynthParam.Delta    = fValue;                      e.Param.NoteSynthParam.Delta    = fValue;
1732                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1733                            isFinal, false, unit
1734                        );
1735                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
1736                  }                  }
1737              }              }
# Line 1178  namespace LinuxSampler { Line 1744  namespace LinuxSampler {
1744    
1745      VMFnResult* InstrumentScriptVMFunction_change_sustain::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_sustain::exec(VMFnArgs* args) {
1746          return VMChangeSynthParamFunction::execTemplate<          return VMChangeSynthParamFunction::execTemplate<
1747          &NoteBase::_Override::Sustain,                      decltype(NoteBase::_Override::Sustain),
1748          Event::synth_param_sustain,                      &NoteBase::_Override::Sustain,
1749          true, NO_LIMIT, 0>( args, "change_sustain" );                      Event::synth_param_sustain,
1750                        /* if value passed without unit */
1751                        0, NO_LIMIT, true,
1752                        /* if value passed WITH 'Bel' unit */
1753                        NO_LIMIT, NO_LIMIT, VM_MILLI, VM_DECI>( args, "change_sustain" );
1754        }
1755    
1756        // change_cutoff_attack() function
1757    
1758        VMFnResult* InstrumentScriptVMFunction_change_cutoff_attack::exec(VMFnArgs* args) {
1759            return VMChangeSynthParamFunction::execTemplate<
1760                        decltype(NoteBase::_Override::CutoffAttack),
1761                        &NoteBase::_Override::CutoffAttack,
1762                        Event::synth_param_cutoff_attack,
1763                        /* if value passed without unit */
1764                        0, NO_LIMIT, true,
1765                        /* if value passed with 'seconds' unit */
1766                        0, NO_LIMIT, VM_MICRO>( args, "change_cutoff_attack" );
1767        }
1768    
1769        // change_cutoff_decay() function
1770    
1771        VMFnResult* InstrumentScriptVMFunction_change_cutoff_decay::exec(VMFnArgs* args) {
1772            return VMChangeSynthParamFunction::execTemplate<
1773                        decltype(NoteBase::_Override::CutoffDecay),
1774                        &NoteBase::_Override::CutoffDecay,
1775                        Event::synth_param_cutoff_decay,
1776                        /* if value passed without unit */
1777                        0, NO_LIMIT, true,
1778                        /* if value passed with 'seconds' unit */
1779                        0, NO_LIMIT, VM_MICRO>( args, "change_cutoff_decay" );
1780        }
1781    
1782        // change_cutoff_sustain() function
1783    
1784        VMFnResult* InstrumentScriptVMFunction_change_cutoff_sustain::exec(VMFnArgs* args) {
1785            return VMChangeSynthParamFunction::execTemplate<
1786                        decltype(NoteBase::_Override::CutoffSustain),
1787                        &NoteBase::_Override::CutoffSustain,
1788                        Event::synth_param_cutoff_sustain,
1789                        /* if value passed without unit */
1790                        0, NO_LIMIT, true,
1791                        /* if value passed WITH 'Bel' unit */
1792                        NO_LIMIT, NO_LIMIT, VM_MILLI, VM_DECI>( args, "change_cutoff_sustain" );
1793        }
1794    
1795        // change_cutoff_release() function
1796    
1797        VMFnResult* InstrumentScriptVMFunction_change_cutoff_release::exec(VMFnArgs* args) {
1798            return VMChangeSynthParamFunction::execTemplate<
1799                        decltype(NoteBase::_Override::CutoffRelease),
1800                        &NoteBase::_Override::CutoffRelease,
1801                        Event::synth_param_cutoff_release,
1802                        /* if value passed without unit */
1803                        0, NO_LIMIT, true,
1804                        /* if value passed with 'seconds' unit */
1805                        0, NO_LIMIT, VM_MICRO>( args, "change_cutoff_release" );
1806      }      }
1807    
1808      // change_amp_lfo_depth() function      // change_amp_lfo_depth() function
1809    
1810      VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_depth::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_depth::exec(VMFnArgs* args) {
1811          return VMChangeSynthParamFunction::execTemplate<          return VMChangeSynthParamFunction::execTemplate<
1812                        decltype(NoteBase::_Override::AmpLFODepth),
1813                      &NoteBase::_Override::AmpLFODepth,                      &NoteBase::_Override::AmpLFODepth,
1814                      Event::synth_param_amp_lfo_depth,                      Event::synth_param_amp_lfo_depth,
1815                      true, 1000000, 0>( args, "change_amp_lfo_depth" );                      /* if value passed without unit */
1816                        0, NO_LIMIT, true,
1817                        /* not used (since this function does not accept unit) */
1818                        NO_LIMIT, NO_LIMIT, VM_NO_PREFIX>( args, "change_amp_lfo_depth" );
1819      }      }
1820    
1821      // change_amp_lfo_freq() function      // change_amp_lfo_freq() function
1822    
1823      VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_freq::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_freq::exec(VMFnArgs* args) {
1824          return VMChangeSynthParamFunction::execTemplate<          return VMChangeSynthParamFunction::execTemplate<
1825                        decltype(NoteBase::_Override::AmpLFOFreq),
1826                      &NoteBase::_Override::AmpLFOFreq,                      &NoteBase::_Override::AmpLFOFreq,
1827                      Event::synth_param_amp_lfo_freq,                      Event::synth_param_amp_lfo_freq,
1828                      true, 1000000, 0>( args, "change_amp_lfo_freq" );                      /* if value passed without unit */
1829                        0, NO_LIMIT, true,
1830                        /* if value passed with 'Hz' unit */
1831                        0, 30000, VM_NO_PREFIX>( args, "change_amp_lfo_freq" );
1832        }
1833    
1834        // change_cutoff_lfo_depth() function
1835    
1836        VMFnResult* InstrumentScriptVMFunction_change_cutoff_lfo_depth::exec(VMFnArgs* args) {
1837            return VMChangeSynthParamFunction::execTemplate<
1838                        decltype(NoteBase::_Override::CutoffLFODepth),
1839                        &NoteBase::_Override::CutoffLFODepth,
1840                        Event::synth_param_cutoff_lfo_depth,
1841                        /* if value passed without unit */
1842                        0, NO_LIMIT, true,
1843                        /* not used (since this function does not accept unit) */
1844                        NO_LIMIT, NO_LIMIT, VM_NO_PREFIX>( args, "change_cutoff_lfo_depth" );
1845        }
1846    
1847        // change_cutoff_lfo_freq() function
1848    
1849        VMFnResult* InstrumentScriptVMFunction_change_cutoff_lfo_freq::exec(VMFnArgs* args) {
1850            return VMChangeSynthParamFunction::execTemplate<
1851                        decltype(NoteBase::_Override::CutoffLFOFreq),
1852                        &NoteBase::_Override::CutoffLFOFreq,
1853                        Event::synth_param_cutoff_lfo_freq,
1854                        /* if value passed without unit */
1855                        0, NO_LIMIT, true,
1856                        /* if value passed with 'Hz' unit */
1857                        0, 30000, VM_NO_PREFIX>( args, "change_cutoff_lfo_freq" );
1858      }      }
1859    
1860      // change_pitch_lfo_depth() function      // change_pitch_lfo_depth() function
1861    
1862      VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_depth::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_depth::exec(VMFnArgs* args) {
1863          return VMChangeSynthParamFunction::execTemplate<          return VMChangeSynthParamFunction::execTemplate<
1864                        decltype(NoteBase::_Override::PitchLFODepth),
1865                      &NoteBase::_Override::PitchLFODepth,                      &NoteBase::_Override::PitchLFODepth,
1866                      Event::synth_param_pitch_lfo_depth,                      Event::synth_param_pitch_lfo_depth,
1867                      true, 1000000, 0>( args, "change_pitch_lfo_depth" );                      /* if value passed without unit */
1868                        0, NO_LIMIT, true,
1869                        /* not used (since this function does not accept unit) */
1870                        NO_LIMIT, NO_LIMIT, VM_NO_PREFIX>( args, "change_pitch_lfo_depth" );
1871      }      }
1872    
1873      // change_pitch_lfo_freq() function      // change_pitch_lfo_freq() function
1874    
1875      VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_freq::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_freq::exec(VMFnArgs* args) {
1876          return VMChangeSynthParamFunction::execTemplate<          return VMChangeSynthParamFunction::execTemplate<
1877                        decltype(NoteBase::_Override::PitchLFOFreq),
1878                      &NoteBase::_Override::PitchLFOFreq,                      &NoteBase::_Override::PitchLFOFreq,
1879                      Event::synth_param_pitch_lfo_freq,                      Event::synth_param_pitch_lfo_freq,
1880                      true, 1000000, 0>( args, "change_pitch_lfo_freq" );                      /* if value passed without unit */
1881                        0, NO_LIMIT, true,
1882                        /* if value passed with 'Hz' unit */
1883                        0, 30000, VM_NO_PREFIX>( args, "change_pitch_lfo_freq" );
1884      }      }
1885    
1886      // change_vol_time() function      // change_vol_time() function
1887    
1888      VMFnResult* InstrumentScriptVMFunction_change_vol_time::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_vol_time::exec(VMFnArgs* args) {
1889          return VMChangeSynthParamFunction::execTemplate<          return VMChangeSynthParamFunction::execTemplate<
1890                        decltype(NoteBase::_Override::VolumeTime),
1891                      &NoteBase::_Override::VolumeTime,                      &NoteBase::_Override::VolumeTime,
1892                      Event::synth_param_volume_time,                      Event::synth_param_volume_time,
1893                      false, NO_LIMIT, 0>( args, "change_vol_time" );                      /* if value passed without unit (implying 'us' unit) */
1894                        0, NO_LIMIT, true,
1895                        /* if value passed with 'seconds' unit */
1896                        0, NO_LIMIT, VM_MICRO>( args, "change_vol_time" );
1897      }      }
1898    
1899      // change_tune_time() function      // change_tune_time() function
1900    
1901      VMFnResult* InstrumentScriptVMFunction_change_tune_time::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_tune_time::exec(VMFnArgs* args) {
1902          return VMChangeSynthParamFunction::execTemplate<          return VMChangeSynthParamFunction::execTemplate<
1903                        decltype(NoteBase::_Override::PitchTime),
1904                      &NoteBase::_Override::PitchTime,                      &NoteBase::_Override::PitchTime,
1905                      Event::synth_param_pitch_time,                      Event::synth_param_pitch_time,
1906                      false, NO_LIMIT, 0>( args, "change_tune_time" );                      /* if value passed without unit (implying 'us' unit) */
1907                        0, NO_LIMIT, true,
1908                        /* if value passed with 'seconds' unit */
1909                        0, NO_LIMIT, VM_MICRO>( args, "change_tune_time" );
1910      }      }
1911    
1912      // change_pan_time() function      // change_pan_time() function
1913    
1914      VMFnResult* InstrumentScriptVMFunction_change_pan_time::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_pan_time::exec(VMFnArgs* args) {
1915          return VMChangeSynthParamFunction::execTemplate<          return VMChangeSynthParamFunction::execTemplate<
1916          &NoteBase::_Override::PanTime,                      decltype(NoteBase::_Override::PanTime),
1917          Event::synth_param_pan_time,                      &NoteBase::_Override::PanTime,
1918          false, NO_LIMIT, 0>( args, "change_pan_time" );                      Event::synth_param_pan_time,
1919                        /* if value passed without unit (implying 'us' unit) */
1920                        0, NO_LIMIT, true,
1921                        /* if value passed with 'seconds' unit */
1922                        0, NO_LIMIT, VM_MICRO>( args, "change_pan_time" );
1923      }      }
1924    
1925      // template for change_*_curve() functions      // template for change_*_curve() functions
1926    
1927      bool VMChangeFadeCurveFunction::acceptsArgType(int iArg, ExprType_t type) const {      bool VMChangeFadeCurveFunction::acceptsArgType(vmint iArg, ExprType_t type) const {
1928          if (iArg == 0)          if (iArg == 0)
1929              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
1930          else          else
1931              return type == INT_EXPR;              return type == INT_EXPR;
1932      }      }
1933    
1934      template<fade_curve_t NoteBase::_Override::*T_noteParam, int T_synthParam>      template<fade_curve_t NoteBase::_Override::*T_noteParam, vmint T_synthParam>
1935      VMFnResult* VMChangeFadeCurveFunction::execTemplate(VMFnArgs* args, const char* functionName) {      VMFnResult* VMChangeFadeCurveFunction::execTemplate(VMFnArgs* args, const char* functionName) {
1936          int value = args->arg(1)->asInt()->evalInt();          vmint value = args->arg(1)->asInt()->evalInt();
1937          switch (value) {          switch (value) {
1938              case FADE_CURVE_LINEAR:              case FADE_CURVE_LINEAR:
1939              case FADE_CURVE_EASE_IN_EASE_OUT:              case FADE_CURVE_EASE_IN_EASE_OUT:
# Line 1296  namespace LinuxSampler { Line 1972  namespace LinuxSampler {
1972                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
1973                  e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;                  e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1974                  e.Param.NoteSynthParam.Delta    = value;                  e.Param.NoteSynthParam.Delta    = value;
1975                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
1976    
1977                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
1978              }              }
1979          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1980              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1981              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
1982                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
1983                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
1984    
# Line 1321  namespace LinuxSampler { Line 1997  namespace LinuxSampler {
1997                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
1998                      e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;                      e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1999                      e.Param.NoteSynthParam.Delta    = value;                      e.Param.NoteSynthParam.Delta    = value;
2000                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2001    
2002                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
2003                  }                  }
# Line 1362  namespace LinuxSampler { Line 2038  namespace LinuxSampler {
2038      {      {
2039      }      }
2040    
2041      bool InstrumentScriptVMFunction_fade_in::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_fade_in::acceptsArgType(vmint iArg, ExprType_t type) const {
2042          if (iArg == 0)          if (iArg == 0)
2043              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
2044          else          else
2045              return type == INT_EXPR;              return type == INT_EXPR || type == REAL_EXPR;
2046        }
2047    
2048        bool InstrumentScriptVMFunction_fade_in::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
2049            if (iArg == 1)
2050                return type == VM_NO_UNIT || type == VM_SECOND;
2051            else
2052                return type == VM_NO_UNIT;
2053        }
2054    
2055        bool InstrumentScriptVMFunction_fade_in::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
2056            return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
2057      }      }
2058    
2059      VMFnResult* InstrumentScriptVMFunction_fade_in::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_fade_in::exec(VMFnArgs* args) {
2060          int duration = args->arg(1)->asInt()->evalInt();          StdUnit_t unit = args->arg(1)->asNumber()->unitType();
2061            vmint duration =
2062                (unit) ?
2063                    args->arg(1)->asNumber()->evalCastInt(VM_MICRO) :
2064                    args->arg(1)->asNumber()->evalCastInt();
2065          if (duration < 0) {          if (duration < 0) {
2066              wrnMsg("fade_in(): argument 2 may not be negative");              wrnMsg("fade_in(): argument 2 may not be negative");
2067              duration = 0;              duration = 0;
# Line 1398  namespace LinuxSampler { Line 2089  namespace LinuxSampler {
2089              // then immediately apply a start volume of zero to Note object,              // then immediately apply a start volume of zero to Note object,
2090              // as well as the fade in duration              // as well as the fade in duration
2091              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
2092                  pNote->Override.Volume = 0.f;                  pNote->Override.Volume.Value = 0.f;
2093                  pNote->Override.VolumeTime = fDuration;                  pNote->Override.VolumeTime = fDuration;
2094              } 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 ...
2095                  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 1407  namespace LinuxSampler { Line 2098  namespace LinuxSampler {
2098                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
2099                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
2100                  e.Param.NoteSynthParam.Delta    = fDuration;                  e.Param.NoteSynthParam.Delta    = fDuration;
2101                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2102    
2103                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
2104              }              }
# Line 1420  namespace LinuxSampler { Line 2111  namespace LinuxSampler {
2111                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
2112                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume;                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
2113                  e.Param.NoteSynthParam.Delta    = 1.f;                  e.Param.NoteSynthParam.Delta    = 1.f;
2114                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2115    
2116                  // scheduling with 0 delay would also work here, but +1 is more                  // scheduling with 0 delay would also work here, but +1 is more
2117                  // safe regarding potential future implementation changes of the                  // safe regarding potential future implementation changes of the
# Line 1429  namespace LinuxSampler { Line 2120  namespace LinuxSampler {
2120              }              }
2121          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
2122              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
2123              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
2124                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
2125                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
2126    
# Line 1440  namespace LinuxSampler { Line 2131  namespace LinuxSampler {
2131                  // then immediately apply a start volume of zero to Note object,                  // then immediately apply a start volume of zero to Note object,
2132                  // as well as the fade in duration                  // as well as the fade in duration
2133                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
2134                      pNote->Override.Volume = 0.f;                      pNote->Override.Volume.Value = 0.f;
2135                      pNote->Override.VolumeTime = fDuration;                      pNote->Override.VolumeTime = fDuration;
2136                  } 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 ...
2137                      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 1449  namespace LinuxSampler { Line 2140  namespace LinuxSampler {
2140                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
2141                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
2142                      e.Param.NoteSynthParam.Delta    = fDuration;                      e.Param.NoteSynthParam.Delta    = fDuration;
2143                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2144    
2145                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
2146                  }                  }
# Line 1462  namespace LinuxSampler { Line 2153  namespace LinuxSampler {
2153                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
2154                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume;                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
2155                      e.Param.NoteSynthParam.Delta    = 1.f;                      e.Param.NoteSynthParam.Delta    = 1.f;
2156                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2157    
2158                      // scheduling with 0 delay would also work here, but +1 is more                      // scheduling with 0 delay would also work here, but +1 is more
2159                      // safe regarding potential future implementation changes of the                      // safe regarding potential future implementation changes of the
# Line 1482  namespace LinuxSampler { Line 2173  namespace LinuxSampler {
2173      {      {
2174      }      }
2175    
2176      bool InstrumentScriptVMFunction_fade_out::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_fade_out::acceptsArgType(vmint iArg, ExprType_t type) const {
2177          if (iArg == 0)          if (iArg == 0)
2178              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
2179          else          else
2180              return type == INT_EXPR;              return type == INT_EXPR || type == REAL_EXPR;
2181        }
2182    
2183        bool InstrumentScriptVMFunction_fade_out::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
2184            if (iArg == 1)
2185                return type == VM_NO_UNIT || type == VM_SECOND;
2186            else
2187                return type == VM_NO_UNIT;
2188        }
2189    
2190        bool InstrumentScriptVMFunction_fade_out::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
2191            return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
2192      }      }
2193    
2194      VMFnResult* InstrumentScriptVMFunction_fade_out::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_fade_out::exec(VMFnArgs* args) {
2195          int duration = args->arg(1)->asInt()->evalInt();          StdUnit_t unit = args->arg(1)->asNumber()->unitType();
2196            vmint duration =
2197                (unit) ?
2198                    args->arg(1)->asNumber()->evalCastInt(VM_MICRO) :
2199                    args->arg(1)->asNumber()->evalCastInt();
2200          if (duration < 0) {          if (duration < 0) {
2201              wrnMsg("fade_out(): argument 2 may not be negative");              wrnMsg("fade_out(): argument 2 may not be negative");
2202              duration = 0;              duration = 0;
# Line 1527  namespace LinuxSampler { Line 2233  namespace LinuxSampler {
2233                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
2234                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
2235                  e.Param.NoteSynthParam.Delta    = fDuration;                  e.Param.NoteSynthParam.Delta    = fDuration;
2236                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2237    
2238                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
2239              }              }
# Line 1540  namespace LinuxSampler { Line 2246  namespace LinuxSampler {
2246                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
2247                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume;                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
2248                  e.Param.NoteSynthParam.Delta    = 0.f;                  e.Param.NoteSynthParam.Delta    = 0.f;
2249                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2250    
2251                  // scheduling with 0 delay would also work here, but +1 is more                  // scheduling with 0 delay would also work here, but +1 is more
2252                  // safe regarding potential future implementation changes of the                  // safe regarding potential future implementation changes of the
# Line 1561  namespace LinuxSampler { Line 2267  namespace LinuxSampler {
2267              }              }
2268          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
2269              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
2270              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
2271                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
2272                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
2273    
# Line 1579  namespace LinuxSampler { Line 2285  namespace LinuxSampler {
2285                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
2286                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
2287                      e.Param.NoteSynthParam.Delta    = fDuration;                      e.Param.NoteSynthParam.Delta    = fDuration;
2288                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2289    
2290                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
2291                  }                  }
# Line 1592  namespace LinuxSampler { Line 2298  namespace LinuxSampler {
2298                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
2299                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume;                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
2300                      e.Param.NoteSynthParam.Delta    = 0.f;                      e.Param.NoteSynthParam.Delta    = 0.f;
2301                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2302    
2303                      // scheduling with 0 delay would also work here, but +1 is more                      // scheduling with 0 delay would also work here, but +1 is more
2304                      // safe regarding potential future implementation changes of the                      // safe regarding potential future implementation changes of the
# Line 1644  namespace LinuxSampler { Line 2350  namespace LinuxSampler {
2350              return successResult(0);              return successResult(0);
2351          }          }
2352    
2353          const int parameter = args->arg(1)->asInt()->evalInt();          const vmint parameter = args->arg(1)->asInt()->evalInt();
2354          switch (parameter) {          switch (parameter) {
2355              case EVENT_PAR_NOTE:              case EVENT_PAR_NOTE:
2356                  return successResult(pNote->cause.Param.Note.Key);                  return successResult(pNote->cause.Param.Note.Key);
# Line 1652  namespace LinuxSampler { Line 2358  namespace LinuxSampler {
2358                  return successResult(pNote->cause.Param.Note.Velocity);                  return successResult(pNote->cause.Param.Note.Velocity);
2359              case EVENT_PAR_VOLUME:              case EVENT_PAR_VOLUME:
2360                  return successResult(                  return successResult(
2361                      RTMath::LinRatioToDecibel(pNote->Override.Volume) * 1000.f                      RTMath::LinRatioToDecibel(pNote->Override.Volume.Value) * 1000.f
2362                  );                  );
2363              case EVENT_PAR_TUNE:              case EVENT_PAR_TUNE:
2364                  return successResult(                  return successResult(
2365                       RTMath::FreqRatioToCents(pNote->Override.Pitch) * 1000.f                       RTMath::FreqRatioToCents(pNote->Override.Pitch.Value) * 1000.f
2366                  );                  );
2367              case EVENT_PAR_0:              case EVENT_PAR_0:
2368                  return successResult(pNote->userPar[0]);                  return successResult(pNote->userPar[0]);
# Line 1696  namespace LinuxSampler { Line 2402  namespace LinuxSampler {
2402          NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );          NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2403          if (!pNote) return successResult();          if (!pNote) return successResult();
2404    
2405          const int parameter = args->arg(1)->asInt()->evalInt();          const vmint parameter = args->arg(1)->asInt()->evalInt();
2406          const int value     = args->arg(2)->asInt()->evalInt();          const vmint value     = args->arg(2)->asInt()->evalInt();
2407    
2408          switch (parameter) {          switch (parameter) {
2409              case EVENT_PAR_NOTE:              case EVENT_PAR_NOTE:
# Line 1772  namespace LinuxSampler { Line 2478  namespace LinuxSampler {
2478          NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );          NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2479          if (!pNote) return successResult();          if (!pNote) return successResult();
2480    
2481          const int value = args->arg(1)->asInt()->evalInt();          const vmint value = args->arg(1)->asInt()->evalInt();
2482          if (value < 0 || value > 127) {          if (value < 0 || value > 127) {
2483              wrnMsg("change_note(): note number of argument 2 is out of range");              wrnMsg("change_note(): note number of argument 2 is out of range");
2484              return successResult();              return successResult();
# Line 1812  namespace LinuxSampler { Line 2518  namespace LinuxSampler {
2518          NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );          NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2519          if (!pNote) return successResult();          if (!pNote) return successResult();
2520    
2521          const int value = args->arg(1)->asInt()->evalInt();          const vmint value = args->arg(1)->asInt()->evalInt();
2522          if (value < 0 || value > 127) {          if (value < 0 || value > 127) {
2523              wrnMsg("change_velo(): velocity of argument 2 is out of range");              wrnMsg("change_velo(): velocity of argument 2 is out of range");
2524              return successResult();              return successResult();
# Line 1831  namespace LinuxSampler { Line 2537  namespace LinuxSampler {
2537      // change_play_pos() function      // change_play_pos() function
2538    
2539      InstrumentScriptVMFunction_change_play_pos::InstrumentScriptVMFunction_change_play_pos(InstrumentScriptVM* parent)      InstrumentScriptVMFunction_change_play_pos::InstrumentScriptVMFunction_change_play_pos(InstrumentScriptVM* parent)
2540      : m_vm(parent)          : m_vm(parent)
2541      {      {
2542      }      }
2543    
2544        bool InstrumentScriptVMFunction_change_play_pos::acceptsArgType(vmint iArg, ExprType_t type) const {
2545            if (iArg == 0)
2546                return type == INT_EXPR;
2547            else
2548                return type == INT_EXPR || type == REAL_EXPR;
2549        }
2550    
2551        bool InstrumentScriptVMFunction_change_play_pos::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
2552            if (iArg == 1)
2553                return type == VM_NO_UNIT || type == VM_SECOND;
2554            else
2555                return type == VM_NO_UNIT;
2556        }
2557    
2558        bool InstrumentScriptVMFunction_change_play_pos::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
2559            return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
2560        }
2561    
2562      VMFnResult* InstrumentScriptVMFunction_change_play_pos::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_play_pos::exec(VMFnArgs* args) {
2563          const ScriptID id = args->arg(0)->asInt()->evalInt();          const ScriptID id = args->arg(0)->asInt()->evalInt();
2564          if (!id) {          if (!id) {
# Line 1846  namespace LinuxSampler { Line 2570  namespace LinuxSampler {
2570              return successResult();              return successResult();
2571          }          }
2572    
2573          const int pos = args->arg(1)->asInt()->evalInt();          StdUnit_t unit = args->arg(1)->asNumber()->unitType();
2574            const vmint pos =
2575                (unit) ?
2576                    args->arg(1)->asNumber()->evalCastInt(VM_MICRO) :
2577                    args->arg(1)->asNumber()->evalCastInt();
2578          if (pos < 0) {          if (pos < 0) {
2579              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");
2580              return successResult();              return successResult();
# Line 1858  namespace LinuxSampler { Line 2586  namespace LinuxSampler {
2586          NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );          NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2587          if (!pNote) return successResult();          if (!pNote) return successResult();
2588    
2589          pNote->Override.SampleOffset = pos;          pNote->Override.SampleOffset =
2590                (decltype(pNote->Override.SampleOffset)) pos;
2591    
2592          return successResult();          return successResult();
2593      }      }
# Line 2005  namespace LinuxSampler { Line 2734  namespace LinuxSampler {
2734    
2735          // 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
2736    
2737          const int n =          const vmint n =
2738              (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : 1;              (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : 1;
2739          const bool bAutoAbort =          const bool bAutoAbort =
2740              (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : true;              (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : true;
# Line 2031  namespace LinuxSampler { Line 2760  namespace LinuxSampler {
2760                  return errorResult(-1); // terminate script                  return errorResult(-1); // terminate script
2761              }              }
2762              // since both parent, as well all child script execution instances              // since both parent, as well all child script execution instances
2763              // all land in this exect() method, the following is (more or less)              // all land in this exec() method, the following is (more or less)
2764              // the only feature that lets us distinguish the parent and              // the only feature that lets us distinguish the parent and
2765              // respective children from each other in this exect() method              // respective children from each other in this exec() method
2766              itChild->forkIndex = iChild + 1;              itChild->forkIndex = iChild + 1;
2767          }          }
2768    

Legend:
Removed from v.3335  
changed lines
  Added in v.3742

  ViewVC Help
Powered by ViewVC