/[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 3316 by schoenebeck, Thu Jul 20 12:05:53 2017 UTC revision 3564 by schoenebeck, Sat Aug 24 09:18:57 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 21  namespace LinuxSampler { Line 22  namespace LinuxSampler {
22      }      }
23    
24      VMFnResult* InstrumentScriptVMFunction_play_note::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_play_note::exec(VMFnArgs* args) {
25          int note = args->arg(0)->asInt()->evalInt();          vmint note = args->arg(0)->asInt()->evalInt();
26          int velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127;          vmint velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127;
27          int duration = (args->argsCount() >= 4) ? args->arg(3)->asInt()->evalInt() : 0; //TODO: -1 might be a better default value instead of 0          vmint duration = (args->argsCount() >= 4) ? args->arg(3)->asInt()->evalInt() : 0; //TODO: -1 might be a better default value instead of 0
28    
29          if (note < 0 || note > 127) {          if (note < 0 || note > 127) {
30              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 69  namespace LinuxSampler {
69          // if a sample offset is supplied, assign the offset as override          // if a sample offset is supplied, assign the offset as override
70          // to the previously created Note object          // to the previously created Note object
71          if (args->argsCount() >= 3) {          if (args->argsCount() >= 3) {
72              int sampleoffset = args->arg(2)->asInt()->evalInt();              vmint sampleoffset = args->arg(2)->asInt()->evalInt();
73              if (sampleoffset >= 0) {              if (sampleoffset >= 0) {
74                  NoteBase* pNote = pEngineChannel->pEngine->NoteByID(id);                  NoteBase* pNote = pEngineChannel->pEngine->NoteByID(id);
75                  if (pNote) {                  if (pNote) {
76                      pNote->Override.SampleOffset = sampleoffset;                      pNote->Override.SampleOffset =
77                            (decltype(pNote->Override.SampleOffset)) sampleoffset;
78                  }                  }
79              } else if (sampleoffset < -1) {              } else if (sampleoffset < -1) {
80                  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 104  namespace LinuxSampler {
104      }      }
105    
106      VMFnResult* InstrumentScriptVMFunction_set_controller::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_set_controller::exec(VMFnArgs* args) {
107          int controller = args->arg(0)->asInt()->evalInt();          vmint controller = args->arg(0)->asInt()->evalInt();
108          int value      = args->arg(1)->asInt()->evalInt();          vmint value      = args->arg(1)->asInt()->evalInt();
109    
110          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
111              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 140  namespace LinuxSampler { Line 142  namespace LinuxSampler {
142      {      {
143      }      }
144    
145      bool InstrumentScriptVMFunction_ignore_event::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_ignore_event::acceptsArgType(vmint iArg, ExprType_t type) const {
146          return type == INT_EXPR || type == INT_ARR_EXPR;          return type == INT_EXPR || type == INT_ARR_EXPR;
147      }      }
148    
# Line 196  namespace LinuxSampler { Line 198  namespace LinuxSampler {
198      {      {
199      }      }
200    
201      bool InstrumentScriptVMFunction_note_off::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_note_off::acceptsArgType(vmint iArg, ExprType_t type) const {
202          return type == INT_EXPR || type == INT_ARR_EXPR;          return type == INT_EXPR || type == INT_ARR_EXPR;
203      }      }
204    
# Line 204  namespace LinuxSampler { Line 206  namespace LinuxSampler {
206          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
207              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
208    
209          int velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127;          vmint velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127;
210          if (velocity < 0 || velocity > 127) {          if (velocity < 0 || velocity > 127) {
211              errMsg("note_off(): argument 2 is an invalid velocity value");              errMsg("note_off(): argument 2 is an invalid velocity value");
212              return errorResult();              return errorResult();
# Line 235  namespace LinuxSampler { Line 237  namespace LinuxSampler {
237              pEngineChannel->ScheduleEventMicroSec(&e, 0);              pEngineChannel->ScheduleEventMicroSec(&e, 0);
238          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
239              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
240              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
241                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
242                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
243    
# Line 266  namespace LinuxSampler { Line 268  namespace LinuxSampler {
268    
269      VMFnResult* InstrumentScriptVMFunction_set_event_mark::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_set_event_mark::exec(VMFnArgs* args) {
270          const ScriptID id = args->arg(0)->asInt()->evalInt();          const ScriptID id = args->arg(0)->asInt()->evalInt();
271          const int groupID = args->arg(1)->asInt()->evalInt();          const vmint groupID = args->arg(1)->asInt()->evalInt();
272    
273          if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {          if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
274              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 306  namespace LinuxSampler {
306    
307      VMFnResult* InstrumentScriptVMFunction_delete_event_mark::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_delete_event_mark::exec(VMFnArgs* args) {
308          const ScriptID id = args->arg(0)->asInt()->evalInt();          const ScriptID id = args->arg(0)->asInt()->evalInt();
309          const int groupID = args->arg(1)->asInt()->evalInt();          const vmint groupID = args->arg(1)->asInt()->evalInt();
310    
311          if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {          if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
312              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 328  namespace LinuxSampler {
328      {      {
329      }      }
330    
331      int InstrumentScriptVMFunction_by_marks::Result::arraySize() const {      vmint InstrumentScriptVMFunction_by_marks::Result::arraySize() const {
332          return eventGroup->size();          return eventGroup->size();
333      }      }
334    
335      int InstrumentScriptVMFunction_by_marks::Result::evalIntElement(uint i) {      vmint InstrumentScriptVMFunction_by_marks::Result::evalIntElement(vmuint i) {
336          return (*eventGroup)[i];          return (*eventGroup)[i];
337      }      }
338    
# Line 347  namespace LinuxSampler { Line 349  namespace LinuxSampler {
349      }      }
350    
351      VMFnResult* InstrumentScriptVMFunction_by_marks::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_by_marks::exec(VMFnArgs* args) {
352          int groupID = args->arg(0)->asInt()->evalInt();          vmint groupID = args->arg(0)->asInt()->evalInt();
353    
354          if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {          if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
355              errMsg("by_marks(): argument is an invalid group id");              errMsg("by_marks(): argument is an invalid group id");
# Line 367  namespace LinuxSampler { Line 369  namespace LinuxSampler {
369      {      {
370      }      }
371    
372      bool InstrumentScriptVMFunction_change_vol::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_change_vol::acceptsArgType(vmint iArg, ExprType_t type) const {
373          if (iArg == 0)          if (iArg == 0)
374              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
375          else          else
376              return type == INT_EXPR;              return type == INT_EXPR;
377      }      }
378    
379        bool InstrumentScriptVMFunction_change_vol::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
380            if (iArg == 1)
381                return type == VM_NO_UNIT || type == VM_BEL;
382            else
383                return type == VM_NO_UNIT;
384        }
385    
386        bool InstrumentScriptVMFunction_change_vol::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
387            return iArg == 1 && type == VM_BEL;
388        }
389    
390        bool InstrumentScriptVMFunction_change_vol::acceptsArgFinal(vmint iArg) const {
391            return iArg == 1;
392        }
393    
394      VMFnResult* InstrumentScriptVMFunction_change_vol::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_vol::exec(VMFnArgs* args) {
395          int volume = args->arg(1)->asInt()->evalInt(); // volume change in milli dB          StdUnit_t unit = args->arg(1)->asInt()->unitType();
396            vmint volume   = (unit) ? args->arg(1)->asInt()->evalInt(VM_MILLI,VM_DECI)
397                                    : args->arg(1)->asInt()->evalInt(); // volume change in milli dB
398            bool isFinal   = args->arg(1)->asInt()->isFinal();
399          bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;          bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
400          const float fVolumeLin = RTMath::DecibelToLinRatio(float(volume) / 1000.f);          const float fVolumeLin = RTMath::DecibelToLinRatio(float(volume) / 1000.f);
401    
# Line 403  namespace LinuxSampler { Line 423  namespace LinuxSampler {
423                  pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S)                  pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S)
424              {              {
425                  if (relative)                  if (relative)
426                      pNote->Override.Volume *= fVolumeLin;                      pNote->Override.Volume.Value *= fVolumeLin;
427                  else                  else
428                      pNote->Override.Volume = fVolumeLin;                      pNote->Override.Volume.Value = fVolumeLin;
429                    pNote->Override.Volume.Final = isFinal;
430              } else { // otherwise schedule the volume change ...              } else { // otherwise schedule the volume change ...
431                  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"
432                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 413  namespace LinuxSampler { Line 434  namespace LinuxSampler {
434                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
435                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume;                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
436                  e.Param.NoteSynthParam.Delta    = fVolumeLin;                  e.Param.NoteSynthParam.Delta    = fVolumeLin;
437                  e.Param.NoteSynthParam.Relative = relative;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
438                        isFinal, relative, unit
439                    );
440                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
441              }              }
442          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
443              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
444              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
445                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
446                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
447    
# Line 433  namespace LinuxSampler { Line 455  namespace LinuxSampler {
455                      pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S)                      pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S)
456                  {                  {
457                      if (relative)                      if (relative)
458                          pNote->Override.Volume *= fVolumeLin;                          pNote->Override.Volume.Value *= fVolumeLin;
459                      else                      else
460                          pNote->Override.Volume = fVolumeLin;                          pNote->Override.Volume.Value = fVolumeLin;
461                        pNote->Override.Volume.Final = isFinal;
462                  } else { // otherwise schedule the volume change ...                  } else { // otherwise schedule the volume change ...
463                      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"
464                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 443  namespace LinuxSampler { Line 466  namespace LinuxSampler {
466                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
467                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume;                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
468                      e.Param.NoteSynthParam.Delta    = fVolumeLin;                      e.Param.NoteSynthParam.Delta    = fVolumeLin;
469                      e.Param.NoteSynthParam.Relative = relative;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
470                            isFinal, relative, unit
471                        );
472                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
473                  }                  }
474              }              }
# Line 460  namespace LinuxSampler { Line 484  namespace LinuxSampler {
484      {      {
485      }      }
486    
487      bool InstrumentScriptVMFunction_change_tune::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_change_tune::acceptsArgType(vmint iArg, ExprType_t type) const {
488          if (iArg == 0)          if (iArg == 0)
489              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
490          else          else
491              return type == INT_EXPR;              return type == INT_EXPR;
492      }      }
493    
494        bool InstrumentScriptVMFunction_change_tune::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
495            return iArg == 1;
496        }
497    
498        bool InstrumentScriptVMFunction_change_tune::acceptsArgFinal(vmint iArg) const {
499            return iArg == 1;
500        }
501    
502      VMFnResult* InstrumentScriptVMFunction_change_tune::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_tune::exec(VMFnArgs* args) {
503          int tune = args->arg(1)->asInt()->evalInt(); // tuning change in milli cents          MetricPrefix_t prefix = args->arg(1)->asInt()->unitPrefix(0);
504            vmint tune     = (prefix) ? args->arg(1)->asInt()->evalInt(VM_MILLI,VM_CENTI)
505                                      : args->arg(1)->asInt()->evalInt(); // tuning change in milli cents
506            bool isFinal   = args->arg(1)->asInt()->isFinal();
507            StdUnit_t unit = args->arg(1)->asInt()->unitType();
508          bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;          bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
509          const float fFreqRatio = RTMath::CentsToFreqRatioUnlimited(float(tune) / 1000.f);          const float fFreqRatio = RTMath::CentsToFreqRatioUnlimited(float(tune) / 1000.f);
510    
# Line 496  namespace LinuxSampler { Line 532  namespace LinuxSampler {
532                  pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S)                  pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S)
533              {              {
534                  if (relative)                  if (relative)
535                      pNote->Override.Pitch *= fFreqRatio;                      pNote->Override.Pitch.Value *= fFreqRatio;
536                  else                  else
537                      pNote->Override.Pitch = fFreqRatio;                      pNote->Override.Pitch.Value = fFreqRatio;
538                    pNote->Override.Pitch.Final = isFinal;
539              } else { // otherwise schedule tuning change ...              } else { // otherwise schedule tuning change ...
540                  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"
541                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 506  namespace LinuxSampler { Line 543  namespace LinuxSampler {
543                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
544                  e.Param.NoteSynthParam.Type     = Event::synth_param_pitch;                  e.Param.NoteSynthParam.Type     = Event::synth_param_pitch;
545                  e.Param.NoteSynthParam.Delta    = fFreqRatio;                  e.Param.NoteSynthParam.Delta    = fFreqRatio;
546                  e.Param.NoteSynthParam.Relative = relative;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
547                        isFinal, relative, unit
548                    );
549                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
550              }              }
551          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
552              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
553              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
554                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
555                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
556    
# Line 526  namespace LinuxSampler { Line 564  namespace LinuxSampler {
564                      pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S)                      pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S)
565                  {                  {
566                      if (relative)                      if (relative)
567                          pNote->Override.Pitch *= fFreqRatio;                          pNote->Override.Pitch.Value *= fFreqRatio;
568                      else                      else
569                          pNote->Override.Pitch = fFreqRatio;                          pNote->Override.Pitch.Value = fFreqRatio;
570                        pNote->Override.Pitch.Final = isFinal;
571                  } else { // otherwise schedule tuning change ...                  } else { // otherwise schedule tuning change ...
572                      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"
573                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 536  namespace LinuxSampler { Line 575  namespace LinuxSampler {
575                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
576                      e.Param.NoteSynthParam.Type     = Event::synth_param_pitch;                      e.Param.NoteSynthParam.Type     = Event::synth_param_pitch;
577                      e.Param.NoteSynthParam.Delta    = fFreqRatio;                      e.Param.NoteSynthParam.Delta    = fFreqRatio;
578                      e.Param.NoteSynthParam.Relative = relative;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
579                            isFinal, relative, unit
580                        );
581                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
582                  }                  }
583              }              }
# Line 553  namespace LinuxSampler { Line 593  namespace LinuxSampler {
593      {      {
594      }      }
595    
596      bool InstrumentScriptVMFunction_change_pan::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_change_pan::acceptsArgType(vmint iArg, ExprType_t type) const {
597          if (iArg == 0)          if (iArg == 0)
598              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
599          else          else
600              return type == INT_EXPR;              return type == INT_EXPR;
601      }      }
602    
603        bool InstrumentScriptVMFunction_change_pan::acceptsArgFinal(vmint iArg) const {
604            return iArg == 1;
605        }
606    
607      VMFnResult* InstrumentScriptVMFunction_change_pan::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_pan::exec(VMFnArgs* args) {
608          int pan = args->arg(1)->asInt()->evalInt();          vmint pan    = args->arg(1)->asInt()->evalInt();
609            bool isFinal = args->arg(1)->asInt()->isFinal();
610          bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;          bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
611    
612          if (pan > 1000) {          if (pan > 1000) {
# Line 594  namespace LinuxSampler { Line 639  namespace LinuxSampler {
639              // then immediately apply the panning to Note object              // then immediately apply the panning to Note object
640              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
641                  if (relative) {                  if (relative) {
642                      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);
643                  } else {                  } else {
644                      pNote->Override.Pan = fPan;                      pNote->Override.Pan.Value = fPan;
645                      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
646                  }                  }
647                    pNote->Override.Pan.Final = isFinal;
648              } else { // otherwise schedule panning change ...              } else { // otherwise schedule panning change ...
649                  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"
650                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 606  namespace LinuxSampler { Line 652  namespace LinuxSampler {
652                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
653                  e.Param.NoteSynthParam.Type     = Event::synth_param_pan;                  e.Param.NoteSynthParam.Type     = Event::synth_param_pan;
654                  e.Param.NoteSynthParam.Delta    = fPan;                  e.Param.NoteSynthParam.Delta    = fPan;
655                  e.Param.NoteSynthParam.Relative = relative;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
656                        isFinal, relative, false
657                    );
658                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
659              }              }
660          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
661              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
662              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
663                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
664                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
665    
# Line 623  namespace LinuxSampler { Line 670  namespace LinuxSampler {
670                  // then immediately apply the panning to Note object                  // then immediately apply the panning to Note object
671                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
672                      if (relative) {                      if (relative) {
673                          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);
674                      } else {                      } else {
675                          pNote->Override.Pan = fPan;                          pNote->Override.Pan.Value = fPan;
676                          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
677                      }                      }
678                        pNote->Override.Pan.Final = isFinal;
679                  } else { // otherwise schedule panning change ...                  } else { // otherwise schedule panning change ...
680                      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"
681                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 635  namespace LinuxSampler { Line 683  namespace LinuxSampler {
683                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
684                      e.Param.NoteSynthParam.Type     = Event::synth_param_pan;                      e.Param.NoteSynthParam.Type     = Event::synth_param_pan;
685                      e.Param.NoteSynthParam.Delta    = fPan;                      e.Param.NoteSynthParam.Delta    = fPan;
686                      e.Param.NoteSynthParam.Relative = relative;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
687                            isFinal, relative, false
688                        );
689                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
690                  }                  }
691              }              }
# Line 646  namespace LinuxSampler { Line 695  namespace LinuxSampler {
695      }      }
696    
697      #define VM_FILTER_PAR_MAX_VALUE 1000000      #define VM_FILTER_PAR_MAX_VALUE 1000000
698        #define VM_FILTER_PAR_MAX_HZ 30000
699      #define VM_EG_PAR_MAX_VALUE 1000000      #define VM_EG_PAR_MAX_VALUE 1000000
700    
701      // change_cutoff() function      // change_cutoff() function
# Line 655  namespace LinuxSampler { Line 705  namespace LinuxSampler {
705      {      {
706      }      }
707    
708      bool InstrumentScriptVMFunction_change_cutoff::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_change_cutoff::acceptsArgType(vmint iArg, ExprType_t type) const {
709          if (iArg == 0)          if (iArg == 0)
710              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
711          else          else
712              return type == INT_EXPR;              return type == INT_EXPR;
713      }      }
714    
715        bool InstrumentScriptVMFunction_change_cutoff::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
716            if (iArg == 1)
717                return type == VM_NO_UNIT || type == VM_HERTZ;
718            else
719                return type == VM_NO_UNIT;
720        }
721    
722        bool InstrumentScriptVMFunction_change_cutoff::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
723            return iArg == 1 && type == VM_HERTZ;
724        }
725    
726        bool InstrumentScriptVMFunction_change_cutoff::acceptsArgFinal(vmint iArg) const {
727            return iArg == 1;
728        }
729    
730      VMFnResult* InstrumentScriptVMFunction_change_cutoff::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_cutoff::exec(VMFnArgs* args) {
731          int cutoff = args->arg(1)->asInt()->evalInt();          StdUnit_t unit = args->arg(1)->asInt()->unitType();
732          if (cutoff > VM_FILTER_PAR_MAX_VALUE) {          vmint cutoff   = (unit) ? args->arg(1)->asInt()->evalInt(VM_NO_PREFIX)
733              wrnMsg("change_cutoff(): argument 2 may not be larger than 1000000");                                  : args->arg(1)->asInt()->evalInt();
734            bool isFinal   = args->arg(1)->asInt()->isFinal();
735            if (!unit && cutoff > VM_FILTER_PAR_MAX_VALUE) {
736                wrnMsg("change_cutoff(): argument 2 may not be larger than " strfy(VM_FILTER_PAR_MAX_VALUE));
737              cutoff = VM_FILTER_PAR_MAX_VALUE;              cutoff = VM_FILTER_PAR_MAX_VALUE;
738            } else if (unit && cutoff > VM_FILTER_PAR_MAX_HZ) {
739                wrnMsg("change_cutoff(): argument 2 may not be larger than " strfy(VM_FILTER_PAR_MAX_HZ) " Hz");
740                cutoff = VM_FILTER_PAR_MAX_HZ;
741          } else if (cutoff < 0) {          } else if (cutoff < 0) {
742              wrnMsg("change_cutoff(): argument 2 may not be negative");              wrnMsg("change_cutoff(): argument 2 may not be negative");
743              cutoff = 0;              cutoff = 0;
744          }          }
745          const float fCutoff = float(cutoff) / float(VM_FILTER_PAR_MAX_VALUE);          const float fCutoff =
746                (unit) ? cutoff : float(cutoff) / float(VM_FILTER_PAR_MAX_VALUE);
747    
748            if (unit && !isFinal) {
749                wrnMsg("change_cutoff(): you must pass argument 2 as 'final' value when using Hz as unit");
750                return successResult();
751            }
752    
753          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
754              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 693  namespace LinuxSampler { Line 770  namespace LinuxSampler {
770              // if change_cutoff() was called immediately after note was triggered              // if change_cutoff() was called immediately after note was triggered
771              // then immediately apply cutoff to Note object              // then immediately apply cutoff to Note object
772              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
773                  pNote->Override.Cutoff = fCutoff;                  pNote->Override.Cutoff.Value = fCutoff;
774                    pNote->Override.Cutoff.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
775              } else { // otherwise schedule cutoff change ...              } else { // otherwise schedule cutoff change ...
776                  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"
777                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 701  namespace LinuxSampler { Line 779  namespace LinuxSampler {
779                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
780                  e.Param.NoteSynthParam.Type     = Event::synth_param_cutoff;                  e.Param.NoteSynthParam.Type     = Event::synth_param_cutoff;
781                  e.Param.NoteSynthParam.Delta    = fCutoff;                  e.Param.NoteSynthParam.Delta    = fCutoff;
782                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
783                        isFinal, false, unit
784                    );
785                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
786              }              }
787          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
788              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
789              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
790                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
791                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
792    
# Line 717  namespace LinuxSampler { Line 796  namespace LinuxSampler {
796                  // if change_cutoff() was called immediately after note was triggered                  // if change_cutoff() was called immediately after note was triggered
797                  // then immediately apply cutoff to Note object                  // then immediately apply cutoff to Note object
798                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
799                      pNote->Override.Cutoff = fCutoff;                      pNote->Override.Cutoff.Value = fCutoff;
800                        pNote->Override.Cutoff.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
801                  } else { // otherwise schedule cutoff change ...                  } else { // otherwise schedule cutoff change ...
802                      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"
803                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 725  namespace LinuxSampler { Line 805  namespace LinuxSampler {
805                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
806                      e.Param.NoteSynthParam.Type     = Event::synth_param_cutoff;                      e.Param.NoteSynthParam.Type     = Event::synth_param_cutoff;
807                      e.Param.NoteSynthParam.Delta    = fCutoff;                      e.Param.NoteSynthParam.Delta    = fCutoff;
808                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
809                            isFinal, false, unit
810                        );
811                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
812                  }                  }
813              }              }
# Line 742  namespace LinuxSampler { Line 823  namespace LinuxSampler {
823      {      {
824      }      }
825    
826      bool InstrumentScriptVMFunction_change_reso::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_change_reso::acceptsArgType(vmint iArg, ExprType_t type) const {
827          if (iArg == 0)          if (iArg == 0)
828              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
829          else          else
830              return type == INT_EXPR;              return type == INT_EXPR;
831      }      }
832    
833        bool InstrumentScriptVMFunction_change_reso::acceptsArgFinal(vmint iArg) const {
834            return iArg == 1;
835        }
836    
837      VMFnResult* InstrumentScriptVMFunction_change_reso::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_reso::exec(VMFnArgs* args) {
838          int resonance = args->arg(1)->asInt()->evalInt();          vmint resonance = args->arg(1)->asInt()->evalInt();
839            bool isFinal    = args->arg(1)->asInt()->isFinal();
840          if (resonance > VM_FILTER_PAR_MAX_VALUE) {          if (resonance > VM_FILTER_PAR_MAX_VALUE) {
841              wrnMsg("change_reso(): argument 2 may not be larger than 1000000");              wrnMsg("change_reso(): argument 2 may not be larger than 1000000");
842              resonance = VM_FILTER_PAR_MAX_VALUE;              resonance = VM_FILTER_PAR_MAX_VALUE;
# Line 780  namespace LinuxSampler { Line 866  namespace LinuxSampler {
866              // if change_reso() was called immediately after note was triggered              // if change_reso() was called immediately after note was triggered
867              // then immediately apply resonance to Note object              // then immediately apply resonance to Note object
868              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
869                  pNote->Override.Resonance = fResonance;                  pNote->Override.Resonance.Value = fResonance;
870                    pNote->Override.Resonance.Final = isFinal;
871              } else { // otherwise schedule resonance change ...              } else { // otherwise schedule resonance change ...
872                  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"
873                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 788  namespace LinuxSampler { Line 875  namespace LinuxSampler {
875                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
876                  e.Param.NoteSynthParam.Type     = Event::synth_param_resonance;                  e.Param.NoteSynthParam.Type     = Event::synth_param_resonance;
877                  e.Param.NoteSynthParam.Delta    = fResonance;                  e.Param.NoteSynthParam.Delta    = fResonance;
878                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
879                        isFinal, false, false
880                    );
881                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
882              }              }
883          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
884              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
885              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
886                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
887                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
888    
# Line 804  namespace LinuxSampler { Line 892  namespace LinuxSampler {
892                  // if change_reso() was called immediately after note was triggered                  // if change_reso() was called immediately after note was triggered
893                  // then immediately apply resonance to Note object                  // then immediately apply resonance to Note object
894                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
895                      pNote->Override.Resonance = fResonance;                      pNote->Override.Resonance.Value = fResonance;
896                        pNote->Override.Resonance.Final = isFinal;
897                  } else { // otherwise schedule resonance change ...                  } else { // otherwise schedule resonance change ...
898                      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"
899                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 812  namespace LinuxSampler { Line 901  namespace LinuxSampler {
901                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
902                      e.Param.NoteSynthParam.Type     = Event::synth_param_resonance;                      e.Param.NoteSynthParam.Type     = Event::synth_param_resonance;
903                      e.Param.NoteSynthParam.Delta    = fResonance;                      e.Param.NoteSynthParam.Delta    = fResonance;
904                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
905                            isFinal, false, false
906                        );
907                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
908                  }                  }
909              }              }
# Line 829  namespace LinuxSampler { Line 919  namespace LinuxSampler {
919      {      {
920      }      }
921    
922      bool InstrumentScriptVMFunction_change_attack::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_change_attack::acceptsArgType(vmint iArg, ExprType_t type) const {
923          if (iArg == 0)          if (iArg == 0)
924              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
925          else          else
926              return type == INT_EXPR;              return type == INT_EXPR;
927      }      }
928    
929        bool InstrumentScriptVMFunction_change_attack::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
930            if (iArg == 1)
931                return type == VM_NO_UNIT || type == VM_SECOND;
932            else
933                return type == VM_NO_UNIT;
934        }
935    
936        bool InstrumentScriptVMFunction_change_attack::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
937            return iArg == 1 && type == VM_SECOND;
938        }
939    
940        bool InstrumentScriptVMFunction_change_attack::acceptsArgFinal(vmint iArg) const {
941            return iArg == 1;
942        }
943    
944      VMFnResult* InstrumentScriptVMFunction_change_attack::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_attack::exec(VMFnArgs* args) {
945          int attack = args->arg(1)->asInt()->evalInt();          StdUnit_t unit = args->arg(1)->asInt()->unitType();
946            vmint attack   = (unit) ? args->arg(1)->asInt()->evalInt(VM_MICRO)
947                                    : args->arg(1)->asInt()->evalInt();
948            bool isFinal   = args->arg(1)->asInt()->isFinal();
949          // note: intentionally not checking against a max. value here!          // note: intentionally not checking against a max. value here!
950          // (to allow i.e. passing 2000000 for doubling the attack time)          // (to allow i.e. passing 2000000 for doubling the attack time)
951          if (attack < 0) {          if (attack < 0) {
# Line 846  namespace LinuxSampler { Line 954  namespace LinuxSampler {
954          }          }
955          const float fAttack = float(attack) / float(VM_EG_PAR_MAX_VALUE);          const float fAttack = float(attack) / float(VM_EG_PAR_MAX_VALUE);
956    
957            if (unit && !isFinal) {
958                wrnMsg("change_attack(): you must pass argument 2 as 'final' value when using seconds as unit");
959                return successResult();
960            }
961    
962          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
963              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
964    
# Line 866  namespace LinuxSampler { Line 979  namespace LinuxSampler {
979              // if change_attack() was called immediately after note was triggered              // if change_attack() was called immediately after note was triggered
980              // then immediately apply attack to Note object              // then immediately apply attack to Note object
981              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
982                  pNote->Override.Attack = fAttack;                  pNote->Override.Attack.Value = fAttack;
983                    pNote->Override.Attack.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
984              } else { // otherwise schedule attack change ...              } else { // otherwise schedule attack change ...
985                  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"
986                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 874  namespace LinuxSampler { Line 988  namespace LinuxSampler {
988                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
989                  e.Param.NoteSynthParam.Type     = Event::synth_param_attack;                  e.Param.NoteSynthParam.Type     = Event::synth_param_attack;
990                  e.Param.NoteSynthParam.Delta    = fAttack;                  e.Param.NoteSynthParam.Delta    = fAttack;
991                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
992                        isFinal, false, unit
993                    );
994                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
995              }              }
996          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
997              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
998              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
999                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
1000                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
1001    
# Line 890  namespace LinuxSampler { Line 1005  namespace LinuxSampler {
1005                  // if change_attack() was called immediately after note was triggered                  // if change_attack() was called immediately after note was triggered
1006                  // then immediately apply attack to Note object                  // then immediately apply attack to Note object
1007                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1008                      pNote->Override.Attack = fAttack;                      pNote->Override.Attack.Value = fAttack;
1009                        pNote->Override.Attack.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1010                  } else { // otherwise schedule attack change ...                  } else { // otherwise schedule attack change ...
1011                      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"
1012                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 898  namespace LinuxSampler { Line 1014  namespace LinuxSampler {
1014                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
1015                      e.Param.NoteSynthParam.Type     = Event::synth_param_attack;                      e.Param.NoteSynthParam.Type     = Event::synth_param_attack;
1016                      e.Param.NoteSynthParam.Delta    = fAttack;                      e.Param.NoteSynthParam.Delta    = fAttack;
1017                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1018                            isFinal, false, unit
1019                        );
1020                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
1021                  }                  }
1022              }              }
# Line 915  namespace LinuxSampler { Line 1032  namespace LinuxSampler {
1032      {      {
1033      }      }
1034    
1035      bool InstrumentScriptVMFunction_change_decay::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_change_decay::acceptsArgType(vmint iArg, ExprType_t type) const {
1036          if (iArg == 0)          if (iArg == 0)
1037              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
1038          else          else
1039              return type == INT_EXPR;              return type == INT_EXPR;
1040      }      }
1041    
1042        bool InstrumentScriptVMFunction_change_decay::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
1043            if (iArg == 1)
1044                return type == VM_NO_UNIT || type == VM_SECOND;
1045            else
1046                return type == VM_NO_UNIT;
1047        }
1048    
1049        bool InstrumentScriptVMFunction_change_decay::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
1050            return iArg == 1 && type == VM_SECOND;
1051        }
1052    
1053        bool InstrumentScriptVMFunction_change_decay::acceptsArgFinal(vmint iArg) const {
1054            return iArg == 1;
1055        }
1056    
1057      VMFnResult* InstrumentScriptVMFunction_change_decay::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_decay::exec(VMFnArgs* args) {
1058          int decay = args->arg(1)->asInt()->evalInt();          StdUnit_t unit = args->arg(1)->asInt()->unitType();
1059            vmint decay    = (unit) ? args->arg(1)->asInt()->evalInt(VM_MICRO)
1060                                    : args->arg(1)->asInt()->evalInt();
1061            bool isFinal   = args->arg(1)->asInt()->isFinal();
1062          // note: intentionally not checking against a max. value here!          // note: intentionally not checking against a max. value here!
1063          // (to allow i.e. passing 2000000 for doubling the decay time)          // (to allow i.e. passing 2000000 for doubling the decay time)
1064          if (decay < 0) {          if (decay < 0) {
# Line 932  namespace LinuxSampler { Line 1067  namespace LinuxSampler {
1067          }          }
1068          const float fDecay = float(decay) / float(VM_EG_PAR_MAX_VALUE);          const float fDecay = float(decay) / float(VM_EG_PAR_MAX_VALUE);
1069    
1070            if (unit && !isFinal) {
1071                wrnMsg("change_decay(): you must pass argument 2 as 'final' value when using seconds as unit");
1072                return successResult();
1073            }
1074    
1075          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
1076              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1077    
# Line 952  namespace LinuxSampler { Line 1092  namespace LinuxSampler {
1092              // if change_decay() was called immediately after note was triggered              // if change_decay() was called immediately after note was triggered
1093              // then immediately apply decay to Note object              // then immediately apply decay to Note object
1094              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1095                  pNote->Override.Decay = fDecay;                  pNote->Override.Decay.Value = fDecay;
1096                    pNote->Override.Decay.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1097              } else { // otherwise schedule decay change ...              } else { // otherwise schedule decay change ...
1098                  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"
1099                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 960  namespace LinuxSampler { Line 1101  namespace LinuxSampler {
1101                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
1102                  e.Param.NoteSynthParam.Type     = Event::synth_param_decay;                  e.Param.NoteSynthParam.Type     = Event::synth_param_decay;
1103                  e.Param.NoteSynthParam.Delta    = fDecay;                  e.Param.NoteSynthParam.Delta    = fDecay;
1104                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1105                        isFinal, false, unit
1106                    );
1107                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
1108              }              }
1109          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1110              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1111              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
1112                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
1113                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
1114    
# Line 976  namespace LinuxSampler { Line 1118  namespace LinuxSampler {
1118                  // if change_decay() was called immediately after note was triggered                  // if change_decay() was called immediately after note was triggered
1119                  // then immediately apply decay to Note object                  // then immediately apply decay to Note object
1120                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1121                      pNote->Override.Decay = fDecay;                      pNote->Override.Decay.Value = fDecay;
1122                        pNote->Override.Decay.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1123                  } else { // otherwise schedule decay change ...                  } else { // otherwise schedule decay change ...
1124                      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"
1125                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 984  namespace LinuxSampler { Line 1127  namespace LinuxSampler {
1127                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
1128                      e.Param.NoteSynthParam.Type     = Event::synth_param_decay;                      e.Param.NoteSynthParam.Type     = Event::synth_param_decay;
1129                      e.Param.NoteSynthParam.Delta    = fDecay;                      e.Param.NoteSynthParam.Delta    = fDecay;
1130                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1131                            isFinal, false, unit
1132                        );
1133                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
1134                  }                  }
1135              }              }
# Line 1001  namespace LinuxSampler { Line 1145  namespace LinuxSampler {
1145      {      {
1146      }      }
1147    
1148      bool InstrumentScriptVMFunction_change_release::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_change_release::acceptsArgType(vmint iArg, ExprType_t type) const {
1149          if (iArg == 0)          if (iArg == 0)
1150              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
1151          else          else
1152              return type == INT_EXPR;              return type == INT_EXPR;
1153      }      }
1154    
1155        bool InstrumentScriptVMFunction_change_release::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
1156            if (iArg == 1)
1157                return type == VM_NO_UNIT || type == VM_SECOND;
1158            else
1159                return type == VM_NO_UNIT;
1160        }
1161    
1162        bool InstrumentScriptVMFunction_change_release::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
1163            return iArg == 1 && type == VM_SECOND;
1164        }
1165    
1166        bool InstrumentScriptVMFunction_change_release::acceptsArgFinal(vmint iArg) const {
1167            return iArg == 1;
1168        }
1169    
1170      VMFnResult* InstrumentScriptVMFunction_change_release::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_release::exec(VMFnArgs* args) {
1171          int release = args->arg(1)->asInt()->evalInt();          StdUnit_t unit = args->arg(1)->asInt()->unitType();
1172            vmint release  = (unit) ? args->arg(1)->asInt()->evalInt(VM_MICRO)
1173                                    : args->arg(1)->asInt()->evalInt();
1174            bool isFinal   = args->arg(1)->asInt()->isFinal();
1175          // note: intentionally not checking against a max. value here!          // note: intentionally not checking against a max. value here!
1176          // (to allow i.e. passing 2000000 for doubling the release time)          // (to allow i.e. passing 2000000 for doubling the release time)
1177          if (release < 0) {          if (release < 0) {
# Line 1018  namespace LinuxSampler { Line 1180  namespace LinuxSampler {
1180          }          }
1181          const float fRelease = float(release) / float(VM_EG_PAR_MAX_VALUE);          const float fRelease = float(release) / float(VM_EG_PAR_MAX_VALUE);
1182    
1183            if (unit && !isFinal) {
1184                wrnMsg("change_release(): you must pass argument 2 as 'final' value when using seconds as unit");
1185                return successResult();
1186            }
1187    
1188          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
1189              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1190    
# Line 1038  namespace LinuxSampler { Line 1205  namespace LinuxSampler {
1205              // if change_release() was called immediately after note was triggered              // if change_release() was called immediately after note was triggered
1206              // then immediately apply relase to Note object              // then immediately apply relase to Note object
1207              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1208                  pNote->Override.Release = fRelease;                  pNote->Override.Release.Value = fRelease;
1209                    pNote->Override.Release.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1210              } else { // otherwise schedule release change ...              } else { // otherwise schedule release change ...
1211                  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"
1212                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 1046  namespace LinuxSampler { Line 1214  namespace LinuxSampler {
1214                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
1215                  e.Param.NoteSynthParam.Type     = Event::synth_param_release;                  e.Param.NoteSynthParam.Type     = Event::synth_param_release;
1216                  e.Param.NoteSynthParam.Delta    = fRelease;                  e.Param.NoteSynthParam.Delta    = fRelease;
1217                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1218                        isFinal, false, unit
1219                    );
1220                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
1221              }              }
1222          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1223              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1224              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
1225                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
1226                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
1227    
# Line 1062  namespace LinuxSampler { Line 1231  namespace LinuxSampler {
1231                  // if change_release() was called immediately after note was triggered                  // if change_release() was called immediately after note was triggered
1232                  // then immediately apply relase to Note object                  // then immediately apply relase to Note object
1233                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1234                      pNote->Override.Release = fRelease;                      pNote->Override.Release.Value = fRelease;
1235                        pNote->Override.Release.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1236                  } else { // otherwise schedule release change ...                  } else { // otherwise schedule release change ...
1237                      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"
1238                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 1070  namespace LinuxSampler { Line 1240  namespace LinuxSampler {
1240                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
1241                      e.Param.NoteSynthParam.Type     = Event::synth_param_release;                      e.Param.NoteSynthParam.Type     = Event::synth_param_release;
1242                      e.Param.NoteSynthParam.Delta    = fRelease;                      e.Param.NoteSynthParam.Delta    = fRelease;
1243                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1244                            isFinal, false, unit
1245                        );
1246                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
1247                  }                  }
1248              }              }
# Line 1082  namespace LinuxSampler { Line 1253  namespace LinuxSampler {
1253    
1254      // template for change_*() functions      // template for change_*() functions
1255    
1256      bool VMChangeSynthParamFunction::acceptsArgType(int iArg, ExprType_t type) const {      bool VMChangeSynthParamFunction::acceptsArgType(vmint iArg, ExprType_t type) const {
1257          if (iArg == 0)          if (iArg == 0)
1258              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
1259          else          else
1260              return type == INT_EXPR;              return type == INT_EXPR;
1261      }      }
1262    
1263        bool VMChangeSynthParamFunction::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
1264            if (iArg == 1)
1265                return type == VM_NO_UNIT || type == m_unit;
1266            else
1267                return type == VM_NO_UNIT;
1268        }
1269    
1270        bool VMChangeSynthParamFunction::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
1271            return m_acceptUnitPrefix && iArg == 1 && type == m_unit;
1272        }
1273    
1274        bool VMChangeSynthParamFunction::acceptsArgFinal(vmint iArg) const {
1275            return (m_acceptFinal) ? (iArg == 1) : false;
1276        }
1277    
1278        inline static void setNoteParamScopeBy_FinalUnit(NoteBase::Param& param, const bool bFinal, const StdUnit_t unit) {
1279            param.Scope = NoteBase::scopeBy_FinalUnit(bFinal, unit);
1280        }
1281    
1282        inline static void setNoteParamScopeBy_FinalUnit(NoteBase::Norm& param, const bool bFinal, const StdUnit_t unit) {
1283            param.Final = bFinal;
1284        }
1285    
1286        inline static void setNoteParamScopeBy_FinalUnit(float& param, const bool bFinal, const StdUnit_t unit) {
1287            /* NOOP */
1288        }
1289    
1290        template<class T>
1291        inline static void setNoteParamValue(T& param, vmfloat value) {
1292            param.Value = value;
1293        }
1294    
1295        inline static void setNoteParamValue(float& param, vmfloat value) {
1296            param = value;
1297        }
1298    
1299      // Arbitrarily chosen constant value symbolizing "no limit".      // Arbitrarily chosen constant value symbolizing "no limit".
1300      #define NO_LIMIT 1315916909      #define NO_LIMIT 1315916909
1301    
1302      template<float NoteBase::_Override::*T_noteParam, int T_synthParam,      template<class T_NoteParamType, T_NoteParamType NoteBase::_Override::*T_noteParam,
1303               bool T_isNormalizedParam, int T_maxValue, int T_minValue>               vmint T_synthParam,
1304      VMFnResult* VMChangeSynthParamFunction::execTemplate(VMFnArgs* args, const char* functionName) {               vmint T_minValueNorm, vmint T_maxValueNorm, bool T_normalizeNorm,
1305          int value = args->arg(1)->asInt()->evalInt();               vmint T_minValueUnit, vmint T_maxValueUnit,
1306          if (T_maxValue != NO_LIMIT && value > T_maxValue) {               MetricPrefix_t T_unitPrefix0, MetricPrefix_t ... T_unitPrefixN>
1307              wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValue));      VMFnResult* VMChangeSynthParamFunction::execTemplate(VMFnArgs* args, const char* functionName)
1308              value = T_maxValue;      {
1309          } else if (T_minValue != NO_LIMIT && value < T_minValue) {          const StdUnit_t unit = args->arg(1)->asInt()->unitType();
1310              if (T_minValue == 0)          const bool isFinal   = args->arg(1)->asInt()->isFinal();
1311                  wrnMsg(String(functionName) + "(): argument 2 may not be negative");          vmint value =
1312              else              (m_acceptUnitPrefix && ((m_unit && unit) || (!m_unit && args->arg(1)->asInt()->unitPrefix(0))))
1313                  wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValue));                  ? args->arg(1)->asInt()->evalInt(T_unitPrefix0, T_unitPrefixN ...)
1314              value = T_minValue;                  : args->arg(1)->asInt()->evalInt();
1315          }  
1316          const float fValue = (T_isNormalizedParam) ?          if (unit && !isFinal && m_unit != VM_BEL && m_unit) {
1317              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");
1318              float(value) / 1000000.f; // assuming microseconds here, convert to seconds              return successResult();
1319            }
1320    
1321            // check if passed value is in allowed range
1322            if (unit && m_unit) {
1323                if (T_maxValueUnit != NO_LIMIT && value > T_maxValueUnit) {
1324                    wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValueUnit));
1325                    value = T_maxValueUnit;
1326                } else if (T_minValueUnit != NO_LIMIT && value < T_minValueUnit) {
1327                    if (T_minValueUnit == 0)
1328                        wrnMsg(String(functionName) + "(): argument 2 may not be negative");
1329                    else
1330                        wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValueUnit));
1331                    value = T_minValueUnit;
1332                }
1333            } else { // value was passed to this function without a unit ...
1334                if (T_maxValueNorm != NO_LIMIT && value > T_maxValueNorm) {
1335                    wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValueNorm));
1336                    value = T_maxValueNorm;
1337                } else if (T_minValueNorm != NO_LIMIT && value < T_minValueNorm) {
1338                    if (T_minValueNorm == 0)
1339                        wrnMsg(String(functionName) + "(): argument 2 may not be negative");
1340                    else
1341                        wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValueNorm));
1342                    value = T_minValueNorm;
1343                }
1344            }
1345    
1346            // convert passed argument value to engine internal expected value range (i.e. 0.0 .. 1.0)
1347            const float fValue =
1348                (unit && m_unit) ?
1349                    (unit == VM_BEL) ?
1350                        RTMath::DecibelToLinRatio(float(value) * float(T_unitPrefix0) /*i.e. mdB -> dB*/) :
1351                        float(value) * VMUnit::unitFactor(T_unitPrefix0, T_unitPrefixN ...) /*i.e. us -> s*/ :
1352                    (T_normalizeNorm) ?
1353                        float(value) / ((T_maxValueNorm != NO_LIMIT) ? float(T_maxValueNorm) : 1000000.f/* fallback: value range used for most */) :
1354                        float(value) /* as is */;
1355    
1356          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
1357              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 1131  namespace LinuxSampler { Line 1374  namespace LinuxSampler {
1374              // note was triggered then immediately apply the synth parameter              // note was triggered then immediately apply the synth parameter
1375              // change to Note object              // change to Note object
1376              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1377                  pNote->Override.*T_noteParam = fValue;                  setNoteParamValue(pNote->Override.*T_noteParam, fValue);
1378                    setNoteParamScopeBy_FinalUnit(
1379                        (pNote->Override.*T_noteParam),
1380                        isFinal, unit
1381                    );
1382              } else { // otherwise schedule this synth parameter change ...              } else { // otherwise schedule this synth parameter change ...
1383                  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"
1384                  e.Init(); // clear IDs                  e.Init(); // clear IDs
# Line 1139  namespace LinuxSampler { Line 1386  namespace LinuxSampler {
1386                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
1387                  e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;                  e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1388                  e.Param.NoteSynthParam.Delta    = fValue;                  e.Param.NoteSynthParam.Delta    = fValue;
1389                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1390                        isFinal, false, unit
1391                    );
1392                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
1393              }              }
1394          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1395              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1396              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
1397                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
1398                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
1399    
# Line 1156  namespace LinuxSampler { Line 1404  namespace LinuxSampler {
1404                  // note was triggered then immediately apply the synth parameter                  // note was triggered then immediately apply the synth parameter
1405                  // change to Note object                  // change to Note object
1406                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1407                      pNote->Override.*T_noteParam = fValue;                      setNoteParamValue(pNote->Override.*T_noteParam, fValue);
1408                        setNoteParamScopeBy_FinalUnit(
1409                            (pNote->Override.*T_noteParam),
1410                            isFinal, unit
1411                        );
1412                  } else { // otherwise schedule this synth parameter change ...                  } else { // otherwise schedule this synth parameter change ...
1413                      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"
1414                      e.Init(); // clear IDs                      e.Init(); // clear IDs
# Line 1164  namespace LinuxSampler { Line 1416  namespace LinuxSampler {
1416                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
1417                      e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;                      e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1418                      e.Param.NoteSynthParam.Delta    = fValue;                      e.Param.NoteSynthParam.Delta    = fValue;
1419                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1420                            isFinal, false, unit
1421                        );
1422                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
1423                  }                  }
1424              }              }
# Line 1178  namespace LinuxSampler { Line 1431  namespace LinuxSampler {
1431    
1432      VMFnResult* InstrumentScriptVMFunction_change_sustain::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_sustain::exec(VMFnArgs* args) {
1433          return VMChangeSynthParamFunction::execTemplate<          return VMChangeSynthParamFunction::execTemplate<
1434          &NoteBase::_Override::Sustain,                      decltype(NoteBase::_Override::Sustain),
1435          Event::synth_param_sustain,                      &NoteBase::_Override::Sustain,
1436          true, 1000000, 0>( args, "change_sustain" );                      Event::synth_param_sustain,
1437                        /* if value passed without unit */
1438                        0, NO_LIMIT, true,
1439                        /* if value passed WITH 'Bel' unit */
1440                        NO_LIMIT, NO_LIMIT, VM_MILLI, VM_DECI>( args, "change_sustain" );
1441        }
1442    
1443        // change_cutoff_attack() function
1444    
1445        VMFnResult* InstrumentScriptVMFunction_change_cutoff_attack::exec(VMFnArgs* args) {
1446            return VMChangeSynthParamFunction::execTemplate<
1447                        decltype(NoteBase::_Override::CutoffAttack),
1448                        &NoteBase::_Override::CutoffAttack,
1449                        Event::synth_param_cutoff_attack,
1450                        /* if value passed without unit */
1451                        0, NO_LIMIT, true,
1452                        /* if value passed with 'seconds' unit */
1453                        0, NO_LIMIT, VM_MICRO>( args, "change_cutoff_attack" );
1454        }
1455    
1456        // change_cutoff_decay() function
1457    
1458        VMFnResult* InstrumentScriptVMFunction_change_cutoff_decay::exec(VMFnArgs* args) {
1459            return VMChangeSynthParamFunction::execTemplate<
1460                        decltype(NoteBase::_Override::CutoffDecay),
1461                        &NoteBase::_Override::CutoffDecay,
1462                        Event::synth_param_cutoff_decay,
1463                        /* if value passed without unit */
1464                        0, NO_LIMIT, true,
1465                        /* if value passed with 'seconds' unit */
1466                        0, NO_LIMIT, VM_MICRO>( args, "change_cutoff_decay" );
1467        }
1468    
1469        // change_cutoff_sustain() function
1470    
1471        VMFnResult* InstrumentScriptVMFunction_change_cutoff_sustain::exec(VMFnArgs* args) {
1472            return VMChangeSynthParamFunction::execTemplate<
1473                        decltype(NoteBase::_Override::CutoffSustain),
1474                        &NoteBase::_Override::CutoffSustain,
1475                        Event::synth_param_cutoff_sustain,
1476                        /* if value passed without unit */
1477                        0, NO_LIMIT, true,
1478                        /* if value passed WITH 'Bel' unit */
1479                        NO_LIMIT, NO_LIMIT, VM_MILLI, VM_DECI>( args, "change_cutoff_sustain" );
1480        }
1481    
1482        // change_cutoff_release() function
1483    
1484        VMFnResult* InstrumentScriptVMFunction_change_cutoff_release::exec(VMFnArgs* args) {
1485            return VMChangeSynthParamFunction::execTemplate<
1486                        decltype(NoteBase::_Override::CutoffRelease),
1487                        &NoteBase::_Override::CutoffRelease,
1488                        Event::synth_param_cutoff_release,
1489                        /* if value passed without unit */
1490                        0, NO_LIMIT, true,
1491                        /* if value passed with 'seconds' unit */
1492                        0, NO_LIMIT, VM_MICRO>( args, "change_cutoff_release" );
1493      }      }
1494    
1495      // change_amp_lfo_depth() function      // change_amp_lfo_depth() function
1496    
1497      VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_depth::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_depth::exec(VMFnArgs* args) {
1498          return VMChangeSynthParamFunction::execTemplate<          return VMChangeSynthParamFunction::execTemplate<
1499                        decltype(NoteBase::_Override::AmpLFODepth),
1500                      &NoteBase::_Override::AmpLFODepth,                      &NoteBase::_Override::AmpLFODepth,
1501                      Event::synth_param_amp_lfo_depth,                      Event::synth_param_amp_lfo_depth,
1502                      true, 1000000, 0>( args, "change_amp_lfo_depth" );                      /* if value passed without unit */
1503                        0, 1000000, true,
1504                        /* not used (since this function does not accept unit) */
1505                        NO_LIMIT, NO_LIMIT, VM_NO_PREFIX>( args, "change_amp_lfo_depth" );
1506      }      }
1507    
1508      // change_amp_lfo_freq() function      // change_amp_lfo_freq() function
1509    
1510      VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_freq::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_freq::exec(VMFnArgs* args) {
1511          return VMChangeSynthParamFunction::execTemplate<          return VMChangeSynthParamFunction::execTemplate<
1512                        decltype(NoteBase::_Override::AmpLFOFreq),
1513                      &NoteBase::_Override::AmpLFOFreq,                      &NoteBase::_Override::AmpLFOFreq,
1514                      Event::synth_param_amp_lfo_freq,                      Event::synth_param_amp_lfo_freq,
1515                      true, 1000000, 0>( args, "change_amp_lfo_freq" );                      /* if value passed without unit */
1516                        0, 1000000, true,
1517                        /* if value passed with 'Hz' unit */
1518                        0, 30000, VM_NO_PREFIX>( args, "change_amp_lfo_freq" );
1519        }
1520    
1521        // change_cutoff_lfo_depth() function
1522    
1523        VMFnResult* InstrumentScriptVMFunction_change_cutoff_lfo_depth::exec(VMFnArgs* args) {
1524            return VMChangeSynthParamFunction::execTemplate<
1525                        decltype(NoteBase::_Override::CutoffLFODepth),
1526                        &NoteBase::_Override::CutoffLFODepth,
1527                        Event::synth_param_cutoff_lfo_depth,
1528                        /* if value passed without unit */
1529                        0, 1000000, true,
1530                        /* not used (since this function does not accept unit) */
1531                        NO_LIMIT, NO_LIMIT, VM_NO_PREFIX>( args, "change_cutoff_lfo_depth" );
1532        }
1533    
1534        // change_cutoff_lfo_freq() function
1535    
1536        VMFnResult* InstrumentScriptVMFunction_change_cutoff_lfo_freq::exec(VMFnArgs* args) {
1537            return VMChangeSynthParamFunction::execTemplate<
1538                        decltype(NoteBase::_Override::CutoffLFOFreq),
1539                        &NoteBase::_Override::CutoffLFOFreq,
1540                        Event::synth_param_cutoff_lfo_freq,
1541                        /* if value passed without unit */
1542                        0, 1000000, true,
1543                        /* if value passed with 'Hz' unit */
1544                        0, 30000, VM_NO_PREFIX>( args, "change_cutoff_lfo_freq" );
1545      }      }
1546    
1547      // change_pitch_lfo_depth() function      // change_pitch_lfo_depth() function
1548    
1549      VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_depth::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_depth::exec(VMFnArgs* args) {
1550          return VMChangeSynthParamFunction::execTemplate<          return VMChangeSynthParamFunction::execTemplate<
1551                        decltype(NoteBase::_Override::PitchLFODepth),
1552                      &NoteBase::_Override::PitchLFODepth,                      &NoteBase::_Override::PitchLFODepth,
1553                      Event::synth_param_pitch_lfo_depth,                      Event::synth_param_pitch_lfo_depth,
1554                      true, 1000000, 0>( args, "change_pitch_lfo_depth" );                      /* if value passed without unit */
1555                        0, 1000000, true,
1556                        /* not used (since this function does not accept unit) */
1557                        NO_LIMIT, NO_LIMIT, VM_NO_PREFIX>( args, "change_pitch_lfo_depth" );
1558      }      }
1559    
1560      // change_pitch_lfo_freq() function      // change_pitch_lfo_freq() function
1561    
1562      VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_freq::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_freq::exec(VMFnArgs* args) {
1563          return VMChangeSynthParamFunction::execTemplate<          return VMChangeSynthParamFunction::execTemplate<
1564                        decltype(NoteBase::_Override::PitchLFOFreq),
1565                      &NoteBase::_Override::PitchLFOFreq,                      &NoteBase::_Override::PitchLFOFreq,
1566                      Event::synth_param_pitch_lfo_freq,                      Event::synth_param_pitch_lfo_freq,
1567                      true, 1000000, 0>( args, "change_pitch_lfo_freq" );                      /* if value passed without unit */
1568                        0, 1000000, true,
1569                        /* if value passed with 'Hz' unit */
1570                        0, 30000, VM_NO_PREFIX>( args, "change_pitch_lfo_freq" );
1571      }      }
1572    
1573      // change_vol_time() function      // change_vol_time() function
1574    
1575      VMFnResult* InstrumentScriptVMFunction_change_vol_time::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_vol_time::exec(VMFnArgs* args) {
1576          return VMChangeSynthParamFunction::execTemplate<          return VMChangeSynthParamFunction::execTemplate<
1577                        decltype(NoteBase::_Override::VolumeTime),
1578                      &NoteBase::_Override::VolumeTime,                      &NoteBase::_Override::VolumeTime,
1579                      Event::synth_param_volume_time,                      Event::synth_param_volume_time,
1580                      false, NO_LIMIT, 0>( args, "change_vol_time" );                      /* if value passed without unit (implying 'us' unit) */
1581                        0, NO_LIMIT, true,
1582                        /* if value passed with 'seconds' unit */
1583                        0, NO_LIMIT, VM_MICRO>( args, "change_vol_time" );
1584      }      }
1585    
1586      // change_tune_time() function      // change_tune_time() function
1587    
1588      VMFnResult* InstrumentScriptVMFunction_change_tune_time::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_tune_time::exec(VMFnArgs* args) {
1589          return VMChangeSynthParamFunction::execTemplate<          return VMChangeSynthParamFunction::execTemplate<
1590                        decltype(NoteBase::_Override::PitchTime),
1591                      &NoteBase::_Override::PitchTime,                      &NoteBase::_Override::PitchTime,
1592                      Event::synth_param_pitch_time,                      Event::synth_param_pitch_time,
1593                      false, NO_LIMIT, 0>( args, "change_tune_time" );                      /* if value passed without unit (implying 'us' unit) */
1594                        0, NO_LIMIT, true,
1595                        /* if value passed with 'seconds' unit */
1596                        0, NO_LIMIT, VM_MICRO>( args, "change_tune_time" );
1597        }
1598    
1599        // change_pan_time() function
1600    
1601        VMFnResult* InstrumentScriptVMFunction_change_pan_time::exec(VMFnArgs* args) {
1602            return VMChangeSynthParamFunction::execTemplate<
1603                        decltype(NoteBase::_Override::PanTime),
1604                        &NoteBase::_Override::PanTime,
1605                        Event::synth_param_pan_time,
1606                        /* if value passed without unit (implying 'us' unit) */
1607                        0, NO_LIMIT, true,
1608                        /* if value passed with 'seconds' unit */
1609                        0, NO_LIMIT, VM_MICRO>( args, "change_pan_time" );
1610      }      }
1611    
1612      // template for change_*_curve() functions      // template for change_*_curve() functions
1613    
1614      bool VMChangeFadeCurveFunction::acceptsArgType(int iArg, ExprType_t type) const {      bool VMChangeFadeCurveFunction::acceptsArgType(vmint iArg, ExprType_t type) const {
1615          if (iArg == 0)          if (iArg == 0)
1616              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
1617          else          else
1618              return type == INT_EXPR;              return type == INT_EXPR;
1619      }      }
1620    
1621      template<fade_curve_t NoteBase::_Override::*T_noteParam, int T_synthParam>      template<fade_curve_t NoteBase::_Override::*T_noteParam, vmint T_synthParam>
1622      VMFnResult* VMChangeFadeCurveFunction::execTemplate(VMFnArgs* args, const char* functionName) {      VMFnResult* VMChangeFadeCurveFunction::execTemplate(VMFnArgs* args, const char* functionName) {
1623          int value = args->arg(1)->asInt()->evalInt();          vmint value = args->arg(1)->asInt()->evalInt();
1624          switch (value) {          switch (value) {
1625              case FADE_CURVE_LINEAR:              case FADE_CURVE_LINEAR:
1626              case FADE_CURVE_EASE_IN_EASE_OUT:              case FADE_CURVE_EASE_IN_EASE_OUT:
# Line 1287  namespace LinuxSampler { Line 1659  namespace LinuxSampler {
1659                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
1660                  e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;                  e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1661                  e.Param.NoteSynthParam.Delta    = value;                  e.Param.NoteSynthParam.Delta    = value;
1662                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
1663    
1664                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
1665              }              }
1666          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1667              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1668              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
1669                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
1670                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
1671    
# Line 1312  namespace LinuxSampler { Line 1684  namespace LinuxSampler {
1684                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
1685                      e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;                      e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1686                      e.Param.NoteSynthParam.Delta    = value;                      e.Param.NoteSynthParam.Delta    = value;
1687                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
1688    
1689                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
1690                  }                  }
# Line 1338  namespace LinuxSampler { Line 1710  namespace LinuxSampler {
1710                      Event::synth_param_pitch_curve>( args, "change_tune_curve" );                      Event::synth_param_pitch_curve>( args, "change_tune_curve" );
1711      }      }
1712    
1713        // change_pan_curve() function
1714    
1715        VMFnResult* InstrumentScriptVMFunction_change_pan_curve::exec(VMFnArgs* args) {
1716            return VMChangeFadeCurveFunction::execTemplate<
1717            &NoteBase::_Override::PanCurve,
1718            Event::synth_param_pan_curve>( args, "change_pan_curve" );
1719        }
1720    
1721      // fade_in() function      // fade_in() function
1722    
1723      InstrumentScriptVMFunction_fade_in::InstrumentScriptVMFunction_fade_in(InstrumentScriptVM* parent)      InstrumentScriptVMFunction_fade_in::InstrumentScriptVMFunction_fade_in(InstrumentScriptVM* parent)
# Line 1345  namespace LinuxSampler { Line 1725  namespace LinuxSampler {
1725      {      {
1726      }      }
1727    
1728      bool InstrumentScriptVMFunction_fade_in::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_fade_in::acceptsArgType(vmint iArg, ExprType_t type) const {
1729          if (iArg == 0)          if (iArg == 0)
1730              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
1731          else          else
1732              return type == INT_EXPR;              return type == INT_EXPR;
1733      }      }
1734    
1735        bool InstrumentScriptVMFunction_fade_in::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
1736            if (iArg == 1)
1737                return type == VM_NO_UNIT || type == VM_SECOND;
1738            else
1739                return type == VM_NO_UNIT;
1740        }
1741    
1742        bool InstrumentScriptVMFunction_fade_in::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
1743            return iArg == 1 && type == VM_SECOND;
1744        }
1745    
1746      VMFnResult* InstrumentScriptVMFunction_fade_in::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_fade_in::exec(VMFnArgs* args) {
1747          int duration = args->arg(1)->asInt()->evalInt();          StdUnit_t unit = args->arg(1)->asInt()->unitType();
1748            vmint duration = (unit) ? args->arg(1)->asInt()->evalInt(VM_MICRO)
1749                                    : args->arg(1)->asInt()->evalInt();
1750          if (duration < 0) {          if (duration < 0) {
1751              wrnMsg("fade_in(): argument 2 may not be negative");              wrnMsg("fade_in(): argument 2 may not be negative");
1752              duration = 0;              duration = 0;
# Line 1381  namespace LinuxSampler { Line 1774  namespace LinuxSampler {
1774              // then immediately apply a start volume of zero to Note object,              // then immediately apply a start volume of zero to Note object,
1775              // as well as the fade in duration              // as well as the fade in duration
1776              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1777                  pNote->Override.Volume = 0.f;                  pNote->Override.Volume.Value = 0.f;
1778                  pNote->Override.VolumeTime = fDuration;                  pNote->Override.VolumeTime = fDuration;
1779              } 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 ...
1780                  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 1390  namespace LinuxSampler { Line 1783  namespace LinuxSampler {
1783                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
1784                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1785                  e.Param.NoteSynthParam.Delta    = fDuration;                  e.Param.NoteSynthParam.Delta    = fDuration;
1786                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
1787    
1788                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
1789              }              }
# Line 1403  namespace LinuxSampler { Line 1796  namespace LinuxSampler {
1796                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
1797                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume;                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1798                  e.Param.NoteSynthParam.Delta    = 1.f;                  e.Param.NoteSynthParam.Delta    = 1.f;
1799                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
1800    
1801                  // scheduling with 0 delay would also work here, but +1 is more                  // scheduling with 0 delay would also work here, but +1 is more
1802                  // safe regarding potential future implementation changes of the                  // safe regarding potential future implementation changes of the
# Line 1412  namespace LinuxSampler { Line 1805  namespace LinuxSampler {
1805              }              }
1806          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1807              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1808              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
1809                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
1810                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
1811    
# Line 1423  namespace LinuxSampler { Line 1816  namespace LinuxSampler {
1816                  // then immediately apply a start volume of zero to Note object,                  // then immediately apply a start volume of zero to Note object,
1817                  // as well as the fade in duration                  // as well as the fade in duration
1818                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1819                      pNote->Override.Volume = 0.f;                      pNote->Override.Volume.Value = 0.f;
1820                      pNote->Override.VolumeTime = fDuration;                      pNote->Override.VolumeTime = fDuration;
1821                  } 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 ...
1822                      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 1432  namespace LinuxSampler { Line 1825  namespace LinuxSampler {
1825                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
1826                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1827                      e.Param.NoteSynthParam.Delta    = fDuration;                      e.Param.NoteSynthParam.Delta    = fDuration;
1828                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
1829    
1830                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
1831                  }                  }
# Line 1445  namespace LinuxSampler { Line 1838  namespace LinuxSampler {
1838                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
1839                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume;                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1840                      e.Param.NoteSynthParam.Delta    = 1.f;                      e.Param.NoteSynthParam.Delta    = 1.f;
1841                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
1842    
1843                      // scheduling with 0 delay would also work here, but +1 is more                      // scheduling with 0 delay would also work here, but +1 is more
1844                      // safe regarding potential future implementation changes of the                      // safe regarding potential future implementation changes of the
# Line 1465  namespace LinuxSampler { Line 1858  namespace LinuxSampler {
1858      {      {
1859      }      }
1860    
1861      bool InstrumentScriptVMFunction_fade_out::acceptsArgType(int iArg, ExprType_t type) const {      bool InstrumentScriptVMFunction_fade_out::acceptsArgType(vmint iArg, ExprType_t type) const {
1862          if (iArg == 0)          if (iArg == 0)
1863              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
1864          else          else
1865              return type == INT_EXPR;              return type == INT_EXPR;
1866      }      }
1867    
1868        bool InstrumentScriptVMFunction_fade_out::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
1869            if (iArg == 1)
1870                return type == VM_NO_UNIT || type == VM_SECOND;
1871            else
1872                return type == VM_NO_UNIT;
1873        }
1874    
1875        bool InstrumentScriptVMFunction_fade_out::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
1876            return iArg == 1 && type == VM_SECOND;
1877        }
1878    
1879      VMFnResult* InstrumentScriptVMFunction_fade_out::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_fade_out::exec(VMFnArgs* args) {
1880          int duration = args->arg(1)->asInt()->evalInt();          StdUnit_t unit = args->arg(1)->asInt()->unitType();
1881            vmint duration = (unit) ? args->arg(1)->asInt()->evalInt(VM_MICRO)
1882                                    : args->arg(1)->asInt()->evalInt();
1883          if (duration < 0) {          if (duration < 0) {
1884              wrnMsg("fade_out(): argument 2 may not be negative");              wrnMsg("fade_out(): argument 2 may not be negative");
1885              duration = 0;              duration = 0;
# Line 1510  namespace LinuxSampler { Line 1916  namespace LinuxSampler {
1916                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
1917                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1918                  e.Param.NoteSynthParam.Delta    = fDuration;                  e.Param.NoteSynthParam.Delta    = fDuration;
1919                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
1920    
1921                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
1922              }              }
# Line 1523  namespace LinuxSampler { Line 1929  namespace LinuxSampler {
1929                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
1930                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume;                  e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1931                  e.Param.NoteSynthParam.Delta    = 0.f;                  e.Param.NoteSynthParam.Delta    = 0.f;
1932                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
1933    
1934                  // scheduling with 0 delay would also work here, but +1 is more                  // scheduling with 0 delay would also work here, but +1 is more
1935                  // safe regarding potential future implementation changes of the                  // safe regarding potential future implementation changes of the
# Line 1544  namespace LinuxSampler { Line 1950  namespace LinuxSampler {
1950              }              }
1951          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1952              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1953              for (int i = 0; i < ids->arraySize(); ++i) {              for (vmint i = 0; i < ids->arraySize(); ++i) {
1954                  const ScriptID id = ids->evalIntElement(i);                  const ScriptID id = ids->evalIntElement(i);
1955                  if (!id || !id.isNoteID()) continue;                  if (!id || !id.isNoteID()) continue;
1956    
# Line 1562  namespace LinuxSampler { Line 1968  namespace LinuxSampler {
1968                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
1969                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1970                      e.Param.NoteSynthParam.Delta    = fDuration;                      e.Param.NoteSynthParam.Delta    = fDuration;
1971                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
1972    
1973                      pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
1974                  }                  }
# Line 1575  namespace LinuxSampler { Line 1981  namespace LinuxSampler {
1981                      e.Param.NoteSynthParam.NoteID   = id.noteID();                      e.Param.NoteSynthParam.NoteID   = id.noteID();
1982                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume;                      e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1983                      e.Param.NoteSynthParam.Delta    = 0.f;                      e.Param.NoteSynthParam.Delta    = 0.f;
1984                      e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
1985    
1986                      // scheduling with 0 delay would also work here, but +1 is more                      // scheduling with 0 delay would also work here, but +1 is more
1987                      // safe regarding potential future implementation changes of the                      // safe regarding potential future implementation changes of the
# Line 1627  namespace LinuxSampler { Line 2033  namespace LinuxSampler {
2033              return successResult(0);              return successResult(0);
2034          }          }
2035    
2036          const int parameter = args->arg(1)->asInt()->evalInt();          const vmint parameter = args->arg(1)->asInt()->evalInt();
2037          switch (parameter) {          switch (parameter) {
2038              case EVENT_PAR_NOTE:              case EVENT_PAR_NOTE:
2039                  return successResult(pNote->cause.Param.Note.Key);                  return successResult(pNote->cause.Param.Note.Key);
# Line 1635  namespace LinuxSampler { Line 2041  namespace LinuxSampler {
2041                  return successResult(pNote->cause.Param.Note.Velocity);                  return successResult(pNote->cause.Param.Note.Velocity);
2042              case EVENT_PAR_VOLUME:              case EVENT_PAR_VOLUME:
2043                  return successResult(                  return successResult(
2044                      RTMath::LinRatioToDecibel(pNote->Override.Volume) * 1000.f                      RTMath::LinRatioToDecibel(pNote->Override.Volume.Value) * 1000.f
2045                  );                  );
2046              case EVENT_PAR_TUNE:              case EVENT_PAR_TUNE:
2047                  return successResult(                  return successResult(
2048                       RTMath::FreqRatioToCents(pNote->Override.Pitch) * 1000.f                       RTMath::FreqRatioToCents(pNote->Override.Pitch.Value) * 1000.f
2049                  );                  );
2050              case EVENT_PAR_0:              case EVENT_PAR_0:
2051                  return successResult(pNote->userPar[0]);                  return successResult(pNote->userPar[0]);
# Line 1679  namespace LinuxSampler { Line 2085  namespace LinuxSampler {
2085          NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );          NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2086          if (!pNote) return successResult();          if (!pNote) return successResult();
2087    
2088          const int parameter = args->arg(1)->asInt()->evalInt();          const vmint parameter = args->arg(1)->asInt()->evalInt();
2089          const int value     = args->arg(2)->asInt()->evalInt();          const vmint value     = args->arg(2)->asInt()->evalInt();
2090    
2091          switch (parameter) {          switch (parameter) {
2092              case EVENT_PAR_NOTE:              case EVENT_PAR_NOTE:
# Line 1755  namespace LinuxSampler { Line 2161  namespace LinuxSampler {
2161          NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );          NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2162          if (!pNote) return successResult();          if (!pNote) return successResult();
2163    
2164          const int value = args->arg(1)->asInt()->evalInt();          const vmint value = args->arg(1)->asInt()->evalInt();
2165          if (value < 0 || value > 127) {          if (value < 0 || value > 127) {
2166              wrnMsg("change_note(): note number of argument 2 is out of range");              wrnMsg("change_note(): note number of argument 2 is out of range");
2167              return successResult();              return successResult();
# Line 1795  namespace LinuxSampler { Line 2201  namespace LinuxSampler {
2201          NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );          NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2202          if (!pNote) return successResult();          if (!pNote) return successResult();
2203    
2204          const int value = args->arg(1)->asInt()->evalInt();          const vmint value = args->arg(1)->asInt()->evalInt();
2205          if (value < 0 || value > 127) {          if (value < 0 || value > 127) {
2206              wrnMsg("change_velo(): velocity of argument 2 is out of range");              wrnMsg("change_velo(): velocity of argument 2 is out of range");
2207              return successResult();              return successResult();
# Line 1818  namespace LinuxSampler { Line 2224  namespace LinuxSampler {
2224      {      {
2225      }      }
2226    
2227        bool InstrumentScriptVMFunction_change_play_pos::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
2228            if (iArg == 1)
2229                return type == VM_NO_UNIT || type == VM_SECOND;
2230            else
2231                return type == VM_NO_UNIT;
2232        }
2233    
2234        bool InstrumentScriptVMFunction_change_play_pos::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
2235            return iArg == 1 && VM_SECOND;
2236        }
2237    
2238      VMFnResult* InstrumentScriptVMFunction_change_play_pos::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_play_pos::exec(VMFnArgs* args) {
2239          const ScriptID id = args->arg(0)->asInt()->evalInt();          const ScriptID id = args->arg(0)->asInt()->evalInt();
2240          if (!id) {          if (!id) {
# Line 1829  namespace LinuxSampler { Line 2246  namespace LinuxSampler {
2246              return successResult();              return successResult();
2247          }          }
2248    
2249          const int pos = args->arg(1)->asInt()->evalInt();          StdUnit_t unit = args->arg(1)->asInt()->unitType();
2250            const vmint pos = (unit) ? args->arg(1)->asInt()->evalInt(VM_MICRO)
2251                                     : args->arg(1)->asInt()->evalInt();
2252          if (pos < 0) {          if (pos < 0) {
2253              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");
2254              return successResult();              return successResult();
# Line 1841  namespace LinuxSampler { Line 2260  namespace LinuxSampler {
2260          NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );          NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2261          if (!pNote) return successResult();          if (!pNote) return successResult();
2262    
2263          pNote->Override.SampleOffset = pos;          pNote->Override.SampleOffset =
2264                (decltype(pNote->Override.SampleOffset)) pos;
2265    
2266          return successResult();          return successResult();
2267      }      }
# Line 1988  namespace LinuxSampler { Line 2408  namespace LinuxSampler {
2408    
2409          // 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
2410    
2411          const int n =          const vmint n =
2412              (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : 1;              (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : 1;
2413          const bool bAutoAbort =          const bool bAutoAbort =
2414              (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : true;              (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : true;
# Line 2014  namespace LinuxSampler { Line 2434  namespace LinuxSampler {
2434                  return errorResult(-1); // terminate script                  return errorResult(-1); // terminate script
2435              }              }
2436              // since both parent, as well all child script execution instances              // since both parent, as well all child script execution instances
2437              // all land in this exect() method, the following is (more or less)              // all land in this exec() method, the following is (more or less)
2438              // the only feature that lets us distinguish the parent and              // the only feature that lets us distinguish the parent and
2439              // respective children from each other in this exect() method              // respective children from each other in this exec() method
2440              itChild->forkIndex = iChild + 1;              itChild->forkIndex = iChild + 1;
2441          }          }
2442    

Legend:
Removed from v.3316  
changed lines
  Added in v.3564

  ViewVC Help
Powered by ViewVC