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

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

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

revision 2938 by schoenebeck, Mon Jul 11 17:10:40 2016 UTC revision 3251 by schoenebeck, Mon May 29 22:19:19 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    
         if (sampleoffset < 0) {  
             errMsg("play_note(): argument 3 may not be a negative sample offset");  
             return errorResult(0);  
         } else if (sampleoffset != 0) {  
             wrnMsg("play_note(): argument 3 does not support a sample offset other than 0 yet");  
         }  
   
38          if (duration < -1) {          if (duration < -1) {
39              errMsg("play_note(): argument 4 must be a duration value of at least -1 or higher");              errMsg("play_note(): argument 4 must be a duration value of at least -1 or higher");
40              return errorResult(0);              return errorResult(0);
# Line 67  namespace LinuxSampler { Line 59  namespace LinuxSampler {
59    
60          const note_id_t id = pEngineChannel->ScheduleNoteMicroSec(&e, 0);          const note_id_t id = pEngineChannel->ScheduleNoteMicroSec(&e, 0);
61    
62            // if a sample offset is supplied, assign the offset as override
63            // to the previously created Note object
64            if (args->argsCount() >= 3) {
65                int sampleoffset = args->arg(2)->asInt()->evalInt();
66                if (sampleoffset >= 0) {
67                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID(id);
68                    if (pNote) {
69                        pNote->Override.SampleOffset = sampleoffset;
70                    }
71                } else if (sampleoffset < -1) {
72                    errMsg("play_note(): sample offset of argument 3 may not be less than -1");
73                }
74            }
75    
76          // if a duration is supplied (and play-note event was scheduled          // if a duration is supplied (and play-note event was scheduled
77          // successfully above), then schedule a subsequent stop-note event          // successfully above), then schedule a subsequent stop-note event
78          if (id && duration > 0) {          if (id && duration > 0) {
# Line 136  namespace LinuxSampler { Line 142  namespace LinuxSampler {
142          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
143                  static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);                  static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
144    
145          if (args->arg(0)->exprType() == INT_EXPR) {          if (args->argsCount() == 0 || args->arg(0)->exprType() == INT_EXPR) {
146              const ScriptID id = args->arg(0)->asInt()->evalInt();              const ScriptID id = (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : m_vm->m_event->id;
147              if (!id) {              if (!id && args->argsCount() >= 1) {
148                  wrnMsg("ignore_event(): event ID argument may not be zero");                  wrnMsg("ignore_event(): event ID argument may not be zero");
149                  // 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
150                  return successResult();                  return successResult();
# Line 359  namespace LinuxSampler { Line 365  namespace LinuxSampler {
365          if (iArg == 0)          if (iArg == 0)
366              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
367          else          else
368              return INT_EXPR;              return type == INT_EXPR;
369      }      }
370    
371      VMFnResult* InstrumentScriptVMFunction_change_vol::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_vol::exec(VMFnArgs* args) {
372          int volume = args->arg(1)->asInt()->evalInt(); // volume change in milli dB          int volume = args->arg(1)->asInt()->evalInt(); // volume change in milli dB
373          bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;          bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
374            const float fVolumeLin = RTMath::DecibelToLinRatio(float(volume) / 1000.f);
375    
376          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
377              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 383  namespace LinuxSampler { Line 390  namespace LinuxSampler {
390              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
391              if (!pNote) return successResult();              if (!pNote) return successResult();
392    
393              const float fVolumeLin = RTMath::DecibelToLinRatio(float(volume) / 1000.f);              // if change_vol() was called immediately after note was triggered
394              // commented out, performed by EngineBase::ProcessNoteSynthParam() for time accuracy behavior              // then immediately apply the volume to note object, but only if
395              /*if (relative)              // change_vol_time() has not been called before
396                  pNote->Override.Volume *= fVolumeLin;              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
397              else                  pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S)
398                  pNote->Override.Volume = fVolumeLin;*/              {
399                    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)  
400                      pNote->Override.Volume *= fVolumeLin;                      pNote->Override.Volume *= fVolumeLin;
401                  else                  else
402                      pNote->Override.Volume = fVolumeLin;*/                      pNote->Override.Volume = fVolumeLin;
403                } else { // otherwise schedule the volume change ...
404                  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"
405                  e.Init(); // clear IDs                  e.Init(); // clear IDs
406                  e.Type = Event::type_note_synth_param;                  e.Type = Event::type_note_synth_param;
# Line 425  namespace LinuxSampler { Line 411  namespace LinuxSampler {
411    
412                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
413              }              }
414            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
415                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
416                for (int i = 0; i < ids->arraySize(); ++i) {
417                    const ScriptID id = ids->evalIntElement(i);
418                    if (!id || !id.isNoteID()) continue;
419    
420                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
421                    if (!pNote) continue;
422    
423                    // if change_vol() was called immediately after note was triggered
424                    // then immediately apply the volume to Note object, but only if
425                    // change_vol_time() has not been called before
426                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
427                        pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S)
428                    {
429                        if (relative)
430                            pNote->Override.Volume *= fVolumeLin;
431                        else
432                            pNote->Override.Volume = fVolumeLin;
433                    } else { // otherwise schedule the volume change ...
434                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
435                        e.Init(); // clear IDs
436                        e.Type = Event::type_note_synth_param;
437                        e.Param.NoteSynthParam.NoteID   = id.noteID();
438                        e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
439                        e.Param.NoteSynthParam.Delta    = fVolumeLin;
440                        e.Param.NoteSynthParam.Relative = relative;
441    
442                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
443                    }
444                }
445          }          }
446    
447          return successResult();          return successResult();
# Line 441  namespace LinuxSampler { Line 458  namespace LinuxSampler {
458          if (iArg == 0)          if (iArg == 0)
459              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
460          else          else
461              return INT_EXPR;              return type == INT_EXPR;
462      }      }
463    
464      VMFnResult* InstrumentScriptVMFunction_change_tune::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_tune::exec(VMFnArgs* args) {
465          int tune = args->arg(1)->asInt()->evalInt(); // tuning change in milli cents          int tune = args->arg(1)->asInt()->evalInt(); // tuning change in milli cents
466          bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;          bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
467            const float fFreqRatio = RTMath::CentsToFreqRatioUnlimited(float(tune) / 1000.f);
468    
469          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
470              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 465  namespace LinuxSampler { Line 483  namespace LinuxSampler {
483              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
484              if (!pNote) return successResult();              if (!pNote) return successResult();
485    
486              const float fFreqRatio = RTMath::CentsToFreqRatioUnlimited(float(tune) / 1000.f);              // if change_tune() was called immediately after note was triggered
487              // commented out, performed by EngineBase::ProcessNoteSynthParam() for time accuracy behavior              // then immediately apply the tuning to Note object, but only if
488              /*if (relative)              // change_tune_time() has not been called before
489                  pNote->Override.Pitch *= fFreqRatio;              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
490              else                  pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S)
491                  pNote->Override.Pitch = fFreqRatio;*/              {
492                    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)  
493                      pNote->Override.Pitch *= fFreqRatio;                      pNote->Override.Pitch *= fFreqRatio;
494                  else                  else
495                      pNote->Override.Pitch = fFreqRatio;*/                      pNote->Override.Pitch = fFreqRatio;
496                } else { // otherwise schedule tuning change ...
497                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
498                  e.Init(); // clear IDs                  e.Init(); // clear IDs
499                  e.Type = Event::type_note_synth_param;                  e.Type = Event::type_note_synth_param;
# Line 507  namespace LinuxSampler { Line 504  namespace LinuxSampler {
504    
505                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
506              }              }
507            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
508                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
509                for (int i = 0; i < ids->arraySize(); ++i) {
510                    const ScriptID id = ids->evalIntElement(i);
511                    if (!id || !id.isNoteID()) continue;
512    
513                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
514                    if (!pNote) continue;
515    
516                    // if change_tune() was called immediately after note was triggered
517                    // then immediately apply the tuning to Note object, but only if
518                    // change_tune_time() has not been called before
519                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
520                        pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S)
521                    {
522                        if (relative)
523                            pNote->Override.Pitch *= fFreqRatio;
524                        else
525                            pNote->Override.Pitch = fFreqRatio;
526                    } else { // otherwise schedule tuning change ...
527                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
528                        e.Init(); // clear IDs
529                        e.Type = Event::type_note_synth_param;
530                        e.Param.NoteSynthParam.NoteID   = id.noteID();
531                        e.Param.NoteSynthParam.Type     = Event::synth_param_pitch;
532                        e.Param.NoteSynthParam.Delta    = fFreqRatio;
533                        e.Param.NoteSynthParam.Relative = relative;
534    
535                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
536                    }
537                }
538          }          }
539    
540          return successResult();          return successResult();
# Line 523  namespace LinuxSampler { Line 551  namespace LinuxSampler {
551          if (iArg == 0)          if (iArg == 0)
552              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
553          else          else
554              return INT_EXPR;              return type == INT_EXPR;
555      }      }
556    
557      VMFnResult* InstrumentScriptVMFunction_change_pan::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_pan::exec(VMFnArgs* args) {
# Line 537  namespace LinuxSampler { Line 565  namespace LinuxSampler {
565              wrnMsg("change_pan(): argument 2 may not be smaller than -1000");              wrnMsg("change_pan(): argument 2 may not be smaller than -1000");
566              pan = -1000;              pan = -1000;
567          }          }
568            const float fPan = float(pan) / 1000.f;
569    
570          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
571              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 555  namespace LinuxSampler { Line 584  namespace LinuxSampler {
584              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
585              if (!pNote) return successResult();              if (!pNote) return successResult();
586    
587              const float fPan = float(pan) / 1000.f;              // if change_pan() was called immediately after note was triggered
588              // commented out, performed by EngineBase::ProcessNoteSynthParam() for time accuracy behavior              // then immediately apply the panning to Note object
589              /*if (relative) {              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
590                  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) {  
591                      pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources);                      pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources);
592                  } else {                  } else {
593                      pNote->Override.Pan = fPan;                      pNote->Override.Pan = fPan;
594                      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
595                  }*/                  }
596                } else { // otherwise schedule panning change ...
597                  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"
598                  e.Init(); // clear IDs                  e.Init(); // clear IDs
599                  e.Type = Event::type_note_synth_param;                  e.Type = Event::type_note_synth_param;
# Line 601  namespace LinuxSampler { Line 604  namespace LinuxSampler {
604    
605                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
606              }              }
607            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
608                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
609                for (int i = 0; i < ids->arraySize(); ++i) {
610                    const ScriptID id = ids->evalIntElement(i);
611                    if (!id || !id.isNoteID()) continue;
612    
613                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
614                    if (!pNote) continue;
615    
616                    // if change_pan() was called immediately after note was triggered
617                    // then immediately apply the panning to Note object
618                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
619                        if (relative) {
620                            pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources);
621                        } else {
622                            pNote->Override.Pan = fPan;
623                            pNote->Override.PanSources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set
624                        }
625                    } else { // otherwise schedule panning change ...
626                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
627                        e.Init(); // clear IDs
628                        e.Type = Event::type_note_synth_param;
629                        e.Param.NoteSynthParam.NoteID   = id.noteID();
630                        e.Param.NoteSynthParam.Type     = Event::synth_param_pan;
631                        e.Param.NoteSynthParam.Delta    = fPan;
632                        e.Param.NoteSynthParam.Relative = relative;
633    
634                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
635                    }
636                }
637          }          }
638    
639          return successResult();          return successResult();
640      }      }
641    
642      #define VM_FILTER_PAR_MAX_VALUE 1000000      #define VM_FILTER_PAR_MAX_VALUE 1000000
643        #define VM_EG_PAR_MAX_VALUE 1000000
644    
645      // change_cutoff() function      // change_cutoff() function
646    
# Line 619  namespace LinuxSampler { Line 653  namespace LinuxSampler {
653          if (iArg == 0)          if (iArg == 0)
654              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
655          else          else
656              return INT_EXPR;              return type == INT_EXPR;
657      }      }
658    
659      VMFnResult* InstrumentScriptVMFunction_change_cutoff::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_cutoff::exec(VMFnArgs* args) {
# Line 631  namespace LinuxSampler { Line 665  namespace LinuxSampler {
665              wrnMsg("change_cutoff(): argument 2 may not be negative");              wrnMsg("change_cutoff(): argument 2 may not be negative");
666              cutoff = 0;              cutoff = 0;
667          }          }
668            const float fCutoff = float(cutoff) / float(VM_FILTER_PAR_MAX_VALUE);
669    
670          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
671              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 649  namespace LinuxSampler { Line 684  namespace LinuxSampler {
684              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
685              if (!pNote) return successResult();              if (!pNote) return successResult();
686    
687              const float fCutoff = float(cutoff) / float(VM_FILTER_PAR_MAX_VALUE);              // if change_cutoff() was called immediately after note was triggered
688                // then immediately apply cutoff to Note object
689              Event e = m_vm->m_event->cause; // copy to get fragment time for "now"              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
690              e.Init(); // clear IDs                  pNote->Override.Cutoff = fCutoff;
691              e.Type = Event::type_note_synth_param;              } else { // otherwise schedule cutoff change ...
692              e.Param.NoteSynthParam.NoteID   = id.noteID();                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
693              e.Param.NoteSynthParam.Type     = Event::synth_param_cutoff;                  e.Init(); // clear IDs
694              e.Param.NoteSynthParam.Delta    = fCutoff;                  e.Type = Event::type_note_synth_param;
695              e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.NoteID   = id.noteID();
696                    e.Param.NoteSynthParam.Type     = Event::synth_param_cutoff;
697                    e.Param.NoteSynthParam.Delta    = fCutoff;
698                    e.Param.NoteSynthParam.Relative = false;
699    
700              pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
701                }
702          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
703              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
704              for (int i = 0; i < ids->arraySize(); ++i) {              for (int i = 0; i < ids->arraySize(); ++i) {
# Line 669  namespace LinuxSampler { Line 708  namespace LinuxSampler {
708                  NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );                  NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
709                  if (!pNote) continue;                  if (!pNote) continue;
710    
711                  const float fCutoff = float(cutoff) / float(VM_FILTER_PAR_MAX_VALUE);                  // if change_cutoff() was called immediately after note was triggered
712                    // then immediately apply cutoff to Note object
713                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
714                  e.Init(); // clear IDs                      pNote->Override.Cutoff = fCutoff;
715                  e.Type = Event::type_note_synth_param;                  } else { // otherwise schedule cutoff change ...
716                  e.Param.NoteSynthParam.NoteID   = id.noteID();                      Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
717                  e.Param.NoteSynthParam.Type     = Event::synth_param_cutoff;                      e.Init(); // clear IDs
718                  e.Param.NoteSynthParam.Delta    = fCutoff;                      e.Type = Event::type_note_synth_param;
719                  e.Param.NoteSynthParam.Relative = false;                      e.Param.NoteSynthParam.NoteID   = id.noteID();
720                        e.Param.NoteSynthParam.Type     = Event::synth_param_cutoff;
721                        e.Param.NoteSynthParam.Delta    = fCutoff;
722                        e.Param.NoteSynthParam.Relative = false;
723    
724                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
725                    }
726              }              }
727          }          }
728    
# Line 697  namespace LinuxSampler { Line 740  namespace LinuxSampler {
740          if (iArg == 0)          if (iArg == 0)
741              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
742          else          else
743              return INT_EXPR;              return type == INT_EXPR;
744      }      }
745    
746      VMFnResult* InstrumentScriptVMFunction_change_reso::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_reso::exec(VMFnArgs* args) {
# Line 709  namespace LinuxSampler { Line 752  namespace LinuxSampler {
752              wrnMsg("change_reso(): argument 2 may not be negative");              wrnMsg("change_reso(): argument 2 may not be negative");
753              resonance = 0;              resonance = 0;
754          }          }
755            const float fResonance = float(resonance) / float(VM_FILTER_PAR_MAX_VALUE);
756    
757          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
758              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 727  namespace LinuxSampler { Line 771  namespace LinuxSampler {
771              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
772              if (!pNote) return successResult();              if (!pNote) return successResult();
773    
774              const float fResonance = float(resonance) / float(VM_FILTER_PAR_MAX_VALUE);              // if change_reso() was called immediately after note was triggered
775                // then immediately apply resonance to Note object
776                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
777                    pNote->Override.Resonance = fResonance;
778                } else { // otherwise schedule resonance change ...
779                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
780                    e.Init(); // clear IDs
781                    e.Type = Event::type_note_synth_param;
782                    e.Param.NoteSynthParam.NoteID   = id.noteID();
783                    e.Param.NoteSynthParam.Type     = Event::synth_param_resonance;
784                    e.Param.NoteSynthParam.Delta    = fResonance;
785                    e.Param.NoteSynthParam.Relative = false;
786    
787              Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
788              e.Init(); // clear IDs              }
789              e.Type = Event::type_note_synth_param;          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
790              e.Param.NoteSynthParam.NoteID   = id.noteID();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
791              e.Param.NoteSynthParam.Type     = Event::synth_param_resonance;              for (int i = 0; i < ids->arraySize(); ++i) {
792              e.Param.NoteSynthParam.Delta    = fResonance;                  const ScriptID id = ids->evalIntElement(i);
793              e.Param.NoteSynthParam.Relative = false;                  if (!id || !id.isNoteID()) continue;
794    
795              pEngineChannel->ScheduleEventMicroSec(&e, 0);                  NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
796                    if (!pNote) continue;
797    
798                    // if change_reso() was called immediately after note was triggered
799                    // then immediately apply resonance to Note object
800                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
801                        pNote->Override.Resonance = fResonance;
802                    } else { // otherwise schedule resonance change ...
803                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
804                        e.Init(); // clear IDs
805                        e.Type = Event::type_note_synth_param;
806                        e.Param.NoteSynthParam.NoteID   = id.noteID();
807                        e.Param.NoteSynthParam.Type     = Event::synth_param_resonance;
808                        e.Param.NoteSynthParam.Delta    = fResonance;
809                        e.Param.NoteSynthParam.Relative = false;
810    
811                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
812                    }
813                }
814            }
815    
816            return successResult();
817        }
818        
819        // change_attack() function
820    
821        InstrumentScriptVMFunction_change_attack::InstrumentScriptVMFunction_change_attack(InstrumentScriptVM* parent)
822            : m_vm(parent)
823        {
824        }
825    
826        bool InstrumentScriptVMFunction_change_attack::acceptsArgType(int iArg, ExprType_t type) const {
827            if (iArg == 0)
828                return type == INT_EXPR || type == INT_ARR_EXPR;
829            else
830                return type == INT_EXPR;
831        }
832    
833        VMFnResult* InstrumentScriptVMFunction_change_attack::exec(VMFnArgs* args) {
834            int attack = args->arg(1)->asInt()->evalInt();
835            if (attack > VM_EG_PAR_MAX_VALUE) {
836                wrnMsg("change_attack(): argument 2 may not be larger than 1000000");
837                attack = VM_EG_PAR_MAX_VALUE;
838            } else if (attack < 0) {
839                wrnMsg("change_attack(): argument 2 may not be negative");
840                attack = 0;
841            }
842            const float fAttack = float(attack) / float(VM_EG_PAR_MAX_VALUE);
843    
844            AbstractEngineChannel* pEngineChannel =
845                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
846    
847            if (args->arg(0)->exprType() == INT_EXPR) {
848                const ScriptID id = args->arg(0)->asInt()->evalInt();
849                if (!id) {
850                    wrnMsg("change_attack(): note ID for argument 1 may not be zero");
851                    return successResult();
852                }
853                if (!id.isNoteID()) {
854                    wrnMsg("change_attack(): argument 1 is not a note ID");
855                    return successResult();
856                }
857    
858                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
859                if (!pNote) return successResult();
860    
861                // if change_attack() was called immediately after note was triggered
862                // then immediately apply attack to Note object
863                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
864                    pNote->Override.Attack = fAttack;
865                } else { // otherwise schedule attack change ...
866                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
867                    e.Init(); // clear IDs
868                    e.Type = Event::type_note_synth_param;
869                    e.Param.NoteSynthParam.NoteID   = id.noteID();
870                    e.Param.NoteSynthParam.Type     = Event::synth_param_attack;
871                    e.Param.NoteSynthParam.Delta    = fAttack;
872                    e.Param.NoteSynthParam.Relative = false;
873    
874                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
875                }
876          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
877              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
878              for (int i = 0; i < ids->arraySize(); ++i) {              for (int i = 0; i < ids->arraySize(); ++i) {
# Line 747  namespace LinuxSampler { Line 882  namespace LinuxSampler {
882                  NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );                  NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
883                  if (!pNote) continue;                  if (!pNote) continue;
884    
885                  const float fResonance = float(resonance) / float(VM_FILTER_PAR_MAX_VALUE);                  // if change_attack() was called immediately after note was triggered
886                    // then immediately apply attack to Note object
887                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
888                        pNote->Override.Attack = fAttack;
889                    } else { // otherwise schedule attack change ...
890                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
891                        e.Init(); // clear IDs
892                        e.Type = Event::type_note_synth_param;
893                        e.Param.NoteSynthParam.NoteID   = id.noteID();
894                        e.Param.NoteSynthParam.Type     = Event::synth_param_attack;
895                        e.Param.NoteSynthParam.Delta    = fAttack;
896                        e.Param.NoteSynthParam.Relative = false;
897    
898                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
899                    }
900                }
901            }
902    
903            return successResult();
904        }
905    
906        // change_decay() function
907        
908        InstrumentScriptVMFunction_change_decay::InstrumentScriptVMFunction_change_decay(InstrumentScriptVM* parent)
909            : m_vm(parent)
910        {
911        }
912    
913        bool InstrumentScriptVMFunction_change_decay::acceptsArgType(int iArg, ExprType_t type) const {
914            if (iArg == 0)
915                return type == INT_EXPR || type == INT_ARR_EXPR;
916            else
917                return type == INT_EXPR;
918        }
919    
920        VMFnResult* InstrumentScriptVMFunction_change_decay::exec(VMFnArgs* args) {
921            int decay = args->arg(1)->asInt()->evalInt();
922            if (decay > VM_EG_PAR_MAX_VALUE) {
923                wrnMsg("change_decay(): argument 2 may not be larger than 1000000");
924                decay = VM_EG_PAR_MAX_VALUE;
925            } else if (decay < 0) {
926                wrnMsg("change_decay(): argument 2 may not be negative");
927                decay = 0;
928            }
929            const float fDecay = float(decay) / float(VM_EG_PAR_MAX_VALUE);
930    
931            AbstractEngineChannel* pEngineChannel =
932                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
933    
934            if (args->arg(0)->exprType() == INT_EXPR) {
935                const ScriptID id = args->arg(0)->asInt()->evalInt();
936                if (!id) {
937                    wrnMsg("change_decay(): note ID for argument 1 may not be zero");
938                    return successResult();
939                }
940                if (!id.isNoteID()) {
941                    wrnMsg("change_decay(): argument 1 is not a note ID");
942                    return successResult();
943                }
944    
945                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
946                if (!pNote) return successResult();
947    
948                // if change_decay() was called immediately after note was triggered
949                // then immediately apply decay to Note object
950                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
951                    pNote->Override.Decay = fDecay;
952                } else { // otherwise schedule decay change ...
953                  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"
954                  e.Init(); // clear IDs                  e.Init(); // clear IDs
955                  e.Type = Event::type_note_synth_param;                  e.Type = Event::type_note_synth_param;
956                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
957                  e.Param.NoteSynthParam.Type     = Event::synth_param_resonance;                  e.Param.NoteSynthParam.Type     = Event::synth_param_decay;
958                  e.Param.NoteSynthParam.Delta    = fResonance;                  e.Param.NoteSynthParam.Delta    = fDecay;
959                    e.Param.NoteSynthParam.Relative = false;
960    
961                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
962                }
963            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
964                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
965                for (int i = 0; i < ids->arraySize(); ++i) {
966                    const ScriptID id = ids->evalIntElement(i);
967                    if (!id || !id.isNoteID()) continue;
968    
969                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
970                    if (!pNote) continue;
971    
972                    // if change_decay() was called immediately after note was triggered
973                    // then immediately apply decay to Note object
974                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
975                        pNote->Override.Decay = fDecay;
976                    } else { // otherwise schedule decay change ...
977                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
978                        e.Init(); // clear IDs
979                        e.Type = Event::type_note_synth_param;
980                        e.Param.NoteSynthParam.NoteID   = id.noteID();
981                        e.Param.NoteSynthParam.Type     = Event::synth_param_decay;
982                        e.Param.NoteSynthParam.Delta    = fDecay;
983                        e.Param.NoteSynthParam.Relative = false;
984    
985                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
986                    }
987                }
988            }
989    
990            return successResult();
991        }
992    
993        // change_release() function
994        
995        InstrumentScriptVMFunction_change_release::InstrumentScriptVMFunction_change_release(InstrumentScriptVM* parent)
996            : m_vm(parent)
997        {
998        }
999    
1000        bool InstrumentScriptVMFunction_change_release::acceptsArgType(int iArg, ExprType_t type) const {
1001            if (iArg == 0)
1002                return type == INT_EXPR || type == INT_ARR_EXPR;
1003            else
1004                return type == INT_EXPR;
1005        }
1006    
1007        VMFnResult* InstrumentScriptVMFunction_change_release::exec(VMFnArgs* args) {
1008            int release = args->arg(1)->asInt()->evalInt();
1009            if (release > VM_EG_PAR_MAX_VALUE) {
1010                wrnMsg("change_release(): argument 2 may not be larger than 1000000");
1011                release = VM_EG_PAR_MAX_VALUE;
1012            } else if (release < 0) {
1013                wrnMsg("change_release(): argument 2 may not be negative");
1014                release = 0;
1015            }
1016            const float fRelease = float(release) / float(VM_EG_PAR_MAX_VALUE);
1017    
1018            AbstractEngineChannel* pEngineChannel =
1019                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1020    
1021            if (args->arg(0)->exprType() == INT_EXPR) {
1022                const ScriptID id = args->arg(0)->asInt()->evalInt();
1023                if (!id) {
1024                    wrnMsg("change_release(): note ID for argument 1 may not be zero");
1025                    return successResult();
1026                }
1027                if (!id.isNoteID()) {
1028                    wrnMsg("change_release(): argument 1 is not a note ID");
1029                    return successResult();
1030                }
1031    
1032                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1033                if (!pNote) return successResult();
1034    
1035                // if change_release() was called immediately after note was triggered
1036                // then immediately apply relase to Note object
1037                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1038                    pNote->Override.Release = fRelease;
1039                } else { // otherwise schedule release change ...
1040                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1041                    e.Init(); // clear IDs
1042                    e.Type = Event::type_note_synth_param;
1043                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1044                    e.Param.NoteSynthParam.Type     = Event::synth_param_release;
1045                    e.Param.NoteSynthParam.Delta    = fRelease;
1046                    e.Param.NoteSynthParam.Relative = false;
1047    
1048                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
1049                }
1050            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1051                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1052                for (int i = 0; i < ids->arraySize(); ++i) {
1053                    const ScriptID id = ids->evalIntElement(i);
1054                    if (!id || !id.isNoteID()) continue;
1055    
1056                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1057                    if (!pNote) continue;
1058    
1059                    // if change_release() was called immediately after note was triggered
1060                    // then immediately apply relase to Note object
1061                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1062                        pNote->Override.Release = fRelease;
1063                    } else { // otherwise schedule release change ...
1064                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1065                        e.Init(); // clear IDs
1066                        e.Type = Event::type_note_synth_param;
1067                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1068                        e.Param.NoteSynthParam.Type     = Event::synth_param_release;
1069                        e.Param.NoteSynthParam.Delta    = fRelease;
1070                        e.Param.NoteSynthParam.Relative = false;
1071    
1072                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
1073                    }
1074                }
1075            }
1076    
1077            return successResult();
1078        }
1079    
1080        // template for change_*() functions
1081    
1082        bool VMChangeSynthParamFunction::acceptsArgType(int iArg, ExprType_t type) const {
1083            if (iArg == 0)
1084                return type == INT_EXPR || type == INT_ARR_EXPR;
1085            else
1086                return type == INT_EXPR;
1087        }
1088    
1089        // Arbitrarily chosen constant value symbolizing "no limit".
1090        #define NO_LIMIT 1315916909
1091    
1092        template<float NoteBase::_Override::*T_noteParam, int T_synthParam,
1093                 bool T_isNormalizedParam, int T_maxValue, int T_minValue>
1094        VMFnResult* VMChangeSynthParamFunction::execTemplate(VMFnArgs* args, const char* functionName) {
1095            int value = args->arg(1)->asInt()->evalInt();
1096            if (T_maxValue != NO_LIMIT && value > T_maxValue) {
1097                wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValue));
1098                value = T_maxValue;
1099            } else if (T_minValue != NO_LIMIT && value < T_minValue) {
1100                if (T_minValue == 0)
1101                    wrnMsg(String(functionName) + "(): argument 2 may not be negative");
1102                else
1103                    wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValue));
1104                value = T_minValue;
1105            }
1106            const float fValue = (T_isNormalizedParam) ?
1107                float(value) / float(T_maxValue) : // convert to 0.0 .. 1.0 value range
1108                float(value) / 1000000.f; // assuming microseconds here, convert to seconds
1109    
1110            AbstractEngineChannel* pEngineChannel =
1111                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1112    
1113            if (args->arg(0)->exprType() == INT_EXPR) {
1114                const ScriptID id = args->arg(0)->asInt()->evalInt();
1115                if (!id) {
1116                    wrnMsg(String(functionName) + "(): note ID for argument 1 may not be zero");
1117                    return successResult();
1118                }
1119                if (!id.isNoteID()) {
1120                    wrnMsg(String(functionName) + "(): argument 1 is not a note ID");
1121                    return successResult();
1122                }
1123    
1124                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1125                if (!pNote) return successResult();
1126    
1127                // if this change_*() script function was called immediately after
1128                // note was triggered then immediately apply the synth parameter
1129                // change to Note object
1130                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1131                    pNote->Override.*T_noteParam = fValue;
1132                } else { // otherwise schedule this synth parameter change ...
1133                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1134                    e.Init(); // clear IDs
1135                    e.Type = Event::type_note_synth_param;
1136                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1137                    e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1138                    e.Param.NoteSynthParam.Delta    = fValue;
1139                    e.Param.NoteSynthParam.Relative = false;
1140    
1141                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
1142                }
1143            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1144                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1145                for (int i = 0; i < ids->arraySize(); ++i) {
1146                    const ScriptID id = ids->evalIntElement(i);
1147                    if (!id || !id.isNoteID()) continue;
1148    
1149                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1150                    if (!pNote) continue;
1151    
1152                    // if this change_*() script function was called immediately after
1153                    // note was triggered then immediately apply the synth parameter
1154                    // change to Note object
1155                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1156                        pNote->Override.*T_noteParam = fValue;
1157                    } else { // otherwise schedule this synth parameter change ...
1158                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1159                        e.Init(); // clear IDs
1160                        e.Type = Event::type_note_synth_param;
1161                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1162                        e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1163                        e.Param.NoteSynthParam.Delta    = fValue;
1164                        e.Param.NoteSynthParam.Relative = false;
1165    
1166                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
1167                    }
1168                }
1169            }
1170    
1171            return successResult();
1172        }
1173    
1174        // change_amp_lfo_depth() function
1175    
1176        VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_depth::exec(VMFnArgs* args) {
1177            return VMChangeSynthParamFunction::execTemplate<
1178                        &NoteBase::_Override::AmpLFODepth,
1179                        Event::synth_param_amp_lfo_depth,
1180                        true, 1000000, 0>( args, "change_amp_lfo_depth" );
1181        }
1182    
1183        // change_amp_lfo_freq() function
1184    
1185        VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_freq::exec(VMFnArgs* args) {
1186            return VMChangeSynthParamFunction::execTemplate<
1187                        &NoteBase::_Override::AmpLFOFreq,
1188                        Event::synth_param_amp_lfo_freq,
1189                        true, 1000000, 0>( args, "change_amp_lfo_freq" );
1190        }
1191    
1192        // change_pitch_lfo_depth() function
1193    
1194        VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_depth::exec(VMFnArgs* args) {
1195            return VMChangeSynthParamFunction::execTemplate<
1196                        &NoteBase::_Override::PitchLFODepth,
1197                        Event::synth_param_pitch_lfo_depth,
1198                        true, 1000000, 0>( args, "change_pitch_lfo_depth" );
1199        }
1200    
1201        // change_pitch_lfo_freq() function
1202    
1203        VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_freq::exec(VMFnArgs* args) {
1204            return VMChangeSynthParamFunction::execTemplate<
1205                        &NoteBase::_Override::PitchLFOFreq,
1206                        Event::synth_param_pitch_lfo_freq,
1207                        true, 1000000, 0>( args, "change_pitch_lfo_freq" );
1208        }
1209    
1210        // change_vol_time() function
1211    
1212        VMFnResult* InstrumentScriptVMFunction_change_vol_time::exec(VMFnArgs* args) {
1213            return VMChangeSynthParamFunction::execTemplate<
1214                        &NoteBase::_Override::VolumeTime,
1215                        Event::synth_param_volume_time,
1216                        false, NO_LIMIT, 0>( args, "change_vol_time" );
1217        }
1218    
1219        // change_tune_time() function
1220    
1221        VMFnResult* InstrumentScriptVMFunction_change_tune_time::exec(VMFnArgs* args) {
1222            return VMChangeSynthParamFunction::execTemplate<
1223                        &NoteBase::_Override::PitchTime,
1224                        Event::synth_param_pitch_time,
1225                        false, NO_LIMIT, 0>( args, "change_tune_time" );
1226        }
1227    
1228        // template for change_*_curve() functions
1229    
1230        bool VMChangeFadeCurveFunction::acceptsArgType(int iArg, ExprType_t type) const {
1231            if (iArg == 0)
1232                return type == INT_EXPR || type == INT_ARR_EXPR;
1233            else
1234                return type == INT_EXPR;
1235        }
1236    
1237        template<fade_curve_t NoteBase::_Override::*T_noteParam, int T_synthParam>
1238        VMFnResult* VMChangeFadeCurveFunction::execTemplate(VMFnArgs* args, const char* functionName) {
1239            int value = args->arg(1)->asInt()->evalInt();
1240            switch (value) {
1241                case FADE_CURVE_LINEAR:
1242                case FADE_CURVE_EASE_IN_EASE_OUT:
1243                    break;
1244                default:
1245                    wrnMsg(String(functionName) + "(): invalid curve type passed as argument 2");
1246                    return successResult();
1247            }
1248    
1249            AbstractEngineChannel* pEngineChannel =
1250                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1251    
1252            if (args->arg(0)->exprType() == INT_EXPR) {
1253                const ScriptID id = args->arg(0)->asInt()->evalInt();
1254                if (!id) {
1255                    wrnMsg(String(functionName) + "(): note ID for argument 1 may not be zero");
1256                    return successResult();
1257                }
1258                if (!id.isNoteID()) {
1259                    wrnMsg(String(functionName) + "(): argument 1 is not a note ID");
1260                    return successResult();
1261                }
1262    
1263                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1264                if (!pNote) return successResult();
1265    
1266                // if this change_*_curve() script function was called immediately after
1267                // note was triggered then immediately apply the synth parameter
1268                // change to Note object
1269                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1270                    pNote->Override.*T_noteParam = (fade_curve_t) value;
1271                } else { // otherwise schedule this synth parameter change ...
1272                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1273                    e.Init(); // clear IDs
1274                    e.Type = Event::type_note_synth_param;
1275                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1276                    e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1277                    e.Param.NoteSynthParam.Delta    = value;
1278                    e.Param.NoteSynthParam.Relative = false;
1279    
1280                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
1281                }
1282            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1283                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1284                for (int i = 0; i < ids->arraySize(); ++i) {
1285                    const ScriptID id = ids->evalIntElement(i);
1286                    if (!id || !id.isNoteID()) continue;
1287    
1288                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1289                    if (!pNote) continue;
1290    
1291                    // if this change_*_curve() script function was called immediately after
1292                    // note was triggered then immediately apply the synth parameter
1293                    // change to Note object
1294                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1295                        pNote->Override.*T_noteParam = (fade_curve_t) value;
1296                    } else { // otherwise schedule this synth parameter change ...
1297                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1298                        e.Init(); // clear IDs
1299                        e.Type = Event::type_note_synth_param;
1300                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1301                        e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1302                        e.Param.NoteSynthParam.Delta    = value;
1303                        e.Param.NoteSynthParam.Relative = false;
1304    
1305                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
1306                    }
1307                }
1308            }
1309    
1310            return successResult();
1311        }
1312    
1313        // change_vol_curve() function
1314    
1315        VMFnResult* InstrumentScriptVMFunction_change_vol_curve::exec(VMFnArgs* args) {
1316            return VMChangeFadeCurveFunction::execTemplate<
1317                        &NoteBase::_Override::VolumeCurve,
1318                        Event::synth_param_volume_curve>( args, "change_vol_curve" );
1319        }
1320    
1321        // change_tune_curve() function
1322    
1323        VMFnResult* InstrumentScriptVMFunction_change_tune_curve::exec(VMFnArgs* args) {
1324            return VMChangeFadeCurveFunction::execTemplate<
1325                        &NoteBase::_Override::PitchCurve,
1326                        Event::synth_param_pitch_curve>( args, "change_tune_curve" );
1327        }
1328    
1329        // fade_in() function
1330    
1331        InstrumentScriptVMFunction_fade_in::InstrumentScriptVMFunction_fade_in(InstrumentScriptVM* parent)
1332            : m_vm(parent)
1333        {
1334        }
1335    
1336        bool InstrumentScriptVMFunction_fade_in::acceptsArgType(int iArg, ExprType_t type) const {
1337            if (iArg == 0)
1338                return type == INT_EXPR || type == INT_ARR_EXPR;
1339            else
1340                return type == INT_EXPR;
1341        }
1342    
1343        VMFnResult* InstrumentScriptVMFunction_fade_in::exec(VMFnArgs* args) {
1344            int duration = args->arg(1)->asInt()->evalInt();
1345            if (duration < 0) {
1346                wrnMsg("fade_in(): argument 2 may not be negative");
1347                duration = 0;
1348            }
1349            const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
1350    
1351            AbstractEngineChannel* pEngineChannel =
1352                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1353    
1354            if (args->arg(0)->exprType() == INT_EXPR) {
1355                const ScriptID id = args->arg(0)->asInt()->evalInt();
1356                if (!id) {
1357                    wrnMsg("fade_in(): note ID for argument 1 may not be zero");
1358                    return successResult();
1359                }
1360                if (!id.isNoteID()) {
1361                    wrnMsg("fade_in(): argument 1 is not a note ID");
1362                    return successResult();
1363                }
1364    
1365                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1366                if (!pNote) return successResult();
1367    
1368                // if fade_in() was called immediately after note was triggered
1369                // then immediately apply a start volume of zero to Note object,
1370                // as well as the fade in duration
1371                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1372                    pNote->Override.Volume = 0.f;
1373                    pNote->Override.VolumeTime = fDuration;
1374                } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
1375                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1376                    e.Init(); // clear IDs
1377                    e.Type = Event::type_note_synth_param;
1378                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1379                    e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1380                    e.Param.NoteSynthParam.Delta    = fDuration;
1381                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Relative = false;
1382    
1383                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
1384              }              }
1385                // and finally schedule a "volume" change, simply one time slice
1386                // ahead, with the final fade in volume (1.0)
1387                {
1388                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1389                    e.Init(); // clear IDs
1390                    e.Type = Event::type_note_synth_param;
1391                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1392                    e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1393                    e.Param.NoteSynthParam.Delta    = 1.f;
1394                    e.Param.NoteSynthParam.Relative = false;
1395    
1396                    // scheduling with 0 delay would also work here, but +1 is more
1397                    // safe regarding potential future implementation changes of the
1398                    // scheduler (see API comments of RTAVLTree::insert())
1399                    pEngineChannel->ScheduleEventMicroSec(&e, 1);
1400                }
1401            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1402                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1403                for (int i = 0; i < ids->arraySize(); ++i) {
1404                    const ScriptID id = ids->evalIntElement(i);
1405                    if (!id || !id.isNoteID()) continue;
1406    
1407                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1408                    if (!pNote) continue;
1409    
1410                    // if fade_in() was called immediately after note was triggered
1411                    // then immediately apply a start volume of zero to Note object,
1412                    // as well as the fade in duration
1413                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1414                        pNote->Override.Volume = 0.f;
1415                        pNote->Override.VolumeTime = fDuration;
1416                    } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
1417                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1418                        e.Init(); // clear IDs
1419                        e.Type = Event::type_note_synth_param;
1420                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1421                        e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1422                        e.Param.NoteSynthParam.Delta    = fDuration;
1423                        e.Param.NoteSynthParam.Relative = false;
1424    
1425                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
1426                    }
1427                    // and finally schedule a "volume" change, simply one time slice
1428                    // ahead, with the final fade in volume (1.0)
1429                    {
1430                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1431                        e.Init(); // clear IDs
1432                        e.Type = Event::type_note_synth_param;
1433                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1434                        e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1435                        e.Param.NoteSynthParam.Delta    = 1.f;
1436                        e.Param.NoteSynthParam.Relative = false;
1437    
1438                        // scheduling with 0 delay would also work here, but +1 is more
1439                        // safe regarding potential future implementation changes of the
1440                        // scheduler (see API comments of RTAVLTree::insert())
1441                        pEngineChannel->ScheduleEventMicroSec(&e, 1);
1442                    }
1443                }
1444            }
1445    
1446            return successResult();
1447        }
1448    
1449        // fade_out() function
1450    
1451        InstrumentScriptVMFunction_fade_out::InstrumentScriptVMFunction_fade_out(InstrumentScriptVM* parent)
1452            : m_vm(parent)
1453        {
1454        }
1455    
1456        bool InstrumentScriptVMFunction_fade_out::acceptsArgType(int iArg, ExprType_t type) const {
1457            if (iArg == 0)
1458                return type == INT_EXPR || type == INT_ARR_EXPR;
1459            else
1460                return type == INT_EXPR;
1461        }
1462    
1463        VMFnResult* InstrumentScriptVMFunction_fade_out::exec(VMFnArgs* args) {
1464            int duration = args->arg(1)->asInt()->evalInt();
1465            if (duration < 0) {
1466                wrnMsg("fade_out(): argument 2 may not be negative");
1467                duration = 0;
1468            }
1469            const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
1470    
1471            bool stop = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : true;
1472    
1473            AbstractEngineChannel* pEngineChannel =
1474                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1475    
1476            if (args->arg(0)->exprType() == INT_EXPR) {
1477                const ScriptID id = args->arg(0)->asInt()->evalInt();
1478                if (!id) {
1479                    wrnMsg("fade_out(): note ID for argument 1 may not be zero");
1480                    return successResult();
1481                }
1482                if (!id.isNoteID()) {
1483                    wrnMsg("fade_out(): argument 1 is not a note ID");
1484                    return successResult();
1485                }
1486    
1487                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1488                if (!pNote) return successResult();
1489    
1490                // if fade_out() was called immediately after note was triggered
1491                // then immediately apply fade out duration to Note object
1492                if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1493                    pNote->Override.VolumeTime = fDuration;
1494                } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
1495                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1496                    e.Init(); // clear IDs
1497                    e.Type = Event::type_note_synth_param;
1498                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1499                    e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1500                    e.Param.NoteSynthParam.Delta    = fDuration;
1501                    e.Param.NoteSynthParam.Relative = false;
1502    
1503                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
1504                }
1505                // now schedule a "volume" change, simply one time slice ahead, with
1506                // the final fade out volume (0.0)
1507                {
1508                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1509                    e.Init(); // clear IDs
1510                    e.Type = Event::type_note_synth_param;
1511                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1512                    e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1513                    e.Param.NoteSynthParam.Delta    = 0.f;
1514                    e.Param.NoteSynthParam.Relative = false;
1515    
1516                    // scheduling with 0 delay would also work here, but +1 is more
1517                    // safe regarding potential future implementation changes of the
1518                    // scheduler (see API comments of RTAVLTree::insert())
1519                    pEngineChannel->ScheduleEventMicroSec(&e, 1);
1520                }
1521                // and finally if stopping the note was requested after the fade out
1522                // completed, then schedule to kill the voice after the requested
1523                // duration
1524                if (stop) {
1525                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1526                    e.Init(); // clear IDs
1527                    e.Type = Event::type_kill_note;
1528                    e.Param.Note.ID = id.noteID();
1529                    e.Param.Note.Key = pNote->hostKey;
1530    
1531                    pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
1532                }
1533            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1534                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1535                for (int i = 0; i < ids->arraySize(); ++i) {
1536                    const ScriptID id = ids->evalIntElement(i);
1537                    if (!id || !id.isNoteID()) continue;
1538    
1539                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1540                    if (!pNote) continue;
1541    
1542                    // if fade_out() was called immediately after note was triggered
1543                    // then immediately apply fade out duration to Note object
1544                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1545                        pNote->Override.VolumeTime = fDuration;
1546                    } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
1547                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1548                        e.Init(); // clear IDs
1549                        e.Type = Event::type_note_synth_param;
1550                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1551                        e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1552                        e.Param.NoteSynthParam.Delta    = fDuration;
1553                        e.Param.NoteSynthParam.Relative = false;
1554    
1555                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
1556                    }
1557                    // now schedule a "volume" change, simply one time slice ahead, with
1558                    // the final fade out volume (0.0)
1559                    {
1560                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1561                        e.Init(); // clear IDs
1562                        e.Type = Event::type_note_synth_param;
1563                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1564                        e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1565                        e.Param.NoteSynthParam.Delta    = 0.f;
1566                        e.Param.NoteSynthParam.Relative = false;
1567    
1568                        // scheduling with 0 delay would also work here, but +1 is more
1569                        // safe regarding potential future implementation changes of the
1570                        // scheduler (see API comments of RTAVLTree::insert())
1571                        pEngineChannel->ScheduleEventMicroSec(&e, 1);
1572                    }
1573                    // and finally if stopping the note was requested after the fade out
1574                    // completed, then schedule to kill the voice after the requested
1575                    // duration
1576                    if (stop) {
1577                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1578                        e.Init(); // clear IDs
1579                        e.Type = Event::type_kill_note;
1580                        e.Param.Note.ID = id.noteID();
1581                        e.Param.Note.Key = pNote->hostKey;
1582                        
1583                        pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
1584                    }
1585                }
1586            }
1587    
1588            return successResult();
1589        }
1590    
1591        // get_event_par() function
1592    
1593        InstrumentScriptVMFunction_get_event_par::InstrumentScriptVMFunction_get_event_par(InstrumentScriptVM* parent)
1594            : m_vm(parent)
1595        {
1596        }
1597    
1598        VMFnResult* InstrumentScriptVMFunction_get_event_par::exec(VMFnArgs* args) {
1599            AbstractEngineChannel* pEngineChannel =
1600                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1601    
1602            const ScriptID id = args->arg(0)->asInt()->evalInt();
1603            if (!id) {
1604                wrnMsg("get_event_par(): note ID for argument 1 may not be zero");
1605                return successResult(0);
1606            }
1607            if (!id.isNoteID()) {
1608                wrnMsg("get_event_par(): argument 1 is not a note ID");
1609                return successResult(0);
1610            }
1611    
1612            NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1613            if (!pNote) {
1614                wrnMsg("get_event_par(): no note alive with that note ID of argument 1");
1615                return successResult(0);
1616            }
1617    
1618            const int parameter = args->arg(1)->asInt()->evalInt();
1619            switch (parameter) {
1620                case EVENT_PAR_NOTE:
1621                    return successResult(pNote->cause.Param.Note.Key);
1622                case EVENT_PAR_VELOCITY:
1623                    return successResult(pNote->cause.Param.Note.Velocity);
1624                case EVENT_PAR_VOLUME:
1625                    return successResult(
1626                        RTMath::LinRatioToDecibel(pNote->Override.Volume) * 1000.f
1627                    );
1628                case EVENT_PAR_TUNE:
1629                    return successResult(
1630                         RTMath::FreqRatioToCents(pNote->Override.Pitch) * 1000.f
1631                    );
1632                case EVENT_PAR_0:
1633                    return successResult(pNote->userPar[0]);
1634                case EVENT_PAR_1:
1635                    return successResult(pNote->userPar[1]);
1636                case EVENT_PAR_2:
1637                    return successResult(pNote->userPar[2]);
1638                case EVENT_PAR_3:
1639                    return successResult(pNote->userPar[3]);
1640            }
1641    
1642            wrnMsg("get_event_par(): argument 2 is an invalid event parameter");
1643            return successResult(0);
1644        }
1645    
1646        // set_event_par() function
1647    
1648        InstrumentScriptVMFunction_set_event_par::InstrumentScriptVMFunction_set_event_par(InstrumentScriptVM* parent)
1649            : m_vm(parent)
1650        {
1651        }
1652    
1653        VMFnResult* InstrumentScriptVMFunction_set_event_par::exec(VMFnArgs* args) {
1654            AbstractEngineChannel* pEngineChannel =
1655                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1656    
1657            const ScriptID id = args->arg(0)->asInt()->evalInt();
1658            if (!id) {
1659                wrnMsg("set_event_par(): note ID for argument 1 may not be zero");
1660                return successResult();
1661            }
1662            if (!id.isNoteID()) {
1663                wrnMsg("set_event_par(): argument 1 is not a note ID");
1664                return successResult();
1665            }
1666    
1667            NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1668            if (!pNote) return successResult();
1669    
1670            const int parameter = args->arg(1)->asInt()->evalInt();
1671            const int value     = args->arg(2)->asInt()->evalInt();
1672    
1673            switch (parameter) {
1674                case EVENT_PAR_NOTE:
1675                    if (value < 0 || value > 127) {
1676                        wrnMsg("set_event_par(): note number of argument 3 is out of range");
1677                        return successResult();
1678                    }
1679                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1680                        pNote->cause.Param.Note.Key = value;
1681                        m_vm->m_event->cause.Param.Note.Key = value;
1682                    } else {
1683                        wrnMsg("set_event_par(): note number can only be changed when note is new");
1684                    }
1685                    return successResult();
1686                case EVENT_PAR_VELOCITY:
1687                    if (value < 0 || value > 127) {
1688                        wrnMsg("set_event_par(): velocity of argument 3 is out of range");
1689                        return successResult();
1690                    }
1691                    if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1692                        pNote->cause.Param.Note.Velocity = value;
1693                        m_vm->m_event->cause.Param.Note.Velocity = value;
1694                    } else {
1695                        wrnMsg("set_event_par(): velocity can only be changed when note is new");
1696                    }
1697                    return successResult();
1698                case EVENT_PAR_VOLUME:
1699                    wrnMsg("set_event_par(): changing volume by this function is currently not supported, use change_vol() instead");
1700                    return successResult();
1701                case EVENT_PAR_TUNE:
1702                    wrnMsg("set_event_par(): changing tune by this function is currently not supported, use change_tune() instead");
1703                    return successResult();
1704                case EVENT_PAR_0:
1705                    pNote->userPar[0] = value;
1706                    return successResult();
1707                case EVENT_PAR_1:
1708                    pNote->userPar[1] = value;
1709                    return successResult();
1710                case EVENT_PAR_2:
1711                    pNote->userPar[2] = value;
1712                    return successResult();
1713                case EVENT_PAR_3:
1714                    pNote->userPar[3] = value;
1715                    return successResult();
1716            }
1717    
1718            wrnMsg("set_event_par(): argument 2 is an invalid event parameter");
1719            return successResult();
1720        }
1721    
1722        // change_note() function
1723    
1724        InstrumentScriptVMFunction_change_note::InstrumentScriptVMFunction_change_note(InstrumentScriptVM* parent)
1725        : m_vm(parent)
1726        {
1727        }
1728    
1729        VMFnResult* InstrumentScriptVMFunction_change_note::exec(VMFnArgs* args) {
1730            AbstractEngineChannel* pEngineChannel =
1731                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1732    
1733            const ScriptID id = args->arg(0)->asInt()->evalInt();
1734            if (!id) {
1735                wrnMsg("change_note(): note ID for argument 1 may not be zero");
1736                return successResult();
1737            }
1738            if (!id.isNoteID()) {
1739                wrnMsg("change_note(): argument 1 is not a note ID");
1740                return successResult();
1741            }
1742    
1743            NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1744            if (!pNote) return successResult();
1745    
1746            const int value = args->arg(1)->asInt()->evalInt();
1747            if (value < 0 || value > 127) {
1748                wrnMsg("change_note(): note number of argument 2 is out of range");
1749                return successResult();
1750            }
1751    
1752            if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1753                pNote->cause.Param.Note.Key = value;
1754                m_vm->m_event->cause.Param.Note.Key = value;
1755            } else {
1756                wrnMsg("change_note(): note number can only be changed when note is new");
1757            }
1758    
1759            return successResult();
1760        }
1761    
1762        // change_velo() function
1763    
1764        InstrumentScriptVMFunction_change_velo::InstrumentScriptVMFunction_change_velo(InstrumentScriptVM* parent)
1765        : m_vm(parent)
1766        {
1767        }
1768    
1769        VMFnResult* InstrumentScriptVMFunction_change_velo::exec(VMFnArgs* args) {
1770            AbstractEngineChannel* pEngineChannel =
1771                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1772    
1773            const ScriptID id = args->arg(0)->asInt()->evalInt();
1774            if (!id) {
1775                wrnMsg("change_velo(): note ID for argument 1 may not be zero");
1776                return successResult();
1777            }
1778            if (!id.isNoteID()) {
1779                wrnMsg("change_velo(): argument 1 is not a note ID");
1780                return successResult();
1781            }
1782    
1783            NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1784            if (!pNote) return successResult();
1785    
1786            const int value = args->arg(1)->asInt()->evalInt();
1787            if (value < 0 || value > 127) {
1788                wrnMsg("change_velo(): velocity of argument 2 is out of range");
1789                return successResult();
1790            }
1791    
1792            if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1793                pNote->cause.Param.Note.Velocity = value;
1794                m_vm->m_event->cause.Param.Note.Velocity = value;
1795            } else {
1796                wrnMsg("change_velo(): velocity can only be changed when note is new");
1797          }          }
1798    
1799          return successResult();          return successResult();
# Line 789  namespace LinuxSampler { Line 1824  namespace LinuxSampler {
1824          return successResult(pNote ? EVENT_STATUS_NOTE_QUEUE : EVENT_STATUS_INACTIVE);          return successResult(pNote ? EVENT_STATUS_NOTE_QUEUE : EVENT_STATUS_INACTIVE);
1825      }      }
1826    
1827        // wait() function (overrides core wait() implementation)
1828    
1829        InstrumentScriptVMFunction_wait::InstrumentScriptVMFunction_wait(InstrumentScriptVM* parent)
1830            : CoreVMFunction_wait(parent)
1831        {    
1832        }
1833    
1834        VMFnResult* InstrumentScriptVMFunction_wait::exec(VMFnArgs* args) {
1835            InstrumentScriptVM* m_vm = (InstrumentScriptVM*) vm;
1836    
1837            // this might be set by passing 1 with the 2nd argument of built-in stop_wait() function
1838            if (m_vm->m_event->ignoreAllWaitCalls) return successResult();
1839    
1840            return CoreVMFunction_wait::exec(args);
1841        }
1842    
1843        // stop_wait() function
1844    
1845        InstrumentScriptVMFunction_stop_wait::InstrumentScriptVMFunction_stop_wait(InstrumentScriptVM* parent)
1846            : m_vm(parent)
1847        {    
1848        }
1849    
1850        VMFnResult* InstrumentScriptVMFunction_stop_wait::exec(VMFnArgs* args) {
1851            AbstractEngineChannel* pEngineChannel =
1852                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1853    
1854            const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
1855            if (!id) {
1856                wrnMsg("stop_wait(): callback ID for argument 1 may not be zero");
1857                return successResult();
1858            }
1859    
1860            RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
1861            if (!itCallback) return successResult(); // ignore if callback is i.e. not alive anymore
1862    
1863            const bool disableWaitForever =
1864                (args->argsCount() >= 2) ? (args->arg(1)->asInt()->evalInt() == 1) : false;
1865    
1866            pEngineChannel->ScheduleResumeOfScriptCallback(
1867                itCallback, m_vm->m_event->scheduleTime, disableWaitForever
1868            );
1869    
1870            return successResult();
1871        }
1872    
1873  } // namespace LinuxSampler  } // namespace LinuxSampler

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

  ViewVC Help
Powered by ViewVC