/[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 2938 by schoenebeck, Mon Jul 11 17:10:40 2016 UTC revision 3335 by schoenebeck, Sun Jul 30 14:33:15 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) {
378          int volume = args->arg(1)->asInt()->evalInt(); // volume change in milli dB          int volume = args->arg(1)->asInt()->evalInt(); // volume change in milli dB
379          bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;          bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
380            const float fVolumeLin = RTMath::DecibelToLinRatio(float(volume) / 1000.f);
381    
382          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
383              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 383  namespace LinuxSampler { Line 396  namespace LinuxSampler {
396              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
397              if (!pNote) return successResult();              if (!pNote) return successResult();
398    
399              const float fVolumeLin = RTMath::DecibelToLinRatio(float(volume) / 1000.f);              // if change_vol() was called immediately after note was triggered
400              // commented out, performed by EngineBase::ProcessNoteSynthParam() for time accuracy behavior              // then immediately apply the volume to note object, but only if
401              /*if (relative)              // change_vol_time() has not been called before
402                  pNote->Override.Volume *= fVolumeLin;              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
403              else                  pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S)
404                  pNote->Override.Volume = fVolumeLin;*/              {
405                    if (relative)
             Event e = m_vm->m_event->cause; // copy to get fragment time for "now"  
             e.Init(); // clear IDs  
             e.Type = Event::type_note_synth_param;  
             e.Param.NoteSynthParam.NoteID   = id.noteID();  
             e.Param.NoteSynthParam.Type     = Event::synth_param_volume;  
             e.Param.NoteSynthParam.Delta    = fVolumeLin;  
             e.Param.NoteSynthParam.Relative = relative;  
   
             pEngineChannel->ScheduleEventMicroSec(&e, 0);  
         } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {  
             VMIntArrayExpr* ids = args->arg(0)->asIntArray();  
             for (int i = 0; i < ids->arraySize(); ++i) {  
                 const ScriptID id = ids->evalIntElement(i);  
                 if (!id || !id.isNoteID()) continue;  
   
                 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );  
                 if (!pNote) continue;  
   
                 const float fVolumeLin = RTMath::DecibelToLinRatio(float(volume) / 1000.f);  
                 // commented out, performed by EngineBase::ProcessNoteSynthParam() for time accuracy behavior  
                 /*if (relative)  
406                      pNote->Override.Volume *= fVolumeLin;                      pNote->Override.Volume *= fVolumeLin;
407                  else                  else
408                      pNote->Override.Volume = fVolumeLin;*/                      pNote->Override.Volume = fVolumeLin;
409                } else { // otherwise schedule the volume change ...
410                  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"
411                  e.Init(); // clear IDs                  e.Init(); // clear IDs
412                  e.Type = Event::type_note_synth_param;                  e.Type = Event::type_note_synth_param;
# Line 425  namespace LinuxSampler { Line 417  namespace LinuxSampler {
417    
418                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
419              }              }
420            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
421                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
422                for (int i = 0; i < ids->arraySize(); ++i) {
423                    const ScriptID id = ids->evalIntElement(i);
424                    if (!id || !id.isNoteID()) continue;
425    
426                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
427                    if (!pNote) continue;
428    
429                    // if change_vol() was called immediately after note was triggered
430                    // then immediately apply the volume to Note object, but only if
431                    // 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)
436                            pNote->Override.Volume *= fVolumeLin;
437                        else
438                            pNote->Override.Volume = fVolumeLin;
439                    } else { // otherwise schedule the volume change ...
440                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
441                        e.Init(); // clear IDs
442                        e.Type = Event::type_note_synth_param;
443                        e.Param.NoteSynthParam.NoteID   = id.noteID();
444                        e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
445                        e.Param.NoteSynthParam.Delta    = fVolumeLin;
446                        e.Param.NoteSynthParam.Relative = relative;
447    
448                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
449                    }
450                }
451          }          }
452    
453          return successResult();          return successResult();
# Line 441  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) {
471          int tune = args->arg(1)->asInt()->evalInt(); // tuning change in milli cents          int tune = args->arg(1)->asInt()->evalInt(); // tuning change in milli cents
472          bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;          bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
473            const float fFreqRatio = RTMath::CentsToFreqRatioUnlimited(float(tune) / 1000.f);
474    
475          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
476              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 465  namespace LinuxSampler { Line 489  namespace LinuxSampler {
489              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
490              if (!pNote) return successResult();              if (!pNote) return successResult();
491    
492              const float fFreqRatio = RTMath::CentsToFreqRatioUnlimited(float(tune) / 1000.f);              // if change_tune() was called immediately after note was triggered
493              // commented out, performed by EngineBase::ProcessNoteSynthParam() for time accuracy behavior              // then immediately apply the tuning to Note object, but only if
494              /*if (relative)              // change_tune_time() has not been called before
495                  pNote->Override.Pitch *= fFreqRatio;              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
496              else                  pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S)
497                  pNote->Override.Pitch = fFreqRatio;*/              {
498                    if (relative)
             Event e = m_vm->m_event->cause; // copy to get fragment time for "now"  
             e.Init(); // clear IDs  
             e.Type = Event::type_note_synth_param;  
             e.Param.NoteSynthParam.NoteID   = id.noteID();  
             e.Param.NoteSynthParam.Type     = Event::synth_param_pitch;  
             e.Param.NoteSynthParam.Delta    = fFreqRatio;  
             e.Param.NoteSynthParam.Relative = relative;  
   
             pEngineChannel->ScheduleEventMicroSec(&e, 0);  
         } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {  
             VMIntArrayExpr* ids = args->arg(0)->asIntArray();  
             for (int i = 0; i < ids->arraySize(); ++i) {  
                 const ScriptID id = ids->evalIntElement(i);  
                 if (!id || !id.isNoteID()) continue;  
   
                 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );  
                 if (!pNote) continue;  
   
                 const float fFreqRatio = RTMath::CentsToFreqRatioUnlimited(float(tune) / 1000.f);  
                 // commented out, performed by EngineBase::ProcessNoteSynthParam() for time accuracy behavior  
                 /*if (relative)  
499                      pNote->Override.Pitch *= fFreqRatio;                      pNote->Override.Pitch *= fFreqRatio;
500                  else                  else
501                      pNote->Override.Pitch = fFreqRatio;*/                      pNote->Override.Pitch = fFreqRatio;
502                } else { // otherwise schedule tuning change ...
503                  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"
504                  e.Init(); // clear IDs                  e.Init(); // clear IDs
505                  e.Type = Event::type_note_synth_param;                  e.Type = Event::type_note_synth_param;
# Line 507  namespace LinuxSampler { Line 510  namespace LinuxSampler {
510    
511                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
512              }              }
513            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
514                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
515                for (int i = 0; i < ids->arraySize(); ++i) {
516                    const ScriptID id = ids->evalIntElement(i);
517                    if (!id || !id.isNoteID()) continue;
518    
519                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
520                    if (!pNote) continue;
521    
522                    // if change_tune() was called immediately after note was triggered
523                    // then immediately apply the tuning to Note object, but only if
524                    // 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)
529                            pNote->Override.Pitch *= fFreqRatio;
530                        else
531                            pNote->Override.Pitch = fFreqRatio;
532                    } else { // otherwise schedule tuning change ...
533                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
534                        e.Init(); // clear IDs
535                        e.Type = Event::type_note_synth_param;
536                        e.Param.NoteSynthParam.NoteID   = id.noteID();
537                        e.Param.NoteSynthParam.Type     = Event::synth_param_pitch;
538                        e.Param.NoteSynthParam.Delta    = fFreqRatio;
539                        e.Param.NoteSynthParam.Relative = relative;
540    
541                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
542                    }
543                }
544          }          }
545    
546          return successResult();          return successResult();
# Line 523  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 537  namespace LinuxSampler { Line 571  namespace LinuxSampler {
571              wrnMsg("change_pan(): argument 2 may not be smaller than -1000");              wrnMsg("change_pan(): argument 2 may not be smaller than -1000");
572              pan = -1000;              pan = -1000;
573          }          }
574            const float fPan = float(pan) / 1000.f;
575    
576          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
577              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 555  namespace LinuxSampler { Line 590  namespace LinuxSampler {
590              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
591              if (!pNote) return successResult();              if (!pNote) return successResult();
592    
593              const float fPan = float(pan) / 1000.f;              // if change_pan() was called immediately after note was triggered
594              // commented out, performed by EngineBase::ProcessNoteSynthParam() for time accuracy behavior              // then immediately apply the panning to Note object
595              /*if (relative) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
596                  pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources);                  if (relative) {
             } else {  
                 pNote->Override.Pan = fPan;  
                 pNote->Override.PanSources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set  
             }*/  
   
             Event e = m_vm->m_event->cause; // copy to get fragment time for "now"  
             e.Init(); // clear IDs  
             e.Type = Event::type_note_synth_param;  
             e.Param.NoteSynthParam.NoteID   = id.noteID();  
             e.Param.NoteSynthParam.Type     = Event::synth_param_pan;  
             e.Param.NoteSynthParam.Delta    = fPan;  
             e.Param.NoteSynthParam.Relative = relative;  
   
             pEngineChannel->ScheduleEventMicroSec(&e, 0);  
         } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {  
             VMIntArrayExpr* ids = args->arg(0)->asIntArray();  
             for (int i = 0; i < ids->arraySize(); ++i) {  
                 const ScriptID id = ids->evalIntElement(i);  
                 if (!id || !id.isNoteID()) continue;  
   
                 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );  
                 if (!pNote) continue;  
   
                 const float fPan = float(pan) / 1000.f;  
                 // commented out, performed by EngineBase::ProcessNoteSynthParam() for time accuracy behavior  
                 /*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 {
599                      pNote->Override.Pan = fPan;                      pNote->Override.Pan = fPan;
600                      pNote->Override.PanSources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set                      pNote->Override.PanSources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set
601                  }*/                  }
602                } else { // otherwise schedule panning change ...
603                  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"
604                  e.Init(); // clear IDs                  e.Init(); // clear IDs
605                  e.Type = Event::type_note_synth_param;                  e.Type = Event::type_note_synth_param;
# Line 601  namespace LinuxSampler { Line 610  namespace LinuxSampler {
610    
611                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
612              }              }
613            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
614                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
615                for (int i = 0; i < ids->arraySize(); ++i) {
616                    const ScriptID id = ids->evalIntElement(i);
617                    if (!id || !id.isNoteID()) continue;
618    
619                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
620                    if (!pNote) continue;
621    
622                    // if change_pan() was called immediately after note was triggered
623                    // then immediately apply the panning to Note object
624                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
625                        if (relative) {
626                            pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources);
627                        } else {
628                            pNote->Override.Pan = fPan;
629                            pNote->Override.PanSources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set
630                        }
631                    } else { // otherwise schedule panning change ...
632                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
633                        e.Init(); // clear IDs
634                        e.Type = Event::type_note_synth_param;
635                        e.Param.NoteSynthParam.NoteID   = id.noteID();
636                        e.Param.NoteSynthParam.Type     = Event::synth_param_pan;
637                        e.Param.NoteSynthParam.Delta    = fPan;
638                        e.Param.NoteSynthParam.Relative = relative;
639    
640                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
641                    }
642                }
643          }          }
644    
645          return successResult();          return successResult();
646      }      }
647    
648      #define VM_FILTER_PAR_MAX_VALUE 1000000      #define VM_FILTER_PAR_MAX_VALUE 1000000
649        #define VM_EG_PAR_MAX_VALUE 1000000
650    
651      // change_cutoff() function      // change_cutoff() function
652    
# Line 619  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 631  namespace LinuxSampler { Line 671  namespace LinuxSampler {
671              wrnMsg("change_cutoff(): argument 2 may not be negative");              wrnMsg("change_cutoff(): argument 2 may not be negative");
672              cutoff = 0;              cutoff = 0;
673          }          }
674            const float fCutoff = float(cutoff) / float(VM_FILTER_PAR_MAX_VALUE);
675    
676          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
677              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 649  namespace LinuxSampler { Line 690  namespace LinuxSampler {
690              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
691              if (!pNote) return successResult();              if (!pNote) return successResult();
692    
693              const float fCutoff = float(cutoff) / float(VM_FILTER_PAR_MAX_VALUE);              // if change_cutoff() was called immediately after note was triggered
694                // then immediately apply cutoff to Note object
695              Event e = m_vm->m_event->cause; // copy to get fragment time for "now"              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
696              e.Init(); // clear IDs                  pNote->Override.Cutoff = fCutoff;
697              e.Type = Event::type_note_synth_param;              } else { // otherwise schedule cutoff change ...
698              e.Param.NoteSynthParam.NoteID   = id.noteID();                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
699              e.Param.NoteSynthParam.Type     = Event::synth_param_cutoff;                  e.Init(); // clear IDs
700              e.Param.NoteSynthParam.Delta    = fCutoff;                  e.Type = Event::type_note_synth_param;
701              e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.NoteID   = id.noteID();
702                    e.Param.NoteSynthParam.Type     = Event::synth_param_cutoff;
703                    e.Param.NoteSynthParam.Delta    = fCutoff;
704                    e.Param.NoteSynthParam.Relative = false;
705    
706              pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
707                }
708          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
709              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
710              for (int i = 0; i < ids->arraySize(); ++i) {              for (int i = 0; i < ids->arraySize(); ++i) {
# Line 669  namespace LinuxSampler { Line 714  namespace LinuxSampler {
714                  NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );                  NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
715                  if (!pNote) continue;                  if (!pNote) continue;
716    
717                  const float fCutoff = float(cutoff) / float(VM_FILTER_PAR_MAX_VALUE);                  // if change_cutoff() was called immediately after note was triggered
718                    // then immediately apply cutoff to Note object
719                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
720                        pNote->Override.Cutoff = fCutoff;
721                    } else { // otherwise schedule cutoff change ...
722                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
723                        e.Init(); // clear IDs
724                        e.Type = Event::type_note_synth_param;
725                        e.Param.NoteSynthParam.NoteID   = id.noteID();
726                        e.Param.NoteSynthParam.Type     = Event::synth_param_cutoff;
727                        e.Param.NoteSynthParam.Delta    = fCutoff;
728                        e.Param.NoteSynthParam.Relative = false;
729    
730                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
731                  e.Init(); // clear IDs                  }
                 e.Type = Event::type_note_synth_param;  
                 e.Param.NoteSynthParam.NoteID   = id.noteID();  
                 e.Param.NoteSynthParam.Type     = Event::synth_param_cutoff;  
                 e.Param.NoteSynthParam.Delta    = fCutoff;  
                 e.Param.NoteSynthParam.Relative = false;  
   
                 pEngineChannel->ScheduleEventMicroSec(&e, 0);  
732              }              }
733          }          }
734    
# Line 697  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 709  namespace LinuxSampler { Line 758  namespace LinuxSampler {
758              wrnMsg("change_reso(): argument 2 may not be negative");              wrnMsg("change_reso(): argument 2 may not be negative");
759              resonance = 0;              resonance = 0;
760          }          }
761            const float fResonance = float(resonance) / float(VM_FILTER_PAR_MAX_VALUE);
762    
763          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
764              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 727  namespace LinuxSampler { Line 777  namespace LinuxSampler {
777              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
778              if (!pNote) return successResult();              if (!pNote) return successResult();
779    
780              const float fResonance = float(resonance) / float(VM_FILTER_PAR_MAX_VALUE);              // if change_reso() was called immediately after note was triggered
781                // then immediately apply resonance to Note object
782                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
783                    pNote->Override.Resonance = fResonance;
784                } else { // otherwise schedule resonance change ...
785                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
786                    e.Init(); // clear IDs
787                    e.Type = Event::type_note_synth_param;
788                    e.Param.NoteSynthParam.NoteID   = id.noteID();
789                    e.Param.NoteSynthParam.Type     = Event::synth_param_resonance;
790                    e.Param.NoteSynthParam.Delta    = fResonance;
791                    e.Param.NoteSynthParam.Relative = false;
792    
793              Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
794              e.Init(); // clear IDs              }
795              e.Type = Event::type_note_synth_param;          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
796              e.Param.NoteSynthParam.NoteID   = id.noteID();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
797              e.Param.NoteSynthParam.Type     = Event::synth_param_resonance;              for (int i = 0; i < ids->arraySize(); ++i) {
798              e.Param.NoteSynthParam.Delta    = fResonance;                  const ScriptID id = ids->evalIntElement(i);
799              e.Param.NoteSynthParam.Relative = false;                  if (!id || !id.isNoteID()) continue;
800    
801              pEngineChannel->ScheduleEventMicroSec(&e, 0);                  NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
802                    if (!pNote) continue;
803    
804                    // if change_reso() was called immediately after note was triggered
805                    // then immediately apply resonance to Note object
806                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
807                        pNote->Override.Resonance = fResonance;
808                    } else { // otherwise schedule resonance change ...
809                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
810                        e.Init(); // clear IDs
811                        e.Type = Event::type_note_synth_param;
812                        e.Param.NoteSynthParam.NoteID   = id.noteID();
813                        e.Param.NoteSynthParam.Type     = Event::synth_param_resonance;
814                        e.Param.NoteSynthParam.Delta    = fResonance;
815                        e.Param.NoteSynthParam.Relative = false;
816    
817                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
818                    }
819                }
820            }
821    
822            return successResult();
823        }
824        
825        // change_attack() function
826    
827        InstrumentScriptVMFunction_change_attack::InstrumentScriptVMFunction_change_attack(InstrumentScriptVM* parent)
828            : m_vm(parent)
829        {
830        }
831    
832        bool InstrumentScriptVMFunction_change_attack::acceptsArgType(int iArg, ExprType_t type) const {
833            if (iArg == 0)
834                return type == INT_EXPR || type == INT_ARR_EXPR;
835            else
836                return type == INT_EXPR;
837        }
838    
839        VMFnResult* InstrumentScriptVMFunction_change_attack::exec(VMFnArgs* args) {
840            int attack = args->arg(1)->asInt()->evalInt();
841            // note: intentionally not checking against a max. value here!
842            // (to allow i.e. passing 2000000 for doubling the attack time)
843            if (attack < 0) {
844                wrnMsg("change_attack(): argument 2 may not be negative");
845                attack = 0;
846            }
847            const float fAttack = float(attack) / float(VM_EG_PAR_MAX_VALUE);
848    
849            AbstractEngineChannel* pEngineChannel =
850                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
851    
852            if (args->arg(0)->exprType() == INT_EXPR) {
853                const ScriptID id = args->arg(0)->asInt()->evalInt();
854                if (!id) {
855                    wrnMsg("change_attack(): note ID for argument 1 may not be zero");
856                    return successResult();
857                }
858                if (!id.isNoteID()) {
859                    wrnMsg("change_attack(): argument 1 is not a note ID");
860                    return successResult();
861                }
862    
863                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
864                if (!pNote) return successResult();
865    
866                // if change_attack() was called immediately after note was triggered
867                // then immediately apply attack to Note object
868                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
869                    pNote->Override.Attack = fAttack;
870                } else { // otherwise schedule attack change ...
871                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
872                    e.Init(); // clear IDs
873                    e.Type = Event::type_note_synth_param;
874                    e.Param.NoteSynthParam.NoteID   = id.noteID();
875                    e.Param.NoteSynthParam.Type     = Event::synth_param_attack;
876                    e.Param.NoteSynthParam.Delta    = fAttack;
877                    e.Param.NoteSynthParam.Relative = false;
878    
879                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
880                }
881          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
882              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
883              for (int i = 0; i < ids->arraySize(); ++i) {              for (int i = 0; i < ids->arraySize(); ++i) {
# Line 747  namespace LinuxSampler { Line 887  namespace LinuxSampler {
887                  NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );                  NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
888                  if (!pNote) continue;                  if (!pNote) continue;
889    
890                  const float fResonance = float(resonance) / float(VM_FILTER_PAR_MAX_VALUE);                  // if change_attack() was called immediately after note was triggered
891                    // then immediately apply attack to Note object
892                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
893                        pNote->Override.Attack = fAttack;
894                    } else { // otherwise schedule attack change ...
895                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
896                        e.Init(); // clear IDs
897                        e.Type = Event::type_note_synth_param;
898                        e.Param.NoteSynthParam.NoteID   = id.noteID();
899                        e.Param.NoteSynthParam.Type     = Event::synth_param_attack;
900                        e.Param.NoteSynthParam.Delta    = fAttack;
901                        e.Param.NoteSynthParam.Relative = false;
902    
903                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
904                    }
905                }
906            }
907    
908            return successResult();
909        }
910    
911        // change_decay() function
912        
913        InstrumentScriptVMFunction_change_decay::InstrumentScriptVMFunction_change_decay(InstrumentScriptVM* parent)
914            : m_vm(parent)
915        {
916        }
917    
918        bool InstrumentScriptVMFunction_change_decay::acceptsArgType(int iArg, ExprType_t type) const {
919            if (iArg == 0)
920                return type == INT_EXPR || type == INT_ARR_EXPR;
921            else
922                return type == INT_EXPR;
923        }
924    
925        VMFnResult* InstrumentScriptVMFunction_change_decay::exec(VMFnArgs* args) {
926            int decay = args->arg(1)->asInt()->evalInt();
927            // note: intentionally not checking against a max. value here!
928            // (to allow i.e. passing 2000000 for doubling the decay time)
929            if (decay < 0) {
930                wrnMsg("change_decay(): argument 2 may not be negative");
931                decay = 0;
932            }
933            const float fDecay = float(decay) / float(VM_EG_PAR_MAX_VALUE);
934    
935            AbstractEngineChannel* pEngineChannel =
936                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
937    
938            if (args->arg(0)->exprType() == INT_EXPR) {
939                const ScriptID id = args->arg(0)->asInt()->evalInt();
940                if (!id) {
941                    wrnMsg("change_decay(): note ID for argument 1 may not be zero");
942                    return successResult();
943                }
944                if (!id.isNoteID()) {
945                    wrnMsg("change_decay(): argument 1 is not a note ID");
946                    return successResult();
947                }
948    
949                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
950                if (!pNote) return successResult();
951    
952                // if change_decay() was called immediately after note was triggered
953                // then immediately apply decay to Note object
954                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
955                    pNote->Override.Decay = fDecay;
956                } 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"
958                  e.Init(); // clear IDs                  e.Init(); // clear IDs
959                  e.Type = Event::type_note_synth_param;                  e.Type = Event::type_note_synth_param;
960                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
961                  e.Param.NoteSynthParam.Type     = Event::synth_param_resonance;                  e.Param.NoteSynthParam.Type     = Event::synth_param_decay;
962                  e.Param.NoteSynthParam.Delta    = fResonance;                  e.Param.NoteSynthParam.Delta    = fDecay;
963                    e.Param.NoteSynthParam.Relative = false;
964    
965                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
966                }
967            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
968                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
969                for (int i = 0; i < ids->arraySize(); ++i) {
970                    const ScriptID id = ids->evalIntElement(i);
971                    if (!id || !id.isNoteID()) continue;
972    
973                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
974                    if (!pNote) continue;
975    
976                    // if change_decay() was called immediately after note was triggered
977                    // then immediately apply decay to Note object
978                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
979                        pNote->Override.Decay = fDecay;
980                    } else { // otherwise schedule decay change ...
981                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
982                        e.Init(); // clear IDs
983                        e.Type = Event::type_note_synth_param;
984                        e.Param.NoteSynthParam.NoteID   = id.noteID();
985                        e.Param.NoteSynthParam.Type     = Event::synth_param_decay;
986                        e.Param.NoteSynthParam.Delta    = fDecay;
987                        e.Param.NoteSynthParam.Relative = false;
988    
989                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
990                    }
991                }
992            }
993    
994            return successResult();
995        }
996    
997        // change_release() function
998        
999        InstrumentScriptVMFunction_change_release::InstrumentScriptVMFunction_change_release(InstrumentScriptVM* parent)
1000            : m_vm(parent)
1001        {
1002        }
1003    
1004        bool InstrumentScriptVMFunction_change_release::acceptsArgType(int iArg, ExprType_t type) const {
1005            if (iArg == 0)
1006                return type == INT_EXPR || type == INT_ARR_EXPR;
1007            else
1008                return type == INT_EXPR;
1009        }
1010    
1011        VMFnResult* InstrumentScriptVMFunction_change_release::exec(VMFnArgs* args) {
1012            int release = args->arg(1)->asInt()->evalInt();
1013            // note: intentionally not checking against a max. value here!
1014            // (to allow i.e. passing 2000000 for doubling the release time)
1015            if (release < 0) {
1016                wrnMsg("change_release(): argument 2 may not be negative");
1017                release = 0;
1018            }
1019            const float fRelease = float(release) / float(VM_EG_PAR_MAX_VALUE);
1020    
1021            AbstractEngineChannel* pEngineChannel =
1022                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1023    
1024            if (args->arg(0)->exprType() == INT_EXPR) {
1025                const ScriptID id = args->arg(0)->asInt()->evalInt();
1026                if (!id) {
1027                    wrnMsg("change_release(): note ID for argument 1 may not be zero");
1028                    return successResult();
1029                }
1030                if (!id.isNoteID()) {
1031                    wrnMsg("change_release(): argument 1 is not a note ID");
1032                    return successResult();
1033                }
1034    
1035                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1036                if (!pNote) return successResult();
1037    
1038                // if change_release() was called immediately after note was triggered
1039                // then immediately apply relase to Note object
1040                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1041                    pNote->Override.Release = fRelease;
1042                } else { // otherwise schedule release change ...
1043                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1044                    e.Init(); // clear IDs
1045                    e.Type = Event::type_note_synth_param;
1046                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1047                    e.Param.NoteSynthParam.Type     = Event::synth_param_release;
1048                    e.Param.NoteSynthParam.Delta    = fRelease;
1049                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Relative = false;
1050    
1051                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
1052              }              }
1053            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1054                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1055                for (int i = 0; i < ids->arraySize(); ++i) {
1056                    const ScriptID id = ids->evalIntElement(i);
1057                    if (!id || !id.isNoteID()) continue;
1058    
1059                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1060                    if (!pNote) continue;
1061    
1062                    // if change_release() was called immediately after note was triggered
1063                    // then immediately apply relase to Note object
1064                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1065                        pNote->Override.Release = fRelease;
1066                    } else { // otherwise schedule release change ...
1067                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1068                        e.Init(); // clear IDs
1069                        e.Type = Event::type_note_synth_param;
1070                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1071                        e.Param.NoteSynthParam.Type     = Event::synth_param_release;
1072                        e.Param.NoteSynthParam.Delta    = fRelease;
1073                        e.Param.NoteSynthParam.Relative = false;
1074    
1075                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
1076                    }
1077                }
1078          }          }
1079    
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            true, NO_LIMIT, 0>( args, "change_sustain" );
1184        }
1185    
1186        // change_amp_lfo_depth() function
1187    
1188        VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_depth::exec(VMFnArgs* args) {
1189            return VMChangeSynthParamFunction::execTemplate<
1190                        &NoteBase::_Override::AmpLFODepth,
1191                        Event::synth_param_amp_lfo_depth,
1192                        true, 1000000, 0>( args, "change_amp_lfo_depth" );
1193        }
1194    
1195        // change_amp_lfo_freq() function
1196    
1197        VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_freq::exec(VMFnArgs* args) {
1198            return VMChangeSynthParamFunction::execTemplate<
1199                        &NoteBase::_Override::AmpLFOFreq,
1200                        Event::synth_param_amp_lfo_freq,
1201                        true, 1000000, 0>( args, "change_amp_lfo_freq" );
1202        }
1203    
1204        // change_pitch_lfo_depth() function
1205    
1206        VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_depth::exec(VMFnArgs* args) {
1207            return VMChangeSynthParamFunction::execTemplate<
1208                        &NoteBase::_Override::PitchLFODepth,
1209                        Event::synth_param_pitch_lfo_depth,
1210                        true, 1000000, 0>( args, "change_pitch_lfo_depth" );
1211        }
1212    
1213        // change_pitch_lfo_freq() function
1214    
1215        VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_freq::exec(VMFnArgs* args) {
1216            return VMChangeSynthParamFunction::execTemplate<
1217                        &NoteBase::_Override::PitchLFOFreq,
1218                        Event::synth_param_pitch_lfo_freq,
1219                        true, 1000000, 0>( args, "change_pitch_lfo_freq" );
1220        }
1221    
1222        // change_vol_time() function
1223    
1224        VMFnResult* InstrumentScriptVMFunction_change_vol_time::exec(VMFnArgs* args) {
1225            return VMChangeSynthParamFunction::execTemplate<
1226                        &NoteBase::_Override::VolumeTime,
1227                        Event::synth_param_volume_time,
1228                        false, NO_LIMIT, 0>( args, "change_vol_time" );
1229        }
1230    
1231        // change_tune_time() function
1232    
1233        VMFnResult* InstrumentScriptVMFunction_change_tune_time::exec(VMFnArgs* args) {
1234            return VMChangeSynthParamFunction::execTemplate<
1235                        &NoteBase::_Override::PitchTime,
1236                        Event::synth_param_pitch_time,
1237                        false, NO_LIMIT, 0>( args, "change_tune_time" );
1238        }
1239    
1240        // change_pan_time() function
1241    
1242        VMFnResult* InstrumentScriptVMFunction_change_pan_time::exec(VMFnArgs* args) {
1243            return VMChangeSynthParamFunction::execTemplate<
1244            &NoteBase::_Override::PanTime,
1245            Event::synth_param_pan_time,
1246            false, NO_LIMIT, 0>( args, "change_pan_time" );
1247        }
1248    
1249        // template for change_*_curve() functions
1250    
1251        bool VMChangeFadeCurveFunction::acceptsArgType(int iArg, ExprType_t type) const {
1252            if (iArg == 0)
1253                return type == INT_EXPR || type == INT_ARR_EXPR;
1254            else
1255                return type == INT_EXPR;
1256        }
1257    
1258        template<fade_curve_t NoteBase::_Override::*T_noteParam, int T_synthParam>
1259        VMFnResult* VMChangeFadeCurveFunction::execTemplate(VMFnArgs* args, const char* functionName) {
1260            int value = args->arg(1)->asInt()->evalInt();
1261            switch (value) {
1262                case FADE_CURVE_LINEAR:
1263                case FADE_CURVE_EASE_IN_EASE_OUT:
1264                    break;
1265                default:
1266                    wrnMsg(String(functionName) + "(): invalid curve type passed as argument 2");
1267                    return successResult();
1268            }
1269    
1270            AbstractEngineChannel* pEngineChannel =
1271                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1272    
1273            if (args->arg(0)->exprType() == INT_EXPR) {
1274                const ScriptID id = args->arg(0)->asInt()->evalInt();
1275                if (!id) {
1276                    wrnMsg(String(functionName) + "(): note ID for argument 1 may not be zero");
1277                    return successResult();
1278                }
1279                if (!id.isNoteID()) {
1280                    wrnMsg(String(functionName) + "(): argument 1 is not a note ID");
1281                    return successResult();
1282                }
1283    
1284                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1285                if (!pNote) return successResult();
1286    
1287                // if this change_*_curve() script function was called immediately after
1288                // note was triggered then immediately apply the synth parameter
1289                // change to Note object
1290                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1291                    pNote->Override.*T_noteParam = (fade_curve_t) value;
1292                } else { // otherwise schedule this synth parameter change ...
1293                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1294                    e.Init(); // clear IDs
1295                    e.Type = Event::type_note_synth_param;
1296                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1297                    e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1298                    e.Param.NoteSynthParam.Delta    = value;
1299                    e.Param.NoteSynthParam.Relative = false;
1300    
1301                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
1302                }
1303            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1304                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1305                for (int i = 0; i < ids->arraySize(); ++i) {
1306                    const ScriptID id = ids->evalIntElement(i);
1307                    if (!id || !id.isNoteID()) continue;
1308    
1309                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1310                    if (!pNote) continue;
1311    
1312                    // if this change_*_curve() script function was called immediately after
1313                    // note was triggered then immediately apply the synth parameter
1314                    // change to Note object
1315                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1316                        pNote->Override.*T_noteParam = (fade_curve_t) value;
1317                    } else { // otherwise schedule this synth parameter change ...
1318                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1319                        e.Init(); // clear IDs
1320                        e.Type = Event::type_note_synth_param;
1321                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1322                        e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1323                        e.Param.NoteSynthParam.Delta    = value;
1324                        e.Param.NoteSynthParam.Relative = false;
1325    
1326                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
1327                    }
1328                }
1329            }
1330    
1331            return successResult();
1332        }
1333    
1334        // change_vol_curve() function
1335    
1336        VMFnResult* InstrumentScriptVMFunction_change_vol_curve::exec(VMFnArgs* args) {
1337            return VMChangeFadeCurveFunction::execTemplate<
1338                        &NoteBase::_Override::VolumeCurve,
1339                        Event::synth_param_volume_curve>( args, "change_vol_curve" );
1340        }
1341    
1342        // change_tune_curve() function
1343    
1344        VMFnResult* InstrumentScriptVMFunction_change_tune_curve::exec(VMFnArgs* args) {
1345            return VMChangeFadeCurveFunction::execTemplate<
1346                        &NoteBase::_Override::PitchCurve,
1347                        Event::synth_param_pitch_curve>( args, "change_tune_curve" );
1348        }
1349    
1350        // change_pan_curve() function
1351    
1352        VMFnResult* InstrumentScriptVMFunction_change_pan_curve::exec(VMFnArgs* args) {
1353            return VMChangeFadeCurveFunction::execTemplate<
1354            &NoteBase::_Override::PanCurve,
1355            Event::synth_param_pan_curve>( args, "change_pan_curve" );
1356        }
1357    
1358        // fade_in() function
1359    
1360        InstrumentScriptVMFunction_fade_in::InstrumentScriptVMFunction_fade_in(InstrumentScriptVM* parent)
1361            : m_vm(parent)
1362        {
1363        }
1364    
1365        bool InstrumentScriptVMFunction_fade_in::acceptsArgType(int iArg, ExprType_t type) const {
1366            if (iArg == 0)
1367                return type == INT_EXPR || type == INT_ARR_EXPR;
1368            else
1369                return type == INT_EXPR;
1370        }
1371    
1372        VMFnResult* InstrumentScriptVMFunction_fade_in::exec(VMFnArgs* args) {
1373            int duration = args->arg(1)->asInt()->evalInt();
1374            if (duration < 0) {
1375                wrnMsg("fade_in(): argument 2 may not be negative");
1376                duration = 0;
1377            }
1378            const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
1379    
1380            AbstractEngineChannel* pEngineChannel =
1381                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1382    
1383            if (args->arg(0)->exprType() == INT_EXPR) {
1384                const ScriptID id = args->arg(0)->asInt()->evalInt();
1385                if (!id) {
1386                    wrnMsg("fade_in(): note ID for argument 1 may not be zero");
1387                    return successResult();
1388                }
1389                if (!id.isNoteID()) {
1390                    wrnMsg("fade_in(): argument 1 is not a note ID");
1391                    return successResult();
1392                }
1393    
1394                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1395                if (!pNote) return successResult();
1396    
1397                // if fade_in() was called immediately after note was triggered
1398                // then immediately apply a start volume of zero to Note object,
1399                // as well as the fade in duration
1400                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1401                    pNote->Override.Volume = 0.f;
1402                    pNote->Override.VolumeTime = fDuration;
1403                } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
1404                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1405                    e.Init(); // clear IDs
1406                    e.Type = Event::type_note_synth_param;
1407                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1408                    e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1409                    e.Param.NoteSynthParam.Delta    = fDuration;
1410                    e.Param.NoteSynthParam.Relative = false;
1411    
1412                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
1413                }
1414                // and finally schedule a "volume" change, simply one time slice
1415                // ahead, with the final fade in volume (1.0)
1416                {
1417                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1418                    e.Init(); // clear IDs
1419                    e.Type = Event::type_note_synth_param;
1420                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1421                    e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1422                    e.Param.NoteSynthParam.Delta    = 1.f;
1423                    e.Param.NoteSynthParam.Relative = false;
1424    
1425                    // scheduling with 0 delay would also work here, but +1 is more
1426                    // safe regarding potential future implementation changes of the
1427                    // scheduler (see API comments of RTAVLTree::insert())
1428                    pEngineChannel->ScheduleEventMicroSec(&e, 1);
1429                }
1430            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1431                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1432                for (int i = 0; i < ids->arraySize(); ++i) {
1433                    const ScriptID id = ids->evalIntElement(i);
1434                    if (!id || !id.isNoteID()) continue;
1435    
1436                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1437                    if (!pNote) continue;
1438    
1439                    // if fade_in() was called immediately after note was triggered
1440                    // then immediately apply a start volume of zero to Note object,
1441                    // as well as the fade in duration
1442                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1443                        pNote->Override.Volume = 0.f;
1444                        pNote->Override.VolumeTime = fDuration;
1445                    } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
1446                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1447                        e.Init(); // clear IDs
1448                        e.Type = Event::type_note_synth_param;
1449                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1450                        e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1451                        e.Param.NoteSynthParam.Delta    = fDuration;
1452                        e.Param.NoteSynthParam.Relative = false;
1453    
1454                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
1455                    }
1456                    // and finally schedule a "volume" change, simply one time slice
1457                    // ahead, with the final fade in volume (1.0)
1458                    {
1459                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1460                        e.Init(); // clear IDs
1461                        e.Type = Event::type_note_synth_param;
1462                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1463                        e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1464                        e.Param.NoteSynthParam.Delta    = 1.f;
1465                        e.Param.NoteSynthParam.Relative = false;
1466    
1467                        // scheduling with 0 delay would also work here, but +1 is more
1468                        // safe regarding potential future implementation changes of the
1469                        // scheduler (see API comments of RTAVLTree::insert())
1470                        pEngineChannel->ScheduleEventMicroSec(&e, 1);
1471                    }
1472                }
1473            }
1474    
1475            return successResult();
1476        }
1477    
1478        // fade_out() function
1479    
1480        InstrumentScriptVMFunction_fade_out::InstrumentScriptVMFunction_fade_out(InstrumentScriptVM* parent)
1481            : m_vm(parent)
1482        {
1483        }
1484    
1485        bool InstrumentScriptVMFunction_fade_out::acceptsArgType(int iArg, ExprType_t type) const {
1486            if (iArg == 0)
1487                return type == INT_EXPR || type == INT_ARR_EXPR;
1488            else
1489                return type == INT_EXPR;
1490        }
1491    
1492        VMFnResult* InstrumentScriptVMFunction_fade_out::exec(VMFnArgs* args) {
1493            int duration = args->arg(1)->asInt()->evalInt();
1494            if (duration < 0) {
1495                wrnMsg("fade_out(): argument 2 may not be negative");
1496                duration = 0;
1497            }
1498            const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
1499    
1500            bool stop = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : true;
1501    
1502            AbstractEngineChannel* pEngineChannel =
1503                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1504    
1505            if (args->arg(0)->exprType() == INT_EXPR) {
1506                const ScriptID id = args->arg(0)->asInt()->evalInt();
1507                if (!id) {
1508                    wrnMsg("fade_out(): note ID for argument 1 may not be zero");
1509                    return successResult();
1510                }
1511                if (!id.isNoteID()) {
1512                    wrnMsg("fade_out(): argument 1 is not a note ID");
1513                    return successResult();
1514                }
1515    
1516                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1517                if (!pNote) return successResult();
1518    
1519                // if fade_out() was called immediately after note was triggered
1520                // then immediately apply fade out duration to Note object
1521                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1522                    pNote->Override.VolumeTime = fDuration;
1523                } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
1524                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1525                    e.Init(); // clear IDs
1526                    e.Type = Event::type_note_synth_param;
1527                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1528                    e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1529                    e.Param.NoteSynthParam.Delta    = fDuration;
1530                    e.Param.NoteSynthParam.Relative = false;
1531    
1532                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
1533                }
1534                // now schedule a "volume" change, simply one time slice ahead, with
1535                // the final fade out volume (0.0)
1536                {
1537                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1538                    e.Init(); // clear IDs
1539                    e.Type = Event::type_note_synth_param;
1540                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1541                    e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1542                    e.Param.NoteSynthParam.Delta    = 0.f;
1543                    e.Param.NoteSynthParam.Relative = false;
1544    
1545                    // scheduling with 0 delay would also work here, but +1 is more
1546                    // safe regarding potential future implementation changes of the
1547                    // scheduler (see API comments of RTAVLTree::insert())
1548                    pEngineChannel->ScheduleEventMicroSec(&e, 1);
1549                }
1550                // and finally if stopping the note was requested after the fade out
1551                // completed, then schedule to kill the voice after the requested
1552                // duration
1553                if (stop) {
1554                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1555                    e.Init(); // clear IDs
1556                    e.Type = Event::type_kill_note;
1557                    e.Param.Note.ID = id.noteID();
1558                    e.Param.Note.Key = pNote->hostKey;
1559    
1560                    pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
1561                }
1562            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1563                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1564                for (int i = 0; i < ids->arraySize(); ++i) {
1565                    const ScriptID id = ids->evalIntElement(i);
1566                    if (!id || !id.isNoteID()) continue;
1567    
1568                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1569                    if (!pNote) continue;
1570    
1571                    // if fade_out() was called immediately after note was triggered
1572                    // then immediately apply fade out duration to Note object
1573                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1574                        pNote->Override.VolumeTime = fDuration;
1575                    } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
1576                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1577                        e.Init(); // clear IDs
1578                        e.Type = Event::type_note_synth_param;
1579                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1580                        e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1581                        e.Param.NoteSynthParam.Delta    = fDuration;
1582                        e.Param.NoteSynthParam.Relative = false;
1583    
1584                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
1585                    }
1586                    // now schedule a "volume" change, simply one time slice ahead, with
1587                    // the final fade out volume (0.0)
1588                    {
1589                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1590                        e.Init(); // clear IDs
1591                        e.Type = Event::type_note_synth_param;
1592                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1593                        e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1594                        e.Param.NoteSynthParam.Delta    = 0.f;
1595                        e.Param.NoteSynthParam.Relative = false;
1596    
1597                        // scheduling with 0 delay would also work here, but +1 is more
1598                        // safe regarding potential future implementation changes of the
1599                        // scheduler (see API comments of RTAVLTree::insert())
1600                        pEngineChannel->ScheduleEventMicroSec(&e, 1);
1601                    }
1602                    // and finally if stopping the note was requested after the fade out
1603                    // completed, then schedule to kill the voice after the requested
1604                    // duration
1605                    if (stop) {
1606                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1607                        e.Init(); // clear IDs
1608                        e.Type = Event::type_kill_note;
1609                        e.Param.Note.ID = id.noteID();
1610                        e.Param.Note.Key = pNote->hostKey;
1611                        
1612                        pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
1613                    }
1614                }
1615            }
1616    
1617            return successResult();
1618        }
1619    
1620        // get_event_par() function
1621    
1622        InstrumentScriptVMFunction_get_event_par::InstrumentScriptVMFunction_get_event_par(InstrumentScriptVM* parent)
1623            : m_vm(parent)
1624        {
1625        }
1626    
1627        VMFnResult* InstrumentScriptVMFunction_get_event_par::exec(VMFnArgs* args) {
1628            AbstractEngineChannel* pEngineChannel =
1629                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1630    
1631            const ScriptID id = args->arg(0)->asInt()->evalInt();
1632            if (!id) {
1633                wrnMsg("get_event_par(): note ID for argument 1 may not be zero");
1634                return successResult(0);
1635            }
1636            if (!id.isNoteID()) {
1637                wrnMsg("get_event_par(): argument 1 is not a note ID");
1638                return successResult(0);
1639            }
1640    
1641            NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1642            if (!pNote) {
1643                wrnMsg("get_event_par(): no note alive with that note ID of argument 1");
1644                return successResult(0);
1645            }
1646    
1647            const int parameter = args->arg(1)->asInt()->evalInt();
1648            switch (parameter) {
1649                case EVENT_PAR_NOTE:
1650                    return successResult(pNote->cause.Param.Note.Key);
1651                case EVENT_PAR_VELOCITY:
1652                    return successResult(pNote->cause.Param.Note.Velocity);
1653                case EVENT_PAR_VOLUME:
1654                    return successResult(
1655                        RTMath::LinRatioToDecibel(pNote->Override.Volume) * 1000.f
1656                    );
1657                case EVENT_PAR_TUNE:
1658                    return successResult(
1659                         RTMath::FreqRatioToCents(pNote->Override.Pitch) * 1000.f
1660                    );
1661                case EVENT_PAR_0:
1662                    return successResult(pNote->userPar[0]);
1663                case EVENT_PAR_1:
1664                    return successResult(pNote->userPar[1]);
1665                case EVENT_PAR_2:
1666                    return successResult(pNote->userPar[2]);
1667                case EVENT_PAR_3:
1668                    return successResult(pNote->userPar[3]);
1669            }
1670    
1671            wrnMsg("get_event_par(): argument 2 is an invalid event parameter");
1672            return successResult(0);
1673        }
1674    
1675        // set_event_par() function
1676    
1677        InstrumentScriptVMFunction_set_event_par::InstrumentScriptVMFunction_set_event_par(InstrumentScriptVM* parent)
1678            : m_vm(parent)
1679        {
1680        }
1681    
1682        VMFnResult* InstrumentScriptVMFunction_set_event_par::exec(VMFnArgs* args) {
1683            AbstractEngineChannel* pEngineChannel =
1684                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1685    
1686            const ScriptID id = args->arg(0)->asInt()->evalInt();
1687            if (!id) {
1688                wrnMsg("set_event_par(): note ID for argument 1 may not be zero");
1689                return successResult();
1690            }
1691            if (!id.isNoteID()) {
1692                wrnMsg("set_event_par(): argument 1 is not a note ID");
1693                return successResult();
1694            }
1695    
1696            NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1697            if (!pNote) return successResult();
1698    
1699            const int parameter = args->arg(1)->asInt()->evalInt();
1700            const int value     = args->arg(2)->asInt()->evalInt();
1701    
1702            switch (parameter) {
1703                case EVENT_PAR_NOTE:
1704                    if (value < 0 || value > 127) {
1705                        wrnMsg("set_event_par(): note number of argument 3 is out of range");
1706                        return successResult();
1707                    }
1708                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1709                        pNote->cause.Param.Note.Key = value;
1710                        m_vm->m_event->cause.Param.Note.Key = value;
1711                    } else {
1712                        wrnMsg("set_event_par(): note number can only be changed when note is new");
1713                    }
1714                    return successResult();
1715                case EVENT_PAR_VELOCITY:
1716                    if (value < 0 || value > 127) {
1717                        wrnMsg("set_event_par(): velocity of argument 3 is out of range");
1718                        return successResult();
1719                    }
1720                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1721                        pNote->cause.Param.Note.Velocity = value;
1722                        m_vm->m_event->cause.Param.Note.Velocity = value;
1723                    } else {
1724                        wrnMsg("set_event_par(): velocity can only be changed when note is new");
1725                    }
1726                    return successResult();
1727                case EVENT_PAR_VOLUME:
1728                    wrnMsg("set_event_par(): changing volume by this function is currently not supported, use change_vol() instead");
1729                    return successResult();
1730                case EVENT_PAR_TUNE:
1731                    wrnMsg("set_event_par(): changing tune by this function is currently not supported, use change_tune() instead");
1732                    return successResult();
1733                case EVENT_PAR_0:
1734                    pNote->userPar[0] = value;
1735                    return successResult();
1736                case EVENT_PAR_1:
1737                    pNote->userPar[1] = value;
1738                    return successResult();
1739                case EVENT_PAR_2:
1740                    pNote->userPar[2] = value;
1741                    return successResult();
1742                case EVENT_PAR_3:
1743                    pNote->userPar[3] = value;
1744                    return successResult();
1745            }
1746    
1747            wrnMsg("set_event_par(): argument 2 is an invalid event parameter");
1748            return successResult();
1749        }
1750    
1751        // change_note() function
1752    
1753        InstrumentScriptVMFunction_change_note::InstrumentScriptVMFunction_change_note(InstrumentScriptVM* parent)
1754        : m_vm(parent)
1755        {
1756        }
1757    
1758        VMFnResult* InstrumentScriptVMFunction_change_note::exec(VMFnArgs* args) {
1759            AbstractEngineChannel* pEngineChannel =
1760                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1761    
1762            const ScriptID id = args->arg(0)->asInt()->evalInt();
1763            if (!id) {
1764                wrnMsg("change_note(): note ID for argument 1 may not be zero");
1765                return successResult();
1766            }
1767            if (!id.isNoteID()) {
1768                wrnMsg("change_note(): argument 1 is not a note ID");
1769                return successResult();
1770            }
1771    
1772            NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1773            if (!pNote) return successResult();
1774    
1775            const int value = args->arg(1)->asInt()->evalInt();
1776            if (value < 0 || value > 127) {
1777                wrnMsg("change_note(): note number of argument 2 is out of range");
1778                return successResult();
1779            }
1780    
1781            if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1782                pNote->cause.Param.Note.Key = value;
1783                m_vm->m_event->cause.Param.Note.Key = value;
1784            } else {
1785                wrnMsg("change_note(): note number can only be changed when note is new");
1786            }
1787    
1788            return successResult();
1789        }
1790    
1791        // change_velo() function
1792    
1793        InstrumentScriptVMFunction_change_velo::InstrumentScriptVMFunction_change_velo(InstrumentScriptVM* parent)
1794        : m_vm(parent)
1795        {
1796        }
1797    
1798        VMFnResult* InstrumentScriptVMFunction_change_velo::exec(VMFnArgs* args) {
1799            AbstractEngineChannel* pEngineChannel =
1800                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1801    
1802            const ScriptID id = args->arg(0)->asInt()->evalInt();
1803            if (!id) {
1804                wrnMsg("change_velo(): note ID for argument 1 may not be zero");
1805                return successResult();
1806            }
1807            if (!id.isNoteID()) {
1808                wrnMsg("change_velo(): argument 1 is not a note ID");
1809                return successResult();
1810            }
1811    
1812            NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1813            if (!pNote) return successResult();
1814    
1815            const int value = args->arg(1)->asInt()->evalInt();
1816            if (value < 0 || value > 127) {
1817                wrnMsg("change_velo(): velocity of argument 2 is out of range");
1818                return successResult();
1819            }
1820    
1821            if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1822                pNote->cause.Param.Note.Velocity = value;
1823                m_vm->m_event->cause.Param.Note.Velocity = value;
1824            } else {
1825                wrnMsg("change_velo(): velocity can only be changed when note is new");
1826            }
1827    
1828            return successResult();
1829        }
1830    
1831        // change_play_pos() function
1832    
1833        InstrumentScriptVMFunction_change_play_pos::InstrumentScriptVMFunction_change_play_pos(InstrumentScriptVM* parent)
1834        : m_vm(parent)
1835        {
1836        }
1837    
1838        VMFnResult* InstrumentScriptVMFunction_change_play_pos::exec(VMFnArgs* args) {
1839            const ScriptID id = args->arg(0)->asInt()->evalInt();
1840            if (!id) {
1841                wrnMsg("change_play_pos(): note ID for argument 1 may not be zero");
1842                return successResult();
1843            }
1844            if (!id.isNoteID()) {
1845                wrnMsg("change_play_pos(): argument 1 is not a note ID");
1846                return successResult();
1847            }
1848    
1849            const int pos = args->arg(1)->asInt()->evalInt();
1850            if (pos < 0) {
1851                wrnMsg("change_play_pos(): playback position of argument 2 may not be negative");
1852                return successResult();
1853            }
1854    
1855            AbstractEngineChannel* pEngineChannel =
1856                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1857    
1858            NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1859            if (!pNote) return successResult();
1860    
1861            pNote->Override.SampleOffset = pos;
1862    
1863            return successResult();
1864        }
1865    
1866      // event_status() function      // event_status() function
1867    
1868      InstrumentScriptVMFunction_event_status::InstrumentScriptVMFunction_event_status(InstrumentScriptVM* parent)      InstrumentScriptVMFunction_event_status::InstrumentScriptVMFunction_event_status(InstrumentScriptVM* parent)
# Line 789  namespace LinuxSampler { Line 1888  namespace LinuxSampler {
1888          return successResult(pNote ? EVENT_STATUS_NOTE_QUEUE : EVENT_STATUS_INACTIVE);          return successResult(pNote ? EVENT_STATUS_NOTE_QUEUE : EVENT_STATUS_INACTIVE);
1889      }      }
1890    
1891        // callback_status() function
1892    
1893        InstrumentScriptVMFunction_callback_status::InstrumentScriptVMFunction_callback_status(InstrumentScriptVM* parent)
1894            : m_vm(parent)
1895        {
1896        }
1897    
1898        VMFnResult* InstrumentScriptVMFunction_callback_status::exec(VMFnArgs* args) {
1899            const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
1900            if (!id) {
1901                wrnMsg("callback_status(): callback ID for argument 1 may not be zero");
1902                return successResult();
1903            }
1904    
1905            AbstractEngineChannel* pEngineChannel =
1906                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1907    
1908            RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
1909            if (!itCallback)
1910                return successResult(CALLBACK_STATUS_TERMINATED);
1911    
1912            return successResult(
1913                (m_vm->m_event->execCtx == itCallback->execCtx) ?
1914                    CALLBACK_STATUS_RUNNING : CALLBACK_STATUS_QUEUE
1915            );
1916        }
1917    
1918        // wait() function (overrides core wait() implementation)
1919    
1920        InstrumentScriptVMFunction_wait::InstrumentScriptVMFunction_wait(InstrumentScriptVM* parent)
1921            : CoreVMFunction_wait(parent)
1922        {    
1923        }
1924    
1925        VMFnResult* InstrumentScriptVMFunction_wait::exec(VMFnArgs* args) {
1926            InstrumentScriptVM* m_vm = (InstrumentScriptVM*) vm;
1927    
1928            // this might be set by passing 1 with the 2nd argument of built-in stop_wait() function
1929            if (m_vm->m_event->ignoreAllWaitCalls) return successResult();
1930    
1931            return CoreVMFunction_wait::exec(args);
1932        }
1933    
1934        // stop_wait() function
1935    
1936        InstrumentScriptVMFunction_stop_wait::InstrumentScriptVMFunction_stop_wait(InstrumentScriptVM* parent)
1937            : m_vm(parent)
1938        {    
1939        }
1940    
1941        VMFnResult* InstrumentScriptVMFunction_stop_wait::exec(VMFnArgs* args) {
1942            AbstractEngineChannel* pEngineChannel =
1943                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1944    
1945            const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
1946            if (!id) {
1947                wrnMsg("stop_wait(): callback ID for argument 1 may not be zero");
1948                return successResult();
1949            }
1950    
1951            RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
1952            if (!itCallback) return successResult(); // ignore if callback is i.e. not alive anymore
1953    
1954            const bool disableWaitForever =
1955                (args->argsCount() >= 2) ? (args->arg(1)->asInt()->evalInt() == 1) : false;
1956    
1957            pEngineChannel->ScheduleResumeOfScriptCallback(
1958                itCallback, m_vm->m_event->scheduleTime, disableWaitForever
1959            );
1960    
1961            return successResult();
1962        }
1963    
1964        // abort() function
1965    
1966        InstrumentScriptVMFunction_abort::InstrumentScriptVMFunction_abort(InstrumentScriptVM* parent)
1967            : m_vm(parent)
1968        {
1969        }
1970    
1971        VMFnResult* InstrumentScriptVMFunction_abort::exec(VMFnArgs* args) {
1972            const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
1973            if (!id) {
1974                wrnMsg("abort(): callback ID for argument 1 may not be zero");
1975                return successResult();
1976            }
1977    
1978            AbstractEngineChannel* pEngineChannel =
1979                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1980    
1981            RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
1982            if (!itCallback) return successResult(); // ignore if callback is i.e. not alive anymore
1983    
1984            itCallback->execCtx->signalAbort();
1985    
1986            return successResult();
1987        }
1988    
1989        // fork() function
1990    
1991        InstrumentScriptVMFunction_fork::InstrumentScriptVMFunction_fork(InstrumentScriptVM* parent)
1992            : m_vm(parent)
1993        {
1994        }
1995    
1996        VMFnResult* InstrumentScriptVMFunction_fork::exec(VMFnArgs* args) {
1997            // check if this is actually the parent going to fork, or rather one of
1998            // the children which is already forked
1999            if (m_vm->m_event->forkIndex != 0) { // this is the entry point for a child ...
2000                int forkResult = m_vm->m_event->forkIndex;
2001                // reset so that this child may i.e. also call fork() later on
2002                m_vm->m_event->forkIndex = 0;
2003                return successResult(forkResult);
2004            }
2005    
2006            // if we are here, then this is the parent, so we must fork this parent
2007    
2008            const int n =
2009                (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : 1;
2010            const bool bAutoAbort =
2011                (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : true;
2012    
2013            if (m_vm->m_event->countChildHandlers() + n > MAX_FORK_PER_SCRIPT_HANDLER) {
2014                wrnMsg("fork(): requested amount would exceed allowed limit per event handler");
2015                return successResult(-1);
2016            }
2017    
2018            AbstractEngineChannel* pEngineChannel =
2019                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2020    
2021            if (!pEngineChannel->hasFreeScriptCallbacks(n)) {
2022                wrnMsg("fork(): global limit of event handlers exceeded");
2023                return successResult(-1);
2024            }
2025    
2026            for (int iChild = 0; iChild < n; ++iChild) {
2027                RTList<ScriptEvent>::Iterator itChild =
2028                    pEngineChannel->forkScriptCallback(m_vm->m_event, bAutoAbort);
2029                if (!itChild) { // should never happen, otherwise its a bug ...
2030                    errMsg("fork(): internal error while allocating child");
2031                    return errorResult(-1); // terminate script
2032                }
2033                // since both parent, as well all child script execution instances
2034                // all land in this exect() method, the following is (more or less)
2035                // the only feature that lets us distinguish the parent and
2036                // respective children from each other in this exect() method
2037                itChild->forkIndex = iChild + 1;
2038            }
2039    
2040            return successResult(0);
2041        }
2042    
2043  } // namespace LinuxSampler  } // namespace LinuxSampler

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

  ViewVC Help
Powered by ViewVC