/[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 3188 by schoenebeck, Fri May 19 14:23:12 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 359  namespace LinuxSampler { Line 359  namespace LinuxSampler {
359          if (iArg == 0)          if (iArg == 0)
360              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
361          else          else
362              return INT_EXPR;              return type == INT_EXPR;
363      }      }
364    
365      VMFnResult* InstrumentScriptVMFunction_change_vol::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_vol::exec(VMFnArgs* args) {
366          int volume = args->arg(1)->asInt()->evalInt(); // volume change in milli dB          int volume = args->arg(1)->asInt()->evalInt(); // volume change in milli dB
367          bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;          bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
368            const float fVolumeLin = RTMath::DecibelToLinRatio(float(volume) / 1000.f);
369    
370          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
371              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 383  namespace LinuxSampler { Line 384  namespace LinuxSampler {
384              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
385              if (!pNote) return successResult();              if (!pNote) return successResult();
386    
387              const float fVolumeLin = RTMath::DecibelToLinRatio(float(volume) / 1000.f);              // if change_vol() was called immediately after note was triggered
388              // commented out, performed by EngineBase::ProcessNoteSynthParam() for time accuracy behavior              // then immediately apply the volume to note object
389              /*if (relative)              if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
390                  pNote->Override.Volume *= fVolumeLin;                  if (relative)
             else  
                 pNote->Override.Volume = fVolumeLin;*/  
   
             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)  
391                      pNote->Override.Volume *= fVolumeLin;                      pNote->Override.Volume *= fVolumeLin;
392                  else                  else
393                      pNote->Override.Volume = fVolumeLin;*/                      pNote->Override.Volume = fVolumeLin;
394                } else { // otherwise schedule the volume change ...
395                  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"
396                  e.Init(); // clear IDs                  e.Init(); // clear IDs
397                  e.Type = Event::type_note_synth_param;                  e.Type = Event::type_note_synth_param;
# Line 425  namespace LinuxSampler { Line 402  namespace LinuxSampler {
402    
403                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
404              }              }
405            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
406                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
407                for (int i = 0; i < ids->arraySize(); ++i) {
408                    const ScriptID id = ids->evalIntElement(i);
409                    if (!id || !id.isNoteID()) continue;
410    
411                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
412                    if (!pNote) continue;
413    
414                    // if change_vol() was called immediately after note was triggered
415                    // then immediately apply the volume to Note object
416                    if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
417                        if (relative)
418                            pNote->Override.Volume *= fVolumeLin;
419                        else
420                            pNote->Override.Volume = fVolumeLin;
421                    } else { // otherwise schedule the volume change ...
422                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
423                        e.Init(); // clear IDs
424                        e.Type = Event::type_note_synth_param;
425                        e.Param.NoteSynthParam.NoteID   = id.noteID();
426                        e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
427                        e.Param.NoteSynthParam.Delta    = fVolumeLin;
428                        e.Param.NoteSynthParam.Relative = relative;
429    
430                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
431                    }
432                }
433          }          }
434    
435          return successResult();          return successResult();
# Line 441  namespace LinuxSampler { Line 446  namespace LinuxSampler {
446          if (iArg == 0)          if (iArg == 0)
447              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
448          else          else
449              return INT_EXPR;              return type == INT_EXPR;
450      }      }
451    
452      VMFnResult* InstrumentScriptVMFunction_change_tune::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_tune::exec(VMFnArgs* args) {
453          int tune = args->arg(1)->asInt()->evalInt(); // tuning change in milli cents          int tune = args->arg(1)->asInt()->evalInt(); // tuning change in milli cents
454          bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;          bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
455            const float fFreqRatio = RTMath::CentsToFreqRatioUnlimited(float(tune) / 1000.f);
456    
457          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
458              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 465  namespace LinuxSampler { Line 471  namespace LinuxSampler {
471              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
472              if (!pNote) return successResult();              if (!pNote) return successResult();
473    
474              const float fFreqRatio = RTMath::CentsToFreqRatioUnlimited(float(tune) / 1000.f);              // if change_tune() was called immediately after note was triggered
475              // commented out, performed by EngineBase::ProcessNoteSynthParam() for time accuracy behavior              // then immediately apply the tuning to Note object
476              /*if (relative)              if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
477                  pNote->Override.Pitch *= fFreqRatio;                  if (relative)
             else  
                 pNote->Override.Pitch = fFreqRatio;*/  
   
             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)  
478                      pNote->Override.Pitch *= fFreqRatio;                      pNote->Override.Pitch *= fFreqRatio;
479                  else                  else
480                      pNote->Override.Pitch = fFreqRatio;*/                      pNote->Override.Pitch = fFreqRatio;
481                } else { // otherwise schedule tuning change ...
482                  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"
483                  e.Init(); // clear IDs                  e.Init(); // clear IDs
484                  e.Type = Event::type_note_synth_param;                  e.Type = Event::type_note_synth_param;
# Line 507  namespace LinuxSampler { Line 489  namespace LinuxSampler {
489    
490                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
491              }              }
492            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
493                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
494                for (int i = 0; i < ids->arraySize(); ++i) {
495                    const ScriptID id = ids->evalIntElement(i);
496                    if (!id || !id.isNoteID()) continue;
497    
498                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
499                    if (!pNote) continue;
500    
501                    // if change_tune() was called immediately after note was triggered
502                    // then immediately apply the tuning to Note object
503                    if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
504                        if (relative)
505                            pNote->Override.Pitch *= fFreqRatio;
506                        else
507                            pNote->Override.Pitch = fFreqRatio;
508                    } else { // otherwise schedule tuning change ...
509                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
510                        e.Init(); // clear IDs
511                        e.Type = Event::type_note_synth_param;
512                        e.Param.NoteSynthParam.NoteID   = id.noteID();
513                        e.Param.NoteSynthParam.Type     = Event::synth_param_pitch;
514                        e.Param.NoteSynthParam.Delta    = fFreqRatio;
515                        e.Param.NoteSynthParam.Relative = relative;
516    
517                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
518                    }
519                }
520          }          }
521    
522          return successResult();          return successResult();
# Line 523  namespace LinuxSampler { Line 533  namespace LinuxSampler {
533          if (iArg == 0)          if (iArg == 0)
534              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
535          else          else
536              return INT_EXPR;              return type == INT_EXPR;
537      }      }
538    
539      VMFnResult* InstrumentScriptVMFunction_change_pan::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_pan::exec(VMFnArgs* args) {
# Line 537  namespace LinuxSampler { Line 547  namespace LinuxSampler {
547              wrnMsg("change_pan(): argument 2 may not be smaller than -1000");              wrnMsg("change_pan(): argument 2 may not be smaller than -1000");
548              pan = -1000;              pan = -1000;
549          }          }
550            const float fPan = float(pan) / 1000.f;
551    
552          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
553              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 555  namespace LinuxSampler { Line 566  namespace LinuxSampler {
566              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
567              if (!pNote) return successResult();              if (!pNote) return successResult();
568    
569              const float fPan = float(pan) / 1000.f;              // if change_pan() was called immediately after note was triggered
570              // commented out, performed by EngineBase::ProcessNoteSynthParam() for time accuracy behavior              // then immediately apply the panning to Note object
571              /*if (relative) {              if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
572                  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) {  
573                      pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources);                      pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources);
574                  } else {                  } else {
575                      pNote->Override.Pan = fPan;                      pNote->Override.Pan = fPan;
576                      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
577                  }*/                  }
578                } else { // otherwise schedule panning change ...
579                  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"
580                  e.Init(); // clear IDs                  e.Init(); // clear IDs
581                  e.Type = Event::type_note_synth_param;                  e.Type = Event::type_note_synth_param;
# Line 601  namespace LinuxSampler { Line 586  namespace LinuxSampler {
586    
587                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
588              }              }
589            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
590                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
591                for (int i = 0; i < ids->arraySize(); ++i) {
592                    const ScriptID id = ids->evalIntElement(i);
593                    if (!id || !id.isNoteID()) continue;
594    
595                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
596                    if (!pNote) continue;
597    
598                    // if change_pan() was called immediately after note was triggered
599                    // then immediately apply the panning to Note object
600                    if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
601                        if (relative) {
602                            pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources);
603                        } else {
604                            pNote->Override.Pan = fPan;
605                            pNote->Override.PanSources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set
606                        }
607                    } else { // otherwise schedule panning change ...
608                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
609                        e.Init(); // clear IDs
610                        e.Type = Event::type_note_synth_param;
611                        e.Param.NoteSynthParam.NoteID   = id.noteID();
612                        e.Param.NoteSynthParam.Type     = Event::synth_param_pan;
613                        e.Param.NoteSynthParam.Delta    = fPan;
614                        e.Param.NoteSynthParam.Relative = relative;
615    
616                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
617                    }
618                }
619          }          }
620    
621          return successResult();          return successResult();
622      }      }
623    
624      #define VM_FILTER_PAR_MAX_VALUE 1000000      #define VM_FILTER_PAR_MAX_VALUE 1000000
625        #define VM_EG_PAR_MAX_VALUE 1000000
626    
627      // change_cutoff() function      // change_cutoff() function
628    
# Line 619  namespace LinuxSampler { Line 635  namespace LinuxSampler {
635          if (iArg == 0)          if (iArg == 0)
636              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
637          else          else
638              return INT_EXPR;              return type == INT_EXPR;
639      }      }
640    
641      VMFnResult* InstrumentScriptVMFunction_change_cutoff::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_cutoff::exec(VMFnArgs* args) {
# Line 631  namespace LinuxSampler { Line 647  namespace LinuxSampler {
647              wrnMsg("change_cutoff(): argument 2 may not be negative");              wrnMsg("change_cutoff(): argument 2 may not be negative");
648              cutoff = 0;              cutoff = 0;
649          }          }
650            const float fCutoff = float(cutoff) / float(VM_FILTER_PAR_MAX_VALUE);
651    
652          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
653              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 649  namespace LinuxSampler { Line 666  namespace LinuxSampler {
666              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
667              if (!pNote) return successResult();              if (!pNote) return successResult();
668    
669              const float fCutoff = float(cutoff) / float(VM_FILTER_PAR_MAX_VALUE);              // if change_cutoff() was called immediately after note was triggered
670                // then immediately apply cutoff to Note object
671              Event e = m_vm->m_event->cause; // copy to get fragment time for "now"              if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
672              e.Init(); // clear IDs                  pNote->Override.Cutoff = fCutoff;
673              e.Type = Event::type_note_synth_param;              } else { // otherwise schedule cutoff change ...
674              e.Param.NoteSynthParam.NoteID   = id.noteID();                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
675              e.Param.NoteSynthParam.Type     = Event::synth_param_cutoff;                  e.Init(); // clear IDs
676              e.Param.NoteSynthParam.Delta    = fCutoff;                  e.Type = Event::type_note_synth_param;
677              e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.NoteID   = id.noteID();
678                    e.Param.NoteSynthParam.Type     = Event::synth_param_cutoff;
679                    e.Param.NoteSynthParam.Delta    = fCutoff;
680                    e.Param.NoteSynthParam.Relative = false;
681    
682              pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
683                }
684          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
685              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
686              for (int i = 0; i < ids->arraySize(); ++i) {              for (int i = 0; i < ids->arraySize(); ++i) {
# Line 669  namespace LinuxSampler { Line 690  namespace LinuxSampler {
690                  NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );                  NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
691                  if (!pNote) continue;                  if (!pNote) continue;
692    
693                  const float fCutoff = float(cutoff) / float(VM_FILTER_PAR_MAX_VALUE);                  // if change_cutoff() was called immediately after note was triggered
694                    // then immediately apply cutoff to Note object
695                    if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
696                        pNote->Override.Cutoff = fCutoff;
697                    } else { // otherwise schedule cutoff change ...
698                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
699                        e.Init(); // clear IDs
700                        e.Type = Event::type_note_synth_param;
701                        e.Param.NoteSynthParam.NoteID   = id.noteID();
702                        e.Param.NoteSynthParam.Type     = Event::synth_param_cutoff;
703                        e.Param.NoteSynthParam.Delta    = fCutoff;
704                        e.Param.NoteSynthParam.Relative = false;
705    
706                  Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                      pEngineChannel->ScheduleEventMicroSec(&e, 0);
707                  e.Init(); // clear IDs                  }
                 e.Type = Event::type_note_synth_param;  
                 e.Param.NoteSynthParam.NoteID   = id.noteID();  
                 e.Param.NoteSynthParam.Type     = Event::synth_param_cutoff;  
                 e.Param.NoteSynthParam.Delta    = fCutoff;  
                 e.Param.NoteSynthParam.Relative = false;  
   
                 pEngineChannel->ScheduleEventMicroSec(&e, 0);  
708              }              }
709          }          }
710    
# Line 697  namespace LinuxSampler { Line 722  namespace LinuxSampler {
722          if (iArg == 0)          if (iArg == 0)
723              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
724          else          else
725              return INT_EXPR;              return type == INT_EXPR;
726      }      }
727    
728      VMFnResult* InstrumentScriptVMFunction_change_reso::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_reso::exec(VMFnArgs* args) {
# Line 709  namespace LinuxSampler { Line 734  namespace LinuxSampler {
734              wrnMsg("change_reso(): argument 2 may not be negative");              wrnMsg("change_reso(): argument 2 may not be negative");
735              resonance = 0;              resonance = 0;
736          }          }
737            const float fResonance = float(resonance) / float(VM_FILTER_PAR_MAX_VALUE);
738    
739          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
740              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 727  namespace LinuxSampler { Line 753  namespace LinuxSampler {
753              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );              NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
754              if (!pNote) return successResult();              if (!pNote) return successResult();
755    
756              const float fResonance = float(resonance) / float(VM_FILTER_PAR_MAX_VALUE);              // if change_reso() was called immediately after note was triggered
757                // then immediately apply resonance to Note object
758                if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
759                    pNote->Override.Resonance = fResonance;
760                } else { // otherwise schedule resonance change ...
761                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
762                    e.Init(); // clear IDs
763                    e.Type = Event::type_note_synth_param;
764                    e.Param.NoteSynthParam.NoteID   = id.noteID();
765                    e.Param.NoteSynthParam.Type     = Event::synth_param_resonance;
766                    e.Param.NoteSynthParam.Delta    = fResonance;
767                    e.Param.NoteSynthParam.Relative = false;
768    
769              Event e = m_vm->m_event->cause; // copy to get fragment time for "now"                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
770              e.Init(); // clear IDs              }
771              e.Type = Event::type_note_synth_param;          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
772              e.Param.NoteSynthParam.NoteID   = id.noteID();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
773              e.Param.NoteSynthParam.Type     = Event::synth_param_resonance;              for (int i = 0; i < ids->arraySize(); ++i) {
774              e.Param.NoteSynthParam.Delta    = fResonance;                  const ScriptID id = ids->evalIntElement(i);
775              e.Param.NoteSynthParam.Relative = false;                  if (!id || !id.isNoteID()) continue;
776    
777              pEngineChannel->ScheduleEventMicroSec(&e, 0);                  NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
778                    if (!pNote) continue;
779    
780                    // if change_reso() was called immediately after note was triggered
781                    // then immediately apply resonance to Note object
782                    if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
783                        pNote->Override.Resonance = fResonance;
784                    } else { // otherwise schedule resonance change ...
785                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
786                        e.Init(); // clear IDs
787                        e.Type = Event::type_note_synth_param;
788                        e.Param.NoteSynthParam.NoteID   = id.noteID();
789                        e.Param.NoteSynthParam.Type     = Event::synth_param_resonance;
790                        e.Param.NoteSynthParam.Delta    = fResonance;
791                        e.Param.NoteSynthParam.Relative = false;
792    
793                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
794                    }
795                }
796            }
797    
798            return successResult();
799        }
800        
801        // change_attack() function
802    
803        InstrumentScriptVMFunction_change_attack::InstrumentScriptVMFunction_change_attack(InstrumentScriptVM* parent)
804            : m_vm(parent)
805        {
806        }
807    
808        bool InstrumentScriptVMFunction_change_attack::acceptsArgType(int iArg, ExprType_t type) const {
809            if (iArg == 0)
810                return type == INT_EXPR || type == INT_ARR_EXPR;
811            else
812                return type == INT_EXPR;
813        }
814    
815        VMFnResult* InstrumentScriptVMFunction_change_attack::exec(VMFnArgs* args) {
816            int attack = args->arg(1)->asInt()->evalInt();
817            if (attack > VM_EG_PAR_MAX_VALUE) {
818                wrnMsg("change_attack(): argument 2 may not be larger than 1000000");
819                attack = VM_EG_PAR_MAX_VALUE;
820            } else if (attack < 0) {
821                wrnMsg("change_attack(): argument 2 may not be negative");
822                attack = 0;
823            }
824            const float fAttack = float(attack) / float(VM_EG_PAR_MAX_VALUE);
825    
826            AbstractEngineChannel* pEngineChannel =
827                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
828    
829            if (args->arg(0)->exprType() == INT_EXPR) {
830                const ScriptID id = args->arg(0)->asInt()->evalInt();
831                if (!id) {
832                    wrnMsg("change_attack(): note ID for argument 1 may not be zero");
833                    return successResult();
834                }
835                if (!id.isNoteID()) {
836                    wrnMsg("change_attack(): argument 1 is not a note ID");
837                    return successResult();
838                }
839    
840                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
841                if (!pNote) return successResult();
842    
843                // if change_attack() was called immediately after note was triggered
844                // then immediately apply attack to Note object
845                if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
846                    pNote->Override.Attack = fAttack;
847                } else { // otherwise schedule attack change ...
848                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
849                    e.Init(); // clear IDs
850                    e.Type = Event::type_note_synth_param;
851                    e.Param.NoteSynthParam.NoteID   = id.noteID();
852                    e.Param.NoteSynthParam.Type     = Event::synth_param_attack;
853                    e.Param.NoteSynthParam.Delta    = fAttack;
854                    e.Param.NoteSynthParam.Relative = false;
855    
856                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
857                }
858          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {          } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
859              VMIntArrayExpr* ids = args->arg(0)->asIntArray();              VMIntArrayExpr* ids = args->arg(0)->asIntArray();
860              for (int i = 0; i < ids->arraySize(); ++i) {              for (int i = 0; i < ids->arraySize(); ++i) {
# Line 747  namespace LinuxSampler { Line 864  namespace LinuxSampler {
864                  NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );                  NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
865                  if (!pNote) continue;                  if (!pNote) continue;
866    
867                  const float fResonance = float(resonance) / float(VM_FILTER_PAR_MAX_VALUE);                  // if change_attack() was called immediately after note was triggered
868                    // then immediately apply attack to Note object
869                    if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
870                        pNote->Override.Attack = fAttack;
871                    } else { // otherwise schedule attack change ...
872                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
873                        e.Init(); // clear IDs
874                        e.Type = Event::type_note_synth_param;
875                        e.Param.NoteSynthParam.NoteID   = id.noteID();
876                        e.Param.NoteSynthParam.Type     = Event::synth_param_attack;
877                        e.Param.NoteSynthParam.Delta    = fAttack;
878                        e.Param.NoteSynthParam.Relative = false;
879    
880                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
881                    }
882                }
883            }
884    
885            return successResult();
886        }
887    
888        // change_decay() function
889        
890        InstrumentScriptVMFunction_change_decay::InstrumentScriptVMFunction_change_decay(InstrumentScriptVM* parent)
891            : m_vm(parent)
892        {
893        }
894    
895        bool InstrumentScriptVMFunction_change_decay::acceptsArgType(int iArg, ExprType_t type) const {
896            if (iArg == 0)
897                return type == INT_EXPR || type == INT_ARR_EXPR;
898            else
899                return type == INT_EXPR;
900        }
901    
902        VMFnResult* InstrumentScriptVMFunction_change_decay::exec(VMFnArgs* args) {
903            int decay = args->arg(1)->asInt()->evalInt();
904            if (decay > VM_EG_PAR_MAX_VALUE) {
905                wrnMsg("change_decay(): argument 2 may not be larger than 1000000");
906                decay = VM_EG_PAR_MAX_VALUE;
907            } else if (decay < 0) {
908                wrnMsg("change_decay(): argument 2 may not be negative");
909                decay = 0;
910            }
911            const float fDecay = float(decay) / float(VM_EG_PAR_MAX_VALUE);
912    
913            AbstractEngineChannel* pEngineChannel =
914                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
915    
916            if (args->arg(0)->exprType() == INT_EXPR) {
917                const ScriptID id = args->arg(0)->asInt()->evalInt();
918                if (!id) {
919                    wrnMsg("change_decay(): note ID for argument 1 may not be zero");
920                    return successResult();
921                }
922                if (!id.isNoteID()) {
923                    wrnMsg("change_decay(): argument 1 is not a note ID");
924                    return successResult();
925                }
926    
927                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
928                if (!pNote) return successResult();
929    
930                // if change_decay() was called immediately after note was triggered
931                // then immediately apply decay to Note object
932                if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
933                    pNote->Override.Decay = fDecay;
934                } else { // otherwise schedule decay change ...
935                  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"
936                  e.Init(); // clear IDs                  e.Init(); // clear IDs
937                  e.Type = Event::type_note_synth_param;                  e.Type = Event::type_note_synth_param;
938                  e.Param.NoteSynthParam.NoteID   = id.noteID();                  e.Param.NoteSynthParam.NoteID   = id.noteID();
939                  e.Param.NoteSynthParam.Type     = Event::synth_param_resonance;                  e.Param.NoteSynthParam.Type     = Event::synth_param_decay;
940                  e.Param.NoteSynthParam.Delta    = fResonance;                  e.Param.NoteSynthParam.Delta    = fDecay;
941                  e.Param.NoteSynthParam.Relative = false;                  e.Param.NoteSynthParam.Relative = false;
942    
943                  pEngineChannel->ScheduleEventMicroSec(&e, 0);                  pEngineChannel->ScheduleEventMicroSec(&e, 0);
944              }              }
945            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
946                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
947                for (int i = 0; i < ids->arraySize(); ++i) {
948                    const ScriptID id = ids->evalIntElement(i);
949                    if (!id || !id.isNoteID()) continue;
950    
951                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
952                    if (!pNote) continue;
953    
954                    // if change_decay() was called immediately after note was triggered
955                    // then immediately apply decay to Note object
956                    if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
957                        pNote->Override.Decay = fDecay;
958                    } else { // otherwise schedule decay change ...
959                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
960                        e.Init(); // clear IDs
961                        e.Type = Event::type_note_synth_param;
962                        e.Param.NoteSynthParam.NoteID   = id.noteID();
963                        e.Param.NoteSynthParam.Type     = Event::synth_param_decay;
964                        e.Param.NoteSynthParam.Delta    = fDecay;
965                        e.Param.NoteSynthParam.Relative = false;
966    
967                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
968                    }
969                }
970            }
971    
972            return successResult();
973        }
974    
975        // change_release() function
976        
977        InstrumentScriptVMFunction_change_release::InstrumentScriptVMFunction_change_release(InstrumentScriptVM* parent)
978            : m_vm(parent)
979        {
980        }
981    
982        bool InstrumentScriptVMFunction_change_release::acceptsArgType(int iArg, ExprType_t type) const {
983            if (iArg == 0)
984                return type == INT_EXPR || type == INT_ARR_EXPR;
985            else
986                return type == INT_EXPR;
987        }
988    
989        VMFnResult* InstrumentScriptVMFunction_change_release::exec(VMFnArgs* args) {
990            int release = args->arg(1)->asInt()->evalInt();
991            if (release > VM_EG_PAR_MAX_VALUE) {
992                wrnMsg("change_release(): argument 2 may not be larger than 1000000");
993                release = VM_EG_PAR_MAX_VALUE;
994            } else if (release < 0) {
995                wrnMsg("change_release(): argument 2 may not be negative");
996                release = 0;
997            }
998            const float fRelease = float(release) / float(VM_EG_PAR_MAX_VALUE);
999    
1000            AbstractEngineChannel* pEngineChannel =
1001                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1002    
1003            if (args->arg(0)->exprType() == INT_EXPR) {
1004                const ScriptID id = args->arg(0)->asInt()->evalInt();
1005                if (!id) {
1006                    wrnMsg("change_release(): note ID for argument 1 may not be zero");
1007                    return successResult();
1008                }
1009                if (!id.isNoteID()) {
1010                    wrnMsg("change_release(): argument 1 is not a note ID");
1011                    return successResult();
1012                }
1013    
1014                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1015                if (!pNote) return successResult();
1016    
1017                // if change_release() was called immediately after note was triggered
1018                // then immediately apply relase to Note object
1019                if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
1020                    pNote->Override.Release = fRelease;
1021                } else { // otherwise schedule release change ...
1022                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1023                    e.Init(); // clear IDs
1024                    e.Type = Event::type_note_synth_param;
1025                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1026                    e.Param.NoteSynthParam.Type     = Event::synth_param_release;
1027                    e.Param.NoteSynthParam.Delta    = fRelease;
1028                    e.Param.NoteSynthParam.Relative = false;
1029    
1030                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
1031                }
1032            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1033                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1034                for (int i = 0; i < ids->arraySize(); ++i) {
1035                    const ScriptID id = ids->evalIntElement(i);
1036                    if (!id || !id.isNoteID()) continue;
1037    
1038                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1039                    if (!pNote) continue;
1040    
1041                    // if change_release() was called immediately after note was triggered
1042                    // then immediately apply relase to Note object
1043                    if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
1044                        pNote->Override.Release = fRelease;
1045                    } else { // otherwise schedule release change ...
1046                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1047                        e.Init(); // clear IDs
1048                        e.Type = Event::type_note_synth_param;
1049                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1050                        e.Param.NoteSynthParam.Type     = Event::synth_param_release;
1051                        e.Param.NoteSynthParam.Delta    = fRelease;
1052                        e.Param.NoteSynthParam.Relative = false;
1053    
1054                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
1055                    }
1056                }
1057            }
1058    
1059            return successResult();
1060        }
1061    
1062        bool VMChangeSynthParamFunction::acceptsArgType(int iArg, ExprType_t type) const {
1063            if (iArg == 0)
1064                return type == INT_EXPR || type == INT_ARR_EXPR;
1065            else
1066                return type == INT_EXPR;
1067        }
1068    
1069        template<float NoteBase::_Override::*T_noteParam, int T_synthParam,
1070                 bool T_isNormalizedParam, int T_maxValue, int T_minValue>
1071        VMFnResult* VMChangeSynthParamFunction::execTemplate(VMFnArgs* args, const char* functionName) {
1072            int value = args->arg(1)->asInt()->evalInt();
1073            if (value > T_maxValue) {
1074                wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValue));
1075                value = T_maxValue;
1076            } else if (value < T_minValue) {
1077                if (T_minValue == 0)
1078                    wrnMsg(String(functionName) + "(): argument 2 may not be negative");
1079                else
1080                    wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValue));
1081                value = T_minValue;
1082            }
1083            const float fValue = (T_isNormalizedParam) ?
1084                float(value) / float(T_maxValue) : // convert to 0.0 .. 1.0 value range
1085                float(value) / 1000000.f; // assuming microseconds here, convert to seconds
1086    
1087            AbstractEngineChannel* pEngineChannel =
1088                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1089    
1090            if (args->arg(0)->exprType() == INT_EXPR) {
1091                const ScriptID id = args->arg(0)->asInt()->evalInt();
1092                if (!id) {
1093                    wrnMsg(String(functionName) + "(): note ID for argument 1 may not be zero");
1094                    return successResult();
1095                }
1096                if (!id.isNoteID()) {
1097                    wrnMsg(String(functionName) + "(): argument 1 is not a note ID");
1098                    return successResult();
1099                }
1100    
1101                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1102                if (!pNote) return successResult();
1103    
1104                // if this change_*() script function was called immediately after
1105                // note was triggered then immediately apply the synth parameter
1106                // change to Note object
1107                if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
1108                    pNote->Override.*T_noteParam = fValue;
1109                } else { // otherwise schedule this synth parameter change ...
1110                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1111                    e.Init(); // clear IDs
1112                    e.Type = Event::type_note_synth_param;
1113                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1114                    e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1115                    e.Param.NoteSynthParam.Delta    = fValue;
1116                    e.Param.NoteSynthParam.Relative = false;
1117    
1118                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
1119                }
1120            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1121                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1122                for (int i = 0; i < ids->arraySize(); ++i) {
1123                    const ScriptID id = ids->evalIntElement(i);
1124                    if (!id || !id.isNoteID()) continue;
1125    
1126                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1127                    if (!pNote) continue;
1128    
1129                    // if this change_*() script function was called immediately after
1130                    // note was triggered then immediately apply the synth parameter
1131                    // change to Note object
1132                    if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
1133                        pNote->Override.*T_noteParam = fValue;
1134                    } else { // otherwise schedule this synth parameter change ...
1135                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1136                        e.Init(); // clear IDs
1137                        e.Type = Event::type_note_synth_param;
1138                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1139                        e.Param.NoteSynthParam.Type     = (Event::synth_param_t) T_synthParam;
1140                        e.Param.NoteSynthParam.Delta    = fValue;
1141                        e.Param.NoteSynthParam.Relative = false;
1142    
1143                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
1144                    }
1145                }
1146            }
1147    
1148            return successResult();
1149        }
1150    
1151        // change_amp_lfo_depth() function
1152    
1153        VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_depth::exec(VMFnArgs* args) {
1154            return VMChangeSynthParamFunction::execTemplate<
1155                        &NoteBase::_Override::AmpLFODepth,
1156                        Event::synth_param_amp_lfo_depth, true>( args, "change_amp_lfo_depth" );
1157        }
1158    
1159        // change_amp_lfo_freq() function
1160    
1161        VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_freq::exec(VMFnArgs* args) {
1162            return VMChangeSynthParamFunction::execTemplate<
1163                        &NoteBase::_Override::AmpLFOFreq,
1164                        Event::synth_param_amp_lfo_freq, true>( args, "change_amp_lfo_freq" );
1165        }
1166    
1167        // change_pitch_lfo_depth() function
1168    
1169        VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_depth::exec(VMFnArgs* args) {
1170            return VMChangeSynthParamFunction::execTemplate<
1171                        &NoteBase::_Override::PitchLFODepth,
1172                        Event::synth_param_pitch_lfo_depth, true>( args, "change_pitch_lfo_depth" );
1173        }
1174    
1175        // change_pitch_lfo_freq() function
1176    
1177        VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_freq::exec(VMFnArgs* args) {
1178            return VMChangeSynthParamFunction::execTemplate<
1179                        &NoteBase::_Override::PitchLFOFreq,
1180                        Event::synth_param_pitch_lfo_freq, true>( args, "change_pitch_lfo_freq" );
1181        }
1182    
1183        // change_vol_time() function
1184    
1185        VMFnResult* InstrumentScriptVMFunction_change_vol_time::exec(VMFnArgs* args) {
1186            return VMChangeSynthParamFunction::execTemplate<
1187                        &NoteBase::_Override::VolumeTime,
1188                        Event::synth_param_volume_time, false>( args, "change_vol_time" );
1189        }
1190    
1191        // change_tune_time() function
1192    
1193        VMFnResult* InstrumentScriptVMFunction_change_tune_time::exec(VMFnArgs* args) {
1194            return VMChangeSynthParamFunction::execTemplate<
1195                        &NoteBase::_Override::PitchTime,
1196                        Event::synth_param_pitch_time, false>( args, "change_tune_time" );
1197        }
1198    
1199        // fade_in() function
1200    
1201        InstrumentScriptVMFunction_fade_in::InstrumentScriptVMFunction_fade_in(InstrumentScriptVM* parent)
1202            : m_vm(parent)
1203        {
1204        }
1205    
1206        bool InstrumentScriptVMFunction_fade_in::acceptsArgType(int iArg, ExprType_t type) const {
1207            if (iArg == 0)
1208                return type == INT_EXPR || type == INT_ARR_EXPR;
1209            else
1210                return type == INT_EXPR;
1211        }
1212    
1213        VMFnResult* InstrumentScriptVMFunction_fade_in::exec(VMFnArgs* args) {
1214            int duration = args->arg(1)->asInt()->evalInt();
1215            if (duration < 0) {
1216                wrnMsg("fade_in(): argument 2 may not be negative");
1217                duration = 0;
1218            }
1219            const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
1220    
1221            AbstractEngineChannel* pEngineChannel =
1222                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1223    
1224            if (args->arg(0)->exprType() == INT_EXPR) {
1225                const ScriptID id = args->arg(0)->asInt()->evalInt();
1226                if (!id) {
1227                    wrnMsg("fade_in(): note ID for argument 1 may not be zero");
1228                    return successResult();
1229                }
1230                if (!id.isNoteID()) {
1231                    wrnMsg("fade_in(): argument 1 is not a note ID");
1232                    return successResult();
1233                }
1234    
1235                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1236                if (!pNote) return successResult();
1237    
1238                // if fade_in() was called immediately after note was triggered
1239                // then immediately apply a start volume of zero to Note object,
1240                // as well as the fade in duration
1241                if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
1242                    pNote->Override.Volume = 0.f;
1243                    pNote->Override.VolumeTime = fDuration;
1244                } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
1245                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1246                    e.Init(); // clear IDs
1247                    e.Type = Event::type_note_synth_param;
1248                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1249                    e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1250                    e.Param.NoteSynthParam.Delta    = fDuration;
1251                    e.Param.NoteSynthParam.Relative = false;
1252    
1253                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
1254                }
1255                // and finally schedule a "volume" change, simply one time slice
1256                // ahead, with the final fade in volume (1.0)
1257                {
1258                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1259                    e.Init(); // clear IDs
1260                    e.Type = Event::type_note_synth_param;
1261                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1262                    e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1263                    e.Param.NoteSynthParam.Delta    = 1.f;
1264                    e.Param.NoteSynthParam.Relative = false;
1265    
1266                    // scheduling with 0 delay would also work here, but +1 is more
1267                    // safe regarding potential future implementation changes of the
1268                    // scheduler (see API comments of RTAVLTree::insert())
1269                    pEngineChannel->ScheduleEventMicroSec(&e, 1);
1270                }
1271            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1272                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1273                for (int i = 0; i < ids->arraySize(); ++i) {
1274                    const ScriptID id = ids->evalIntElement(i);
1275                    if (!id || !id.isNoteID()) continue;
1276    
1277                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1278                    if (!pNote) continue;
1279    
1280                    // if fade_in() was called immediately after note was triggered
1281                    // then immediately apply a start volume of zero to Note object,
1282                    // as well as the fade in duration
1283                    if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
1284                        pNote->Override.Volume = 0.f;
1285                        pNote->Override.VolumeTime = fDuration;
1286                    } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
1287                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1288                        e.Init(); // clear IDs
1289                        e.Type = Event::type_note_synth_param;
1290                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1291                        e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1292                        e.Param.NoteSynthParam.Delta    = fDuration;
1293                        e.Param.NoteSynthParam.Relative = false;
1294    
1295                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
1296                    }
1297                    // and finally schedule a "volume" change, simply one time slice
1298                    // ahead, with the final fade in volume (1.0)
1299                    {
1300                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1301                        e.Init(); // clear IDs
1302                        e.Type = Event::type_note_synth_param;
1303                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1304                        e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1305                        e.Param.NoteSynthParam.Delta    = 1.f;
1306                        e.Param.NoteSynthParam.Relative = false;
1307    
1308                        // scheduling with 0 delay would also work here, but +1 is more
1309                        // safe regarding potential future implementation changes of the
1310                        // scheduler (see API comments of RTAVLTree::insert())
1311                        pEngineChannel->ScheduleEventMicroSec(&e, 1);
1312                    }
1313                }
1314            }
1315    
1316            return successResult();
1317        }
1318    
1319        // fade_out() function
1320    
1321        InstrumentScriptVMFunction_fade_out::InstrumentScriptVMFunction_fade_out(InstrumentScriptVM* parent)
1322            : m_vm(parent)
1323        {
1324        }
1325    
1326        bool InstrumentScriptVMFunction_fade_out::acceptsArgType(int iArg, ExprType_t type) const {
1327            if (iArg == 0)
1328                return type == INT_EXPR || type == INT_ARR_EXPR;
1329            else
1330                return type == INT_EXPR;
1331        }
1332    
1333        VMFnResult* InstrumentScriptVMFunction_fade_out::exec(VMFnArgs* args) {
1334            int duration = args->arg(1)->asInt()->evalInt();
1335            if (duration < 0) {
1336                wrnMsg("fade_out(): argument 2 may not be negative");
1337                duration = 0;
1338            }
1339            const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
1340    
1341            bool stop = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : true;
1342    
1343            AbstractEngineChannel* pEngineChannel =
1344                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1345    
1346            if (args->arg(0)->exprType() == INT_EXPR) {
1347                const ScriptID id = args->arg(0)->asInt()->evalInt();
1348                if (!id) {
1349                    wrnMsg("fade_out(): note ID for argument 1 may not be zero");
1350                    return successResult();
1351                }
1352                if (!id.isNoteID()) {
1353                    wrnMsg("fade_out(): argument 1 is not a note ID");
1354                    return successResult();
1355                }
1356    
1357                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1358                if (!pNote) return successResult();
1359    
1360                // if fade_out() was called immediately after note was triggered
1361                // then immediately apply fade out duration to Note object
1362                if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
1363                    pNote->Override.VolumeTime = fDuration;
1364                } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
1365                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1366                    e.Init(); // clear IDs
1367                    e.Type = Event::type_note_synth_param;
1368                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1369                    e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1370                    e.Param.NoteSynthParam.Delta    = fDuration;
1371                    e.Param.NoteSynthParam.Relative = false;
1372    
1373                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
1374                }
1375                // now schedule a "volume" change, simply one time slice ahead, with
1376                // the final fade out volume (0.0)
1377                {
1378                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1379                    e.Init(); // clear IDs
1380                    e.Type = Event::type_note_synth_param;
1381                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1382                    e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1383                    e.Param.NoteSynthParam.Delta    = 0.f;
1384                    e.Param.NoteSynthParam.Relative = false;
1385    
1386                    // scheduling with 0 delay would also work here, but +1 is more
1387                    // safe regarding potential future implementation changes of the
1388                    // scheduler (see API comments of RTAVLTree::insert())
1389                    pEngineChannel->ScheduleEventMicroSec(&e, 1);
1390                }
1391                // and finally if stopping the note was requested after the fade out
1392                // completed, then schedule to kill the voice after the requested
1393                // duration
1394                if (stop) {
1395                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1396                    e.Init(); // clear IDs
1397                    e.Type = Event::type_kill_note;
1398                    e.Param.Note.ID = id.noteID();
1399                    e.Param.Note.Key = pNote->hostKey;
1400    
1401                    pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
1402                }
1403            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1404                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1405                for (int i = 0; i < ids->arraySize(); ++i) {
1406                    const ScriptID id = ids->evalIntElement(i);
1407                    if (!id || !id.isNoteID()) continue;
1408    
1409                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1410                    if (!pNote) continue;
1411    
1412                    // if fade_out() was called immediately after note was triggered
1413                    // then immediately apply fade out duration to Note object
1414                    if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
1415                        pNote->Override.VolumeTime = fDuration;
1416                    } else { // otherwise schedule a "volume time" change with the requested fade out 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                    // now schedule a "volume" change, simply one time slice ahead, with
1428                    // the final fade out volume (0.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    = 0.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                    // and finally if stopping the note was requested after the fade out
1444                    // completed, then schedule to kill the voice after the requested
1445                    // duration
1446                    if (stop) {
1447                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1448                        e.Init(); // clear IDs
1449                        e.Type = Event::type_kill_note;
1450                        e.Param.Note.ID = id.noteID();
1451                        e.Param.Note.Key = pNote->hostKey;
1452                        
1453                        pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
1454                    }
1455                }
1456          }          }
1457    
1458          return successResult();          return successResult();
# Line 789  namespace LinuxSampler { Line 1483  namespace LinuxSampler {
1483          return successResult(pNote ? EVENT_STATUS_NOTE_QUEUE : EVENT_STATUS_INACTIVE);          return successResult(pNote ? EVENT_STATUS_NOTE_QUEUE : EVENT_STATUS_INACTIVE);
1484      }      }
1485    
1486        // wait() function (overrides core wait() implementation)
1487    
1488        InstrumentScriptVMFunction_wait::InstrumentScriptVMFunction_wait(InstrumentScriptVM* parent)
1489            : CoreVMFunction_wait(parent)
1490        {    
1491        }
1492    
1493        VMFnResult* InstrumentScriptVMFunction_wait::exec(VMFnArgs* args) {
1494            InstrumentScriptVM* m_vm = (InstrumentScriptVM*) vm;
1495    
1496            // this might be set by passing 1 with the 2nd argument of built-in stop_wait() function
1497            if (m_vm->m_event->ignoreAllWaitCalls) return successResult();
1498    
1499            return CoreVMFunction_wait::exec(args);
1500        }
1501    
1502        // stop_wait() function
1503    
1504        InstrumentScriptVMFunction_stop_wait::InstrumentScriptVMFunction_stop_wait(InstrumentScriptVM* parent)
1505            : m_vm(parent)
1506        {    
1507        }
1508    
1509        VMFnResult* InstrumentScriptVMFunction_stop_wait::exec(VMFnArgs* args) {
1510            AbstractEngineChannel* pEngineChannel =
1511                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1512    
1513            const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
1514            if (!id) {
1515                wrnMsg("stop_wait(): callback ID for argument 1 may not be zero");
1516                return successResult();
1517            }
1518    
1519            RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
1520            if (!itCallback) return successResult(); // ignore if callback is i.e. not alive anymore
1521    
1522            const bool disableWaitForever =
1523                (args->argsCount() >= 2) ? (args->arg(1)->asInt()->evalInt() == 1) : false;
1524    
1525            pEngineChannel->ScheduleResumeOfScriptCallback(
1526                itCallback, m_vm->m_event->cause.SchedTime(), disableWaitForever
1527            );
1528    
1529            return successResult();
1530        }
1531    
1532  } // namespace LinuxSampler  } // namespace LinuxSampler

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

  ViewVC Help
Powered by ViewVC