/[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 2953 by schoenebeck, Sat Jul 16 11:24:39 2016 UTC revision 3296 by schoenebeck, Wed Jun 28 09:45:56 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();
# Line 620  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 632  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 650  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 670  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 698  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 710  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 728  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              Event e = m_vm->m_event->cause; // copy to get fragment time for "now"              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
783              e.Init(); // clear IDs                  pNote->Override.Resonance = fResonance;
784              e.Type = Event::type_note_synth_param;              } else { // otherwise schedule resonance change ...
785              e.Param.NoteSynthParam.NoteID   = id.noteID();                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
786              e.Param.NoteSynthParam.Type     = Event::synth_param_resonance;                  e.Init(); // clear IDs
787              e.Param.NoteSynthParam.Delta    = fResonance;                  e.Type = Event::type_note_synth_param;
788              e.Param.NoteSynthParam.Relative = false;                  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              pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
794                }
795          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
796              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
797              for (int i = 0; i < ids->arraySize(); ++i) {              for (int i = 0; i < ids->arraySize(); ++i) {
# Line 748  namespace LinuxSampler { Line 801  namespace LinuxSampler {
801                  NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );                  NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
802                  if (!pNote) continue;                  if (!pNote) continue;
803    
804                  const float fResonance = float(resonance) / float(VM_FILTER_PAR_MAX_VALUE);                  // 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                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
818                  e.Init(); // clear IDs                  }
                 e.Type = Event::type_note_synth_param;  
                 e.Param.NoteSynthParam.NoteID   = id.noteID();  
                 e.Param.NoteSynthParam.Type     = Event::synth_param_resonance;  
                 e.Param.NoteSynthParam.Delta    = fResonance;  
                 e.Param.NoteSynthParam.Relative = false;  
   
                 pEngineChannel->ScheduleEventMicroSec(&e, 0);  
819              }              }
820          }          }
821    
# Line 776  namespace LinuxSampler { Line 833  namespace LinuxSampler {
833          if (iArg == 0)          if (iArg == 0)
834              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
835          else          else
836              return INT_EXPR;              return type == INT_EXPR;
837      }      }
838    
839      VMFnResult* InstrumentScriptVMFunction_change_attack::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_attack::exec(VMFnArgs* args) {
# Line 805  namespace LinuxSampler { Line 862  namespace LinuxSampler {
862              }              }
863    
864              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
865              if (!pNote) return successResult();                          if (!pNote) return successResult();
866    
867              Event e = m_vm->m_event->cause; // copy to get fragment time for "now"              // if change_attack() was called immediately after note was triggered
868              e.Init(); // clear IDs              // then immediately apply attack to Note object
869              e.Type = Event::type_note_synth_param;              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
870              e.Param.NoteSynthParam.NoteID   = id.noteID();                  pNote->Override.Attack = fAttack;
871              e.Param.NoteSynthParam.Type     = Event::synth_param_attack;              } else { // otherwise schedule attack change ...
872              e.Param.NoteSynthParam.Delta    = fAttack;                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
873              e.Param.NoteSynthParam.Relative = false;                  e.Init(); // clear IDs
874                    e.Type = Event::type_note_synth_param;
875                    e.Param.NoteSynthParam.NoteID   = id.noteID();
876                    e.Param.NoteSynthParam.Type     = Event::synth_param_attack;
877                    e.Param.NoteSynthParam.Delta    = fAttack;
878                    e.Param.NoteSynthParam.Relative = false;
879    
880              pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
881                }
882          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
883              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
884              for (int i = 0; i < ids->arraySize(); ++i) {              for (int i = 0; i < ids->arraySize(); ++i) {
# Line 825  namespace LinuxSampler { Line 888  namespace LinuxSampler {
888                  NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );                  NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
889                  if (!pNote) continue;                  if (!pNote) continue;
890    
891                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                  // if change_attack() was called immediately after note was triggered
892                  e.Init(); // clear IDs                  // then immediately apply attack to Note object
893                  e.Type = Event::type_note_synth_param;                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
894                  e.Param.NoteSynthParam.NoteID   = id.noteID();                      pNote->Override.Attack = fAttack;
895                  e.Param.NoteSynthParam.Type     = Event::synth_param_attack;                  } else { // otherwise schedule attack change ...
896                  e.Param.NoteSynthParam.Delta    = fAttack;                      Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
897                  e.Param.NoteSynthParam.Relative = false;                      e.Init(); // clear IDs
898                        e.Type = Event::type_note_synth_param;
899                        e.Param.NoteSynthParam.NoteID   = id.noteID();
900                        e.Param.NoteSynthParam.Type     = Event::synth_param_attack;
901                        e.Param.NoteSynthParam.Delta    = fAttack;
902                        e.Param.NoteSynthParam.Relative = false;
903    
904                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
905                    }
906              }              }
907          }          }
908    
# Line 851  namespace LinuxSampler { Line 920  namespace LinuxSampler {
920          if (iArg == 0)          if (iArg == 0)
921              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
922          else          else
923              return INT_EXPR;              return type == INT_EXPR;
924      }      }
925    
926      VMFnResult* InstrumentScriptVMFunction_change_decay::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_decay::exec(VMFnArgs* args) {
# Line 880  namespace LinuxSampler { Line 949  namespace LinuxSampler {
949              }              }
950    
951              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
952              if (!pNote) return successResult();                          if (!pNote) return successResult();
953    
954              Event e = m_vm->m_event->cause; // copy to get fragment time for "now"              // if change_decay() was called immediately after note was triggered
955              e.Init(); // clear IDs              // then immediately apply decay to Note object
956              e.Type = Event::type_note_synth_param;              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
957              e.Param.NoteSynthParam.NoteID   = id.noteID();                  pNote->Override.Decay = fDecay;
958              e.Param.NoteSynthParam.Type     = Event::synth_param_decay;              } else { // otherwise schedule decay change ...
959              e.Param.NoteSynthParam.Delta    = fDecay;                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
960              e.Param.NoteSynthParam.Relative = false;                  e.Init(); // clear IDs
961                    e.Type = Event::type_note_synth_param;
962                    e.Param.NoteSynthParam.NoteID   = id.noteID();
963                    e.Param.NoteSynthParam.Type     = Event::synth_param_decay;
964                    e.Param.NoteSynthParam.Delta    = fDecay;
965                    e.Param.NoteSynthParam.Relative = false;
966    
967              pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
968                }
969          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
970              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
971              for (int i = 0; i < ids->arraySize(); ++i) {              for (int i = 0; i < ids->arraySize(); ++i) {
# Line 900  namespace LinuxSampler { Line 975  namespace LinuxSampler {
975                  NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );                  NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
976                  if (!pNote) continue;                  if (!pNote) continue;
977    
978                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                  // if change_decay() was called immediately after note was triggered
979                  e.Init(); // clear IDs                  // then immediately apply decay to Note object
980                  e.Type = Event::type_note_synth_param;                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
981                  e.Param.NoteSynthParam.NoteID   = id.noteID();                      pNote->Override.Decay = fDecay;
982                  e.Param.NoteSynthParam.Type     = Event::synth_param_decay;                  } else { // otherwise schedule decay change ...
983                  e.Param.NoteSynthParam.Delta    = fDecay;                      Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
984                  e.Param.NoteSynthParam.Relative = false;                      e.Init(); // clear IDs
985                        e.Type = Event::type_note_synth_param;
986                        e.Param.NoteSynthParam.NoteID   = id.noteID();
987                        e.Param.NoteSynthParam.Type     = Event::synth_param_decay;
988                        e.Param.NoteSynthParam.Delta    = fDecay;
989                        e.Param.NoteSynthParam.Relative = false;
990    
991                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
992                    }
993              }              }
994          }          }
995    
# Line 926  namespace LinuxSampler { Line 1007  namespace LinuxSampler {
1007          if (iArg == 0)          if (iArg == 0)
1008              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
1009          else          else
1010              return INT_EXPR;              return type == INT_EXPR;
1011      }      }
1012    
1013      VMFnResult* InstrumentScriptVMFunction_change_release::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_release::exec(VMFnArgs* args) {
# Line 955  namespace LinuxSampler { Line 1036  namespace LinuxSampler {
1036              }              }
1037    
1038              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1039              if (!pNote) return successResult();                          if (!pNote) return successResult();
1040    
1041              Event e = m_vm->m_event->cause; // copy to get fragment time for "now"              // if change_release() was called immediately after note was triggered
1042              e.Init(); // clear IDs              // then immediately apply relase to Note object
1043              e.Type = Event::type_note_synth_param;              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1044              e.Param.NoteSynthParam.NoteID   = id.noteID();                  pNote->Override.Release = fRelease;
1045              e.Param.NoteSynthParam.Type     = Event::synth_param_release;              } else { // otherwise schedule release change ...
1046              e.Param.NoteSynthParam.Delta    = fRelease;                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1047              e.Param.NoteSynthParam.Relative = false;                  e.Init(); // clear IDs
1048                    e.Type = Event::type_note_synth_param;
1049                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1050                    e.Param.NoteSynthParam.Type     = Event::synth_param_release;
1051                    e.Param.NoteSynthParam.Delta    = fRelease;
1052                    e.Param.NoteSynthParam.Relative = false;
1053    
1054              pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
1055                }
1056          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1057              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1058              for (int i = 0; i < ids->arraySize(); ++i) {              for (int i = 0; i < ids->arraySize(); ++i) {
# Line 975  namespace LinuxSampler { Line 1062  namespace LinuxSampler {
1062                  NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );                  NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1063                  if (!pNote) continue;                  if (!pNote) continue;
1064    
1065                    // if change_release() was called immediately after note was triggered
1066                    // then immediately apply relase to Note object
1067                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1068                        pNote->Override.Release = fRelease;
1069                    } else { // otherwise schedule release change ...
1070                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1071                        e.Init(); // clear IDs
1072                        e.Type = Event::type_note_synth_param;
1073                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1074                        e.Param.NoteSynthParam.Type     = Event::synth_param_release;
1075                        e.Param.NoteSynthParam.Delta    = fRelease;
1076                        e.Param.NoteSynthParam.Relative = false;
1077    
1078                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
1079                    }
1080                }
1081            }
1082    
1083            return successResult();
1084        }
1085    
1086        // template for change_*() functions
1087    
1088        bool VMChangeSynthParamFunction::acceptsArgType(int iArg, ExprType_t type) const {
1089            if (iArg == 0)
1090                return type == INT_EXPR || type == INT_ARR_EXPR;
1091            else
1092                return type == INT_EXPR;
1093        }
1094    
1095        // Arbitrarily chosen constant value symbolizing "no limit".
1096        #define NO_LIMIT 1315916909
1097    
1098        template<float NoteBase::_Override::*T_noteParam, int T_synthParam,
1099                 bool T_isNormalizedParam, int T_maxValue, int T_minValue>
1100        VMFnResult* VMChangeSynthParamFunction::execTemplate(VMFnArgs* args, const char* functionName) {
1101            int value = args->arg(1)->asInt()->evalInt();
1102            if (T_maxValue != NO_LIMIT && value > T_maxValue) {
1103                wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValue));
1104                value = T_maxValue;
1105            } else if (T_minValue != NO_LIMIT && value < T_minValue) {
1106                if (T_minValue == 0)
1107                    wrnMsg(String(functionName) + "(): argument 2 may not be negative");
1108                else
1109                    wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValue));
1110                value = T_minValue;
1111            }
1112            const float fValue = (T_isNormalizedParam) ?
1113                float(value) / float(T_maxValue) : // convert to 0.0 .. 1.0 value range
1114                float(value) / 1000000.f; // assuming microseconds here, convert to seconds
1115    
1116            AbstractEngineChannel* pEngineChannel =
1117                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1118    
1119            if (args->arg(0)->exprType() == INT_EXPR) {
1120                const ScriptID id = args->arg(0)->asInt()->evalInt();
1121                if (!id) {
1122                    wrnMsg(String(functionName) + "(): note ID for argument 1 may not be zero");
1123                    return successResult();
1124                }
1125                if (!id.isNoteID()) {
1126                    wrnMsg(String(functionName) + "(): argument 1 is not a note ID");
1127                    return successResult();
1128                }
1129    
1130                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1131                if (!pNote) return successResult();
1132    
1133                // if this change_*() script function was called immediately after
1134                // note was triggered then immediately apply the synth parameter
1135                // change to Note object
1136                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1137                    pNote->Override.*T_noteParam = fValue;
1138                } else { // otherwise schedule this synth parameter change ...
1139                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1140                  e.Init(); // clear IDs                  e.Init(); // clear IDs
1141                  e.Type = Event::type_note_synth_param;                  e.Type = Event::type_note_synth_param;
1142                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
1143                  e.Param.NoteSynthParam.Type     = Event::synth_param_release;                  e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1144                  e.Param.NoteSynthParam.Delta    = fRelease;                  e.Param.NoteSynthParam.Delta    = fValue;
1145                    e.Param.NoteSynthParam.Relative = false;
1146    
1147                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
1148                }
1149            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1150                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1151                for (int i = 0; i < ids->arraySize(); ++i) {
1152                    const ScriptID id = ids->evalIntElement(i);
1153                    if (!id || !id.isNoteID()) continue;
1154    
1155                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1156                    if (!pNote) continue;
1157    
1158                    // if this change_*() script function was called immediately after
1159                    // note was triggered then immediately apply the synth parameter
1160                    // change to Note object
1161                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1162                        pNote->Override.*T_noteParam = fValue;
1163                    } else { // otherwise schedule this synth parameter change ...
1164                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1165                        e.Init(); // clear IDs
1166                        e.Type = Event::type_note_synth_param;
1167                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1168                        e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1169                        e.Param.NoteSynthParam.Delta    = fValue;
1170                        e.Param.NoteSynthParam.Relative = false;
1171    
1172                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
1173                    }
1174                }
1175            }
1176    
1177            return successResult();
1178        }
1179    
1180        // change_amp_lfo_depth() function
1181    
1182        VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_depth::exec(VMFnArgs* args) {
1183            return VMChangeSynthParamFunction::execTemplate<
1184                        &NoteBase::_Override::AmpLFODepth,
1185                        Event::synth_param_amp_lfo_depth,
1186                        true, 1000000, 0>( args, "change_amp_lfo_depth" );
1187        }
1188    
1189        // change_amp_lfo_freq() function
1190    
1191        VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_freq::exec(VMFnArgs* args) {
1192            return VMChangeSynthParamFunction::execTemplate<
1193                        &NoteBase::_Override::AmpLFOFreq,
1194                        Event::synth_param_amp_lfo_freq,
1195                        true, 1000000, 0>( args, "change_amp_lfo_freq" );
1196        }
1197    
1198        // change_pitch_lfo_depth() function
1199    
1200        VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_depth::exec(VMFnArgs* args) {
1201            return VMChangeSynthParamFunction::execTemplate<
1202                        &NoteBase::_Override::PitchLFODepth,
1203                        Event::synth_param_pitch_lfo_depth,
1204                        true, 1000000, 0>( args, "change_pitch_lfo_depth" );
1205        }
1206    
1207        // change_pitch_lfo_freq() function
1208    
1209        VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_freq::exec(VMFnArgs* args) {
1210            return VMChangeSynthParamFunction::execTemplate<
1211                        &NoteBase::_Override::PitchLFOFreq,
1212                        Event::synth_param_pitch_lfo_freq,
1213                        true, 1000000, 0>( args, "change_pitch_lfo_freq" );
1214        }
1215    
1216        // change_vol_time() function
1217    
1218        VMFnResult* InstrumentScriptVMFunction_change_vol_time::exec(VMFnArgs* args) {
1219            return VMChangeSynthParamFunction::execTemplate<
1220                        &NoteBase::_Override::VolumeTime,
1221                        Event::synth_param_volume_time,
1222                        false, NO_LIMIT, 0>( args, "change_vol_time" );
1223        }
1224    
1225        // change_tune_time() function
1226    
1227        VMFnResult* InstrumentScriptVMFunction_change_tune_time::exec(VMFnArgs* args) {
1228            return VMChangeSynthParamFunction::execTemplate<
1229                        &NoteBase::_Override::PitchTime,
1230                        Event::synth_param_pitch_time,
1231                        false, NO_LIMIT, 0>( args, "change_tune_time" );
1232        }
1233    
1234        // template for change_*_curve() functions
1235    
1236        bool VMChangeFadeCurveFunction::acceptsArgType(int iArg, ExprType_t type) const {
1237            if (iArg == 0)
1238                return type == INT_EXPR || type == INT_ARR_EXPR;
1239            else
1240                return type == INT_EXPR;
1241        }
1242    
1243        template<fade_curve_t NoteBase::_Override::*T_noteParam, int T_synthParam>
1244        VMFnResult* VMChangeFadeCurveFunction::execTemplate(VMFnArgs* args, const char* functionName) {
1245            int value = args->arg(1)->asInt()->evalInt();
1246            switch (value) {
1247                case FADE_CURVE_LINEAR:
1248                case FADE_CURVE_EASE_IN_EASE_OUT:
1249                    break;
1250                default:
1251                    wrnMsg(String(functionName) + "(): invalid curve type passed as argument 2");
1252                    return successResult();
1253            }
1254    
1255            AbstractEngineChannel* pEngineChannel =
1256                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1257    
1258            if (args->arg(0)->exprType() == INT_EXPR) {
1259                const ScriptID id = args->arg(0)->asInt()->evalInt();
1260                if (!id) {
1261                    wrnMsg(String(functionName) + "(): note ID for argument 1 may not be zero");
1262                    return successResult();
1263                }
1264                if (!id.isNoteID()) {
1265                    wrnMsg(String(functionName) + "(): argument 1 is not a note ID");
1266                    return successResult();
1267                }
1268    
1269                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1270                if (!pNote) return successResult();
1271    
1272                // if this change_*_curve() script function was called immediately after
1273                // note was triggered then immediately apply the synth parameter
1274                // change to Note object
1275                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1276                    pNote->Override.*T_noteParam = (fade_curve_t) value;
1277                } else { // otherwise schedule this synth parameter change ...
1278                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1279                    e.Init(); // clear IDs
1280                    e.Type = Event::type_note_synth_param;
1281                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1282                    e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1283                    e.Param.NoteSynthParam.Delta    = value;
1284                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Relative = false;
1285    
1286                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
1287              }              }
1288            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1289                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1290                for (int i = 0; i < ids->arraySize(); ++i) {
1291                    const ScriptID id = ids->evalIntElement(i);
1292                    if (!id || !id.isNoteID()) continue;
1293    
1294                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1295                    if (!pNote) continue;
1296    
1297                    // if this change_*_curve() script function was called immediately after
1298                    // note was triggered then immediately apply the synth parameter
1299                    // change to Note object
1300                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1301                        pNote->Override.*T_noteParam = (fade_curve_t) value;
1302                    } else { // otherwise schedule this synth parameter change ...
1303                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1304                        e.Init(); // clear IDs
1305                        e.Type = Event::type_note_synth_param;
1306                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1307                        e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1308                        e.Param.NoteSynthParam.Delta    = value;
1309                        e.Param.NoteSynthParam.Relative = false;
1310    
1311                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
1312                    }
1313                }
1314          }          }
1315    
1316          return successResult();          return successResult();
1317      }      }
1318    
1319        // change_vol_curve() function
1320    
1321        VMFnResult* InstrumentScriptVMFunction_change_vol_curve::exec(VMFnArgs* args) {
1322            return VMChangeFadeCurveFunction::execTemplate<
1323                        &NoteBase::_Override::VolumeCurve,
1324                        Event::synth_param_volume_curve>( args, "change_vol_curve" );
1325        }
1326    
1327        // change_tune_curve() function
1328    
1329        VMFnResult* InstrumentScriptVMFunction_change_tune_curve::exec(VMFnArgs* args) {
1330            return VMChangeFadeCurveFunction::execTemplate<
1331                        &NoteBase::_Override::PitchCurve,
1332                        Event::synth_param_pitch_curve>( args, "change_tune_curve" );
1333        }
1334    
1335        // fade_in() function
1336    
1337        InstrumentScriptVMFunction_fade_in::InstrumentScriptVMFunction_fade_in(InstrumentScriptVM* parent)
1338            : m_vm(parent)
1339        {
1340        }
1341    
1342        bool InstrumentScriptVMFunction_fade_in::acceptsArgType(int iArg, ExprType_t type) const {
1343            if (iArg == 0)
1344                return type == INT_EXPR || type == INT_ARR_EXPR;
1345            else
1346                return type == INT_EXPR;
1347        }
1348    
1349        VMFnResult* InstrumentScriptVMFunction_fade_in::exec(VMFnArgs* args) {
1350            int duration = args->arg(1)->asInt()->evalInt();
1351            if (duration < 0) {
1352                wrnMsg("fade_in(): argument 2 may not be negative");
1353                duration = 0;
1354            }
1355            const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
1356    
1357            AbstractEngineChannel* pEngineChannel =
1358                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1359    
1360            if (args->arg(0)->exprType() == INT_EXPR) {
1361                const ScriptID id = args->arg(0)->asInt()->evalInt();
1362                if (!id) {
1363                    wrnMsg("fade_in(): note ID for argument 1 may not be zero");
1364                    return successResult();
1365                }
1366                if (!id.isNoteID()) {
1367                    wrnMsg("fade_in(): argument 1 is not a note ID");
1368                    return successResult();
1369                }
1370    
1371                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1372                if (!pNote) return successResult();
1373    
1374                // if fade_in() was called immediately after note was triggered
1375                // then immediately apply a start volume of zero to Note object,
1376                // as well as the fade in duration
1377                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1378                    pNote->Override.Volume = 0.f;
1379                    pNote->Override.VolumeTime = fDuration;
1380                } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
1381                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1382                    e.Init(); // clear IDs
1383                    e.Type = Event::type_note_synth_param;
1384                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1385                    e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1386                    e.Param.NoteSynthParam.Delta    = fDuration;
1387                    e.Param.NoteSynthParam.Relative = false;
1388    
1389                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
1390                }
1391                // and finally schedule a "volume" change, simply one time slice
1392                // ahead, with the final fade in volume (1.0)
1393                {
1394                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1395                    e.Init(); // clear IDs
1396                    e.Type = Event::type_note_synth_param;
1397                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1398                    e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1399                    e.Param.NoteSynthParam.Delta    = 1.f;
1400                    e.Param.NoteSynthParam.Relative = false;
1401    
1402                    // scheduling with 0 delay would also work here, but +1 is more
1403                    // safe regarding potential future implementation changes of the
1404                    // scheduler (see API comments of RTAVLTree::insert())
1405                    pEngineChannel->ScheduleEventMicroSec(&e, 1);
1406                }
1407            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1408                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1409                for (int i = 0; i < ids->arraySize(); ++i) {
1410                    const ScriptID id = ids->evalIntElement(i);
1411                    if (!id || !id.isNoteID()) continue;
1412    
1413                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1414                    if (!pNote) continue;
1415    
1416                    // if fade_in() was called immediately after note was triggered
1417                    // then immediately apply a start volume of zero to Note object,
1418                    // as well as the fade in duration
1419                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1420                        pNote->Override.Volume = 0.f;
1421                        pNote->Override.VolumeTime = fDuration;
1422                    } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
1423                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1424                        e.Init(); // clear IDs
1425                        e.Type = Event::type_note_synth_param;
1426                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1427                        e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1428                        e.Param.NoteSynthParam.Delta    = fDuration;
1429                        e.Param.NoteSynthParam.Relative = false;
1430    
1431                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
1432                    }
1433                    // and finally schedule a "volume" change, simply one time slice
1434                    // ahead, with the final fade in volume (1.0)
1435                    {
1436                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1437                        e.Init(); // clear IDs
1438                        e.Type = Event::type_note_synth_param;
1439                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1440                        e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1441                        e.Param.NoteSynthParam.Delta    = 1.f;
1442                        e.Param.NoteSynthParam.Relative = false;
1443    
1444                        // scheduling with 0 delay would also work here, but +1 is more
1445                        // safe regarding potential future implementation changes of the
1446                        // scheduler (see API comments of RTAVLTree::insert())
1447                        pEngineChannel->ScheduleEventMicroSec(&e, 1);
1448                    }
1449                }
1450            }
1451    
1452            return successResult();
1453        }
1454    
1455        // fade_out() function
1456    
1457        InstrumentScriptVMFunction_fade_out::InstrumentScriptVMFunction_fade_out(InstrumentScriptVM* parent)
1458            : m_vm(parent)
1459        {
1460        }
1461    
1462        bool InstrumentScriptVMFunction_fade_out::acceptsArgType(int iArg, ExprType_t type) const {
1463            if (iArg == 0)
1464                return type == INT_EXPR || type == INT_ARR_EXPR;
1465            else
1466                return type == INT_EXPR;
1467        }
1468    
1469        VMFnResult* InstrumentScriptVMFunction_fade_out::exec(VMFnArgs* args) {
1470            int duration = args->arg(1)->asInt()->evalInt();
1471            if (duration < 0) {
1472                wrnMsg("fade_out(): argument 2 may not be negative");
1473                duration = 0;
1474            }
1475            const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
1476    
1477            bool stop = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : true;
1478    
1479            AbstractEngineChannel* pEngineChannel =
1480                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1481    
1482            if (args->arg(0)->exprType() == INT_EXPR) {
1483                const ScriptID id = args->arg(0)->asInt()->evalInt();
1484                if (!id) {
1485                    wrnMsg("fade_out(): note ID for argument 1 may not be zero");
1486                    return successResult();
1487                }
1488                if (!id.isNoteID()) {
1489                    wrnMsg("fade_out(): argument 1 is not a note ID");
1490                    return successResult();
1491                }
1492    
1493                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1494                if (!pNote) return successResult();
1495    
1496                // if fade_out() was called immediately after note was triggered
1497                // then immediately apply fade out duration to Note object
1498                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1499                    pNote->Override.VolumeTime = fDuration;
1500                } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
1501                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1502                    e.Init(); // clear IDs
1503                    e.Type = Event::type_note_synth_param;
1504                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1505                    e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1506                    e.Param.NoteSynthParam.Delta    = fDuration;
1507                    e.Param.NoteSynthParam.Relative = false;
1508    
1509                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
1510                }
1511                // now schedule a "volume" change, simply one time slice ahead, with
1512                // the final fade out volume (0.0)
1513                {
1514                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1515                    e.Init(); // clear IDs
1516                    e.Type = Event::type_note_synth_param;
1517                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1518                    e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1519                    e.Param.NoteSynthParam.Delta    = 0.f;
1520                    e.Param.NoteSynthParam.Relative = false;
1521    
1522                    // scheduling with 0 delay would also work here, but +1 is more
1523                    // safe regarding potential future implementation changes of the
1524                    // scheduler (see API comments of RTAVLTree::insert())
1525                    pEngineChannel->ScheduleEventMicroSec(&e, 1);
1526                }
1527                // and finally if stopping the note was requested after the fade out
1528                // completed, then schedule to kill the voice after the requested
1529                // duration
1530                if (stop) {
1531                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1532                    e.Init(); // clear IDs
1533                    e.Type = Event::type_kill_note;
1534                    e.Param.Note.ID = id.noteID();
1535                    e.Param.Note.Key = pNote->hostKey;
1536    
1537                    pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
1538                }
1539            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1540                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1541                for (int i = 0; i < ids->arraySize(); ++i) {
1542                    const ScriptID id = ids->evalIntElement(i);
1543                    if (!id || !id.isNoteID()) continue;
1544    
1545                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1546                    if (!pNote) continue;
1547    
1548                    // if fade_out() was called immediately after note was triggered
1549                    // then immediately apply fade out duration to Note object
1550                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1551                        pNote->Override.VolumeTime = fDuration;
1552                    } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
1553                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1554                        e.Init(); // clear IDs
1555                        e.Type = Event::type_note_synth_param;
1556                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1557                        e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1558                        e.Param.NoteSynthParam.Delta    = fDuration;
1559                        e.Param.NoteSynthParam.Relative = false;
1560    
1561                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
1562                    }
1563                    // now schedule a "volume" change, simply one time slice ahead, with
1564                    // the final fade out volume (0.0)
1565                    {
1566                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1567                        e.Init(); // clear IDs
1568                        e.Type = Event::type_note_synth_param;
1569                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1570                        e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1571                        e.Param.NoteSynthParam.Delta    = 0.f;
1572                        e.Param.NoteSynthParam.Relative = false;
1573    
1574                        // scheduling with 0 delay would also work here, but +1 is more
1575                        // safe regarding potential future implementation changes of the
1576                        // scheduler (see API comments of RTAVLTree::insert())
1577                        pEngineChannel->ScheduleEventMicroSec(&e, 1);
1578                    }
1579                    // and finally if stopping the note was requested after the fade out
1580                    // completed, then schedule to kill the voice after the requested
1581                    // duration
1582                    if (stop) {
1583                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1584                        e.Init(); // clear IDs
1585                        e.Type = Event::type_kill_note;
1586                        e.Param.Note.ID = id.noteID();
1587                        e.Param.Note.Key = pNote->hostKey;
1588                        
1589                        pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
1590                    }
1591                }
1592            }
1593    
1594            return successResult();
1595        }
1596    
1597        // get_event_par() function
1598    
1599        InstrumentScriptVMFunction_get_event_par::InstrumentScriptVMFunction_get_event_par(InstrumentScriptVM* parent)
1600            : m_vm(parent)
1601        {
1602        }
1603    
1604        VMFnResult* InstrumentScriptVMFunction_get_event_par::exec(VMFnArgs* args) {
1605            AbstractEngineChannel* pEngineChannel =
1606                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1607    
1608            const ScriptID id = args->arg(0)->asInt()->evalInt();
1609            if (!id) {
1610                wrnMsg("get_event_par(): note ID for argument 1 may not be zero");
1611                return successResult(0);
1612            }
1613            if (!id.isNoteID()) {
1614                wrnMsg("get_event_par(): argument 1 is not a note ID");
1615                return successResult(0);
1616            }
1617    
1618            NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1619            if (!pNote) {
1620                wrnMsg("get_event_par(): no note alive with that note ID of argument 1");
1621                return successResult(0);
1622            }
1623    
1624            const int parameter = args->arg(1)->asInt()->evalInt();
1625            switch (parameter) {
1626                case EVENT_PAR_NOTE:
1627                    return successResult(pNote->cause.Param.Note.Key);
1628                case EVENT_PAR_VELOCITY:
1629                    return successResult(pNote->cause.Param.Note.Velocity);
1630                case EVENT_PAR_VOLUME:
1631                    return successResult(
1632                        RTMath::LinRatioToDecibel(pNote->Override.Volume) * 1000.f
1633                    );
1634                case EVENT_PAR_TUNE:
1635                    return successResult(
1636                         RTMath::FreqRatioToCents(pNote->Override.Pitch) * 1000.f
1637                    );
1638                case EVENT_PAR_0:
1639                    return successResult(pNote->userPar[0]);
1640                case EVENT_PAR_1:
1641                    return successResult(pNote->userPar[1]);
1642                case EVENT_PAR_2:
1643                    return successResult(pNote->userPar[2]);
1644                case EVENT_PAR_3:
1645                    return successResult(pNote->userPar[3]);
1646            }
1647    
1648            wrnMsg("get_event_par(): argument 2 is an invalid event parameter");
1649            return successResult(0);
1650        }
1651    
1652        // set_event_par() function
1653    
1654        InstrumentScriptVMFunction_set_event_par::InstrumentScriptVMFunction_set_event_par(InstrumentScriptVM* parent)
1655            : m_vm(parent)
1656        {
1657        }
1658    
1659        VMFnResult* InstrumentScriptVMFunction_set_event_par::exec(VMFnArgs* args) {
1660            AbstractEngineChannel* pEngineChannel =
1661                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1662    
1663            const ScriptID id = args->arg(0)->asInt()->evalInt();
1664            if (!id) {
1665                wrnMsg("set_event_par(): note ID for argument 1 may not be zero");
1666                return successResult();
1667            }
1668            if (!id.isNoteID()) {
1669                wrnMsg("set_event_par(): argument 1 is not a note ID");
1670                return successResult();
1671            }
1672    
1673            NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1674            if (!pNote) return successResult();
1675    
1676            const int parameter = args->arg(1)->asInt()->evalInt();
1677            const int value     = args->arg(2)->asInt()->evalInt();
1678    
1679            switch (parameter) {
1680                case EVENT_PAR_NOTE:
1681                    if (value < 0 || value > 127) {
1682                        wrnMsg("set_event_par(): note number of argument 3 is out of range");
1683                        return successResult();
1684                    }
1685                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1686                        pNote->cause.Param.Note.Key = value;
1687                        m_vm->m_event->cause.Param.Note.Key = value;
1688                    } else {
1689                        wrnMsg("set_event_par(): note number can only be changed when note is new");
1690                    }
1691                    return successResult();
1692                case EVENT_PAR_VELOCITY:
1693                    if (value < 0 || value > 127) {
1694                        wrnMsg("set_event_par(): velocity of argument 3 is out of range");
1695                        return successResult();
1696                    }
1697                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1698                        pNote->cause.Param.Note.Velocity = value;
1699                        m_vm->m_event->cause.Param.Note.Velocity = value;
1700                    } else {
1701                        wrnMsg("set_event_par(): velocity can only be changed when note is new");
1702                    }
1703                    return successResult();
1704                case EVENT_PAR_VOLUME:
1705                    wrnMsg("set_event_par(): changing volume by this function is currently not supported, use change_vol() instead");
1706                    return successResult();
1707                case EVENT_PAR_TUNE:
1708                    wrnMsg("set_event_par(): changing tune by this function is currently not supported, use change_tune() instead");
1709                    return successResult();
1710                case EVENT_PAR_0:
1711                    pNote->userPar[0] = value;
1712                    return successResult();
1713                case EVENT_PAR_1:
1714                    pNote->userPar[1] = value;
1715                    return successResult();
1716                case EVENT_PAR_2:
1717                    pNote->userPar[2] = value;
1718                    return successResult();
1719                case EVENT_PAR_3:
1720                    pNote->userPar[3] = value;
1721                    return successResult();
1722            }
1723    
1724            wrnMsg("set_event_par(): argument 2 is an invalid event parameter");
1725            return successResult();
1726        }
1727    
1728        // change_note() function
1729    
1730        InstrumentScriptVMFunction_change_note::InstrumentScriptVMFunction_change_note(InstrumentScriptVM* parent)
1731        : m_vm(parent)
1732        {
1733        }
1734    
1735        VMFnResult* InstrumentScriptVMFunction_change_note::exec(VMFnArgs* args) {
1736            AbstractEngineChannel* pEngineChannel =
1737                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1738    
1739            const ScriptID id = args->arg(0)->asInt()->evalInt();
1740            if (!id) {
1741                wrnMsg("change_note(): note ID for argument 1 may not be zero");
1742                return successResult();
1743            }
1744            if (!id.isNoteID()) {
1745                wrnMsg("change_note(): argument 1 is not a note ID");
1746                return successResult();
1747            }
1748    
1749            NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1750            if (!pNote) return successResult();
1751    
1752            const int value = args->arg(1)->asInt()->evalInt();
1753            if (value < 0 || value > 127) {
1754                wrnMsg("change_note(): note number of argument 2 is out of range");
1755                return successResult();
1756            }
1757    
1758            if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1759                pNote->cause.Param.Note.Key = value;
1760                m_vm->m_event->cause.Param.Note.Key = value;
1761            } else {
1762                wrnMsg("change_note(): note number can only be changed when note is new");
1763            }
1764    
1765            return successResult();
1766        }
1767    
1768        // change_velo() function
1769    
1770        InstrumentScriptVMFunction_change_velo::InstrumentScriptVMFunction_change_velo(InstrumentScriptVM* parent)
1771        : m_vm(parent)
1772        {
1773        }
1774    
1775        VMFnResult* InstrumentScriptVMFunction_change_velo::exec(VMFnArgs* args) {
1776            AbstractEngineChannel* pEngineChannel =
1777                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1778    
1779            const ScriptID id = args->arg(0)->asInt()->evalInt();
1780            if (!id) {
1781                wrnMsg("change_velo(): note ID for argument 1 may not be zero");
1782                return successResult();
1783            }
1784            if (!id.isNoteID()) {
1785                wrnMsg("change_velo(): argument 1 is not a note ID");
1786                return successResult();
1787            }
1788    
1789            NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1790            if (!pNote) return successResult();
1791    
1792            const int value = args->arg(1)->asInt()->evalInt();
1793            if (value < 0 || value > 127) {
1794                wrnMsg("change_velo(): velocity of argument 2 is out of range");
1795                return successResult();
1796            }
1797    
1798            if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1799                pNote->cause.Param.Note.Velocity = value;
1800                m_vm->m_event->cause.Param.Note.Velocity = value;
1801            } else {
1802                wrnMsg("change_velo(): velocity can only be changed when note is new");
1803            }
1804    
1805            return successResult();
1806        }
1807    
1808        // change_play_pos() function
1809    
1810        InstrumentScriptVMFunction_change_play_pos::InstrumentScriptVMFunction_change_play_pos(InstrumentScriptVM* parent)
1811        : m_vm(parent)
1812        {
1813        }
1814    
1815        VMFnResult* InstrumentScriptVMFunction_change_play_pos::exec(VMFnArgs* args) {
1816            const ScriptID id = args->arg(0)->asInt()->evalInt();
1817            if (!id) {
1818                wrnMsg("change_play_pos(): note ID for argument 1 may not be zero");
1819                return successResult();
1820            }
1821            if (!id.isNoteID()) {
1822                wrnMsg("change_play_pos(): argument 1 is not a note ID");
1823                return successResult();
1824            }
1825    
1826            const int pos = args->arg(1)->asInt()->evalInt();
1827            if (pos < 0) {
1828                wrnMsg("change_play_pos(): playback position of argument 2 may not be negative");
1829                return successResult();
1830            }
1831    
1832            AbstractEngineChannel* pEngineChannel =
1833                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1834    
1835            NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1836            if (!pNote) return successResult();
1837    
1838            pNote->Override.SampleOffset = pos;
1839    
1840            return successResult();
1841        }
1842    
1843      // event_status() function      // event_status() function
1844    
1845      InstrumentScriptVMFunction_event_status::InstrumentScriptVMFunction_event_status(InstrumentScriptVM* parent)      InstrumentScriptVMFunction_event_status::InstrumentScriptVMFunction_event_status(InstrumentScriptVM* parent)
# Line 1015  namespace LinuxSampler { Line 1865  namespace LinuxSampler {
1865          return successResult(pNote ? EVENT_STATUS_NOTE_QUEUE : EVENT_STATUS_INACTIVE);          return successResult(pNote ? EVENT_STATUS_NOTE_QUEUE : EVENT_STATUS_INACTIVE);
1866      }      }
1867    
1868        // callback_status() function
1869    
1870        InstrumentScriptVMFunction_callback_status::InstrumentScriptVMFunction_callback_status(InstrumentScriptVM* parent)
1871            : m_vm(parent)
1872        {
1873        }
1874    
1875        VMFnResult* InstrumentScriptVMFunction_callback_status::exec(VMFnArgs* args) {
1876            const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
1877            if (!id) {
1878                wrnMsg("callback_status(): callback ID for argument 1 may not be zero");
1879                return successResult();
1880            }
1881    
1882            AbstractEngineChannel* pEngineChannel =
1883                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1884    
1885            RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
1886            if (!itCallback)
1887                return successResult(CALLBACK_STATUS_TERMINATED);
1888    
1889            return successResult(
1890                (m_vm->m_event->execCtx == itCallback->execCtx) ?
1891                    CALLBACK_STATUS_RUNNING : CALLBACK_STATUS_QUEUE
1892            );
1893        }
1894    
1895      // wait() function (overrides core wait() implementation)      // wait() function (overrides core wait() implementation)
1896    
1897      InstrumentScriptVMFunction_wait::InstrumentScriptVMFunction_wait(InstrumentScriptVM* parent)      InstrumentScriptVMFunction_wait::InstrumentScriptVMFunction_wait(InstrumentScriptVM* parent)
# Line 1055  namespace LinuxSampler { Line 1932  namespace LinuxSampler {
1932              (args->argsCount() >= 2) ? (args->arg(1)->asInt()->evalInt() == 1) : false;              (args->argsCount() >= 2) ? (args->arg(1)->asInt()->evalInt() == 1) : false;
1933    
1934          pEngineChannel->ScheduleResumeOfScriptCallback(          pEngineChannel->ScheduleResumeOfScriptCallback(
1935              itCallback, m_vm->m_event->cause.SchedTime(), disableWaitForever              itCallback, m_vm->m_event->scheduleTime, disableWaitForever
1936          );          );
1937    
1938          return successResult();          return successResult();
1939      }      }
1940    
1941        // abort() function
1942    
1943        InstrumentScriptVMFunction_abort::InstrumentScriptVMFunction_abort(InstrumentScriptVM* parent)
1944            : m_vm(parent)
1945        {
1946        }
1947    
1948        VMFnResult* InstrumentScriptVMFunction_abort::exec(VMFnArgs* args) {
1949            const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
1950            if (!id) {
1951                wrnMsg("abort(): callback ID for argument 1 may not be zero");
1952                return successResult();
1953            }
1954    
1955            AbstractEngineChannel* pEngineChannel =
1956                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1957    
1958            RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
1959            if (!itCallback) return successResult(); // ignore if callback is i.e. not alive anymore
1960    
1961            itCallback->execCtx->signalAbort();
1962    
1963            return successResult();
1964        }
1965    
1966        // fork() function
1967    
1968        InstrumentScriptVMFunction_fork::InstrumentScriptVMFunction_fork(InstrumentScriptVM* parent)
1969            : m_vm(parent)
1970        {
1971        }
1972    
1973        VMFnResult* InstrumentScriptVMFunction_fork::exec(VMFnArgs* args) {
1974            // check if this is actually the parent going to fork, or rather one of
1975            // the children which is already forked
1976            if (m_vm->m_event->forkIndex != 0) { // this is the entry point for a child ...
1977                int forkResult = m_vm->m_event->forkIndex;
1978                // reset so that this child may i.e. also call fork() later on
1979                m_vm->m_event->forkIndex = 0;
1980                return successResult(forkResult);
1981            }
1982    
1983            // if we are here, then this is the parent, so we must fork this parent
1984    
1985            const int n =
1986                (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : 1;
1987            const bool bAutoAbort =
1988                (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : true;
1989    
1990            if (m_vm->m_event->countChildHandlers() + n > MAX_FORK_PER_SCRIPT_HANDLER) {
1991                wrnMsg("fork(): requested amount would exceed allowed limit per event handler");
1992                return successResult(-1);
1993            }
1994    
1995            AbstractEngineChannel* pEngineChannel =
1996                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1997    
1998            if (!pEngineChannel->hasFreeScriptCallbacks(n)) {
1999                wrnMsg("fork(): global limit of event handlers exceeded");
2000                return successResult(-1);
2001            }
2002    
2003            for (int iChild = 0; iChild < n; ++iChild) {
2004                RTList<ScriptEvent>::Iterator itChild =
2005                    pEngineChannel->forkScriptCallback(m_vm->m_event, bAutoAbort);
2006                if (!itChild) { // should never happen, otherwise its a bug ...
2007                    errMsg("fork(): internal error while allocating child");
2008                    return errorResult(-1); // terminate script
2009                }
2010                // since both parent, as well all child script execution instances
2011                // all land in this exect() method, the following is (more or less)
2012                // the only feature that lets us distinguish the parent and
2013                // respective children from each other in this exect() method
2014                itChild->forkIndex = iChild + 1;
2015            }
2016    
2017            return successResult(0);
2018        }
2019    
2020  } // namespace LinuxSampler  } // namespace LinuxSampler

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

  ViewVC Help
Powered by ViewVC