/[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 2962 by schoenebeck, Sun Jul 17 17:54:04 2016 UTC revision 3381 by schoenebeck, Tue Nov 28 15:54:49 2017 UTC
# Line 1  Line 1 
1  /*  /*
2   * Copyright (c) 2014-2016 Christian Schoenebeck   * Copyright (c) 2014-2017 Christian Schoenebeck
3   *   *
4   * http://www.linuxsampler.org   * http://www.linuxsampler.org
5   *   *
# Line 23  namespace LinuxSampler { Line 23  namespace LinuxSampler {
23      VMFnResult* InstrumentScriptVMFunction_play_note::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_play_note::exec(VMFnArgs* args) {
24          int note = args->arg(0)->asInt()->evalInt();          int note = args->arg(0)->asInt()->evalInt();
25          int velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127;          int velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127;
         int sampleoffset = (args->argsCount() >= 3) ? args->arg(2)->asInt()->evalInt() : 0;  
26          int duration = (args->argsCount() >= 4) ? args->arg(3)->asInt()->evalInt() : 0; //TODO: -1 might be a better default value instead of 0          int duration = (args->argsCount() >= 4) ? args->arg(3)->asInt()->evalInt() : 0; //TODO: -1 might be a better default value instead of 0
27    
28          if (note < 0 || note > 127) {          if (note < 0 || note > 127) {
# Line 36  namespace LinuxSampler { Line 35  namespace LinuxSampler {
35              return errorResult(0);              return errorResult(0);
36          }          }
37    
38          if (sampleoffset < 0) {          if (duration < -2) {
39              errMsg("play_note(): argument 3 may not be a negative sample offset");              errMsg("play_note(): argument 4 must be a duration value of at least -2 or higher");
             return errorResult(0);  
         } else if (sampleoffset != 0) {  
             wrnMsg("play_note(): argument 3 does not support a sample offset other than 0 yet");  
         }  
   
         if (duration < -1) {  
             errMsg("play_note(): argument 4 must be a duration value of at least -1 or higher");  
40              return errorResult(0);              return errorResult(0);
41          }          }
42    
# Line 63  namespace LinuxSampler { Line 55  namespace LinuxSampler {
55                  return errorResult(0);                  return errorResult(0);
56              }              }
57              e.Param.Note.ParentNoteID = m_vm->m_event->cause.Param.Note.ID;              e.Param.Note.ParentNoteID = m_vm->m_event->cause.Param.Note.ID;
58                // check if that requested parent note is actually still alive
59                NoteBase* pParentNote =
60                    pEngineChannel->pEngine->NoteByID( e.Param.Note.ParentNoteID );
61                // if parent note is already gone then this new note is not required anymore
62                if (!pParentNote)
63                    return successResult(0);
64          }          }
65    
66          const note_id_t id = pEngineChannel->ScheduleNoteMicroSec(&e, 0);          const note_id_t id = pEngineChannel->ScheduleNoteMicroSec(&e, 0);
67    
68            // if a sample offset is supplied, assign the offset as override
69            // to the previously created Note object
70            if (args->argsCount() >= 3) {
71                int sampleoffset = args->arg(2)->asInt()->evalInt();
72                if (sampleoffset >= 0) {
73                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID(id);
74                    if (pNote) {
75                        pNote->Override.SampleOffset = sampleoffset;
76                    }
77                } else if (sampleoffset < -1) {
78                    errMsg("play_note(): sample offset of argument 3 may not be less than -1");
79                }
80            }
81    
82          // if a duration is supplied (and play-note event was scheduled          // if a duration is supplied (and play-note event was scheduled
83          // successfully above), then schedule a subsequent stop-note event          // successfully above), then schedule a subsequent stop-note event
84          if (id && duration > 0) {          if (id && duration > 0) {
# Line 136  namespace LinuxSampler { Line 148  namespace LinuxSampler {
148          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
149                  static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);                  static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
150    
151          if (args->arg(0)->exprType() == INT_EXPR) {          if (args->argsCount() == 0 || args->arg(0)->exprType() == INT_EXPR) {
152              const ScriptID id = args->arg(0)->asInt()->evalInt();              const ScriptID id = (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : m_vm->m_event->id;
153              if (!id) {              if (!id && args->argsCount() >= 1) {
154                  wrnMsg("ignore_event(): event ID argument may not be zero");                  wrnMsg("ignore_event(): event ID argument may not be zero");
155                  // not errorResult(), because that would abort the script, not intentional in this case                  // not errorResult(), because that would abort the script, not intentional in this case
156                  return successResult();                  return successResult();
# Line 359  namespace LinuxSampler { Line 371  namespace LinuxSampler {
371          if (iArg == 0)          if (iArg == 0)
372              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
373          else          else
374              return INT_EXPR;              return type == INT_EXPR;
375      }      }
376    
377      VMFnResult* InstrumentScriptVMFunction_change_vol::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_vol::exec(VMFnArgs* args) {
# Line 385  namespace LinuxSampler { Line 397  namespace LinuxSampler {
397              if (!pNote) return successResult();              if (!pNote) return successResult();
398    
399              // if change_vol() was called immediately after note was triggered              // if change_vol() was called immediately after note was triggered
400              // then immediately apply the volume to note object              // then immediately apply the volume to note object, but only if
401              if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {              // change_vol_time() has not been called before
402                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
403                    pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S)
404                {
405                  if (relative)                  if (relative)
406                      pNote->Override.Volume *= fVolumeLin;                      pNote->Override.Volume *= fVolumeLin;
407                  else                  else
# Line 412  namespace LinuxSampler { Line 427  namespace LinuxSampler {
427                  if (!pNote) continue;                  if (!pNote) continue;
428    
429                  // if change_vol() was called immediately after note was triggered                  // if change_vol() was called immediately after note was triggered
430                  // then immediately apply the volume to Note object                  // then immediately apply the volume to Note object, but only if
431                  if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {                  // change_vol_time() has not been called before
432                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
433                        pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S)
434                    {
435                      if (relative)                      if (relative)
436                          pNote->Override.Volume *= fVolumeLin;                          pNote->Override.Volume *= fVolumeLin;
437                      else                      else
# Line 446  namespace LinuxSampler { Line 464  namespace LinuxSampler {
464          if (iArg == 0)          if (iArg == 0)
465              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
466          else          else
467              return INT_EXPR;              return type == INT_EXPR;
468      }      }
469    
470      VMFnResult* InstrumentScriptVMFunction_change_tune::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_tune::exec(VMFnArgs* args) {
# Line 472  namespace LinuxSampler { Line 490  namespace LinuxSampler {
490              if (!pNote) return successResult();              if (!pNote) return successResult();
491    
492              // if change_tune() was called immediately after note was triggered              // if change_tune() was called immediately after note was triggered
493              // then immediately apply the tuning to Note object              // then immediately apply the tuning to Note object, but only if
494              if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {              // change_tune_time() has not been called before
495                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
496                    pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S)
497                {
498                  if (relative)                  if (relative)
499                      pNote->Override.Pitch *= fFreqRatio;                      pNote->Override.Pitch *= fFreqRatio;
500                  else                  else
# Line 499  namespace LinuxSampler { Line 520  namespace LinuxSampler {
520                  if (!pNote) continue;                  if (!pNote) continue;
521    
522                  // if change_tune() was called immediately after note was triggered                  // if change_tune() was called immediately after note was triggered
523                  // then immediately apply the tuning to Note object                  // then immediately apply the tuning to Note object, but only if
524                  if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {                  // change_tune_time() has not been called before
525                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
526                        pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S)
527                    {
528                      if (relative)                      if (relative)
529                          pNote->Override.Pitch *= fFreqRatio;                          pNote->Override.Pitch *= fFreqRatio;
530                      else                      else
# Line 533  namespace LinuxSampler { Line 557  namespace LinuxSampler {
557          if (iArg == 0)          if (iArg == 0)
558              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
559          else          else
560              return INT_EXPR;              return type == INT_EXPR;
561      }      }
562    
563      VMFnResult* InstrumentScriptVMFunction_change_pan::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_pan::exec(VMFnArgs* args) {
# Line 568  namespace LinuxSampler { Line 592  namespace LinuxSampler {
592    
593              // if change_pan() was called immediately after note was triggered              // if change_pan() was called immediately after note was triggered
594              // then immediately apply the panning to Note object              // then immediately apply the panning to Note object
595              if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
596                  if (relative) {                  if (relative) {
597                      pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources);                      pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources);
598                  } else {                  } else {
# Line 597  namespace LinuxSampler { Line 621  namespace LinuxSampler {
621    
622                  // if change_pan() was called immediately after note was triggered                  // if change_pan() was called immediately after note was triggered
623                  // then immediately apply the panning to Note object                  // then immediately apply the panning to Note object
624                  if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
625                      if (relative) {                      if (relative) {
626                          pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources);                          pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources);
627                      } else {                      } else {
# Line 635  namespace LinuxSampler { Line 659  namespace LinuxSampler {
659          if (iArg == 0)          if (iArg == 0)
660              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
661          else          else
662              return INT_EXPR;              return type == INT_EXPR;
663      }      }
664    
665      VMFnResult* InstrumentScriptVMFunction_change_cutoff::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_cutoff::exec(VMFnArgs* args) {
# Line 668  namespace LinuxSampler { Line 692  namespace LinuxSampler {
692    
693              // if change_cutoff() was called immediately after note was triggered              // if change_cutoff() was called immediately after note was triggered
694              // then immediately apply cutoff to Note object              // then immediately apply cutoff to Note object
695              if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
696                  pNote->Override.Cutoff = fCutoff;                  pNote->Override.Cutoff = fCutoff;
697              } else { // otherwise schedule cutoff change ...              } else { // otherwise schedule cutoff change ...
698                  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 692  namespace LinuxSampler { Line 716  namespace LinuxSampler {
716    
717                  // if change_cutoff() was called immediately after note was triggered                  // if change_cutoff() was called immediately after note was triggered
718                  // then immediately apply cutoff to Note object                  // then immediately apply cutoff to Note object
719                  if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
720                      pNote->Override.Cutoff = fCutoff;                      pNote->Override.Cutoff = fCutoff;
721                  } else { // otherwise schedule cutoff change ...                  } else { // otherwise schedule cutoff change ...
722                      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 722  namespace LinuxSampler { Line 746  namespace LinuxSampler {
746          if (iArg == 0)          if (iArg == 0)
747              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
748          else          else
749              return INT_EXPR;              return type == INT_EXPR;
750      }      }
751    
752      VMFnResult* InstrumentScriptVMFunction_change_reso::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_reso::exec(VMFnArgs* args) {
# Line 755  namespace LinuxSampler { Line 779  namespace LinuxSampler {
779    
780              // if change_reso() was called immediately after note was triggered              // if change_reso() was called immediately after note was triggered
781              // then immediately apply resonance to Note object              // then immediately apply resonance to Note object
782              if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
783                  pNote->Override.Resonance = fResonance;                  pNote->Override.Resonance = fResonance;
784              } else { // otherwise schedule resonance change ...              } else { // otherwise schedule resonance change ...
785                  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 779  namespace LinuxSampler { Line 803  namespace LinuxSampler {
803    
804                  // if change_reso() was called immediately after note was triggered                  // if change_reso() was called immediately after note was triggered
805                  // then immediately apply resonance to Note object                  // then immediately apply resonance to Note object
806                  if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
807                      pNote->Override.Resonance = fResonance;                      pNote->Override.Resonance = fResonance;
808                  } else { // otherwise schedule resonance change ...                  } else { // otherwise schedule resonance change ...
809                      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 809  namespace LinuxSampler { Line 833  namespace LinuxSampler {
833          if (iArg == 0)          if (iArg == 0)
834              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
835          else          else
836              return INT_EXPR;              return type == INT_EXPR;
837      }      }
838    
839      VMFnResult* InstrumentScriptVMFunction_change_attack::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_attack::exec(VMFnArgs* args) {
840          int attack = args->arg(1)->asInt()->evalInt();          int attack = args->arg(1)->asInt()->evalInt();
841          if (attack > VM_EG_PAR_MAX_VALUE) {          // note: intentionally not checking against a max. value here!
842              wrnMsg("change_attack(): argument 2 may not be larger than 1000000");          // (to allow i.e. passing 2000000 for doubling the attack time)
843              attack = VM_EG_PAR_MAX_VALUE;          if (attack < 0) {
         } else if (attack < 0) {  
844              wrnMsg("change_attack(): argument 2 may not be negative");              wrnMsg("change_attack(): argument 2 may not be negative");
845              attack = 0;              attack = 0;
846          }          }
# Line 842  namespace LinuxSampler { Line 865  namespace LinuxSampler {
865    
866              // if change_attack() was called immediately after note was triggered              // if change_attack() was called immediately after note was triggered
867              // then immediately apply attack to Note object              // then immediately apply attack to Note object
868              if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
869                  pNote->Override.Attack = fAttack;                  pNote->Override.Attack = fAttack;
870              } else { // otherwise schedule attack change ...              } else { // otherwise schedule attack change ...
871                  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 866  namespace LinuxSampler { Line 889  namespace LinuxSampler {
889    
890                  // if change_attack() was called immediately after note was triggered                  // if change_attack() was called immediately after note was triggered
891                  // then immediately apply attack to Note object                  // then immediately apply attack to Note object
892                  if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
893                      pNote->Override.Attack = fAttack;                      pNote->Override.Attack = fAttack;
894                  } else { // otherwise schedule attack change ...                  } else { // otherwise schedule attack change ...
895                      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 896  namespace LinuxSampler { Line 919  namespace LinuxSampler {
919          if (iArg == 0)          if (iArg == 0)
920              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
921          else          else
922              return INT_EXPR;              return type == INT_EXPR;
923      }      }
924    
925      VMFnResult* InstrumentScriptVMFunction_change_decay::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_decay::exec(VMFnArgs* args) {
926          int decay = args->arg(1)->asInt()->evalInt();          int decay = args->arg(1)->asInt()->evalInt();
927          if (decay > VM_EG_PAR_MAX_VALUE) {          // note: intentionally not checking against a max. value here!
928              wrnMsg("change_decay(): argument 2 may not be larger than 1000000");          // (to allow i.e. passing 2000000 for doubling the decay time)
929              decay = VM_EG_PAR_MAX_VALUE;          if (decay < 0) {
         } else if (decay < 0) {  
930              wrnMsg("change_decay(): argument 2 may not be negative");              wrnMsg("change_decay(): argument 2 may not be negative");
931              decay = 0;              decay = 0;
932          }          }
# Line 929  namespace LinuxSampler { Line 951  namespace LinuxSampler {
951    
952              // if change_decay() was called immediately after note was triggered              // if change_decay() was called immediately after note was triggered
953              // then immediately apply decay to Note object              // then immediately apply decay to Note object
954              if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
955                  pNote->Override.Decay = fDecay;                  pNote->Override.Decay = fDecay;
956              } else { // otherwise schedule decay change ...              } else { // otherwise schedule decay change ...
957                  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 953  namespace LinuxSampler { Line 975  namespace LinuxSampler {
975    
976                  // if change_decay() was called immediately after note was triggered                  // if change_decay() was called immediately after note was triggered
977                  // then immediately apply decay to Note object                  // then immediately apply decay to Note object
978                  if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
979                      pNote->Override.Decay = fDecay;                      pNote->Override.Decay = fDecay;
980                  } else { // otherwise schedule decay change ...                  } else { // otherwise schedule decay change ...
981                      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 983  namespace LinuxSampler { Line 1005  namespace LinuxSampler {
1005          if (iArg == 0)          if (iArg == 0)
1006              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
1007          else          else
1008              return INT_EXPR;              return type == INT_EXPR;
1009      }      }
1010    
1011      VMFnResult* InstrumentScriptVMFunction_change_release::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_release::exec(VMFnArgs* args) {
1012          int release = args->arg(1)->asInt()->evalInt();          int release = args->arg(1)->asInt()->evalInt();
1013          if (release > VM_EG_PAR_MAX_VALUE) {          // note: intentionally not checking against a max. value here!
1014              wrnMsg("change_release(): argument 2 may not be larger than 1000000");          // (to allow i.e. passing 2000000 for doubling the release time)
1015              release = VM_EG_PAR_MAX_VALUE;          if (release < 0) {
         } else if (release < 0) {  
1016              wrnMsg("change_release(): argument 2 may not be negative");              wrnMsg("change_release(): argument 2 may not be negative");
1017              release = 0;              release = 0;
1018          }          }
# Line 1016  namespace LinuxSampler { Line 1037  namespace LinuxSampler {
1037    
1038              // if change_release() was called immediately after note was triggered              // if change_release() was called immediately after note was triggered
1039              // then immediately apply relase to Note object              // then immediately apply relase to Note object
1040              if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1041                  pNote->Override.Release = fRelease;                  pNote->Override.Release = fRelease;
1042              } else { // otherwise schedule release change ...              } else { // otherwise schedule release change ...
1043                  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 1040  namespace LinuxSampler { Line 1061  namespace LinuxSampler {
1061    
1062                  // if change_release() was called immediately after note was triggered                  // if change_release() was called immediately after note was triggered
1063                  // then immediately apply relase to Note object                  // then immediately apply relase to Note object
1064                  if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1065                      pNote->Override.Release = fRelease;                      pNote->Override.Release = fRelease;
1066                  } else { // otherwise schedule release change ...                  } else { // otherwise schedule release change ...
1067                      Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                      Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
# Line 1059  namespace LinuxSampler { Line 1080  namespace LinuxSampler {
1080          return successResult();          return successResult();
1081      }      }
1082    
1083        // template for change_*() functions
1084    
1085        bool VMChangeSynthParamFunction::acceptsArgType(int iArg, ExprType_t type) const {
1086            if (iArg == 0)
1087                return type == INT_EXPR || type == INT_ARR_EXPR;
1088            else
1089                return type == INT_EXPR;
1090        }
1091    
1092        // Arbitrarily chosen constant value symbolizing "no limit".
1093        #define NO_LIMIT 1315916909
1094    
1095        template<float NoteBase::_Override::*T_noteParam, int T_synthParam,
1096                 bool T_isNormalizedParam, int T_maxValue, int T_minValue>
1097        VMFnResult* VMChangeSynthParamFunction::execTemplate(VMFnArgs* args, const char* functionName) {
1098            int value = args->arg(1)->asInt()->evalInt();
1099            if (T_maxValue != NO_LIMIT && value > T_maxValue) {
1100                wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValue));
1101                value = T_maxValue;
1102            } else if (T_minValue != NO_LIMIT && value < T_minValue) {
1103                if (T_minValue == 0)
1104                    wrnMsg(String(functionName) + "(): argument 2 may not be negative");
1105                else
1106                    wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValue));
1107                value = T_minValue;
1108            }
1109            const float fValue = (T_isNormalizedParam) ?
1110                float(value) / float(T_maxValue) : // convert to 0.0 .. 1.0 value range
1111                float(value) / 1000000.f; // assuming microseconds here, convert to seconds
1112    
1113            AbstractEngineChannel* pEngineChannel =
1114                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1115    
1116            if (args->arg(0)->exprType() == INT_EXPR) {
1117                const ScriptID id = args->arg(0)->asInt()->evalInt();
1118                if (!id) {
1119                    wrnMsg(String(functionName) + "(): note ID for argument 1 may not be zero");
1120                    return successResult();
1121                }
1122                if (!id.isNoteID()) {
1123                    wrnMsg(String(functionName) + "(): argument 1 is not a note ID");
1124                    return successResult();
1125                }
1126    
1127                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1128                if (!pNote) return successResult();
1129    
1130                // if this change_*() script function was called immediately after
1131                // note was triggered then immediately apply the synth parameter
1132                // change to Note object
1133                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1134                    pNote->Override.*T_noteParam = fValue;
1135                } else { // otherwise schedule this synth parameter change ...
1136                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1137                    e.Init(); // clear IDs
1138                    e.Type = Event::type_note_synth_param;
1139                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1140                    e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1141                    e.Param.NoteSynthParam.Delta    = fValue;
1142                    e.Param.NoteSynthParam.Relative = false;
1143    
1144                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
1145                }
1146            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1147                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1148                for (int i = 0; i < ids->arraySize(); ++i) {
1149                    const ScriptID id = ids->evalIntElement(i);
1150                    if (!id || !id.isNoteID()) continue;
1151    
1152                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1153                    if (!pNote) continue;
1154    
1155                    // if this change_*() script function was called immediately after
1156                    // note was triggered then immediately apply the synth parameter
1157                    // change to Note object
1158                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1159                        pNote->Override.*T_noteParam = fValue;
1160                    } else { // otherwise schedule this synth parameter change ...
1161                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1162                        e.Init(); // clear IDs
1163                        e.Type = Event::type_note_synth_param;
1164                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1165                        e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1166                        e.Param.NoteSynthParam.Delta    = fValue;
1167                        e.Param.NoteSynthParam.Relative = false;
1168    
1169                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
1170                    }
1171                }
1172            }
1173    
1174            return successResult();
1175        }
1176    
1177        // change_sustain() function
1178    
1179        VMFnResult* InstrumentScriptVMFunction_change_sustain::exec(VMFnArgs* args) {
1180            return VMChangeSynthParamFunction::execTemplate<
1181                        &NoteBase::_Override::Sustain,
1182                        Event::synth_param_sustain,
1183                        false, NO_LIMIT, 0>( args, "change_sustain" );
1184        }
1185    
1186        // change_cutoff_attack() function
1187    
1188        VMFnResult* InstrumentScriptVMFunction_change_cutoff_attack::exec(VMFnArgs* args) {
1189            return VMChangeSynthParamFunction::execTemplate<
1190                        &NoteBase::_Override::CutoffAttack,
1191                        Event::synth_param_cutoff_attack,
1192                        false, NO_LIMIT, 0>( args, "change_cutoff_attack" );
1193        }
1194    
1195        // change_cutoff_decay() function
1196    
1197        VMFnResult* InstrumentScriptVMFunction_change_cutoff_decay::exec(VMFnArgs* args) {
1198            return VMChangeSynthParamFunction::execTemplate<
1199                        &NoteBase::_Override::CutoffDecay,
1200                        Event::synth_param_cutoff_decay,
1201                        false, NO_LIMIT, 0>( args, "change_cutoff_decay" );
1202        }
1203    
1204        // change_cutoff_sustain() function
1205    
1206        VMFnResult* InstrumentScriptVMFunction_change_cutoff_sustain::exec(VMFnArgs* args) {
1207            return VMChangeSynthParamFunction::execTemplate<
1208                        &NoteBase::_Override::CutoffSustain,
1209                        Event::synth_param_cutoff_sustain,
1210                        false, NO_LIMIT, 0>( args, "change_cutoff_sustain" );
1211        }
1212    
1213        // change_cutoff_release() function
1214    
1215        VMFnResult* InstrumentScriptVMFunction_change_cutoff_release::exec(VMFnArgs* args) {
1216            return VMChangeSynthParamFunction::execTemplate<
1217                        &NoteBase::_Override::CutoffRelease,
1218                        Event::synth_param_cutoff_release,
1219                        false, NO_LIMIT, 0>( args, "change_cutoff_release" );
1220        }
1221    
1222        // change_amp_lfo_depth() function
1223    
1224        VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_depth::exec(VMFnArgs* args) {
1225            return VMChangeSynthParamFunction::execTemplate<
1226                        &NoteBase::_Override::AmpLFODepth,
1227                        Event::synth_param_amp_lfo_depth,
1228                        true, 1000000, 0>( args, "change_amp_lfo_depth" );
1229        }
1230    
1231        // change_amp_lfo_freq() function
1232    
1233        VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_freq::exec(VMFnArgs* args) {
1234            return VMChangeSynthParamFunction::execTemplate<
1235                        &NoteBase::_Override::AmpLFOFreq,
1236                        Event::synth_param_amp_lfo_freq,
1237                        true, 1000000, 0>( args, "change_amp_lfo_freq" );
1238        }
1239    
1240        // change_cutoff_lfo_depth() function
1241    
1242        VMFnResult* InstrumentScriptVMFunction_change_cutoff_lfo_depth::exec(VMFnArgs* args) {
1243            return VMChangeSynthParamFunction::execTemplate<
1244                        &NoteBase::_Override::CutoffLFODepth,
1245                        Event::synth_param_cutoff_lfo_depth,
1246                        true, 1000000, 0>( args, "change_cutoff_lfo_depth" );
1247        }
1248    
1249        // change_cutoff_lfo_freq() function
1250    
1251        VMFnResult* InstrumentScriptVMFunction_change_cutoff_lfo_freq::exec(VMFnArgs* args) {
1252            return VMChangeSynthParamFunction::execTemplate<
1253                        &NoteBase::_Override::CutoffLFOFreq,
1254                        Event::synth_param_cutoff_lfo_freq,
1255                        true, 1000000, 0>( args, "change_cutoff_lfo_freq" );
1256        }
1257    
1258        // change_pitch_lfo_depth() function
1259    
1260        VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_depth::exec(VMFnArgs* args) {
1261            return VMChangeSynthParamFunction::execTemplate<
1262                        &NoteBase::_Override::PitchLFODepth,
1263                        Event::synth_param_pitch_lfo_depth,
1264                        true, 1000000, 0>( args, "change_pitch_lfo_depth" );
1265        }
1266    
1267        // change_pitch_lfo_freq() function
1268    
1269        VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_freq::exec(VMFnArgs* args) {
1270            return VMChangeSynthParamFunction::execTemplate<
1271                        &NoteBase::_Override::PitchLFOFreq,
1272                        Event::synth_param_pitch_lfo_freq,
1273                        true, 1000000, 0>( args, "change_pitch_lfo_freq" );
1274        }
1275    
1276        // change_vol_time() function
1277    
1278        VMFnResult* InstrumentScriptVMFunction_change_vol_time::exec(VMFnArgs* args) {
1279            return VMChangeSynthParamFunction::execTemplate<
1280                        &NoteBase::_Override::VolumeTime,
1281                        Event::synth_param_volume_time,
1282                        false, NO_LIMIT, 0>( args, "change_vol_time" );
1283        }
1284    
1285        // change_tune_time() function
1286    
1287        VMFnResult* InstrumentScriptVMFunction_change_tune_time::exec(VMFnArgs* args) {
1288            return VMChangeSynthParamFunction::execTemplate<
1289                        &NoteBase::_Override::PitchTime,
1290                        Event::synth_param_pitch_time,
1291                        false, NO_LIMIT, 0>( args, "change_tune_time" );
1292        }
1293    
1294        // change_pan_time() function
1295    
1296        VMFnResult* InstrumentScriptVMFunction_change_pan_time::exec(VMFnArgs* args) {
1297            return VMChangeSynthParamFunction::execTemplate<
1298            &NoteBase::_Override::PanTime,
1299            Event::synth_param_pan_time,
1300            false, NO_LIMIT, 0>( args, "change_pan_time" );
1301        }
1302    
1303        // template for change_*_curve() functions
1304    
1305        bool VMChangeFadeCurveFunction::acceptsArgType(int iArg, ExprType_t type) const {
1306            if (iArg == 0)
1307                return type == INT_EXPR || type == INT_ARR_EXPR;
1308            else
1309                return type == INT_EXPR;
1310        }
1311    
1312        template<fade_curve_t NoteBase::_Override::*T_noteParam, int T_synthParam>
1313        VMFnResult* VMChangeFadeCurveFunction::execTemplate(VMFnArgs* args, const char* functionName) {
1314            int value = args->arg(1)->asInt()->evalInt();
1315            switch (value) {
1316                case FADE_CURVE_LINEAR:
1317                case FADE_CURVE_EASE_IN_EASE_OUT:
1318                    break;
1319                default:
1320                    wrnMsg(String(functionName) + "(): invalid curve type passed as argument 2");
1321                    return successResult();
1322            }
1323    
1324            AbstractEngineChannel* pEngineChannel =
1325                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1326    
1327            if (args->arg(0)->exprType() == INT_EXPR) {
1328                const ScriptID id = args->arg(0)->asInt()->evalInt();
1329                if (!id) {
1330                    wrnMsg(String(functionName) + "(): note ID for argument 1 may not be zero");
1331                    return successResult();
1332                }
1333                if (!id.isNoteID()) {
1334                    wrnMsg(String(functionName) + "(): argument 1 is not a note ID");
1335                    return successResult();
1336                }
1337    
1338                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1339                if (!pNote) return successResult();
1340    
1341                // if this change_*_curve() script function was called immediately after
1342                // note was triggered then immediately apply the synth parameter
1343                // change to Note object
1344                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1345                    pNote->Override.*T_noteParam = (fade_curve_t) value;
1346                } else { // otherwise schedule this synth parameter change ...
1347                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1348                    e.Init(); // clear IDs
1349                    e.Type = Event::type_note_synth_param;
1350                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1351                    e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1352                    e.Param.NoteSynthParam.Delta    = value;
1353                    e.Param.NoteSynthParam.Relative = false;
1354    
1355                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
1356                }
1357            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1358                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1359                for (int i = 0; i < ids->arraySize(); ++i) {
1360                    const ScriptID id = ids->evalIntElement(i);
1361                    if (!id || !id.isNoteID()) continue;
1362    
1363                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1364                    if (!pNote) continue;
1365    
1366                    // if this change_*_curve() script function was called immediately after
1367                    // note was triggered then immediately apply the synth parameter
1368                    // change to Note object
1369                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1370                        pNote->Override.*T_noteParam = (fade_curve_t) value;
1371                    } else { // otherwise schedule this synth parameter change ...
1372                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1373                        e.Init(); // clear IDs
1374                        e.Type = Event::type_note_synth_param;
1375                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1376                        e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1377                        e.Param.NoteSynthParam.Delta    = value;
1378                        e.Param.NoteSynthParam.Relative = false;
1379    
1380                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
1381                    }
1382                }
1383            }
1384    
1385            return successResult();
1386        }
1387    
1388        // change_vol_curve() function
1389    
1390        VMFnResult* InstrumentScriptVMFunction_change_vol_curve::exec(VMFnArgs* args) {
1391            return VMChangeFadeCurveFunction::execTemplate<
1392                        &NoteBase::_Override::VolumeCurve,
1393                        Event::synth_param_volume_curve>( args, "change_vol_curve" );
1394        }
1395    
1396        // change_tune_curve() function
1397    
1398        VMFnResult* InstrumentScriptVMFunction_change_tune_curve::exec(VMFnArgs* args) {
1399            return VMChangeFadeCurveFunction::execTemplate<
1400                        &NoteBase::_Override::PitchCurve,
1401                        Event::synth_param_pitch_curve>( args, "change_tune_curve" );
1402        }
1403    
1404        // change_pan_curve() function
1405    
1406        VMFnResult* InstrumentScriptVMFunction_change_pan_curve::exec(VMFnArgs* args) {
1407            return VMChangeFadeCurveFunction::execTemplate<
1408            &NoteBase::_Override::PanCurve,
1409            Event::synth_param_pan_curve>( args, "change_pan_curve" );
1410        }
1411    
1412        // fade_in() function
1413    
1414        InstrumentScriptVMFunction_fade_in::InstrumentScriptVMFunction_fade_in(InstrumentScriptVM* parent)
1415            : m_vm(parent)
1416        {
1417        }
1418    
1419        bool InstrumentScriptVMFunction_fade_in::acceptsArgType(int iArg, ExprType_t type) const {
1420            if (iArg == 0)
1421                return type == INT_EXPR || type == INT_ARR_EXPR;
1422            else
1423                return type == INT_EXPR;
1424        }
1425    
1426        VMFnResult* InstrumentScriptVMFunction_fade_in::exec(VMFnArgs* args) {
1427            int duration = args->arg(1)->asInt()->evalInt();
1428            if (duration < 0) {
1429                wrnMsg("fade_in(): argument 2 may not be negative");
1430                duration = 0;
1431            }
1432            const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
1433    
1434            AbstractEngineChannel* pEngineChannel =
1435                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1436    
1437            if (args->arg(0)->exprType() == INT_EXPR) {
1438                const ScriptID id = args->arg(0)->asInt()->evalInt();
1439                if (!id) {
1440                    wrnMsg("fade_in(): note ID for argument 1 may not be zero");
1441                    return successResult();
1442                }
1443                if (!id.isNoteID()) {
1444                    wrnMsg("fade_in(): argument 1 is not a note ID");
1445                    return successResult();
1446                }
1447    
1448                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1449                if (!pNote) return successResult();
1450    
1451                // if fade_in() was called immediately after note was triggered
1452                // then immediately apply a start volume of zero to Note object,
1453                // as well as the fade in duration
1454                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1455                    pNote->Override.Volume = 0.f;
1456                    pNote->Override.VolumeTime = fDuration;
1457                } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
1458                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1459                    e.Init(); // clear IDs
1460                    e.Type = Event::type_note_synth_param;
1461                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1462                    e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1463                    e.Param.NoteSynthParam.Delta    = fDuration;
1464                    e.Param.NoteSynthParam.Relative = false;
1465    
1466                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
1467                }
1468                // and finally schedule a "volume" change, simply one time slice
1469                // ahead, with the final fade in volume (1.0)
1470                {
1471                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1472                    e.Init(); // clear IDs
1473                    e.Type = Event::type_note_synth_param;
1474                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1475                    e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1476                    e.Param.NoteSynthParam.Delta    = 1.f;
1477                    e.Param.NoteSynthParam.Relative = false;
1478    
1479                    // scheduling with 0 delay would also work here, but +1 is more
1480                    // safe regarding potential future implementation changes of the
1481                    // scheduler (see API comments of RTAVLTree::insert())
1482                    pEngineChannel->ScheduleEventMicroSec(&e, 1);
1483                }
1484            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1485                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1486                for (int i = 0; i < ids->arraySize(); ++i) {
1487                    const ScriptID id = ids->evalIntElement(i);
1488                    if (!id || !id.isNoteID()) continue;
1489    
1490                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1491                    if (!pNote) continue;
1492    
1493                    // if fade_in() was called immediately after note was triggered
1494                    // then immediately apply a start volume of zero to Note object,
1495                    // as well as the fade in duration
1496                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1497                        pNote->Override.Volume = 0.f;
1498                        pNote->Override.VolumeTime = fDuration;
1499                    } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
1500                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1501                        e.Init(); // clear IDs
1502                        e.Type = Event::type_note_synth_param;
1503                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1504                        e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1505                        e.Param.NoteSynthParam.Delta    = fDuration;
1506                        e.Param.NoteSynthParam.Relative = false;
1507    
1508                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
1509                    }
1510                    // and finally schedule a "volume" change, simply one time slice
1511                    // ahead, with the final fade in volume (1.0)
1512                    {
1513                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1514                        e.Init(); // clear IDs
1515                        e.Type = Event::type_note_synth_param;
1516                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1517                        e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1518                        e.Param.NoteSynthParam.Delta    = 1.f;
1519                        e.Param.NoteSynthParam.Relative = false;
1520    
1521                        // scheduling with 0 delay would also work here, but +1 is more
1522                        // safe regarding potential future implementation changes of the
1523                        // scheduler (see API comments of RTAVLTree::insert())
1524                        pEngineChannel->ScheduleEventMicroSec(&e, 1);
1525                    }
1526                }
1527            }
1528    
1529            return successResult();
1530        }
1531    
1532        // fade_out() function
1533    
1534        InstrumentScriptVMFunction_fade_out::InstrumentScriptVMFunction_fade_out(InstrumentScriptVM* parent)
1535            : m_vm(parent)
1536        {
1537        }
1538    
1539        bool InstrumentScriptVMFunction_fade_out::acceptsArgType(int iArg, ExprType_t type) const {
1540            if (iArg == 0)
1541                return type == INT_EXPR || type == INT_ARR_EXPR;
1542            else
1543                return type == INT_EXPR;
1544        }
1545    
1546        VMFnResult* InstrumentScriptVMFunction_fade_out::exec(VMFnArgs* args) {
1547            int duration = args->arg(1)->asInt()->evalInt();
1548            if (duration < 0) {
1549                wrnMsg("fade_out(): argument 2 may not be negative");
1550                duration = 0;
1551            }
1552            const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
1553    
1554            bool stop = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : true;
1555    
1556            AbstractEngineChannel* pEngineChannel =
1557                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1558    
1559            if (args->arg(0)->exprType() == INT_EXPR) {
1560                const ScriptID id = args->arg(0)->asInt()->evalInt();
1561                if (!id) {
1562                    wrnMsg("fade_out(): note ID for argument 1 may not be zero");
1563                    return successResult();
1564                }
1565                if (!id.isNoteID()) {
1566                    wrnMsg("fade_out(): argument 1 is not a note ID");
1567                    return successResult();
1568                }
1569    
1570                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1571                if (!pNote) return successResult();
1572    
1573                // if fade_out() was called immediately after note was triggered
1574                // then immediately apply fade out duration to Note object
1575                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1576                    pNote->Override.VolumeTime = fDuration;
1577                } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
1578                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1579                    e.Init(); // clear IDs
1580                    e.Type = Event::type_note_synth_param;
1581                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1582                    e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1583                    e.Param.NoteSynthParam.Delta    = fDuration;
1584                    e.Param.NoteSynthParam.Relative = false;
1585    
1586                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
1587                }
1588                // now schedule a "volume" change, simply one time slice ahead, with
1589                // the final fade out volume (0.0)
1590                {
1591                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1592                    e.Init(); // clear IDs
1593                    e.Type = Event::type_note_synth_param;
1594                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1595                    e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1596                    e.Param.NoteSynthParam.Delta    = 0.f;
1597                    e.Param.NoteSynthParam.Relative = false;
1598    
1599                    // scheduling with 0 delay would also work here, but +1 is more
1600                    // safe regarding potential future implementation changes of the
1601                    // scheduler (see API comments of RTAVLTree::insert())
1602                    pEngineChannel->ScheduleEventMicroSec(&e, 1);
1603                }
1604                // and finally if stopping the note was requested after the fade out
1605                // completed, then schedule to kill the voice after the requested
1606                // duration
1607                if (stop) {
1608                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1609                    e.Init(); // clear IDs
1610                    e.Type = Event::type_kill_note;
1611                    e.Param.Note.ID = id.noteID();
1612                    e.Param.Note.Key = pNote->hostKey;
1613    
1614                    pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
1615                }
1616            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1617                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1618                for (int i = 0; i < ids->arraySize(); ++i) {
1619                    const ScriptID id = ids->evalIntElement(i);
1620                    if (!id || !id.isNoteID()) continue;
1621    
1622                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1623                    if (!pNote) continue;
1624    
1625                    // if fade_out() was called immediately after note was triggered
1626                    // then immediately apply fade out duration to Note object
1627                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1628                        pNote->Override.VolumeTime = fDuration;
1629                    } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
1630                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1631                        e.Init(); // clear IDs
1632                        e.Type = Event::type_note_synth_param;
1633                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1634                        e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1635                        e.Param.NoteSynthParam.Delta    = fDuration;
1636                        e.Param.NoteSynthParam.Relative = false;
1637    
1638                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
1639                    }
1640                    // now schedule a "volume" change, simply one time slice ahead, with
1641                    // the final fade out volume (0.0)
1642                    {
1643                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1644                        e.Init(); // clear IDs
1645                        e.Type = Event::type_note_synth_param;
1646                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1647                        e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1648                        e.Param.NoteSynthParam.Delta    = 0.f;
1649                        e.Param.NoteSynthParam.Relative = false;
1650    
1651                        // scheduling with 0 delay would also work here, but +1 is more
1652                        // safe regarding potential future implementation changes of the
1653                        // scheduler (see API comments of RTAVLTree::insert())
1654                        pEngineChannel->ScheduleEventMicroSec(&e, 1);
1655                    }
1656                    // and finally if stopping the note was requested after the fade out
1657                    // completed, then schedule to kill the voice after the requested
1658                    // duration
1659                    if (stop) {
1660                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1661                        e.Init(); // clear IDs
1662                        e.Type = Event::type_kill_note;
1663                        e.Param.Note.ID = id.noteID();
1664                        e.Param.Note.Key = pNote->hostKey;
1665                        
1666                        pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
1667                    }
1668                }
1669            }
1670    
1671            return successResult();
1672        }
1673    
1674        // get_event_par() function
1675    
1676        InstrumentScriptVMFunction_get_event_par::InstrumentScriptVMFunction_get_event_par(InstrumentScriptVM* parent)
1677            : m_vm(parent)
1678        {
1679        }
1680    
1681        VMFnResult* InstrumentScriptVMFunction_get_event_par::exec(VMFnArgs* args) {
1682            AbstractEngineChannel* pEngineChannel =
1683                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1684    
1685            const ScriptID id = args->arg(0)->asInt()->evalInt();
1686            if (!id) {
1687                wrnMsg("get_event_par(): note ID for argument 1 may not be zero");
1688                return successResult(0);
1689            }
1690            if (!id.isNoteID()) {
1691                wrnMsg("get_event_par(): argument 1 is not a note ID");
1692                return successResult(0);
1693            }
1694    
1695            NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1696            if (!pNote) {
1697                wrnMsg("get_event_par(): no note alive with that note ID of argument 1");
1698                return successResult(0);
1699            }
1700    
1701            const int parameter = args->arg(1)->asInt()->evalInt();
1702            switch (parameter) {
1703                case EVENT_PAR_NOTE:
1704                    return successResult(pNote->cause.Param.Note.Key);
1705                case EVENT_PAR_VELOCITY:
1706                    return successResult(pNote->cause.Param.Note.Velocity);
1707                case EVENT_PAR_VOLUME:
1708                    return successResult(
1709                        RTMath::LinRatioToDecibel(pNote->Override.Volume) * 1000.f
1710                    );
1711                case EVENT_PAR_TUNE:
1712                    return successResult(
1713                         RTMath::FreqRatioToCents(pNote->Override.Pitch) * 1000.f
1714                    );
1715                case EVENT_PAR_0:
1716                    return successResult(pNote->userPar[0]);
1717                case EVENT_PAR_1:
1718                    return successResult(pNote->userPar[1]);
1719                case EVENT_PAR_2:
1720                    return successResult(pNote->userPar[2]);
1721                case EVENT_PAR_3:
1722                    return successResult(pNote->userPar[3]);
1723            }
1724    
1725            wrnMsg("get_event_par(): argument 2 is an invalid event parameter");
1726            return successResult(0);
1727        }
1728    
1729        // set_event_par() function
1730    
1731        InstrumentScriptVMFunction_set_event_par::InstrumentScriptVMFunction_set_event_par(InstrumentScriptVM* parent)
1732            : m_vm(parent)
1733        {
1734        }
1735    
1736        VMFnResult* InstrumentScriptVMFunction_set_event_par::exec(VMFnArgs* args) {
1737            AbstractEngineChannel* pEngineChannel =
1738                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1739    
1740            const ScriptID id = args->arg(0)->asInt()->evalInt();
1741            if (!id) {
1742                wrnMsg("set_event_par(): note ID for argument 1 may not be zero");
1743                return successResult();
1744            }
1745            if (!id.isNoteID()) {
1746                wrnMsg("set_event_par(): argument 1 is not a note ID");
1747                return successResult();
1748            }
1749    
1750            NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1751            if (!pNote) return successResult();
1752    
1753            const int parameter = args->arg(1)->asInt()->evalInt();
1754            const int value     = args->arg(2)->asInt()->evalInt();
1755    
1756            switch (parameter) {
1757                case EVENT_PAR_NOTE:
1758                    if (value < 0 || value > 127) {
1759                        wrnMsg("set_event_par(): note number of argument 3 is out of range");
1760                        return successResult();
1761                    }
1762                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1763                        pNote->cause.Param.Note.Key = value;
1764                        m_vm->m_event->cause.Param.Note.Key = value;
1765                    } else {
1766                        wrnMsg("set_event_par(): note number can only be changed when note is new");
1767                    }
1768                    return successResult();
1769                case EVENT_PAR_VELOCITY:
1770                    if (value < 0 || value > 127) {
1771                        wrnMsg("set_event_par(): velocity of argument 3 is out of range");
1772                        return successResult();
1773                    }
1774                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1775                        pNote->cause.Param.Note.Velocity = value;
1776                        m_vm->m_event->cause.Param.Note.Velocity = value;
1777                    } else {
1778                        wrnMsg("set_event_par(): velocity can only be changed when note is new");
1779                    }
1780                    return successResult();
1781                case EVENT_PAR_VOLUME:
1782                    wrnMsg("set_event_par(): changing volume by this function is currently not supported, use change_vol() instead");
1783                    return successResult();
1784                case EVENT_PAR_TUNE:
1785                    wrnMsg("set_event_par(): changing tune by this function is currently not supported, use change_tune() instead");
1786                    return successResult();
1787                case EVENT_PAR_0:
1788                    pNote->userPar[0] = value;
1789                    return successResult();
1790                case EVENT_PAR_1:
1791                    pNote->userPar[1] = value;
1792                    return successResult();
1793                case EVENT_PAR_2:
1794                    pNote->userPar[2] = value;
1795                    return successResult();
1796                case EVENT_PAR_3:
1797                    pNote->userPar[3] = value;
1798                    return successResult();
1799            }
1800    
1801            wrnMsg("set_event_par(): argument 2 is an invalid event parameter");
1802            return successResult();
1803        }
1804    
1805        // change_note() function
1806    
1807        InstrumentScriptVMFunction_change_note::InstrumentScriptVMFunction_change_note(InstrumentScriptVM* parent)
1808        : m_vm(parent)
1809        {
1810        }
1811    
1812        VMFnResult* InstrumentScriptVMFunction_change_note::exec(VMFnArgs* args) {
1813            AbstractEngineChannel* pEngineChannel =
1814                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1815    
1816            const ScriptID id = args->arg(0)->asInt()->evalInt();
1817            if (!id) {
1818                wrnMsg("change_note(): note ID for argument 1 may not be zero");
1819                return successResult();
1820            }
1821            if (!id.isNoteID()) {
1822                wrnMsg("change_note(): argument 1 is not a note ID");
1823                return successResult();
1824            }
1825    
1826            NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1827            if (!pNote) return successResult();
1828    
1829            const int value = args->arg(1)->asInt()->evalInt();
1830            if (value < 0 || value > 127) {
1831                wrnMsg("change_note(): note number of argument 2 is out of range");
1832                return successResult();
1833            }
1834    
1835            if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1836                pNote->cause.Param.Note.Key = value;
1837                m_vm->m_event->cause.Param.Note.Key = value;
1838            } else {
1839                wrnMsg("change_note(): note number can only be changed when note is new");
1840            }
1841    
1842            return successResult();
1843        }
1844    
1845        // change_velo() function
1846    
1847        InstrumentScriptVMFunction_change_velo::InstrumentScriptVMFunction_change_velo(InstrumentScriptVM* parent)
1848        : m_vm(parent)
1849        {
1850        }
1851    
1852        VMFnResult* InstrumentScriptVMFunction_change_velo::exec(VMFnArgs* args) {
1853            AbstractEngineChannel* pEngineChannel =
1854                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1855    
1856            const ScriptID id = args->arg(0)->asInt()->evalInt();
1857            if (!id) {
1858                wrnMsg("change_velo(): note ID for argument 1 may not be zero");
1859                return successResult();
1860            }
1861            if (!id.isNoteID()) {
1862                wrnMsg("change_velo(): argument 1 is not a note ID");
1863                return successResult();
1864            }
1865    
1866            NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1867            if (!pNote) return successResult();
1868    
1869            const int value = args->arg(1)->asInt()->evalInt();
1870            if (value < 0 || value > 127) {
1871                wrnMsg("change_velo(): velocity of argument 2 is out of range");
1872                return successResult();
1873            }
1874    
1875            if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1876                pNote->cause.Param.Note.Velocity = value;
1877                m_vm->m_event->cause.Param.Note.Velocity = value;
1878            } else {
1879                wrnMsg("change_velo(): velocity can only be changed when note is new");
1880            }
1881    
1882            return successResult();
1883        }
1884    
1885        // change_play_pos() function
1886    
1887        InstrumentScriptVMFunction_change_play_pos::InstrumentScriptVMFunction_change_play_pos(InstrumentScriptVM* parent)
1888        : m_vm(parent)
1889        {
1890        }
1891    
1892        VMFnResult* InstrumentScriptVMFunction_change_play_pos::exec(VMFnArgs* args) {
1893            const ScriptID id = args->arg(0)->asInt()->evalInt();
1894            if (!id) {
1895                wrnMsg("change_play_pos(): note ID for argument 1 may not be zero");
1896                return successResult();
1897            }
1898            if (!id.isNoteID()) {
1899                wrnMsg("change_play_pos(): argument 1 is not a note ID");
1900                return successResult();
1901            }
1902    
1903            const int pos = args->arg(1)->asInt()->evalInt();
1904            if (pos < 0) {
1905                wrnMsg("change_play_pos(): playback position of argument 2 may not be negative");
1906                return successResult();
1907            }
1908    
1909            AbstractEngineChannel* pEngineChannel =
1910                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1911    
1912            NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1913            if (!pNote) return successResult();
1914    
1915            pNote->Override.SampleOffset = pos;
1916    
1917            return successResult();
1918        }
1919    
1920      // event_status() function      // event_status() function
1921    
1922      InstrumentScriptVMFunction_event_status::InstrumentScriptVMFunction_event_status(InstrumentScriptVM* parent)      InstrumentScriptVMFunction_event_status::InstrumentScriptVMFunction_event_status(InstrumentScriptVM* parent)
# Line 1084  namespace LinuxSampler { Line 1942  namespace LinuxSampler {
1942          return successResult(pNote ? EVENT_STATUS_NOTE_QUEUE : EVENT_STATUS_INACTIVE);          return successResult(pNote ? EVENT_STATUS_NOTE_QUEUE : EVENT_STATUS_INACTIVE);
1943      }      }
1944    
1945        // callback_status() function
1946    
1947        InstrumentScriptVMFunction_callback_status::InstrumentScriptVMFunction_callback_status(InstrumentScriptVM* parent)
1948            : m_vm(parent)
1949        {
1950        }
1951    
1952        VMFnResult* InstrumentScriptVMFunction_callback_status::exec(VMFnArgs* args) {
1953            const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
1954            if (!id) {
1955                wrnMsg("callback_status(): callback ID for argument 1 may not be zero");
1956                return successResult();
1957            }
1958    
1959            AbstractEngineChannel* pEngineChannel =
1960                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1961    
1962            RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
1963            if (!itCallback)
1964                return successResult(CALLBACK_STATUS_TERMINATED);
1965    
1966            return successResult(
1967                (m_vm->m_event->execCtx == itCallback->execCtx) ?
1968                    CALLBACK_STATUS_RUNNING : CALLBACK_STATUS_QUEUE
1969            );
1970        }
1971    
1972      // wait() function (overrides core wait() implementation)      // wait() function (overrides core wait() implementation)
1973    
1974      InstrumentScriptVMFunction_wait::InstrumentScriptVMFunction_wait(InstrumentScriptVM* parent)      InstrumentScriptVMFunction_wait::InstrumentScriptVMFunction_wait(InstrumentScriptVM* parent)
# Line 1124  namespace LinuxSampler { Line 2009  namespace LinuxSampler {
2009              (args->argsCount() >= 2) ? (args->arg(1)->asInt()->evalInt() == 1) : false;              (args->argsCount() >= 2) ? (args->arg(1)->asInt()->evalInt() == 1) : false;
2010    
2011          pEngineChannel->ScheduleResumeOfScriptCallback(          pEngineChannel->ScheduleResumeOfScriptCallback(
2012              itCallback, m_vm->m_event->cause.SchedTime(), disableWaitForever              itCallback, m_vm->m_event->scheduleTime, disableWaitForever
2013          );          );
2014    
2015          return successResult();          return successResult();
2016      }      }
2017    
2018        // abort() function
2019    
2020        InstrumentScriptVMFunction_abort::InstrumentScriptVMFunction_abort(InstrumentScriptVM* parent)
2021            : m_vm(parent)
2022        {
2023        }
2024    
2025        VMFnResult* InstrumentScriptVMFunction_abort::exec(VMFnArgs* args) {
2026            const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
2027            if (!id) {
2028                wrnMsg("abort(): callback ID for argument 1 may not be zero");
2029                return successResult();
2030            }
2031    
2032            AbstractEngineChannel* pEngineChannel =
2033                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2034    
2035            RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
2036            if (!itCallback) return successResult(); // ignore if callback is i.e. not alive anymore
2037    
2038            itCallback->execCtx->signalAbort();
2039    
2040            return successResult();
2041        }
2042    
2043        // fork() function
2044    
2045        InstrumentScriptVMFunction_fork::InstrumentScriptVMFunction_fork(InstrumentScriptVM* parent)
2046            : m_vm(parent)
2047        {
2048        }
2049    
2050        VMFnResult* InstrumentScriptVMFunction_fork::exec(VMFnArgs* args) {
2051            // check if this is actually the parent going to fork, or rather one of
2052            // the children which is already forked
2053            if (m_vm->m_event->forkIndex != 0) { // this is the entry point for a child ...
2054                int forkResult = m_vm->m_event->forkIndex;
2055                // reset so that this child may i.e. also call fork() later on
2056                m_vm->m_event->forkIndex = 0;
2057                return successResult(forkResult);
2058            }
2059    
2060            // if we are here, then this is the parent, so we must fork this parent
2061    
2062            const int n =
2063                (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : 1;
2064            const bool bAutoAbort =
2065                (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : true;
2066    
2067            if (m_vm->m_event->countChildHandlers() + n > MAX_FORK_PER_SCRIPT_HANDLER) {
2068                wrnMsg("fork(): requested amount would exceed allowed limit per event handler");
2069                return successResult(-1);
2070            }
2071    
2072            AbstractEngineChannel* pEngineChannel =
2073                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2074    
2075            if (!pEngineChannel->hasFreeScriptCallbacks(n)) {
2076                wrnMsg("fork(): global limit of event handlers exceeded");
2077                return successResult(-1);
2078            }
2079    
2080            for (int iChild = 0; iChild < n; ++iChild) {
2081                RTList<ScriptEvent>::Iterator itChild =
2082                    pEngineChannel->forkScriptCallback(m_vm->m_event, bAutoAbort);
2083                if (!itChild) { // should never happen, otherwise its a bug ...
2084                    errMsg("fork(): internal error while allocating child");
2085                    return errorResult(-1); // terminate script
2086                }
2087                // since both parent, as well all child script execution instances
2088                // all land in this exect() method, the following is (more or less)
2089                // the only feature that lets us distinguish the parent and
2090                // respective children from each other in this exect() method
2091                itChild->forkIndex = iChild + 1;
2092            }
2093    
2094            return successResult(0);
2095        }
2096    
2097  } // namespace LinuxSampler  } // namespace LinuxSampler

Legend:
Removed from v.2962  
changed lines
  Added in v.3381

  ViewVC Help
Powered by ViewVC