/[svn]/linuxsampler/trunk/src/engines/common/InstrumentScriptVMFunctions.cpp
ViewVC logotype

Diff of /linuxsampler/trunk/src/engines/common/InstrumentScriptVMFunctions.cpp

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 3296 by schoenebeck, Wed Jun 28 09:45:56 2017 UTC revision 3587 by schoenebeck, Sat Aug 31 12:08:49 2019 UTC
# Line 1  Line 1 
1  /*  /*
2   * Copyright (c) 2014-2017 Christian Schoenebeck   * Copyright (c) 2014-2019 Christian Schoenebeck
3   *   *
4   * http://www.linuxsampler.org   * http://www.linuxsampler.org
5   *   *
# Line 10  Line 10 
10  #include "InstrumentScriptVMFunctions.h"  #include "InstrumentScriptVMFunctions.h"
11  #include "InstrumentScriptVM.h"  #include "InstrumentScriptVM.h"
12  #include "../AbstractEngineChannel.h"  #include "../AbstractEngineChannel.h"
13    #include "../../common/global_private.h"
14    
15  namespace LinuxSampler {  namespace LinuxSampler {
16            
# Line 20  namespace LinuxSampler { Line 21  namespace LinuxSampler {
21      {      {
22      }      }
23    
24        bool InstrumentScriptVMFunction_play_note::acceptsArgType(vmint iArg, ExprType_t type) const {
25            if (iArg == 2 || iArg == 3)
26                return type == INT_EXPR || type == REAL_EXPR;
27            else
28                return type == INT_EXPR;
29        }
30    
31        bool InstrumentScriptVMFunction_play_note::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
32            if (iArg == 2 || iArg == 3)
33                return type == VM_NO_UNIT || type == VM_SECOND;
34            else
35                return type == VM_NO_UNIT;
36        }
37    
38        bool InstrumentScriptVMFunction_play_note::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
39            if (iArg == 2 || iArg == 3)
40                return type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
41            else
42                return false;
43        }
44    
45      VMFnResult* InstrumentScriptVMFunction_play_note::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_play_note::exec(VMFnArgs* args) {
46          int note = args->arg(0)->asInt()->evalInt();          vmint note = args->arg(0)->asInt()->evalInt();
47          int velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127;          vmint velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127;
48          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;
49            vmint duration =
50                (argDuration) ?
51                    (argDuration->unitType()) ?
52                        argDuration->evalCastInt(VM_MICRO) :
53                        argDuration->evalCastInt() : 0; //TODO: -1 might be a better default value instead of 0
54    
55          if (note < 0 || note > 127) {          if (note < 0 || note > 127) {
56              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 95  namespace LinuxSampler {
95          // if a sample offset is supplied, assign the offset as override          // if a sample offset is supplied, assign the offset as override
96          // to the previously created Note object          // to the previously created Note object
97          if (args->argsCount() >= 3) {          if (args->argsCount() >= 3) {
98              int sampleoffset = args->arg(2)->asInt()->evalInt();              VMNumberExpr* argSampleOffset = args->arg(2)->asNumber();
99                vmint sampleoffset =
100                    (argSampleOffset->unitType()) ?
101                        argSampleOffset->evalCastInt(VM_MICRO) :
102                        argSampleOffset->evalCastInt();
103              if (sampleoffset >= 0) {              if (sampleoffset >= 0) {
104                  NoteBase* pNote = pEngineChannel->pEngine->NoteByID(id);                  NoteBase* pNote = pEngineChannel->pEngine->NoteByID(id);
105                  if (pNote) {                  if (pNote) {
106                      pNote->Override.SampleOffset = sampleoffset;                      pNote->Override.SampleOffset =
107                            (decltype(pNote->Override.SampleOffset)) sampleoffset;
108                  }                  }
109              } else if (sampleoffset < -1) {              } else if (sampleoffset < -1) {
110                  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 134  namespace LinuxSampler {
134      }      }
135    
136      VMFnResult* InstrumentScriptVMFunction_set_controller::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_set_controller::exec(VMFnArgs* args) {
137          int controller = args->arg(0)->asInt()->evalInt();          vmint controller = args->arg(0)->asInt()->evalInt();
138          int value      = args->arg(1)->asInt()->evalInt();          vmint value      = args->arg(1)->asInt()->evalInt();
139    
140          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
141              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 140  namespace LinuxSampler { Line 172  namespace LinuxSampler {
172      {      {
173      }      }
174    
175      bool InstrumentScriptVMFunction_ignore_event::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_ignore_event::acceptsArgType(vmint iArg, ExprType_t type) const {
176          return type == INT_EXPR || type == INT_ARR_EXPR;          return type == INT_EXPR || type == INT_ARR_EXPR;
177      }      }
178    
# Line 196  namespace LinuxSampler { Line 228  namespace LinuxSampler {
228      {      {
229      }      }
230    
231      bool InstrumentScriptVMFunction_note_off::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_note_off::acceptsArgType(vmint iArg, ExprType_t type) const {
232          return type == INT_EXPR || type == INT_ARR_EXPR;          return type == INT_EXPR || type == INT_ARR_EXPR;
233      }      }
234    
# Line 204  namespace LinuxSampler { Line 236  namespace LinuxSampler {
236          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
237              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
238    
239          int velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127;          vmint velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127;
240          if (velocity < 0 || velocity > 127) {          if (velocity < 0 || velocity > 127) {
241              errMsg("note_off(): argument 2 is an invalid velocity value");              errMsg("note_off(): argument 2 is an invalid velocity value");
242              return errorResult();              return errorResult();
# Line 235  namespace LinuxSampler { Line 267  namespace LinuxSampler {
267              pEngineChannel->ScheduleEventMicroSec(&e, 0);              pEngineChannel->ScheduleEventMicroSec(&e, 0);
268          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
269              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
270              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
271                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
272                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
273    
# Line 266  namespace LinuxSampler { Line 298  namespace LinuxSampler {
298    
299      VMFnResult* InstrumentScriptVMFunction_set_event_mark::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_set_event_mark::exec(VMFnArgs* args) {
300          const ScriptID id = args->arg(0)->asInt()->evalInt();          const ScriptID id = args->arg(0)->asInt()->evalInt();
301          const int groupID = args->arg(1)->asInt()->evalInt();          const vmint groupID = args->arg(1)->asInt()->evalInt();
302    
303          if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {          if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
304              errMsg("set_event_mark(): argument 2 is an invalid group id");              errMsg("set_event_mark(): argument 2 is an invalid group id");
# Line 304  namespace LinuxSampler { Line 336  namespace LinuxSampler {
336    
337      VMFnResult* InstrumentScriptVMFunction_delete_event_mark::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_delete_event_mark::exec(VMFnArgs* args) {
338          const ScriptID id = args->arg(0)->asInt()->evalInt();          const ScriptID id = args->arg(0)->asInt()->evalInt();
339          const int groupID = args->arg(1)->asInt()->evalInt();          const vmint groupID = args->arg(1)->asInt()->evalInt();
340    
341          if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {          if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
342              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 358  namespace LinuxSampler {
358      {      {
359      }      }
360    
361      int InstrumentScriptVMFunction_by_marks::Result::arraySize() const {      vmint InstrumentScriptVMFunction_by_marks::Result::arraySize() const {
362          return eventGroup->size();          return eventGroup->size();
363      }      }
364    
365      int InstrumentScriptVMFunction_by_marks::Result::evalIntElement(uint i) {      vmint InstrumentScriptVMFunction_by_marks::Result::evalIntElement(vmuint i) {
366          return (*eventGroup)[i];          return (*eventGroup)[i];
367      }      }
368    
# Line 347  namespace LinuxSampler { Line 379  namespace LinuxSampler {
379      }      }
380    
381      VMFnResult* InstrumentScriptVMFunction_by_marks::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_by_marks::exec(VMFnArgs* args) {
382          int groupID = args->arg(0)->asInt()->evalInt();          vmint groupID = args->arg(0)->asInt()->evalInt();
383    
384          if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {          if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
385              errMsg("by_marks(): argument is an invalid group id");              errMsg("by_marks(): argument is an invalid group id");
# Line 367  namespace LinuxSampler { Line 399  namespace LinuxSampler {
399      {      {
400      }      }
401    
402      bool InstrumentScriptVMFunction_change_vol::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_change_vol::acceptsArgType(vmint iArg, ExprType_t type) const {
403          if (iArg == 0)          if (iArg == 0)
404              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
405            else if (iArg == 1)
406                return type == INT_EXPR || type == REAL_EXPR;
407          else          else
408              return type == INT_EXPR;              return type == INT_EXPR;
409      }      }
410    
411        bool InstrumentScriptVMFunction_change_vol::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
412            if (iArg == 1)
413                return type == VM_NO_UNIT || type == VM_BEL;
414            else
415                return type == VM_NO_UNIT;
416        }
417    
418        bool InstrumentScriptVMFunction_change_vol::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
419            return iArg == 1 && type == VM_BEL; // only allow metric prefix(es) if 'Bel' is used as unit type
420        }
421    
422        bool InstrumentScriptVMFunction_change_vol::acceptsArgFinal(vmint iArg) const {
423            return iArg == 1;
424        }
425    
426      VMFnResult* InstrumentScriptVMFunction_change_vol::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_vol::exec(VMFnArgs* args) {
427          int volume = args->arg(1)->asInt()->evalInt(); // volume change in milli dB          StdUnit_t unit = args->arg(1)->asNumber()->unitType();
428            vmint volume =
429                (unit) ?
430                    args->arg(1)->asNumber()->evalCastInt(VM_MILLI,VM_DECI) :
431                    args->arg(1)->asNumber()->evalCastInt(); // volume change in milli dB
432            bool isFinal = args->arg(1)->asNumber()->isFinal();
433          bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;          bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
434          const float fVolumeLin = RTMath::DecibelToLinRatio(float(volume) / 1000.f);          const float fVolumeLin = RTMath::DecibelToLinRatio(float(volume) / 1000.f);
435    
# Line 403  namespace LinuxSampler { Line 457  namespace LinuxSampler {
457                  pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S)                  pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S)
458              {              {
459                  if (relative)                  if (relative)
460                      pNote->Override.Volume *= fVolumeLin;                      pNote->Override.Volume.Value *= fVolumeLin;
461                  else                  else
462                      pNote->Override.Volume = fVolumeLin;                      pNote->Override.Volume.Value = fVolumeLin;
463                    pNote->Override.Volume.Final = isFinal;
464              } else { // otherwise schedule the volume change ...              } else { // otherwise schedule the volume change ...
465                  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"
466                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 413  namespace LinuxSampler { Line 468  namespace LinuxSampler {
468                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
469                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume;                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
470                  e.Param.NoteSynthParam.Delta    = fVolumeLin;                  e.Param.NoteSynthParam.Delta    = fVolumeLin;
471                  e.Param.NoteSynthParam.Relative = relative;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
472                        isFinal, relative, unit
473                    );
474                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
475              }              }
476          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
477              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
478              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
479                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
480                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
481    
# Line 433  namespace LinuxSampler { Line 489  namespace LinuxSampler {
489                      pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S)                      pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S)
490                  {                  {
491                      if (relative)                      if (relative)
492                          pNote->Override.Volume *= fVolumeLin;                          pNote->Override.Volume.Value *= fVolumeLin;
493                      else                      else
494                          pNote->Override.Volume = fVolumeLin;                          pNote->Override.Volume.Value = fVolumeLin;
495                        pNote->Override.Volume.Final = isFinal;
496                  } else { // otherwise schedule the volume change ...                  } else { // otherwise schedule the volume change ...
497                      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"
498                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 443  namespace LinuxSampler { Line 500  namespace LinuxSampler {
500                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
501                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume;                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
502                      e.Param.NoteSynthParam.Delta    = fVolumeLin;                      e.Param.NoteSynthParam.Delta    = fVolumeLin;
503                      e.Param.NoteSynthParam.Relative = relative;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
504                            isFinal, relative, unit
505                        );
506                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
507                  }                  }
508              }              }
# Line 460  namespace LinuxSampler { Line 518  namespace LinuxSampler {
518      {      {
519      }      }
520    
521      bool InstrumentScriptVMFunction_change_tune::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_change_tune::acceptsArgType(vmint iArg, ExprType_t type) const {
522          if (iArg == 0)          if (iArg == 0)
523              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
524            else if (iArg == 1)
525                return type == INT_EXPR || type == REAL_EXPR;
526          else          else
527              return type == INT_EXPR;              return type == INT_EXPR;
528      }      }
529    
530        bool InstrumentScriptVMFunction_change_tune::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
531            return iArg == 1;
532        }
533    
534        bool InstrumentScriptVMFunction_change_tune::acceptsArgFinal(vmint iArg) const {
535            return iArg == 1;
536        }
537    
538      VMFnResult* InstrumentScriptVMFunction_change_tune::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_tune::exec(VMFnArgs* args) {
539          int tune = args->arg(1)->asInt()->evalInt(); // tuning change in milli cents          vmint tune =
540                (args->arg(1)->asNumber()->hasUnitFactorNow())
541                    ? args->arg(1)->asNumber()->evalCastInt(VM_MILLI,VM_CENTI)
542                    : args->arg(1)->asNumber()->evalCastInt(); // tuning change in milli cents
543            bool isFinal = args->arg(1)->asNumber()->isFinal();
544            StdUnit_t unit = args->arg(1)->asNumber()->unitType();
545          bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;          bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
546          const float fFreqRatio = RTMath::CentsToFreqRatioUnlimited(float(tune) / 1000.f);          const float fFreqRatio = RTMath::CentsToFreqRatioUnlimited(float(tune) / 1000.f);
547    
# Line 496  namespace LinuxSampler { Line 569  namespace LinuxSampler {
569                  pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S)                  pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S)
570              {              {
571                  if (relative)                  if (relative)
572                      pNote->Override.Pitch *= fFreqRatio;                      pNote->Override.Pitch.Value *= fFreqRatio;
573                  else                  else
574                      pNote->Override.Pitch = fFreqRatio;                      pNote->Override.Pitch.Value = fFreqRatio;
575                    pNote->Override.Pitch.Final = isFinal;
576              } else { // otherwise schedule tuning change ...              } else { // otherwise schedule tuning change ...
577                  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"
578                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 506  namespace LinuxSampler { Line 580  namespace LinuxSampler {
580                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
581                  e.Param.NoteSynthParam.Type     = Event::synth_param_pitch;                  e.Param.NoteSynthParam.Type     = Event::synth_param_pitch;
582                  e.Param.NoteSynthParam.Delta    = fFreqRatio;                  e.Param.NoteSynthParam.Delta    = fFreqRatio;
583                  e.Param.NoteSynthParam.Relative = relative;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
584                        isFinal, relative, unit
585                    );
586                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
587              }              }
588          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
589              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
590              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
591                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
592                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
593    
# Line 526  namespace LinuxSampler { Line 601  namespace LinuxSampler {
601                      pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S)                      pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S)
602                  {                  {
603                      if (relative)                      if (relative)
604                          pNote->Override.Pitch *= fFreqRatio;                          pNote->Override.Pitch.Value *= fFreqRatio;
605                      else                      else
606                          pNote->Override.Pitch = fFreqRatio;                          pNote->Override.Pitch.Value = fFreqRatio;
607                        pNote->Override.Pitch.Final = isFinal;
608                  } else { // otherwise schedule tuning change ...                  } else { // otherwise schedule tuning change ...
609                      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"
610                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 536  namespace LinuxSampler { Line 612  namespace LinuxSampler {
612                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
613                      e.Param.NoteSynthParam.Type     = Event::synth_param_pitch;                      e.Param.NoteSynthParam.Type     = Event::synth_param_pitch;
614                      e.Param.NoteSynthParam.Delta    = fFreqRatio;                      e.Param.NoteSynthParam.Delta    = fFreqRatio;
615                      e.Param.NoteSynthParam.Relative = relative;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
616                            isFinal, relative, unit
617                        );
618                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
619                  }                  }
620              }              }
# Line 553  namespace LinuxSampler { Line 630  namespace LinuxSampler {
630      {      {
631      }      }
632    
633      bool InstrumentScriptVMFunction_change_pan::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_change_pan::acceptsArgType(vmint iArg, ExprType_t type) const {
634          if (iArg == 0)          if (iArg == 0)
635              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
636          else          else
637              return type == INT_EXPR;              return type == INT_EXPR;
638      }      }
639    
640        bool InstrumentScriptVMFunction_change_pan::acceptsArgFinal(vmint iArg) const {
641            return iArg == 1;
642        }
643    
644      VMFnResult* InstrumentScriptVMFunction_change_pan::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_pan::exec(VMFnArgs* args) {
645          int pan = args->arg(1)->asInt()->evalInt();          vmint pan    = args->arg(1)->asInt()->evalInt();
646            bool isFinal = args->arg(1)->asInt()->isFinal();
647          bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;          bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
648    
649          if (pan > 1000) {          if (pan > 1000) {
# Line 594  namespace LinuxSampler { Line 676  namespace LinuxSampler {
676              // then immediately apply the panning to Note object              // then immediately apply the panning to Note object
677              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
678                  if (relative) {                  if (relative) {
679                      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);
680                  } else {                  } else {
681                      pNote->Override.Pan = fPan;                      pNote->Override.Pan.Value = fPan;
682                      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
683                  }                  }
684                    pNote->Override.Pan.Final = isFinal;
685              } else { // otherwise schedule panning change ...              } else { // otherwise schedule panning change ...
686                  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"
687                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 606  namespace LinuxSampler { Line 689  namespace LinuxSampler {
689                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
690                  e.Param.NoteSynthParam.Type     = Event::synth_param_pan;                  e.Param.NoteSynthParam.Type     = Event::synth_param_pan;
691                  e.Param.NoteSynthParam.Delta    = fPan;                  e.Param.NoteSynthParam.Delta    = fPan;
692                  e.Param.NoteSynthParam.Relative = relative;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
693                        isFinal, relative, false
694                    );
695                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
696              }              }
697          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
698              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
699              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
700                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
701                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
702    
# Line 623  namespace LinuxSampler { Line 707  namespace LinuxSampler {
707                  // then immediately apply the panning to Note object                  // then immediately apply the panning to Note object
708                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
709                      if (relative) {                      if (relative) {
710                          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);
711                      } else {                      } else {
712                          pNote->Override.Pan = fPan;                          pNote->Override.Pan.Value = fPan;
713                          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
714                      }                      }
715                        pNote->Override.Pan.Final = isFinal;
716                  } else { // otherwise schedule panning change ...                  } else { // otherwise schedule panning change ...
717                      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"
718                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 635  namespace LinuxSampler { Line 720  namespace LinuxSampler {
720                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
721                      e.Param.NoteSynthParam.Type     = Event::synth_param_pan;                      e.Param.NoteSynthParam.Type     = Event::synth_param_pan;
722                      e.Param.NoteSynthParam.Delta    = fPan;                      e.Param.NoteSynthParam.Delta    = fPan;
723                      e.Param.NoteSynthParam.Relative = relative;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
724                            isFinal, relative, false
725                        );
726                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
727                  }                  }
728              }              }
# Line 646  namespace LinuxSampler { Line 732  namespace LinuxSampler {
732      }      }
733    
734      #define VM_FILTER_PAR_MAX_VALUE 1000000      #define VM_FILTER_PAR_MAX_VALUE 1000000
735        #define VM_FILTER_PAR_MAX_HZ 30000
736      #define VM_EG_PAR_MAX_VALUE 1000000      #define VM_EG_PAR_MAX_VALUE 1000000
737    
738      // change_cutoff() function      // change_cutoff() function
# Line 655  namespace LinuxSampler { Line 742  namespace LinuxSampler {
742      {      {
743      }      }
744    
745      bool InstrumentScriptVMFunction_change_cutoff::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_change_cutoff::acceptsArgType(vmint iArg, ExprType_t type) const {
746          if (iArg == 0)          if (iArg == 0)
747              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
748            else if (iArg == 1)
749                return type == INT_EXPR || type == REAL_EXPR;
750          else          else
751              return type == INT_EXPR;              return type == INT_EXPR;
752      }      }
753    
754        bool InstrumentScriptVMFunction_change_cutoff::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
755            if (iArg == 1)
756                return type == VM_NO_UNIT || type == VM_HERTZ;
757            else
758                return type == VM_NO_UNIT;
759        }
760    
761        bool InstrumentScriptVMFunction_change_cutoff::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
762            return iArg == 1 && type == VM_HERTZ; // only allow metric prefix(es) if 'Hz' is used as unit type
763        }
764    
765        bool InstrumentScriptVMFunction_change_cutoff::acceptsArgFinal(vmint iArg) const {
766            return iArg == 1;
767        }
768    
769      VMFnResult* InstrumentScriptVMFunction_change_cutoff::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_cutoff::exec(VMFnArgs* args) {
770          int cutoff = args->arg(1)->asInt()->evalInt();          StdUnit_t unit = args->arg(1)->asNumber()->unitType();
771          if (cutoff > VM_FILTER_PAR_MAX_VALUE) {          vmint cutoff =
772              wrnMsg("change_cutoff(): argument 2 may not be larger than 1000000");              (unit) ?
773                    args->arg(1)->asNumber()->evalCastInt(VM_NO_PREFIX) :
774                    args->arg(1)->asNumber()->evalCastInt();
775            bool isFinal = args->arg(1)->asNumber()->isFinal();
776            if (!unit && cutoff > VM_FILTER_PAR_MAX_VALUE) {
777                wrnMsg("change_cutoff(): argument 2 may not be larger than " strfy(VM_FILTER_PAR_MAX_VALUE));
778              cutoff = VM_FILTER_PAR_MAX_VALUE;              cutoff = VM_FILTER_PAR_MAX_VALUE;
779            } else if (unit && cutoff > VM_FILTER_PAR_MAX_HZ) {
780                wrnMsg("change_cutoff(): argument 2 may not be larger than " strfy(VM_FILTER_PAR_MAX_HZ) " Hz");
781                cutoff = VM_FILTER_PAR_MAX_HZ;
782          } else if (cutoff < 0) {          } else if (cutoff < 0) {
783              wrnMsg("change_cutoff(): argument 2 may not be negative");              wrnMsg("change_cutoff(): argument 2 may not be negative");
784              cutoff = 0;              cutoff = 0;
785          }          }
786          const float fCutoff = float(cutoff) / float(VM_FILTER_PAR_MAX_VALUE);          const float fCutoff =
787                (unit) ? cutoff : float(cutoff) / float(VM_FILTER_PAR_MAX_VALUE);
788    
789            if (unit && !isFinal) {
790                wrnMsg("change_cutoff(): you must pass argument 2 as 'final' value when using Hz as unit");
791                return successResult();
792            }
793    
794          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
795              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 693  namespace LinuxSampler { Line 811  namespace LinuxSampler {
811              // if change_cutoff() was called immediately after note was triggered              // if change_cutoff() was called immediately after note was triggered
812              // then immediately apply cutoff to Note object              // then immediately apply cutoff to Note object
813              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
814                  pNote->Override.Cutoff = fCutoff;                  pNote->Override.Cutoff.Value = fCutoff;
815                    pNote->Override.Cutoff.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
816              } else { // otherwise schedule cutoff change ...              } else { // otherwise schedule cutoff change ...
817                  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"
818                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 701  namespace LinuxSampler { Line 820  namespace LinuxSampler {
820                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
821                  e.Param.NoteSynthParam.Type     = Event::synth_param_cutoff;                  e.Param.NoteSynthParam.Type     = Event::synth_param_cutoff;
822                  e.Param.NoteSynthParam.Delta    = fCutoff;                  e.Param.NoteSynthParam.Delta    = fCutoff;
823                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
824                        isFinal, false, unit
825                    );
826                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
827              }              }
828          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
829              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
830              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
831                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
832                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
833    
# Line 717  namespace LinuxSampler { Line 837  namespace LinuxSampler {
837                  // if change_cutoff() was called immediately after note was triggered                  // if change_cutoff() was called immediately after note was triggered
838                  // then immediately apply cutoff to Note object                  // then immediately apply cutoff to Note object
839                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
840                      pNote->Override.Cutoff = fCutoff;                      pNote->Override.Cutoff.Value = fCutoff;
841                        pNote->Override.Cutoff.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
842                  } else { // otherwise schedule cutoff change ...                  } else { // otherwise schedule cutoff change ...
843                      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"
844                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 725  namespace LinuxSampler { Line 846  namespace LinuxSampler {
846                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
847                      e.Param.NoteSynthParam.Type     = Event::synth_param_cutoff;                      e.Param.NoteSynthParam.Type     = Event::synth_param_cutoff;
848                      e.Param.NoteSynthParam.Delta    = fCutoff;                      e.Param.NoteSynthParam.Delta    = fCutoff;
849                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
850                            isFinal, false, unit
851                        );
852                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
853                  }                  }
854              }              }
# Line 742  namespace LinuxSampler { Line 864  namespace LinuxSampler {
864      {      {
865      }      }
866    
867      bool InstrumentScriptVMFunction_change_reso::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_change_reso::acceptsArgType(vmint iArg, ExprType_t type) const {
868          if (iArg == 0)          if (iArg == 0)
869              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
870          else          else
871              return type == INT_EXPR;              return type == INT_EXPR;
872      }      }
873    
874        bool InstrumentScriptVMFunction_change_reso::acceptsArgFinal(vmint iArg) const {
875            return iArg == 1;
876        }
877    
878      VMFnResult* InstrumentScriptVMFunction_change_reso::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_reso::exec(VMFnArgs* args) {
879          int resonance = args->arg(1)->asInt()->evalInt();          vmint resonance = args->arg(1)->asInt()->evalInt();
880            bool isFinal    = args->arg(1)->asInt()->isFinal();
881          if (resonance > VM_FILTER_PAR_MAX_VALUE) {          if (resonance > VM_FILTER_PAR_MAX_VALUE) {
882              wrnMsg("change_reso(): argument 2 may not be larger than 1000000");              wrnMsg("change_reso(): argument 2 may not be larger than 1000000");
883              resonance = VM_FILTER_PAR_MAX_VALUE;              resonance = VM_FILTER_PAR_MAX_VALUE;
# Line 780  namespace LinuxSampler { Line 907  namespace LinuxSampler {
907              // if change_reso() was called immediately after note was triggered              // if change_reso() was called immediately after note was triggered
908              // then immediately apply resonance to Note object              // then immediately apply resonance to Note object
909              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
910                  pNote->Override.Resonance = fResonance;                  pNote->Override.Resonance.Value = fResonance;
911                    pNote->Override.Resonance.Final = isFinal;
912              } else { // otherwise schedule resonance change ...              } else { // otherwise schedule resonance change ...
913                  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"
914                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 788  namespace LinuxSampler { Line 916  namespace LinuxSampler {
916                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
917                  e.Param.NoteSynthParam.Type     = Event::synth_param_resonance;                  e.Param.NoteSynthParam.Type     = Event::synth_param_resonance;
918                  e.Param.NoteSynthParam.Delta    = fResonance;                  e.Param.NoteSynthParam.Delta    = fResonance;
919                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
920                        isFinal, false, false
921                    );
922                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
923              }              }
924          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
925              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
926              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
927                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
928                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
929    
# Line 804  namespace LinuxSampler { Line 933  namespace LinuxSampler {
933                  // if change_reso() was called immediately after note was triggered                  // if change_reso() was called immediately after note was triggered
934                  // then immediately apply resonance to Note object                  // then immediately apply resonance to Note object
935                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
936                      pNote->Override.Resonance = fResonance;                      pNote->Override.Resonance.Value = fResonance;
937                        pNote->Override.Resonance.Final = isFinal;
938                  } else { // otherwise schedule resonance change ...                  } else { // otherwise schedule resonance change ...
939                      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"
940                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 812  namespace LinuxSampler { Line 942  namespace LinuxSampler {
942                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
943                      e.Param.NoteSynthParam.Type     = Event::synth_param_resonance;                      e.Param.NoteSynthParam.Type     = Event::synth_param_resonance;
944                      e.Param.NoteSynthParam.Delta    = fResonance;                      e.Param.NoteSynthParam.Delta    = fResonance;
945                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
946                            isFinal, false, false
947                        );
948                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
949                  }                  }
950              }              }
# Line 829  namespace LinuxSampler { Line 960  namespace LinuxSampler {
960      {      {
961      }      }
962    
963      bool InstrumentScriptVMFunction_change_attack::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_change_attack::acceptsArgType(vmint iArg, ExprType_t type) const {
964          if (iArg == 0)          if (iArg == 0)
965              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
966          else          else
967              return type == INT_EXPR;              return type == INT_EXPR || type == REAL_EXPR;
968        }
969    
970        bool InstrumentScriptVMFunction_change_attack::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
971            if (iArg == 1)
972                return type == VM_NO_UNIT || type == VM_SECOND;
973            else
974                return type == VM_NO_UNIT;
975        }
976    
977        bool InstrumentScriptVMFunction_change_attack::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
978            return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
979        }
980    
981        bool InstrumentScriptVMFunction_change_attack::acceptsArgFinal(vmint iArg) const {
982            return iArg == 1;
983      }      }
984    
985      VMFnResult* InstrumentScriptVMFunction_change_attack::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_attack::exec(VMFnArgs* args) {
986          int attack = args->arg(1)->asInt()->evalInt();          StdUnit_t unit = args->arg(1)->asNumber()->unitType();
987          if (attack > VM_EG_PAR_MAX_VALUE) {          vmint attack =
988              wrnMsg("change_attack(): argument 2 may not be larger than 1000000");              (unit) ?
989              attack = VM_EG_PAR_MAX_VALUE;                  args->arg(1)->asNumber()->evalCastInt(VM_MICRO) :
990          } else if (attack < 0) {                  args->arg(1)->asNumber()->evalCastInt();
991            bool isFinal = args->arg(1)->asNumber()->isFinal();
992            // note: intentionally not checking against a max. value here!
993            // (to allow i.e. passing 2000000 for doubling the attack time)
994            if (attack < 0) {
995              wrnMsg("change_attack(): argument 2 may not be negative");              wrnMsg("change_attack(): argument 2 may not be negative");
996              attack = 0;              attack = 0;
997          }          }
998          const float fAttack = float(attack) / float(VM_EG_PAR_MAX_VALUE);          const float fAttack = float(attack) / float(VM_EG_PAR_MAX_VALUE);
999    
1000            if (unit && !isFinal) {
1001                wrnMsg("change_attack(): you must pass argument 2 as 'final' value when using seconds as unit");
1002                return successResult();
1003            }
1004    
1005          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
1006              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1007    
# Line 867  namespace LinuxSampler { Line 1022  namespace LinuxSampler {
1022              // if change_attack() was called immediately after note was triggered              // if change_attack() was called immediately after note was triggered
1023              // then immediately apply attack to Note object              // then immediately apply attack to Note object
1024              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1025                  pNote->Override.Attack = fAttack;                  pNote->Override.Attack.Value = fAttack;
1026                    pNote->Override.Attack.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1027              } else { // otherwise schedule attack change ...              } else { // otherwise schedule attack change ...
1028                  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"
1029                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 875  namespace LinuxSampler { Line 1031  namespace LinuxSampler {
1031                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
1032                  e.Param.NoteSynthParam.Type     = Event::synth_param_attack;                  e.Param.NoteSynthParam.Type     = Event::synth_param_attack;
1033                  e.Param.NoteSynthParam.Delta    = fAttack;                  e.Param.NoteSynthParam.Delta    = fAttack;
1034                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1035                        isFinal, false, unit
1036                    );
1037                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
1038              }              }
1039          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1040              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1041              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
1042                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
1043                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
1044    
# Line 891  namespace LinuxSampler { Line 1048  namespace LinuxSampler {
1048                  // if change_attack() was called immediately after note was triggered                  // if change_attack() was called immediately after note was triggered
1049                  // then immediately apply attack to Note object                  // then immediately apply attack to Note object
1050                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1051                      pNote->Override.Attack = fAttack;                      pNote->Override.Attack.Value = fAttack;
1052                        pNote->Override.Attack.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1053                  } else { // otherwise schedule attack change ...                  } else { // otherwise schedule attack change ...
1054                      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"
1055                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 899  namespace LinuxSampler { Line 1057  namespace LinuxSampler {
1057                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
1058                      e.Param.NoteSynthParam.Type     = Event::synth_param_attack;                      e.Param.NoteSynthParam.Type     = Event::synth_param_attack;
1059                      e.Param.NoteSynthParam.Delta    = fAttack;                      e.Param.NoteSynthParam.Delta    = fAttack;
1060                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1061                            isFinal, false, unit
1062                        );
1063                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
1064                  }                  }
1065              }              }
# Line 916  namespace LinuxSampler { Line 1075  namespace LinuxSampler {
1075      {      {
1076      }      }
1077    
1078      bool InstrumentScriptVMFunction_change_decay::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_change_decay::acceptsArgType(vmint iArg, ExprType_t type) const {
1079          if (iArg == 0)          if (iArg == 0)
1080              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
1081          else          else
1082              return type == INT_EXPR;              return type == INT_EXPR || type == REAL_EXPR;
1083        }
1084    
1085        bool InstrumentScriptVMFunction_change_decay::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
1086            if (iArg == 1)
1087                return type == VM_NO_UNIT || type == VM_SECOND;
1088            else
1089                return type == VM_NO_UNIT;
1090        }
1091    
1092        bool InstrumentScriptVMFunction_change_decay::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
1093            return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
1094        }
1095    
1096        bool InstrumentScriptVMFunction_change_decay::acceptsArgFinal(vmint iArg) const {
1097            return iArg == 1;
1098      }      }
1099    
1100      VMFnResult* InstrumentScriptVMFunction_change_decay::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_decay::exec(VMFnArgs* args) {
1101          int decay = args->arg(1)->asInt()->evalInt();          StdUnit_t unit = args->arg(1)->asNumber()->unitType();
1102          if (decay > VM_EG_PAR_MAX_VALUE) {          vmint decay =
1103              wrnMsg("change_decay(): argument 2 may not be larger than 1000000");              (unit) ?
1104              decay = VM_EG_PAR_MAX_VALUE;                  args->arg(1)->asNumber()->evalCastInt(VM_MICRO) :
1105          } else if (decay < 0) {                  args->arg(1)->asNumber()->evalCastInt();
1106            bool isFinal = args->arg(1)->asNumber()->isFinal();
1107            // note: intentionally not checking against a max. value here!
1108            // (to allow i.e. passing 2000000 for doubling the decay time)
1109            if (decay < 0) {
1110              wrnMsg("change_decay(): argument 2 may not be negative");              wrnMsg("change_decay(): argument 2 may not be negative");
1111              decay = 0;              decay = 0;
1112          }          }
1113          const float fDecay = float(decay) / float(VM_EG_PAR_MAX_VALUE);          const float fDecay = float(decay) / float(VM_EG_PAR_MAX_VALUE);
1114    
1115            if (unit && !isFinal) {
1116                wrnMsg("change_decay(): you must pass argument 2 as 'final' value when using seconds as unit");
1117                return successResult();
1118            }
1119    
1120          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
1121              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1122    
# Line 954  namespace LinuxSampler { Line 1137  namespace LinuxSampler {
1137              // if change_decay() was called immediately after note was triggered              // if change_decay() was called immediately after note was triggered
1138              // then immediately apply decay to Note object              // then immediately apply decay to Note object
1139              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1140                  pNote->Override.Decay = fDecay;                  pNote->Override.Decay.Value = fDecay;
1141                    pNote->Override.Decay.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1142              } else { // otherwise schedule decay change ...              } else { // otherwise schedule decay change ...
1143                  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"
1144                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 962  namespace LinuxSampler { Line 1146  namespace LinuxSampler {
1146                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
1147                  e.Param.NoteSynthParam.Type     = Event::synth_param_decay;                  e.Param.NoteSynthParam.Type     = Event::synth_param_decay;
1148                  e.Param.NoteSynthParam.Delta    = fDecay;                  e.Param.NoteSynthParam.Delta    = fDecay;
1149                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1150                        isFinal, false, unit
1151                    );
1152                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
1153              }              }
1154          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1155              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1156              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
1157                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
1158                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
1159    
# Line 978  namespace LinuxSampler { Line 1163  namespace LinuxSampler {
1163                  // if change_decay() was called immediately after note was triggered                  // if change_decay() was called immediately after note was triggered
1164                  // then immediately apply decay to Note object                  // then immediately apply decay to Note object
1165                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1166                      pNote->Override.Decay = fDecay;                      pNote->Override.Decay.Value = fDecay;
1167                        pNote->Override.Decay.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1168                  } else { // otherwise schedule decay change ...                  } else { // otherwise schedule decay change ...
1169                      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"
1170                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 986  namespace LinuxSampler { Line 1172  namespace LinuxSampler {
1172                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
1173                      e.Param.NoteSynthParam.Type     = Event::synth_param_decay;                      e.Param.NoteSynthParam.Type     = Event::synth_param_decay;
1174                      e.Param.NoteSynthParam.Delta    = fDecay;                      e.Param.NoteSynthParam.Delta    = fDecay;
1175                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1176                            isFinal, false, unit
1177                        );
1178                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
1179                  }                  }
1180              }              }
# Line 1003  namespace LinuxSampler { Line 1190  namespace LinuxSampler {
1190      {      {
1191      }      }
1192    
1193      bool InstrumentScriptVMFunction_change_release::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_change_release::acceptsArgType(vmint iArg, ExprType_t type) const {
1194          if (iArg == 0)          if (iArg == 0)
1195              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
1196          else          else
1197              return type == INT_EXPR;              return type == INT_EXPR || type == REAL_EXPR;
1198        }
1199    
1200        bool InstrumentScriptVMFunction_change_release::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
1201            if (iArg == 1)
1202                return type == VM_NO_UNIT || type == VM_SECOND;
1203            else
1204                return type == VM_NO_UNIT;
1205        }
1206    
1207        bool InstrumentScriptVMFunction_change_release::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
1208            return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
1209        }
1210    
1211        bool InstrumentScriptVMFunction_change_release::acceptsArgFinal(vmint iArg) const {
1212            return iArg == 1;
1213      }      }
1214    
1215      VMFnResult* InstrumentScriptVMFunction_change_release::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_release::exec(VMFnArgs* args) {
1216          int release = args->arg(1)->asInt()->evalInt();          StdUnit_t unit = args->arg(1)->asNumber()->unitType();
1217          if (release > VM_EG_PAR_MAX_VALUE) {          vmint release =
1218              wrnMsg("change_release(): argument 2 may not be larger than 1000000");              (unit) ?
1219              release = VM_EG_PAR_MAX_VALUE;                  args->arg(1)->asNumber()->evalCastInt(VM_MICRO) :
1220          } else if (release < 0) {                  args->arg(1)->asNumber()->evalCastInt();
1221            bool isFinal = args->arg(1)->asNumber()->isFinal();
1222            // note: intentionally not checking against a max. value here!
1223            // (to allow i.e. passing 2000000 for doubling the release time)
1224            if (release < 0) {
1225              wrnMsg("change_release(): argument 2 may not be negative");              wrnMsg("change_release(): argument 2 may not be negative");
1226              release = 0;              release = 0;
1227          }          }
1228          const float fRelease = float(release) / float(VM_EG_PAR_MAX_VALUE);          const float fRelease = float(release) / float(VM_EG_PAR_MAX_VALUE);
1229    
1230            if (unit && !isFinal) {
1231                wrnMsg("change_release(): you must pass argument 2 as 'final' value when using seconds as unit");
1232                return successResult();
1233            }
1234    
1235          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
1236              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1237    
# Line 1041  namespace LinuxSampler { Line 1252  namespace LinuxSampler {
1252              // if change_release() was called immediately after note was triggered              // if change_release() was called immediately after note was triggered
1253              // then immediately apply relase to Note object              // then immediately apply relase to Note object
1254              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1255                  pNote->Override.Release = fRelease;                  pNote->Override.Release.Value = fRelease;
1256                    pNote->Override.Release.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1257              } else { // otherwise schedule release change ...              } else { // otherwise schedule release change ...
1258                  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"
1259                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 1049  namespace LinuxSampler { Line 1261  namespace LinuxSampler {
1261                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
1262                  e.Param.NoteSynthParam.Type     = Event::synth_param_release;                  e.Param.NoteSynthParam.Type     = Event::synth_param_release;
1263                  e.Param.NoteSynthParam.Delta    = fRelease;                  e.Param.NoteSynthParam.Delta    = fRelease;
1264                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1265                        isFinal, false, unit
1266                    );
1267                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
1268              }              }
1269          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1270              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1271              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
1272                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
1273                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
1274    
# Line 1065  namespace LinuxSampler { Line 1278  namespace LinuxSampler {
1278                  // if change_release() was called immediately after note was triggered                  // if change_release() was called immediately after note was triggered
1279                  // then immediately apply relase to Note object                  // then immediately apply relase to Note object
1280                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1281                      pNote->Override.Release = fRelease;                      pNote->Override.Release.Value = fRelease;
1282                        pNote->Override.Release.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1283                  } else { // otherwise schedule release change ...                  } else { // otherwise schedule release change ...
1284                      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"
1285                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 1073  namespace LinuxSampler { Line 1287  namespace LinuxSampler {
1287                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
1288                      e.Param.NoteSynthParam.Type     = Event::synth_param_release;                      e.Param.NoteSynthParam.Type     = Event::synth_param_release;
1289                      e.Param.NoteSynthParam.Delta    = fRelease;                      e.Param.NoteSynthParam.Delta    = fRelease;
1290                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1291                            isFinal, false, unit
1292                        );
1293                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
1294                  }                  }
1295              }              }
# Line 1085  namespace LinuxSampler { Line 1300  namespace LinuxSampler {
1300    
1301      // template for change_*() functions      // template for change_*() functions
1302    
1303      bool VMChangeSynthParamFunction::acceptsArgType(int iArg, ExprType_t type) const {      bool VMChangeSynthParamFunction::acceptsArgType(vmint iArg, ExprType_t type) const {
1304          if (iArg == 0)          if (iArg == 0)
1305              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
1306          else          else
1307              return type == INT_EXPR;              return type == INT_EXPR || (m_acceptReal && type == REAL_EXPR);
1308        }
1309    
1310        bool VMChangeSynthParamFunction::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
1311            if (iArg == 1)
1312                return type == VM_NO_UNIT || type == m_unit;
1313            else
1314                return type == VM_NO_UNIT;
1315        }
1316    
1317        bool VMChangeSynthParamFunction::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
1318            return m_acceptUnitPrefix && iArg == 1 && type == m_unit; // only allow metric prefix(es) if approprirate unit type is used (e.g. Hz)
1319        }
1320    
1321        bool VMChangeSynthParamFunction::acceptsArgFinal(vmint iArg) const {
1322            return (m_acceptFinal) ? (iArg == 1) : false;
1323        }
1324    
1325        inline static void setNoteParamScopeBy_FinalUnit(NoteBase::Param& param, const bool bFinal, const StdUnit_t unit) {
1326            param.Scope = NoteBase::scopeBy_FinalUnit(bFinal, unit);
1327        }
1328    
1329        inline static void setNoteParamScopeBy_FinalUnit(NoteBase::Norm& param, const bool bFinal, const StdUnit_t unit) {
1330            param.Final = bFinal;
1331        }
1332    
1333        inline static void setNoteParamScopeBy_FinalUnit(float& param, const bool bFinal, const StdUnit_t unit) {
1334            /* NOOP */
1335        }
1336    
1337        template<class T>
1338        inline static void setNoteParamValue(T& param, vmfloat value) {
1339            param.Value = value;
1340        }
1341    
1342        inline static void setNoteParamValue(float& param, vmfloat value) {
1343            param = value;
1344      }      }
1345    
1346      // Arbitrarily chosen constant value symbolizing "no limit".      // Arbitrarily chosen constant value symbolizing "no limit".
1347      #define NO_LIMIT 1315916909      #define NO_LIMIT 1315916909
1348    
1349      template<float NoteBase::_Override::*T_noteParam, int T_synthParam,      template<class T_NoteParamType, T_NoteParamType NoteBase::_Override::*T_noteParam,
1350               bool T_isNormalizedParam, int T_maxValue, int T_minValue>               vmint T_synthParam,
1351      VMFnResult* VMChangeSynthParamFunction::execTemplate(VMFnArgs* args, const char* functionName) {               vmint T_minValueNorm, vmint T_maxValueNorm, bool T_normalizeNorm,
1352          int value = args->arg(1)->asInt()->evalInt();               vmint T_minValueUnit, vmint T_maxValueUnit,
1353          if (T_maxValue != NO_LIMIT && value > T_maxValue) {               MetricPrefix_t T_unitPrefix0, MetricPrefix_t ... T_unitPrefixN>
1354              wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValue));      VMFnResult* VMChangeSynthParamFunction::execTemplate(VMFnArgs* args, const char* functionName)
1355              value = T_maxValue;      {
1356          } else if (T_minValue != NO_LIMIT && value < T_minValue) {          const StdUnit_t unit = args->arg(1)->asNumber()->unitType();
1357              if (T_minValue == 0)          const bool isFinal   = args->arg(1)->asNumber()->isFinal();
1358                  wrnMsg(String(functionName) + "(): argument 2 may not be negative");          vmint value =
1359              else              (m_acceptUnitPrefix && ((m_unit && unit) || (!m_unit && args->arg(1)->asNumber()->hasUnitFactorNow())))
1360                  wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValue));                  ? args->arg(1)->asNumber()->evalCastInt(T_unitPrefix0, T_unitPrefixN ...)
1361              value = T_minValue;                  : args->arg(1)->asNumber()->evalCastInt();
1362          }  
1363          const float fValue = (T_isNormalizedParam) ?          if (unit && !isFinal && m_unit != VM_BEL && m_unit) {
1364              float(value) / float(T_maxValue) : // convert to 0.0 .. 1.0 value range              wrnMsg(String(functionName) + "(): you must pass argument 2 as 'final' value when using a unit");
1365              float(value) / 1000000.f; // assuming microseconds here, convert to seconds              return successResult();
1366            }
1367    
1368            // check if passed value is in allowed range
1369            if (unit && m_unit) {
1370                if (T_maxValueUnit != NO_LIMIT && value > T_maxValueUnit) {
1371                    wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValueUnit));
1372                    value = T_maxValueUnit;
1373                } else if (T_minValueUnit != NO_LIMIT && value < T_minValueUnit) {
1374                    if (T_minValueUnit == 0)
1375                        wrnMsg(String(functionName) + "(): argument 2 may not be negative");
1376                    else
1377                        wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValueUnit));
1378                    value = T_minValueUnit;
1379                }
1380            } else { // value was passed to this function without a unit ...
1381                if (T_maxValueNorm != NO_LIMIT && value > T_maxValueNorm) {
1382                    wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValueNorm));
1383                    value = T_maxValueNorm;
1384                } else if (T_minValueNorm != NO_LIMIT && value < T_minValueNorm) {
1385                    if (T_minValueNorm == 0)
1386                        wrnMsg(String(functionName) + "(): argument 2 may not be negative");
1387                    else
1388                        wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValueNorm));
1389                    value = T_minValueNorm;
1390                }
1391            }
1392    
1393            // convert passed argument value to engine internal expected value range (i.e. 0.0 .. 1.0)
1394            const float fValue =
1395                (unit && m_unit) ?
1396                    (unit == VM_BEL) ?
1397                        RTMath::DecibelToLinRatio(float(value) * float(T_unitPrefix0) /*i.e. mdB -> dB*/) :
1398                        float(value) * VMUnit::unitFactor(T_unitPrefix0, T_unitPrefixN ...) /*i.e. us -> s*/ :
1399                    (T_normalizeNorm) ?
1400                        float(value) / ((T_maxValueNorm != NO_LIMIT) ? float(T_maxValueNorm) : 1000000.f/* fallback: value range used for most */) :
1401                        float(value) /* as is */;
1402    
1403          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
1404              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 1134  namespace LinuxSampler { Line 1421  namespace LinuxSampler {
1421              // note was triggered then immediately apply the synth parameter              // note was triggered then immediately apply the synth parameter
1422              // change to Note object              // change to Note object
1423              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1424                  pNote->Override.*T_noteParam = fValue;                  setNoteParamValue(pNote->Override.*T_noteParam, fValue);
1425                    setNoteParamScopeBy_FinalUnit(
1426                        (pNote->Override.*T_noteParam),
1427                        isFinal, unit
1428                    );
1429              } else { // otherwise schedule this synth parameter change ...              } else { // otherwise schedule this synth parameter change ...
1430                  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"
1431                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 1142  namespace LinuxSampler { Line 1433  namespace LinuxSampler {
1433                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
1434                  e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;                  e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1435                  e.Param.NoteSynthParam.Delta    = fValue;                  e.Param.NoteSynthParam.Delta    = fValue;
1436                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1437                        isFinal, false, unit
1438                    );
1439                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
1440              }              }
1441          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1442              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1443              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
1444                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
1445                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
1446    
# Line 1159  namespace LinuxSampler { Line 1451  namespace LinuxSampler {
1451                  // note was triggered then immediately apply the synth parameter                  // note was triggered then immediately apply the synth parameter
1452                  // change to Note object                  // change to Note object
1453                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1454                      pNote->Override.*T_noteParam = fValue;                      setNoteParamValue(pNote->Override.*T_noteParam, fValue);
1455                        setNoteParamScopeBy_FinalUnit(
1456                            (pNote->Override.*T_noteParam),
1457                            isFinal, unit
1458                        );
1459                  } else { // otherwise schedule this synth parameter change ...                  } else { // otherwise schedule this synth parameter change ...
1460                      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"
1461                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 1167  namespace LinuxSampler { Line 1463  namespace LinuxSampler {
1463                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
1464                      e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;                      e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1465                      e.Param.NoteSynthParam.Delta    = fValue;                      e.Param.NoteSynthParam.Delta    = fValue;
1466                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1467                            isFinal, false, unit
1468                        );
1469                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
1470                  }                  }
1471              }              }
# Line 1177  namespace LinuxSampler { Line 1474  namespace LinuxSampler {
1474          return successResult();          return successResult();
1475      }      }
1476    
1477        // change_sustain() function
1478    
1479        VMFnResult* InstrumentScriptVMFunction_change_sustain::exec(VMFnArgs* args) {
1480            return VMChangeSynthParamFunction::execTemplate<
1481                        decltype(NoteBase::_Override::Sustain),
1482                        &NoteBase::_Override::Sustain,
1483                        Event::synth_param_sustain,
1484                        /* if value passed without unit */
1485                        0, NO_LIMIT, true,
1486                        /* if value passed WITH 'Bel' unit */
1487                        NO_LIMIT, NO_LIMIT, VM_MILLI, VM_DECI>( args, "change_sustain" );
1488        }
1489    
1490        // change_cutoff_attack() function
1491    
1492        VMFnResult* InstrumentScriptVMFunction_change_cutoff_attack::exec(VMFnArgs* args) {
1493            return VMChangeSynthParamFunction::execTemplate<
1494                        decltype(NoteBase::_Override::CutoffAttack),
1495                        &NoteBase::_Override::CutoffAttack,
1496                        Event::synth_param_cutoff_attack,
1497                        /* if value passed without unit */
1498                        0, NO_LIMIT, true,
1499                        /* if value passed with 'seconds' unit */
1500                        0, NO_LIMIT, VM_MICRO>( args, "change_cutoff_attack" );
1501        }
1502    
1503        // change_cutoff_decay() function
1504    
1505        VMFnResult* InstrumentScriptVMFunction_change_cutoff_decay::exec(VMFnArgs* args) {
1506            return VMChangeSynthParamFunction::execTemplate<
1507                        decltype(NoteBase::_Override::CutoffDecay),
1508                        &NoteBase::_Override::CutoffDecay,
1509                        Event::synth_param_cutoff_decay,
1510                        /* if value passed without unit */
1511                        0, NO_LIMIT, true,
1512                        /* if value passed with 'seconds' unit */
1513                        0, NO_LIMIT, VM_MICRO>( args, "change_cutoff_decay" );
1514        }
1515    
1516        // change_cutoff_sustain() function
1517    
1518        VMFnResult* InstrumentScriptVMFunction_change_cutoff_sustain::exec(VMFnArgs* args) {
1519            return VMChangeSynthParamFunction::execTemplate<
1520                        decltype(NoteBase::_Override::CutoffSustain),
1521                        &NoteBase::_Override::CutoffSustain,
1522                        Event::synth_param_cutoff_sustain,
1523                        /* if value passed without unit */
1524                        0, NO_LIMIT, true,
1525                        /* if value passed WITH 'Bel' unit */
1526                        NO_LIMIT, NO_LIMIT, VM_MILLI, VM_DECI>( args, "change_cutoff_sustain" );
1527        }
1528    
1529        // change_cutoff_release() function
1530    
1531        VMFnResult* InstrumentScriptVMFunction_change_cutoff_release::exec(VMFnArgs* args) {
1532            return VMChangeSynthParamFunction::execTemplate<
1533                        decltype(NoteBase::_Override::CutoffRelease),
1534                        &NoteBase::_Override::CutoffRelease,
1535                        Event::synth_param_cutoff_release,
1536                        /* if value passed without unit */
1537                        0, NO_LIMIT, true,
1538                        /* if value passed with 'seconds' unit */
1539                        0, NO_LIMIT, VM_MICRO>( args, "change_cutoff_release" );
1540        }
1541    
1542      // change_amp_lfo_depth() function      // change_amp_lfo_depth() function
1543    
1544      VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_depth::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_depth::exec(VMFnArgs* args) {
1545          return VMChangeSynthParamFunction::execTemplate<          return VMChangeSynthParamFunction::execTemplate<
1546                        decltype(NoteBase::_Override::AmpLFODepth),
1547                      &NoteBase::_Override::AmpLFODepth,                      &NoteBase::_Override::AmpLFODepth,
1548                      Event::synth_param_amp_lfo_depth,                      Event::synth_param_amp_lfo_depth,
1549                      true, 1000000, 0>( args, "change_amp_lfo_depth" );                      /* if value passed without unit */
1550                        0, 1000000, true,
1551                        /* not used (since this function does not accept unit) */
1552                        NO_LIMIT, NO_LIMIT, VM_NO_PREFIX>( args, "change_amp_lfo_depth" );
1553      }      }
1554    
1555      // change_amp_lfo_freq() function      // change_amp_lfo_freq() function
1556    
1557      VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_freq::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_freq::exec(VMFnArgs* args) {
1558          return VMChangeSynthParamFunction::execTemplate<          return VMChangeSynthParamFunction::execTemplate<
1559                        decltype(NoteBase::_Override::AmpLFOFreq),
1560                      &NoteBase::_Override::AmpLFOFreq,                      &NoteBase::_Override::AmpLFOFreq,
1561                      Event::synth_param_amp_lfo_freq,                      Event::synth_param_amp_lfo_freq,
1562                      true, 1000000, 0>( args, "change_amp_lfo_freq" );                      /* if value passed without unit */
1563                        0, 1000000, true,
1564                        /* if value passed with 'Hz' unit */
1565                        0, 30000, VM_NO_PREFIX>( args, "change_amp_lfo_freq" );
1566        }
1567    
1568        // change_cutoff_lfo_depth() function
1569    
1570        VMFnResult* InstrumentScriptVMFunction_change_cutoff_lfo_depth::exec(VMFnArgs* args) {
1571            return VMChangeSynthParamFunction::execTemplate<
1572                        decltype(NoteBase::_Override::CutoffLFODepth),
1573                        &NoteBase::_Override::CutoffLFODepth,
1574                        Event::synth_param_cutoff_lfo_depth,
1575                        /* if value passed without unit */
1576                        0, 1000000, true,
1577                        /* not used (since this function does not accept unit) */
1578                        NO_LIMIT, NO_LIMIT, VM_NO_PREFIX>( args, "change_cutoff_lfo_depth" );
1579        }
1580    
1581        // change_cutoff_lfo_freq() function
1582    
1583        VMFnResult* InstrumentScriptVMFunction_change_cutoff_lfo_freq::exec(VMFnArgs* args) {
1584            return VMChangeSynthParamFunction::execTemplate<
1585                        decltype(NoteBase::_Override::CutoffLFOFreq),
1586                        &NoteBase::_Override::CutoffLFOFreq,
1587                        Event::synth_param_cutoff_lfo_freq,
1588                        /* if value passed without unit */
1589                        0, 1000000, true,
1590                        /* if value passed with 'Hz' unit */
1591                        0, 30000, VM_NO_PREFIX>( args, "change_cutoff_lfo_freq" );
1592      }      }
1593    
1594      // change_pitch_lfo_depth() function      // change_pitch_lfo_depth() function
1595    
1596      VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_depth::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_depth::exec(VMFnArgs* args) {
1597          return VMChangeSynthParamFunction::execTemplate<          return VMChangeSynthParamFunction::execTemplate<
1598                        decltype(NoteBase::_Override::PitchLFODepth),
1599                      &NoteBase::_Override::PitchLFODepth,                      &NoteBase::_Override::PitchLFODepth,
1600                      Event::synth_param_pitch_lfo_depth,                      Event::synth_param_pitch_lfo_depth,
1601                      true, 1000000, 0>( args, "change_pitch_lfo_depth" );                      /* if value passed without unit */
1602                        0, 1000000, true,
1603                        /* not used (since this function does not accept unit) */
1604                        NO_LIMIT, NO_LIMIT, VM_NO_PREFIX>( args, "change_pitch_lfo_depth" );
1605      }      }
1606    
1607      // change_pitch_lfo_freq() function      // change_pitch_lfo_freq() function
1608    
1609      VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_freq::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_freq::exec(VMFnArgs* args) {
1610          return VMChangeSynthParamFunction::execTemplate<          return VMChangeSynthParamFunction::execTemplate<
1611                        decltype(NoteBase::_Override::PitchLFOFreq),
1612                      &NoteBase::_Override::PitchLFOFreq,                      &NoteBase::_Override::PitchLFOFreq,
1613                      Event::synth_param_pitch_lfo_freq,                      Event::synth_param_pitch_lfo_freq,
1614                      true, 1000000, 0>( args, "change_pitch_lfo_freq" );                      /* if value passed without unit */
1615                        0, 1000000, true,
1616                        /* if value passed with 'Hz' unit */
1617                        0, 30000, VM_NO_PREFIX>( args, "change_pitch_lfo_freq" );
1618      }      }
1619    
1620      // change_vol_time() function      // change_vol_time() function
1621    
1622      VMFnResult* InstrumentScriptVMFunction_change_vol_time::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_vol_time::exec(VMFnArgs* args) {
1623          return VMChangeSynthParamFunction::execTemplate<          return VMChangeSynthParamFunction::execTemplate<
1624                        decltype(NoteBase::_Override::VolumeTime),
1625                      &NoteBase::_Override::VolumeTime,                      &NoteBase::_Override::VolumeTime,
1626                      Event::synth_param_volume_time,                      Event::synth_param_volume_time,
1627                      false, NO_LIMIT, 0>( args, "change_vol_time" );                      /* if value passed without unit (implying 'us' unit) */
1628                        0, NO_LIMIT, true,
1629                        /* if value passed with 'seconds' unit */
1630                        0, NO_LIMIT, VM_MICRO>( args, "change_vol_time" );
1631      }      }
1632    
1633      // change_tune_time() function      // change_tune_time() function
1634    
1635      VMFnResult* InstrumentScriptVMFunction_change_tune_time::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_tune_time::exec(VMFnArgs* args) {
1636          return VMChangeSynthParamFunction::execTemplate<          return VMChangeSynthParamFunction::execTemplate<
1637                        decltype(NoteBase::_Override::PitchTime),
1638                      &NoteBase::_Override::PitchTime,                      &NoteBase::_Override::PitchTime,
1639                      Event::synth_param_pitch_time,                      Event::synth_param_pitch_time,
1640                      false, NO_LIMIT, 0>( args, "change_tune_time" );                      /* if value passed without unit (implying 'us' unit) */
1641                        0, NO_LIMIT, true,
1642                        /* if value passed with 'seconds' unit */
1643                        0, NO_LIMIT, VM_MICRO>( args, "change_tune_time" );
1644        }
1645    
1646        // change_pan_time() function
1647    
1648        VMFnResult* InstrumentScriptVMFunction_change_pan_time::exec(VMFnArgs* args) {
1649            return VMChangeSynthParamFunction::execTemplate<
1650                        decltype(NoteBase::_Override::PanTime),
1651                        &NoteBase::_Override::PanTime,
1652                        Event::synth_param_pan_time,
1653                        /* if value passed without unit (implying 'us' unit) */
1654                        0, NO_LIMIT, true,
1655                        /* if value passed with 'seconds' unit */
1656                        0, NO_LIMIT, VM_MICRO>( args, "change_pan_time" );
1657      }      }
1658    
1659      // template for change_*_curve() functions      // template for change_*_curve() functions
1660    
1661      bool VMChangeFadeCurveFunction::acceptsArgType(int iArg, ExprType_t type) const {      bool VMChangeFadeCurveFunction::acceptsArgType(vmint iArg, ExprType_t type) const {
1662          if (iArg == 0)          if (iArg == 0)
1663              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
1664          else          else
1665              return type == INT_EXPR;              return type == INT_EXPR;
1666      }      }
1667    
1668      template<fade_curve_t NoteBase::_Override::*T_noteParam, int T_synthParam>      template<fade_curve_t NoteBase::_Override::*T_noteParam, vmint T_synthParam>
1669      VMFnResult* VMChangeFadeCurveFunction::execTemplate(VMFnArgs* args, const char* functionName) {      VMFnResult* VMChangeFadeCurveFunction::execTemplate(VMFnArgs* args, const char* functionName) {
1670          int value = args->arg(1)->asInt()->evalInt();          vmint value = args->arg(1)->asInt()->evalInt();
1671          switch (value) {          switch (value) {
1672              case FADE_CURVE_LINEAR:              case FADE_CURVE_LINEAR:
1673              case FADE_CURVE_EASE_IN_EASE_OUT:              case FADE_CURVE_EASE_IN_EASE_OUT:
# Line 1281  namespace LinuxSampler { Line 1706  namespace LinuxSampler {
1706                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
1707                  e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;                  e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1708                  e.Param.NoteSynthParam.Delta    = value;                  e.Param.NoteSynthParam.Delta    = value;
1709                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
1710    
1711                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
1712              }              }
1713          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1714              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1715              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
1716                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
1717                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
1718    
# Line 1306  namespace LinuxSampler { Line 1731  namespace LinuxSampler {
1731                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
1732                      e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;                      e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1733                      e.Param.NoteSynthParam.Delta    = value;                      e.Param.NoteSynthParam.Delta    = value;
1734                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
1735    
1736                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
1737                  }                  }
# Line 1332  namespace LinuxSampler { Line 1757  namespace LinuxSampler {
1757                      Event::synth_param_pitch_curve>( args, "change_tune_curve" );                      Event::synth_param_pitch_curve>( args, "change_tune_curve" );
1758      }      }
1759    
1760        // change_pan_curve() function
1761    
1762        VMFnResult* InstrumentScriptVMFunction_change_pan_curve::exec(VMFnArgs* args) {
1763            return VMChangeFadeCurveFunction::execTemplate<
1764            &NoteBase::_Override::PanCurve,
1765            Event::synth_param_pan_curve>( args, "change_pan_curve" );
1766        }
1767    
1768      // fade_in() function      // fade_in() function
1769    
1770      InstrumentScriptVMFunction_fade_in::InstrumentScriptVMFunction_fade_in(InstrumentScriptVM* parent)      InstrumentScriptVMFunction_fade_in::InstrumentScriptVMFunction_fade_in(InstrumentScriptVM* parent)
# Line 1339  namespace LinuxSampler { Line 1772  namespace LinuxSampler {
1772      {      {
1773      }      }
1774    
1775      bool InstrumentScriptVMFunction_fade_in::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_fade_in::acceptsArgType(vmint iArg, ExprType_t type) const {
1776          if (iArg == 0)          if (iArg == 0)
1777              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
1778          else          else
1779              return type == INT_EXPR;              return type == INT_EXPR || type == REAL_EXPR;
1780        }
1781    
1782        bool InstrumentScriptVMFunction_fade_in::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
1783            if (iArg == 1)
1784                return type == VM_NO_UNIT || type == VM_SECOND;
1785            else
1786                return type == VM_NO_UNIT;
1787        }
1788    
1789        bool InstrumentScriptVMFunction_fade_in::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
1790            return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
1791      }      }
1792    
1793      VMFnResult* InstrumentScriptVMFunction_fade_in::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_fade_in::exec(VMFnArgs* args) {
1794          int duration = args->arg(1)->asInt()->evalInt();          StdUnit_t unit = args->arg(1)->asNumber()->unitType();
1795            vmint duration =
1796                (unit) ?
1797                    args->arg(1)->asNumber()->evalCastInt(VM_MICRO) :
1798                    args->arg(1)->asNumber()->evalCastInt();
1799          if (duration < 0) {          if (duration < 0) {
1800              wrnMsg("fade_in(): argument 2 may not be negative");              wrnMsg("fade_in(): argument 2 may not be negative");
1801              duration = 0;              duration = 0;
# Line 1375  namespace LinuxSampler { Line 1823  namespace LinuxSampler {
1823              // then immediately apply a start volume of zero to Note object,              // then immediately apply a start volume of zero to Note object,
1824              // as well as the fade in duration              // as well as the fade in duration
1825              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1826                  pNote->Override.Volume = 0.f;                  pNote->Override.Volume.Value = 0.f;
1827                  pNote->Override.VolumeTime = fDuration;                  pNote->Override.VolumeTime = fDuration;
1828              } 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 ...
1829                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
# Line 1384  namespace LinuxSampler { Line 1832  namespace LinuxSampler {
1832                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
1833                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1834                  e.Param.NoteSynthParam.Delta    = fDuration;                  e.Param.NoteSynthParam.Delta    = fDuration;
1835                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
1836    
1837                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
1838              }              }
# Line 1397  namespace LinuxSampler { Line 1845  namespace LinuxSampler {
1845                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
1846                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume;                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1847                  e.Param.NoteSynthParam.Delta    = 1.f;                  e.Param.NoteSynthParam.Delta    = 1.f;
1848                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
1849    
1850                  // scheduling with 0 delay would also work here, but +1 is more                  // scheduling with 0 delay would also work here, but +1 is more
1851                  // safe regarding potential future implementation changes of the                  // safe regarding potential future implementation changes of the
# Line 1406  namespace LinuxSampler { Line 1854  namespace LinuxSampler {
1854              }              }
1855          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1856              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1857              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
1858                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
1859                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
1860    
# Line 1417  namespace LinuxSampler { Line 1865  namespace LinuxSampler {
1865                  // then immediately apply a start volume of zero to Note object,                  // then immediately apply a start volume of zero to Note object,
1866                  // as well as the fade in duration                  // as well as the fade in duration
1867                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1868                      pNote->Override.Volume = 0.f;                      pNote->Override.Volume.Value = 0.f;
1869                      pNote->Override.VolumeTime = fDuration;                      pNote->Override.VolumeTime = fDuration;
1870                  } 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 ...
1871                      Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                      Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
# Line 1426  namespace LinuxSampler { Line 1874  namespace LinuxSampler {
1874                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
1875                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1876                      e.Param.NoteSynthParam.Delta    = fDuration;                      e.Param.NoteSynthParam.Delta    = fDuration;
1877                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
1878    
1879                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
1880                  }                  }
# Line 1439  namespace LinuxSampler { Line 1887  namespace LinuxSampler {
1887                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
1888                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume;                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1889                      e.Param.NoteSynthParam.Delta    = 1.f;                      e.Param.NoteSynthParam.Delta    = 1.f;
1890                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
1891    
1892                      // scheduling with 0 delay would also work here, but +1 is more                      // scheduling with 0 delay would also work here, but +1 is more
1893                      // safe regarding potential future implementation changes of the                      // safe regarding potential future implementation changes of the
# Line 1459  namespace LinuxSampler { Line 1907  namespace LinuxSampler {
1907      {      {
1908      }      }
1909    
1910      bool InstrumentScriptVMFunction_fade_out::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_fade_out::acceptsArgType(vmint iArg, ExprType_t type) const {
1911          if (iArg == 0)          if (iArg == 0)
1912              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
1913          else          else
1914              return type == INT_EXPR;              return type == INT_EXPR || type == REAL_EXPR;
1915        }
1916    
1917        bool InstrumentScriptVMFunction_fade_out::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
1918            if (iArg == 1)
1919                return type == VM_NO_UNIT || type == VM_SECOND;
1920            else
1921                return type == VM_NO_UNIT;
1922        }
1923    
1924        bool InstrumentScriptVMFunction_fade_out::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
1925            return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
1926      }      }
1927    
1928      VMFnResult* InstrumentScriptVMFunction_fade_out::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_fade_out::exec(VMFnArgs* args) {
1929          int duration = args->arg(1)->asInt()->evalInt();          StdUnit_t unit = args->arg(1)->asNumber()->unitType();
1930            vmint duration =
1931                (unit) ?
1932                    args->arg(1)->asNumber()->evalCastInt(VM_MICRO) :
1933                    args->arg(1)->asNumber()->evalCastInt();
1934          if (duration < 0) {          if (duration < 0) {
1935              wrnMsg("fade_out(): argument 2 may not be negative");              wrnMsg("fade_out(): argument 2 may not be negative");
1936              duration = 0;              duration = 0;
# Line 1504  namespace LinuxSampler { Line 1967  namespace LinuxSampler {
1967                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
1968                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1969                  e.Param.NoteSynthParam.Delta    = fDuration;                  e.Param.NoteSynthParam.Delta    = fDuration;
1970                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
1971    
1972                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
1973              }              }
# Line 1517  namespace LinuxSampler { Line 1980  namespace LinuxSampler {
1980                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
1981                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume;                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1982                  e.Param.NoteSynthParam.Delta    = 0.f;                  e.Param.NoteSynthParam.Delta    = 0.f;
1983                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
1984    
1985                  // scheduling with 0 delay would also work here, but +1 is more                  // scheduling with 0 delay would also work here, but +1 is more
1986                  // safe regarding potential future implementation changes of the                  // safe regarding potential future implementation changes of the
# Line 1538  namespace LinuxSampler { Line 2001  namespace LinuxSampler {
2001              }              }
2002          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
2003              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
2004              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
2005                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
2006                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
2007    
# Line 1556  namespace LinuxSampler { Line 2019  namespace LinuxSampler {
2019                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
2020                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
2021                      e.Param.NoteSynthParam.Delta    = fDuration;                      e.Param.NoteSynthParam.Delta    = fDuration;
2022                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2023    
2024                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
2025                  }                  }
# Line 1569  namespace LinuxSampler { Line 2032  namespace LinuxSampler {
2032                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
2033                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume;                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
2034                      e.Param.NoteSynthParam.Delta    = 0.f;                      e.Param.NoteSynthParam.Delta    = 0.f;
2035                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2036    
2037                      // scheduling with 0 delay would also work here, but +1 is more                      // scheduling with 0 delay would also work here, but +1 is more
2038                      // safe regarding potential future implementation changes of the                      // safe regarding potential future implementation changes of the
# Line 1621  namespace LinuxSampler { Line 2084  namespace LinuxSampler {
2084              return successResult(0);              return successResult(0);
2085          }          }
2086    
2087          const int parameter = args->arg(1)->asInt()->evalInt();          const vmint parameter = args->arg(1)->asInt()->evalInt();
2088          switch (parameter) {          switch (parameter) {
2089              case EVENT_PAR_NOTE:              case EVENT_PAR_NOTE:
2090                  return successResult(pNote->cause.Param.Note.Key);                  return successResult(pNote->cause.Param.Note.Key);
# Line 1629  namespace LinuxSampler { Line 2092  namespace LinuxSampler {
2092                  return successResult(pNote->cause.Param.Note.Velocity);                  return successResult(pNote->cause.Param.Note.Velocity);
2093              case EVENT_PAR_VOLUME:              case EVENT_PAR_VOLUME:
2094                  return successResult(                  return successResult(
2095                      RTMath::LinRatioToDecibel(pNote->Override.Volume) * 1000.f                      RTMath::LinRatioToDecibel(pNote->Override.Volume.Value) * 1000.f
2096                  );                  );
2097              case EVENT_PAR_TUNE:              case EVENT_PAR_TUNE:
2098                  return successResult(                  return successResult(
2099                       RTMath::FreqRatioToCents(pNote->Override.Pitch) * 1000.f                       RTMath::FreqRatioToCents(pNote->Override.Pitch.Value) * 1000.f
2100                  );                  );
2101              case EVENT_PAR_0:              case EVENT_PAR_0:
2102                  return successResult(pNote->userPar[0]);                  return successResult(pNote->userPar[0]);
# Line 1673  namespace LinuxSampler { Line 2136  namespace LinuxSampler {
2136          NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );          NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2137          if (!pNote) return successResult();          if (!pNote) return successResult();
2138    
2139          const int parameter = args->arg(1)->asInt()->evalInt();          const vmint parameter = args->arg(1)->asInt()->evalInt();
2140          const int value     = args->arg(2)->asInt()->evalInt();          const vmint value     = args->arg(2)->asInt()->evalInt();
2141    
2142          switch (parameter) {          switch (parameter) {
2143              case EVENT_PAR_NOTE:              case EVENT_PAR_NOTE:
# Line 1749  namespace LinuxSampler { Line 2212  namespace LinuxSampler {
2212          NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );          NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2213          if (!pNote) return successResult();          if (!pNote) return successResult();
2214    
2215          const int value = args->arg(1)->asInt()->evalInt();          const vmint value = args->arg(1)->asInt()->evalInt();
2216          if (value < 0 || value > 127) {          if (value < 0 || value > 127) {
2217              wrnMsg("change_note(): note number of argument 2 is out of range");              wrnMsg("change_note(): note number of argument 2 is out of range");
2218              return successResult();              return successResult();
# Line 1789  namespace LinuxSampler { Line 2252  namespace LinuxSampler {
2252          NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );          NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2253          if (!pNote) return successResult();          if (!pNote) return successResult();
2254    
2255          const int value = args->arg(1)->asInt()->evalInt();          const vmint value = args->arg(1)->asInt()->evalInt();
2256          if (value < 0 || value > 127) {          if (value < 0 || value > 127) {
2257              wrnMsg("change_velo(): velocity of argument 2 is out of range");              wrnMsg("change_velo(): velocity of argument 2 is out of range");
2258              return successResult();              return successResult();
# Line 1808  namespace LinuxSampler { Line 2271  namespace LinuxSampler {
2271      // change_play_pos() function      // change_play_pos() function
2272    
2273      InstrumentScriptVMFunction_change_play_pos::InstrumentScriptVMFunction_change_play_pos(InstrumentScriptVM* parent)      InstrumentScriptVMFunction_change_play_pos::InstrumentScriptVMFunction_change_play_pos(InstrumentScriptVM* parent)
2274      : m_vm(parent)          : m_vm(parent)
2275      {      {
2276      }      }
2277    
2278        bool InstrumentScriptVMFunction_change_play_pos::acceptsArgType(vmint iArg, ExprType_t type) const {
2279            if (iArg == 0)
2280                return type == INT_EXPR;
2281            else
2282                return type == INT_EXPR || type == REAL_EXPR;
2283        }
2284    
2285        bool InstrumentScriptVMFunction_change_play_pos::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
2286            if (iArg == 1)
2287                return type == VM_NO_UNIT || type == VM_SECOND;
2288            else
2289                return type == VM_NO_UNIT;
2290        }
2291    
2292        bool InstrumentScriptVMFunction_change_play_pos::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
2293            return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
2294        }
2295    
2296      VMFnResult* InstrumentScriptVMFunction_change_play_pos::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_play_pos::exec(VMFnArgs* args) {
2297          const ScriptID id = args->arg(0)->asInt()->evalInt();          const ScriptID id = args->arg(0)->asInt()->evalInt();
2298          if (!id) {          if (!id) {
# Line 1823  namespace LinuxSampler { Line 2304  namespace LinuxSampler {
2304              return successResult();              return successResult();
2305          }          }
2306    
2307          const int pos = args->arg(1)->asInt()->evalInt();          StdUnit_t unit = args->arg(1)->asNumber()->unitType();
2308            const vmint pos =
2309                (unit) ?
2310                    args->arg(1)->asNumber()->evalCastInt(VM_MICRO) :
2311                    args->arg(1)->asNumber()->evalCastInt();
2312          if (pos < 0) {          if (pos < 0) {
2313              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");
2314              return successResult();              return successResult();
# Line 1835  namespace LinuxSampler { Line 2320  namespace LinuxSampler {
2320          NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );          NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2321          if (!pNote) return successResult();          if (!pNote) return successResult();
2322    
2323          pNote->Override.SampleOffset = pos;          pNote->Override.SampleOffset =
2324                (decltype(pNote->Override.SampleOffset)) pos;
2325    
2326          return successResult();          return successResult();
2327      }      }
# Line 1982  namespace LinuxSampler { Line 2468  namespace LinuxSampler {
2468    
2469          // 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
2470    
2471          const int n =          const vmint n =
2472              (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : 1;              (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : 1;
2473          const bool bAutoAbort =          const bool bAutoAbort =
2474              (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : true;              (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : true;
# Line 2008  namespace LinuxSampler { Line 2494  namespace LinuxSampler {
2494                  return errorResult(-1); // terminate script                  return errorResult(-1); // terminate script
2495              }              }
2496              // since both parent, as well all child script execution instances              // since both parent, as well all child script execution instances
2497              // all land in this exect() method, the following is (more or less)              // all land in this exec() method, the following is (more or less)
2498              // the only feature that lets us distinguish the parent and              // the only feature that lets us distinguish the parent and
2499              // respective children from each other in this exect() method              // respective children from each other in this exec() method
2500              itChild->forkIndex = iChild + 1;              itChild->forkIndex = iChild + 1;
2501          }          }
2502    

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

  ViewVC Help
Powered by ViewVC