/[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 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();
# Line 620  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 632  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 650  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 670  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 698  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 710  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 728  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              Event e = m_vm->m_event->cause; // copy to get fragment time for "now"              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
777              e.Init(); // clear IDs                  pNote->Override.Resonance = fResonance;
778              e.Type = Event::type_note_synth_param;              } else { // otherwise schedule resonance change ...
779              e.Param.NoteSynthParam.NoteID   = id.noteID();                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
780              e.Param.NoteSynthParam.Type     = Event::synth_param_resonance;                  e.Init(); // clear IDs
781              e.Param.NoteSynthParam.Delta    = fResonance;                  e.Type = Event::type_note_synth_param;
782              e.Param.NoteSynthParam.Relative = false;                  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              pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
788                }
789          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
790              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
791              for (int i = 0; i < ids->arraySize(); ++i) {              for (int i = 0; i < ids->arraySize(); ++i) {
# Line 748  namespace LinuxSampler { Line 795  namespace LinuxSampler {
795                  NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );                  NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
796                  if (!pNote) continue;                  if (!pNote) continue;
797    
798                  const float fResonance = float(resonance) / float(VM_FILTER_PAR_MAX_VALUE);                  // if change_reso() was called immediately after note was triggered
799                    // then immediately apply resonance to Note object
800                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
801                  e.Init(); // clear IDs                      pNote->Override.Resonance = fResonance;
802                  e.Type = Event::type_note_synth_param;                  } else { // otherwise schedule resonance change ...
803                  e.Param.NoteSynthParam.NoteID   = id.noteID();                      Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
804                  e.Param.NoteSynthParam.Type     = Event::synth_param_resonance;                      e.Init(); // clear IDs
805                  e.Param.NoteSynthParam.Delta    = fResonance;                      e.Type = Event::type_note_synth_param;
806                  e.Param.NoteSynthParam.Relative = false;                      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);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
812                    }
813              }              }
814          }          }
815    
# Line 776  namespace LinuxSampler { Line 827  namespace LinuxSampler {
827          if (iArg == 0)          if (iArg == 0)
828              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
829          else          else
830              return INT_EXPR;              return type == INT_EXPR;
831      }      }
832    
833      VMFnResult* InstrumentScriptVMFunction_change_attack::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_attack::exec(VMFnArgs* args) {
# Line 805  namespace LinuxSampler { Line 856  namespace LinuxSampler {
856              }              }
857    
858              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
859              if (!pNote) return successResult();                          if (!pNote) return successResult();
860    
861              Event e = m_vm->m_event->cause; // copy to get fragment time for "now"              // if change_attack() was called immediately after note was triggered
862              e.Init(); // clear IDs              // then immediately apply attack to Note object
863              e.Type = Event::type_note_synth_param;              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
864              e.Param.NoteSynthParam.NoteID   = id.noteID();                  pNote->Override.Attack = fAttack;
865              e.Param.NoteSynthParam.Type     = Event::synth_param_attack;              } else { // otherwise schedule attack change ...
866              e.Param.NoteSynthParam.Delta    = fAttack;                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
867              e.Param.NoteSynthParam.Relative = false;                  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);                  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 825  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                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                  // if change_attack() was called immediately after note was triggered
886                  e.Init(); // clear IDs                  // then immediately apply attack to Note object
887                  e.Type = Event::type_note_synth_param;                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
888                  e.Param.NoteSynthParam.NoteID   = id.noteID();                      pNote->Override.Attack = fAttack;
889                  e.Param.NoteSynthParam.Type     = Event::synth_param_attack;                  } else { // otherwise schedule attack change ...
890                  e.Param.NoteSynthParam.Delta    = fAttack;                      Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
891                  e.Param.NoteSynthParam.Relative = false;                      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);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
899                    }
900              }              }
901          }          }
902    
# Line 851  namespace LinuxSampler { Line 914  namespace LinuxSampler {
914          if (iArg == 0)          if (iArg == 0)
915              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
916          else          else
917              return INT_EXPR;              return type == INT_EXPR;
918      }      }
919    
920      VMFnResult* InstrumentScriptVMFunction_change_decay::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_decay::exec(VMFnArgs* args) {
# Line 880  namespace LinuxSampler { Line 943  namespace LinuxSampler {
943              }              }
944    
945              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
946              if (!pNote) return successResult();                          if (!pNote) return successResult();
947    
948              Event e = m_vm->m_event->cause; // copy to get fragment time for "now"              // if change_decay() was called immediately after note was triggered
949              e.Init(); // clear IDs              // then immediately apply decay to Note object
950              e.Type = Event::type_note_synth_param;              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
951              e.Param.NoteSynthParam.NoteID   = id.noteID();                  pNote->Override.Decay = fDecay;
952              e.Param.NoteSynthParam.Type     = Event::synth_param_decay;              } else { // otherwise schedule decay change ...
953              e.Param.NoteSynthParam.Delta    = fDecay;                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
954              e.Param.NoteSynthParam.Relative = false;                  e.Init(); // clear IDs
955                    e.Type = Event::type_note_synth_param;
956                    e.Param.NoteSynthParam.NoteID   = id.noteID();
957                    e.Param.NoteSynthParam.Type     = Event::synth_param_decay;
958                    e.Param.NoteSynthParam.Delta    = fDecay;
959                    e.Param.NoteSynthParam.Relative = false;
960    
961              pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
962                }
963          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
964              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
965              for (int i = 0; i < ids->arraySize(); ++i) {              for (int i = 0; i < ids->arraySize(); ++i) {
# Line 900  namespace LinuxSampler { Line 969  namespace LinuxSampler {
969                  NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );                  NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
970                  if (!pNote) continue;                  if (!pNote) continue;
971    
972                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                  // if change_decay() was called immediately after note was triggered
973                  e.Init(); // clear IDs                  // then immediately apply decay to Note object
974                  e.Type = Event::type_note_synth_param;                  if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
975                  e.Param.NoteSynthParam.NoteID   = id.noteID();                      pNote->Override.Decay = fDecay;
976                  e.Param.NoteSynthParam.Type     = Event::synth_param_decay;                  } else { // otherwise schedule decay change ...
977                  e.Param.NoteSynthParam.Delta    = fDecay;                      Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
978                  e.Param.NoteSynthParam.Relative = false;                      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);                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
986                    }
987              }              }
988          }          }
989    
# Line 926  namespace LinuxSampler { Line 1001  namespace LinuxSampler {
1001          if (iArg == 0)          if (iArg == 0)
1002              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
1003          else          else
1004              return INT_EXPR;              return type == INT_EXPR;
1005      }      }
1006    
1007      VMFnResult* InstrumentScriptVMFunction_change_release::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_release::exec(VMFnArgs* args) {
# Line 955  namespace LinuxSampler { Line 1030  namespace LinuxSampler {
1030              }              }
1031    
1032              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1033              if (!pNote) return successResult();                          if (!pNote) return successResult();
1034    
1035              Event e = m_vm->m_event->cause; // copy to get fragment time for "now"              // if change_release() was called immediately after note was triggered
1036              e.Init(); // clear IDs              // then immediately apply relase to Note object
1037              e.Type = Event::type_note_synth_param;              if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1038              e.Param.NoteSynthParam.NoteID   = id.noteID();                  pNote->Override.Release = fRelease;
1039              e.Param.NoteSynthParam.Type     = Event::synth_param_release;              } else { // otherwise schedule release change ...
1040              e.Param.NoteSynthParam.Delta    = fRelease;                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1041              e.Param.NoteSynthParam.Relative = false;                  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);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
1049                }
1050          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1051              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1052              for (int i = 0; i < ids->arraySize(); ++i) {              for (int i = 0; i < ids->arraySize(); ++i) {
# Line 975  namespace LinuxSampler { Line 1056  namespace LinuxSampler {
1056                  NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );                  NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1057                  if (!pNote) continue;                  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"                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1134                  e.Init(); // clear IDs                  e.Init(); // clear IDs
1135                  e.Type = Event::type_note_synth_param;                  e.Type = Event::type_note_synth_param;
1136                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
1137                  e.Param.NoteSynthParam.Type     = Event::synth_param_release;                  e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1138                  e.Param.NoteSynthParam.Delta    = fRelease;                  e.Param.NoteSynthParam.Delta    = fValue;
1139                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Relative = false;
1140    
1141                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  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;
1382    
1383                    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 1055  namespace LinuxSampler { Line 1864  namespace LinuxSampler {
1864              (args->argsCount() >= 2) ? (args->arg(1)->asInt()->evalInt() == 1) : false;              (args->argsCount() >= 2) ? (args->arg(1)->asInt()->evalInt() == 1) : false;
1865    
1866          pEngineChannel->ScheduleResumeOfScriptCallback(          pEngineChannel->ScheduleResumeOfScriptCallback(
1867              itCallback, m_vm->m_event->cause.SchedTime(), disableWaitForever              itCallback, m_vm->m_event->scheduleTime, disableWaitForever
1868          );          );
1869    
1870          return successResult();          return successResult();

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

  ViewVC Help
Powered by ViewVC