--- linuxsampler/trunk/src/engines/common/InstrumentScriptVMFunctions.cpp 2019/08/18 00:06:04 3557 +++ linuxsampler/trunk/src/engines/common/InstrumentScriptVMFunctions.cpp 2019/08/23 11:44:00 3561 @@ -10,6 +10,7 @@ #include "InstrumentScriptVMFunctions.h" #include "InstrumentScriptVM.h" #include "../AbstractEngineChannel.h" +#include "../../common/global_private.h" namespace LinuxSampler { @@ -375,8 +376,25 @@ return type == INT_EXPR; } + bool InstrumentScriptVMFunction_change_vol::acceptsArgUnitType(vmint iArg, StdUnit_t type) const { + if (iArg == 1) + return type == VM_NO_UNIT || type == VM_BEL; + else + return type == VM_NO_UNIT; + } + + bool InstrumentScriptVMFunction_change_vol::acceptsArgUnitPrefix(vmint iArg) const { + return iArg == 1; + } + + bool InstrumentScriptVMFunction_change_vol::acceptsArgFinal(vmint iArg) const { + return iArg == 1; + } + VMFnResult* InstrumentScriptVMFunction_change_vol::exec(VMFnArgs* args) { - vmint volume = args->arg(1)->asInt()->evalInt(); // volume change in milli dB + vmint volume = args->arg(1)->asInt()->evalInt(VM_MILLI,VM_DECI); // volume change in milli dB + bool isFinal = args->arg(1)->asInt()->isFinal(); + StdUnit_t unit = args->arg(1)->asInt()->unitType(); bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false; const float fVolumeLin = RTMath::DecibelToLinRatio(float(volume) / 1000.f); @@ -404,9 +422,10 @@ pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S) { if (relative) - pNote->Override.Volume *= fVolumeLin; + pNote->Override.Volume.Value *= fVolumeLin; else - pNote->Override.Volume = fVolumeLin; + pNote->Override.Volume.Value = fVolumeLin; + pNote->Override.Volume.Final = isFinal; } else { // otherwise schedule the volume change ... Event e = m_vm->m_event->cause; // copy to get fragment time for "now" e.Init(); // clear IDs @@ -414,8 +433,9 @@ e.Param.NoteSynthParam.NoteID = id.noteID(); e.Param.NoteSynthParam.Type = Event::synth_param_volume; e.Param.NoteSynthParam.Delta = fVolumeLin; - e.Param.NoteSynthParam.Relative = relative; - + e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit( + isFinal, relative, unit + ); pEngineChannel->ScheduleEventMicroSec(&e, 0); } } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { @@ -434,9 +454,10 @@ pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S) { if (relative) - pNote->Override.Volume *= fVolumeLin; + pNote->Override.Volume.Value *= fVolumeLin; else - pNote->Override.Volume = fVolumeLin; + pNote->Override.Volume.Value = fVolumeLin; + pNote->Override.Volume.Final = isFinal; } else { // otherwise schedule the volume change ... Event e = m_vm->m_event->cause; // copy to get fragment time for "now" e.Init(); // clear IDs @@ -444,8 +465,9 @@ e.Param.NoteSynthParam.NoteID = id.noteID(); e.Param.NoteSynthParam.Type = Event::synth_param_volume; e.Param.NoteSynthParam.Delta = fVolumeLin; - e.Param.NoteSynthParam.Relative = relative; - + e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit( + isFinal, relative, unit + ); pEngineChannel->ScheduleEventMicroSec(&e, 0); } } @@ -468,8 +490,18 @@ return type == INT_EXPR; } + bool InstrumentScriptVMFunction_change_tune::acceptsArgUnitPrefix(vmint iArg) const { + return iArg == 1; + } + + bool InstrumentScriptVMFunction_change_tune::acceptsArgFinal(vmint iArg) const { + return iArg == 1; + } + VMFnResult* InstrumentScriptVMFunction_change_tune::exec(VMFnArgs* args) { - vmint tune = args->arg(1)->asInt()->evalInt(); // tuning change in milli cents + vmint tune = args->arg(1)->asInt()->evalInt(VM_MILLI,VM_CENTI); // tuning change in milli cents + bool isFinal = args->arg(1)->asInt()->isFinal(); + StdUnit_t unit = args->arg(1)->asInt()->unitType(); bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false; const float fFreqRatio = RTMath::CentsToFreqRatioUnlimited(float(tune) / 1000.f); @@ -497,9 +529,10 @@ pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S) { if (relative) - pNote->Override.Pitch *= fFreqRatio; + pNote->Override.Pitch.Value *= fFreqRatio; else - pNote->Override.Pitch = fFreqRatio; + pNote->Override.Pitch.Value = fFreqRatio; + pNote->Override.Pitch.Final = isFinal; } else { // otherwise schedule tuning change ... Event e = m_vm->m_event->cause; // copy to get fragment time for "now" e.Init(); // clear IDs @@ -507,8 +540,9 @@ e.Param.NoteSynthParam.NoteID = id.noteID(); e.Param.NoteSynthParam.Type = Event::synth_param_pitch; e.Param.NoteSynthParam.Delta = fFreqRatio; - e.Param.NoteSynthParam.Relative = relative; - + e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit( + isFinal, relative, unit + ); pEngineChannel->ScheduleEventMicroSec(&e, 0); } } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { @@ -527,9 +561,10 @@ pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S) { if (relative) - pNote->Override.Pitch *= fFreqRatio; + pNote->Override.Pitch.Value *= fFreqRatio; else - pNote->Override.Pitch = fFreqRatio; + pNote->Override.Pitch.Value = fFreqRatio; + pNote->Override.Pitch.Final = isFinal; } else { // otherwise schedule tuning change ... Event e = m_vm->m_event->cause; // copy to get fragment time for "now" e.Init(); // clear IDs @@ -537,8 +572,9 @@ e.Param.NoteSynthParam.NoteID = id.noteID(); e.Param.NoteSynthParam.Type = Event::synth_param_pitch; e.Param.NoteSynthParam.Delta = fFreqRatio; - e.Param.NoteSynthParam.Relative = relative; - + e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit( + isFinal, relative, unit + ); pEngineChannel->ScheduleEventMicroSec(&e, 0); } } @@ -561,8 +597,13 @@ return type == INT_EXPR; } + bool InstrumentScriptVMFunction_change_pan::acceptsArgFinal(vmint iArg) const { + return iArg == 1; + } + VMFnResult* InstrumentScriptVMFunction_change_pan::exec(VMFnArgs* args) { - vmint pan = args->arg(1)->asInt()->evalInt(); + vmint pan = args->arg(1)->asInt()->evalInt(); + bool isFinal = args->arg(1)->asInt()->isFinal(); bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false; if (pan > 1000) { @@ -595,11 +636,12 @@ // then immediately apply the panning to Note object if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { if (relative) { - pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources); + pNote->Override.Pan.Value = RTMath::RelativeSummedAvg(pNote->Override.Pan.Value, fPan, ++pNote->Override.Pan.Sources); } else { - pNote->Override.Pan = fPan; - pNote->Override.PanSources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set + pNote->Override.Pan.Value = fPan; + pNote->Override.Pan.Sources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set } + pNote->Override.Pan.Final = isFinal; } else { // otherwise schedule panning change ... Event e = m_vm->m_event->cause; // copy to get fragment time for "now" e.Init(); // clear IDs @@ -607,8 +649,9 @@ e.Param.NoteSynthParam.NoteID = id.noteID(); e.Param.NoteSynthParam.Type = Event::synth_param_pan; e.Param.NoteSynthParam.Delta = fPan; - e.Param.NoteSynthParam.Relative = relative; - + e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit( + isFinal, relative, false + ); pEngineChannel->ScheduleEventMicroSec(&e, 0); } } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { @@ -624,11 +667,12 @@ // then immediately apply the panning to Note object if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { if (relative) { - pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources); + pNote->Override.Pan.Value = RTMath::RelativeSummedAvg(pNote->Override.Pan.Value, fPan, ++pNote->Override.Pan.Sources); } else { - pNote->Override.Pan = fPan; - pNote->Override.PanSources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set + pNote->Override.Pan.Value = fPan; + pNote->Override.Pan.Sources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set } + pNote->Override.Pan.Final = isFinal; } else { // otherwise schedule panning change ... Event e = m_vm->m_event->cause; // copy to get fragment time for "now" e.Init(); // clear IDs @@ -636,8 +680,9 @@ e.Param.NoteSynthParam.NoteID = id.noteID(); e.Param.NoteSynthParam.Type = Event::synth_param_pan; e.Param.NoteSynthParam.Delta = fPan; - e.Param.NoteSynthParam.Relative = relative; - + e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit( + isFinal, relative, false + ); pEngineChannel->ScheduleEventMicroSec(&e, 0); } } @@ -647,6 +692,7 @@ } #define VM_FILTER_PAR_MAX_VALUE 1000000 + #define VM_FILTER_PAR_MAX_HZ 30000 #define VM_EG_PAR_MAX_VALUE 1000000 // change_cutoff() function @@ -663,16 +709,42 @@ return type == INT_EXPR; } + bool InstrumentScriptVMFunction_change_cutoff::acceptsArgUnitType(vmint iArg, StdUnit_t type) const { + if (iArg == 1) + return type == VM_NO_UNIT || type == VM_HERTZ; + else + return type == VM_NO_UNIT; + } + + bool InstrumentScriptVMFunction_change_cutoff::acceptsArgUnitPrefix(vmint iArg) const { + return iArg == 1; + } + + bool InstrumentScriptVMFunction_change_cutoff::acceptsArgFinal(vmint iArg) const { + return iArg == 1; + } + VMFnResult* InstrumentScriptVMFunction_change_cutoff::exec(VMFnArgs* args) { - vmint cutoff = args->arg(1)->asInt()->evalInt(); - if (cutoff > VM_FILTER_PAR_MAX_VALUE) { - wrnMsg("change_cutoff(): argument 2 may not be larger than 1000000"); + vmint cutoff = args->arg(1)->asInt()->evalInt(VM_NO_PREFIX); + bool isFinal = args->arg(1)->asInt()->isFinal(); + StdUnit_t unit = args->arg(1)->asInt()->unitType(); + if (!unit && cutoff > VM_FILTER_PAR_MAX_VALUE) { + wrnMsg("change_cutoff(): argument 2 may not be larger than " strfy(VM_FILTER_PAR_MAX_VALUE)); cutoff = VM_FILTER_PAR_MAX_VALUE; + } else if (unit && cutoff > VM_FILTER_PAR_MAX_HZ) { + wrnMsg("change_cutoff(): argument 2 may not be larger than " strfy(VM_FILTER_PAR_MAX_HZ) " Hz"); + cutoff = VM_FILTER_PAR_MAX_HZ; } else if (cutoff < 0) { wrnMsg("change_cutoff(): argument 2 may not be negative"); cutoff = 0; } - const float fCutoff = float(cutoff) / float(VM_FILTER_PAR_MAX_VALUE); + const float fCutoff = + (unit) ? cutoff : float(cutoff) / float(VM_FILTER_PAR_MAX_VALUE); + + if (unit && !isFinal) { + wrnMsg("change_cutoff(): you must pass argument 2 as 'final' value when using Hz as unit"); + return successResult(); + } AbstractEngineChannel* pEngineChannel = static_cast(m_vm->m_event->cause.pEngineChannel); @@ -694,7 +766,8 @@ // if change_cutoff() was called immediately after note was triggered // then immediately apply cutoff to Note object if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { - pNote->Override.Cutoff = fCutoff; + pNote->Override.Cutoff.Value = fCutoff; + pNote->Override.Cutoff.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit); } else { // otherwise schedule cutoff change ... Event e = m_vm->m_event->cause; // copy to get fragment time for "now" e.Init(); // clear IDs @@ -702,8 +775,9 @@ e.Param.NoteSynthParam.NoteID = id.noteID(); e.Param.NoteSynthParam.Type = Event::synth_param_cutoff; e.Param.NoteSynthParam.Delta = fCutoff; - e.Param.NoteSynthParam.Relative = false; - + e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit( + isFinal, false, unit + ); pEngineChannel->ScheduleEventMicroSec(&e, 0); } } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { @@ -718,7 +792,8 @@ // if change_cutoff() was called immediately after note was triggered // then immediately apply cutoff to Note object if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { - pNote->Override.Cutoff = fCutoff; + pNote->Override.Cutoff.Value = fCutoff; + pNote->Override.Cutoff.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit); } else { // otherwise schedule cutoff change ... Event e = m_vm->m_event->cause; // copy to get fragment time for "now" e.Init(); // clear IDs @@ -726,8 +801,9 @@ e.Param.NoteSynthParam.NoteID = id.noteID(); e.Param.NoteSynthParam.Type = Event::synth_param_cutoff; e.Param.NoteSynthParam.Delta = fCutoff; - e.Param.NoteSynthParam.Relative = false; - + e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit( + isFinal, false, unit + ); pEngineChannel->ScheduleEventMicroSec(&e, 0); } } @@ -750,8 +826,13 @@ return type == INT_EXPR; } + bool InstrumentScriptVMFunction_change_reso::acceptsArgFinal(vmint iArg) const { + return iArg == 1; + } + VMFnResult* InstrumentScriptVMFunction_change_reso::exec(VMFnArgs* args) { vmint resonance = args->arg(1)->asInt()->evalInt(); + bool isFinal = args->arg(1)->asInt()->isFinal(); if (resonance > VM_FILTER_PAR_MAX_VALUE) { wrnMsg("change_reso(): argument 2 may not be larger than 1000000"); resonance = VM_FILTER_PAR_MAX_VALUE; @@ -781,7 +862,8 @@ // if change_reso() was called immediately after note was triggered // then immediately apply resonance to Note object if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { - pNote->Override.Resonance = fResonance; + pNote->Override.Resonance.Value = fResonance; + pNote->Override.Resonance.Final = isFinal; } else { // otherwise schedule resonance change ... Event e = m_vm->m_event->cause; // copy to get fragment time for "now" e.Init(); // clear IDs @@ -789,8 +871,9 @@ e.Param.NoteSynthParam.NoteID = id.noteID(); e.Param.NoteSynthParam.Type = Event::synth_param_resonance; e.Param.NoteSynthParam.Delta = fResonance; - e.Param.NoteSynthParam.Relative = false; - + e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit( + isFinal, false, false + ); pEngineChannel->ScheduleEventMicroSec(&e, 0); } } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { @@ -805,7 +888,8 @@ // if change_reso() was called immediately after note was triggered // then immediately apply resonance to Note object if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { - pNote->Override.Resonance = fResonance; + pNote->Override.Resonance.Value = fResonance; + pNote->Override.Resonance.Final = isFinal; } else { // otherwise schedule resonance change ... Event e = m_vm->m_event->cause; // copy to get fragment time for "now" e.Init(); // clear IDs @@ -813,8 +897,9 @@ e.Param.NoteSynthParam.NoteID = id.noteID(); e.Param.NoteSynthParam.Type = Event::synth_param_resonance; e.Param.NoteSynthParam.Delta = fResonance; - e.Param.NoteSynthParam.Relative = false; - + e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit( + isFinal, false, false + ); pEngineChannel->ScheduleEventMicroSec(&e, 0); } } @@ -837,8 +922,25 @@ return type == INT_EXPR; } + bool InstrumentScriptVMFunction_change_attack::acceptsArgUnitType(vmint iArg, StdUnit_t type) const { + if (iArg == 1) + return type == VM_NO_UNIT || type == VM_SECOND; + else + return type == VM_NO_UNIT; + } + + bool InstrumentScriptVMFunction_change_attack::acceptsArgUnitPrefix(vmint iArg) const { + return iArg == 1; + } + + bool InstrumentScriptVMFunction_change_attack::acceptsArgFinal(vmint iArg) const { + return iArg == 1; + } + VMFnResult* InstrumentScriptVMFunction_change_attack::exec(VMFnArgs* args) { - vmint attack = args->arg(1)->asInt()->evalInt(); + vmint attack = args->arg(1)->asInt()->evalInt(VM_MICRO); + bool isFinal = args->arg(1)->asInt()->isFinal(); + StdUnit_t unit = args->arg(1)->asInt()->unitType(); // note: intentionally not checking against a max. value here! // (to allow i.e. passing 2000000 for doubling the attack time) if (attack < 0) { @@ -847,6 +949,11 @@ } const float fAttack = float(attack) / float(VM_EG_PAR_MAX_VALUE); + if (unit && !isFinal) { + wrnMsg("change_attack(): you must pass argument 2 as 'final' value when using seconds as unit"); + return successResult(); + } + AbstractEngineChannel* pEngineChannel = static_cast(m_vm->m_event->cause.pEngineChannel); @@ -867,7 +974,8 @@ // if change_attack() was called immediately after note was triggered // then immediately apply attack to Note object if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { - pNote->Override.Attack = fAttack; + pNote->Override.Attack.Value = fAttack; + pNote->Override.Attack.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit); } else { // otherwise schedule attack change ... Event e = m_vm->m_event->cause; // copy to get fragment time for "now" e.Init(); // clear IDs @@ -875,8 +983,9 @@ e.Param.NoteSynthParam.NoteID = id.noteID(); e.Param.NoteSynthParam.Type = Event::synth_param_attack; e.Param.NoteSynthParam.Delta = fAttack; - e.Param.NoteSynthParam.Relative = false; - + e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit( + isFinal, false, unit + ); pEngineChannel->ScheduleEventMicroSec(&e, 0); } } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { @@ -891,7 +1000,8 @@ // if change_attack() was called immediately after note was triggered // then immediately apply attack to Note object if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { - pNote->Override.Attack = fAttack; + pNote->Override.Attack.Value = fAttack; + pNote->Override.Attack.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit); } else { // otherwise schedule attack change ... Event e = m_vm->m_event->cause; // copy to get fragment time for "now" e.Init(); // clear IDs @@ -899,8 +1009,9 @@ e.Param.NoteSynthParam.NoteID = id.noteID(); e.Param.NoteSynthParam.Type = Event::synth_param_attack; e.Param.NoteSynthParam.Delta = fAttack; - e.Param.NoteSynthParam.Relative = false; - + e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit( + isFinal, false, unit + ); pEngineChannel->ScheduleEventMicroSec(&e, 0); } } @@ -923,8 +1034,25 @@ return type == INT_EXPR; } + bool InstrumentScriptVMFunction_change_decay::acceptsArgUnitType(vmint iArg, StdUnit_t type) const { + if (iArg == 1) + return type == VM_NO_UNIT || type == VM_SECOND; + else + return type == VM_NO_UNIT; + } + + bool InstrumentScriptVMFunction_change_decay::acceptsArgUnitPrefix(vmint iArg) const { + return iArg == 1; + } + + bool InstrumentScriptVMFunction_change_decay::acceptsArgFinal(vmint iArg) const { + return iArg == 1; + } + VMFnResult* InstrumentScriptVMFunction_change_decay::exec(VMFnArgs* args) { - vmint decay = args->arg(1)->asInt()->evalInt(); + vmint decay = args->arg(1)->asInt()->evalInt(VM_MICRO); + bool isFinal = args->arg(1)->asInt()->isFinal(); + StdUnit_t unit = args->arg(1)->asInt()->unitType(); // note: intentionally not checking against a max. value here! // (to allow i.e. passing 2000000 for doubling the decay time) if (decay < 0) { @@ -933,6 +1061,11 @@ } const float fDecay = float(decay) / float(VM_EG_PAR_MAX_VALUE); + if (unit && !isFinal) { + wrnMsg("change_decay(): you must pass argument 2 as 'final' value when using seconds as unit"); + return successResult(); + } + AbstractEngineChannel* pEngineChannel = static_cast(m_vm->m_event->cause.pEngineChannel); @@ -953,7 +1086,8 @@ // if change_decay() was called immediately after note was triggered // then immediately apply decay to Note object if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { - pNote->Override.Decay = fDecay; + pNote->Override.Decay.Value = fDecay; + pNote->Override.Decay.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit); } else { // otherwise schedule decay change ... Event e = m_vm->m_event->cause; // copy to get fragment time for "now" e.Init(); // clear IDs @@ -961,8 +1095,9 @@ e.Param.NoteSynthParam.NoteID = id.noteID(); e.Param.NoteSynthParam.Type = Event::synth_param_decay; e.Param.NoteSynthParam.Delta = fDecay; - e.Param.NoteSynthParam.Relative = false; - + e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit( + isFinal, false, unit + ); pEngineChannel->ScheduleEventMicroSec(&e, 0); } } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { @@ -977,7 +1112,8 @@ // if change_decay() was called immediately after note was triggered // then immediately apply decay to Note object if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { - pNote->Override.Decay = fDecay; + pNote->Override.Decay.Value = fDecay; + pNote->Override.Decay.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit); } else { // otherwise schedule decay change ... Event e = m_vm->m_event->cause; // copy to get fragment time for "now" e.Init(); // clear IDs @@ -985,8 +1121,9 @@ e.Param.NoteSynthParam.NoteID = id.noteID(); e.Param.NoteSynthParam.Type = Event::synth_param_decay; e.Param.NoteSynthParam.Delta = fDecay; - e.Param.NoteSynthParam.Relative = false; - + e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit( + isFinal, false, unit + ); pEngineChannel->ScheduleEventMicroSec(&e, 0); } } @@ -1009,8 +1146,25 @@ return type == INT_EXPR; } + bool InstrumentScriptVMFunction_change_release::acceptsArgUnitType(vmint iArg, StdUnit_t type) const { + if (iArg == 1) + return type == VM_NO_UNIT || type == VM_SECOND; + else + return type == VM_NO_UNIT; + } + + bool InstrumentScriptVMFunction_change_release::acceptsArgUnitPrefix(vmint iArg) const { + return iArg == 1; + } + + bool InstrumentScriptVMFunction_change_release::acceptsArgFinal(vmint iArg) const { + return iArg == 1; + } + VMFnResult* InstrumentScriptVMFunction_change_release::exec(VMFnArgs* args) { - vmint release = args->arg(1)->asInt()->evalInt(); + vmint release = args->arg(1)->asInt()->evalInt(VM_MICRO); + bool isFinal = args->arg(1)->asInt()->isFinal(); + StdUnit_t unit = args->arg(1)->asInt()->unitType(); // note: intentionally not checking against a max. value here! // (to allow i.e. passing 2000000 for doubling the release time) if (release < 0) { @@ -1019,6 +1173,11 @@ } const float fRelease = float(release) / float(VM_EG_PAR_MAX_VALUE); + if (unit && !isFinal) { + wrnMsg("change_release(): you must pass argument 2 as 'final' value when using seconds as unit"); + return successResult(); + } + AbstractEngineChannel* pEngineChannel = static_cast(m_vm->m_event->cause.pEngineChannel); @@ -1039,7 +1198,8 @@ // if change_release() was called immediately after note was triggered // then immediately apply relase to Note object if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { - pNote->Override.Release = fRelease; + pNote->Override.Release.Value = fRelease; + pNote->Override.Release.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit); } else { // otherwise schedule release change ... Event e = m_vm->m_event->cause; // copy to get fragment time for "now" e.Init(); // clear IDs @@ -1047,8 +1207,9 @@ e.Param.NoteSynthParam.NoteID = id.noteID(); e.Param.NoteSynthParam.Type = Event::synth_param_release; e.Param.NoteSynthParam.Delta = fRelease; - e.Param.NoteSynthParam.Relative = false; - + e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit( + isFinal, false, unit + ); pEngineChannel->ScheduleEventMicroSec(&e, 0); } } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { @@ -1063,7 +1224,8 @@ // if change_release() was called immediately after note was triggered // then immediately apply relase to Note object if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { - pNote->Override.Release = fRelease; + pNote->Override.Release.Value = fRelease; + pNote->Override.Release.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit); } else { // otherwise schedule release change ... Event e = m_vm->m_event->cause; // copy to get fragment time for "now" e.Init(); // clear IDs @@ -1071,8 +1233,9 @@ e.Param.NoteSynthParam.NoteID = id.noteID(); e.Param.NoteSynthParam.Type = Event::synth_param_release; e.Param.NoteSynthParam.Delta = fRelease; - e.Param.NoteSynthParam.Relative = false; - + e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit( + isFinal, false, unit + ); pEngineChannel->ScheduleEventMicroSec(&e, 0); } } @@ -1090,26 +1253,98 @@ return type == INT_EXPR; } + bool VMChangeSynthParamFunction::acceptsArgUnitType(vmint iArg, StdUnit_t type) const { + if (iArg == 1) + return type == VM_NO_UNIT || type == m_unit; + else + return type == VM_NO_UNIT; + } + + bool VMChangeSynthParamFunction::acceptsArgUnitPrefix(vmint iArg) const { + return m_unit && iArg == 1; + } + + bool VMChangeSynthParamFunction::acceptsArgFinal(vmint iArg) const { + return (m_acceptFinal) ? (iArg == 1) : false; + } + + inline static void setNoteParamScopeBy_FinalUnit(NoteBase::Param& param, const bool bFinal, const StdUnit_t unit) { + param.Scope = NoteBase::scopeBy_FinalUnit(bFinal, unit); + } + + inline static void setNoteParamScopeBy_FinalUnit(NoteBase::Norm& param, const bool bFinal, const StdUnit_t unit) { + param.Final = bFinal; + } + + inline static void setNoteParamScopeBy_FinalUnit(float& param, const bool bFinal, const StdUnit_t unit) { + /* NOOP */ + } + + template + inline static void setNoteParamValue(T& param, vmfloat value) { + param.Value = value; + } + + inline static void setNoteParamValue(float& param, vmfloat value) { + param = value; + } + // Arbitrarily chosen constant value symbolizing "no limit". #define NO_LIMIT 1315916909 - template - VMFnResult* VMChangeSynthParamFunction::execTemplate(VMFnArgs* args, const char* functionName) { - vmint value = args->arg(1)->asInt()->evalInt(); - if (T_maxValue != NO_LIMIT && value > T_maxValue) { - wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValue)); - value = T_maxValue; - } else if (T_minValue != NO_LIMIT && value < T_minValue) { - if (T_minValue == 0) - wrnMsg(String(functionName) + "(): argument 2 may not be negative"); - else - wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValue)); - value = T_minValue; - } - const float fValue = (T_isNormalizedParam) ? - float(value) / float(T_maxValue) : // convert to 0.0 .. 1.0 value range - float(value) / 1000000.f; // assuming microseconds here, convert to seconds + template + VMFnResult* VMChangeSynthParamFunction::execTemplate(VMFnArgs* args, const char* functionName) + { + const StdUnit_t unit = args->arg(1)->asInt()->unitType(); + const bool isFinal = args->arg(1)->asInt()->isFinal(); + vmint value = + (unit & m_unit) ? + args->arg(1)->asInt()->evalInt(T_unitPrefix0, T_unitPrefixN ...) : + args->arg(1)->asInt()->evalInt(VM_NO_PREFIX); + + if (unit && !isFinal && m_unit != VM_BEL && m_unit) { + wrnMsg(String(functionName) + "(): you must pass argument 2 as 'final' value when using a unit"); + return successResult(); + } + + // check if passed value is in allowed range + if (unit && m_unit) { + if (T_maxValueUnit != NO_LIMIT && value > T_maxValueUnit) { + wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValueUnit)); + value = T_maxValueUnit; + } else if (T_minValueUnit != NO_LIMIT && value < T_minValueUnit) { + if (T_minValueUnit == 0) + wrnMsg(String(functionName) + "(): argument 2 may not be negative"); + else + wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValueUnit)); + value = T_minValueUnit; + } + } else { // value was passed to this function without a unit ... + if (T_maxValueNorm != NO_LIMIT && value > T_maxValueNorm) { + wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValueNorm)); + value = T_maxValueNorm; + } else if (T_minValueNorm != NO_LIMIT && value < T_minValueNorm) { + if (T_minValueNorm == 0) + wrnMsg(String(functionName) + "(): argument 2 may not be negative"); + else + wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValueNorm)); + value = T_minValueNorm; + } + } + + // convert passed argument value to engine internal expected value range (i.e. 0.0 .. 1.0) + const float fValue = + (unit && m_unit) ? + (unit == VM_BEL) ? + RTMath::DecibelToLinRatio(float(value) * float(T_unitPrefix0) /*i.e. mdB -> dB*/) : + float(value) * VMUnit::unitFactor(T_unitPrefix0, T_unitPrefixN ...) /*i.e. us -> s*/ : + (T_normalizeNorm) ? + float(value) / ((T_maxValueNorm != NO_LIMIT) ? float(T_maxValueNorm) : 1000000.f/* fallback: value range used for most */) : + float(value) /* as is */; AbstractEngineChannel* pEngineChannel = static_cast(m_vm->m_event->cause.pEngineChannel); @@ -1132,7 +1367,11 @@ // note was triggered then immediately apply the synth parameter // change to Note object if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { - pNote->Override.*T_noteParam = fValue; + setNoteParamValue(pNote->Override.*T_noteParam, fValue); + setNoteParamScopeBy_FinalUnit( + (pNote->Override.*T_noteParam), + isFinal, unit + ); } else { // otherwise schedule this synth parameter change ... Event e = m_vm->m_event->cause; // copy to get fragment time for "now" e.Init(); // clear IDs @@ -1140,8 +1379,9 @@ e.Param.NoteSynthParam.NoteID = id.noteID(); e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam; e.Param.NoteSynthParam.Delta = fValue; - e.Param.NoteSynthParam.Relative = false; - + e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit( + isFinal, false, unit + ); pEngineChannel->ScheduleEventMicroSec(&e, 0); } } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { @@ -1157,7 +1397,11 @@ // note was triggered then immediately apply the synth parameter // change to Note object if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { - pNote->Override.*T_noteParam = fValue; + setNoteParamValue(pNote->Override.*T_noteParam, fValue); + setNoteParamScopeBy_FinalUnit( + (pNote->Override.*T_noteParam), + isFinal, unit + ); } else { // otherwise schedule this synth parameter change ... Event e = m_vm->m_event->cause; // copy to get fragment time for "now" e.Init(); // clear IDs @@ -1165,8 +1409,9 @@ e.Param.NoteSynthParam.NoteID = id.noteID(); e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam; e.Param.NoteSynthParam.Delta = fValue; - e.Param.NoteSynthParam.Relative = false; - + e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit( + isFinal, false, unit + ); pEngineChannel->ScheduleEventMicroSec(&e, 0); } } @@ -1179,126 +1424,182 @@ VMFnResult* InstrumentScriptVMFunction_change_sustain::exec(VMFnArgs* args) { return VMChangeSynthParamFunction::execTemplate< + decltype(NoteBase::_Override::Sustain), &NoteBase::_Override::Sustain, Event::synth_param_sustain, - false, NO_LIMIT, 0>( args, "change_sustain" ); + /* if value passed without unit */ + 0, NO_LIMIT, true, + /* if value passed WITH 'Bel' unit */ + NO_LIMIT, NO_LIMIT, VM_MILLI, VM_DECI>( args, "change_sustain" ); } // change_cutoff_attack() function VMFnResult* InstrumentScriptVMFunction_change_cutoff_attack::exec(VMFnArgs* args) { return VMChangeSynthParamFunction::execTemplate< + decltype(NoteBase::_Override::CutoffAttack), &NoteBase::_Override::CutoffAttack, Event::synth_param_cutoff_attack, - false, NO_LIMIT, 0>( args, "change_cutoff_attack" ); + /* if value passed without unit */ + 0, NO_LIMIT, true, + /* if value passed with 'seconds' unit */ + 0, NO_LIMIT, VM_MICRO>( args, "change_cutoff_attack" ); } // change_cutoff_decay() function VMFnResult* InstrumentScriptVMFunction_change_cutoff_decay::exec(VMFnArgs* args) { return VMChangeSynthParamFunction::execTemplate< + decltype(NoteBase::_Override::CutoffDecay), &NoteBase::_Override::CutoffDecay, Event::synth_param_cutoff_decay, - false, NO_LIMIT, 0>( args, "change_cutoff_decay" ); + /* if value passed without unit */ + 0, NO_LIMIT, true, + /* if value passed with 'seconds' unit */ + 0, NO_LIMIT, VM_MICRO>( args, "change_cutoff_decay" ); } // change_cutoff_sustain() function VMFnResult* InstrumentScriptVMFunction_change_cutoff_sustain::exec(VMFnArgs* args) { return VMChangeSynthParamFunction::execTemplate< + decltype(NoteBase::_Override::CutoffSustain), &NoteBase::_Override::CutoffSustain, Event::synth_param_cutoff_sustain, - false, NO_LIMIT, 0>( args, "change_cutoff_sustain" ); + /* if value passed without unit */ + 0, NO_LIMIT, true, + /* if value passed WITH 'Bel' unit */ + NO_LIMIT, NO_LIMIT, VM_MILLI, VM_DECI>( args, "change_cutoff_sustain" ); } // change_cutoff_release() function VMFnResult* InstrumentScriptVMFunction_change_cutoff_release::exec(VMFnArgs* args) { return VMChangeSynthParamFunction::execTemplate< + decltype(NoteBase::_Override::CutoffRelease), &NoteBase::_Override::CutoffRelease, Event::synth_param_cutoff_release, - false, NO_LIMIT, 0>( args, "change_cutoff_release" ); + /* if value passed without unit */ + 0, NO_LIMIT, true, + /* if value passed with 'seconds' unit */ + 0, NO_LIMIT, VM_MICRO>( args, "change_cutoff_release" ); } // change_amp_lfo_depth() function VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_depth::exec(VMFnArgs* args) { return VMChangeSynthParamFunction::execTemplate< + decltype(NoteBase::_Override::AmpLFODepth), &NoteBase::_Override::AmpLFODepth, Event::synth_param_amp_lfo_depth, - true, 1000000, 0>( args, "change_amp_lfo_depth" ); + /* if value passed without unit */ + 0, 1000000, true, + /* not used (since this function does not accept unit) */ + NO_LIMIT, NO_LIMIT, VM_NO_PREFIX>( args, "change_amp_lfo_depth" ); } // change_amp_lfo_freq() function VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_freq::exec(VMFnArgs* args) { return VMChangeSynthParamFunction::execTemplate< + decltype(NoteBase::_Override::AmpLFOFreq), &NoteBase::_Override::AmpLFOFreq, Event::synth_param_amp_lfo_freq, - true, 1000000, 0>( args, "change_amp_lfo_freq" ); + /* if value passed without unit */ + 0, 1000000, true, + /* if value passed with 'Hz' unit */ + 0, 30000, VM_NO_PREFIX>( args, "change_amp_lfo_freq" ); } // change_cutoff_lfo_depth() function VMFnResult* InstrumentScriptVMFunction_change_cutoff_lfo_depth::exec(VMFnArgs* args) { return VMChangeSynthParamFunction::execTemplate< + decltype(NoteBase::_Override::CutoffLFODepth), &NoteBase::_Override::CutoffLFODepth, Event::synth_param_cutoff_lfo_depth, - true, 1000000, 0>( args, "change_cutoff_lfo_depth" ); + /* if value passed without unit */ + 0, 1000000, true, + /* not used (since this function does not accept unit) */ + NO_LIMIT, NO_LIMIT, VM_NO_PREFIX>( args, "change_cutoff_lfo_depth" ); } // change_cutoff_lfo_freq() function VMFnResult* InstrumentScriptVMFunction_change_cutoff_lfo_freq::exec(VMFnArgs* args) { return VMChangeSynthParamFunction::execTemplate< + decltype(NoteBase::_Override::CutoffLFOFreq), &NoteBase::_Override::CutoffLFOFreq, Event::synth_param_cutoff_lfo_freq, - true, 1000000, 0>( args, "change_cutoff_lfo_freq" ); + /* if value passed without unit */ + 0, 1000000, true, + /* if value passed with 'Hz' unit */ + 0, 30000, VM_NO_PREFIX>( args, "change_cutoff_lfo_freq" ); } // change_pitch_lfo_depth() function VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_depth::exec(VMFnArgs* args) { return VMChangeSynthParamFunction::execTemplate< + decltype(NoteBase::_Override::PitchLFODepth), &NoteBase::_Override::PitchLFODepth, Event::synth_param_pitch_lfo_depth, - true, 1000000, 0>( args, "change_pitch_lfo_depth" ); + /* if value passed without unit */ + 0, 1000000, true, + /* not used (since this function does not accept unit) */ + NO_LIMIT, NO_LIMIT, VM_NO_PREFIX>( args, "change_pitch_lfo_depth" ); } // change_pitch_lfo_freq() function VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_freq::exec(VMFnArgs* args) { return VMChangeSynthParamFunction::execTemplate< + decltype(NoteBase::_Override::PitchLFOFreq), &NoteBase::_Override::PitchLFOFreq, Event::synth_param_pitch_lfo_freq, - true, 1000000, 0>( args, "change_pitch_lfo_freq" ); + /* if value passed without unit */ + 0, 1000000, true, + /* if value passed with 'Hz' unit */ + 0, 30000, VM_NO_PREFIX>( args, "change_pitch_lfo_freq" ); } // change_vol_time() function VMFnResult* InstrumentScriptVMFunction_change_vol_time::exec(VMFnArgs* args) { return VMChangeSynthParamFunction::execTemplate< + decltype(NoteBase::_Override::VolumeTime), &NoteBase::_Override::VolumeTime, Event::synth_param_volume_time, - false, NO_LIMIT, 0>( args, "change_vol_time" ); + /* if value passed without unit (implying 'us' unit) */ + 0, NO_LIMIT, true, + /* if value passed with 'seconds' unit */ + 0, NO_LIMIT, VM_MICRO>( args, "change_vol_time" ); } // change_tune_time() function VMFnResult* InstrumentScriptVMFunction_change_tune_time::exec(VMFnArgs* args) { return VMChangeSynthParamFunction::execTemplate< + decltype(NoteBase::_Override::PitchTime), &NoteBase::_Override::PitchTime, Event::synth_param_pitch_time, - false, NO_LIMIT, 0>( args, "change_tune_time" ); + /* if value passed without unit (implying 'us' unit) */ + 0, NO_LIMIT, true, + /* if value passed with 'seconds' unit */ + 0, NO_LIMIT, VM_MICRO>( args, "change_tune_time" ); } // change_pan_time() function VMFnResult* InstrumentScriptVMFunction_change_pan_time::exec(VMFnArgs* args) { return VMChangeSynthParamFunction::execTemplate< - &NoteBase::_Override::PanTime, - Event::synth_param_pan_time, - false, NO_LIMIT, 0>( args, "change_pan_time" ); + decltype(NoteBase::_Override::PanTime), + &NoteBase::_Override::PanTime, + Event::synth_param_pan_time, + /* if value passed without unit (implying 'us' unit) */ + 0, NO_LIMIT, true, + /* if value passed with 'seconds' unit */ + 0, NO_LIMIT, VM_MICRO>( args, "change_pan_time" ); } // template for change_*_curve() functions @@ -1351,7 +1652,7 @@ e.Param.NoteSynthParam.NoteID = id.noteID(); e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam; e.Param.NoteSynthParam.Delta = value; - e.Param.NoteSynthParam.Relative = false; + e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored pEngineChannel->ScheduleEventMicroSec(&e, 0); } @@ -1376,7 +1677,7 @@ e.Param.NoteSynthParam.NoteID = id.noteID(); e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam; e.Param.NoteSynthParam.Delta = value; - e.Param.NoteSynthParam.Relative = false; + e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored pEngineChannel->ScheduleEventMicroSec(&e, 0); } @@ -1424,8 +1725,19 @@ return type == INT_EXPR; } + bool InstrumentScriptVMFunction_fade_in::acceptsArgUnitType(vmint iArg, StdUnit_t type) const { + if (iArg == 1) + return type == VM_NO_UNIT || type == VM_SECOND; + else + return type == VM_NO_UNIT; + } + + bool InstrumentScriptVMFunction_fade_in::acceptsArgUnitPrefix(vmint iArg) const { + return iArg == 1; + } + VMFnResult* InstrumentScriptVMFunction_fade_in::exec(VMFnArgs* args) { - vmint duration = args->arg(1)->asInt()->evalInt(); + vmint duration = args->arg(1)->asInt()->evalInt(VM_MICRO); if (duration < 0) { wrnMsg("fade_in(): argument 2 may not be negative"); duration = 0; @@ -1453,7 +1765,7 @@ // then immediately apply a start volume of zero to Note object, // as well as the fade in duration if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { - pNote->Override.Volume = 0.f; + pNote->Override.Volume.Value = 0.f; pNote->Override.VolumeTime = fDuration; } else { // otherwise schedule a "volume time" change with the requested fade in duration ... Event e = m_vm->m_event->cause; // copy to get fragment time for "now" @@ -1462,7 +1774,7 @@ e.Param.NoteSynthParam.NoteID = id.noteID(); e.Param.NoteSynthParam.Type = Event::synth_param_volume_time; e.Param.NoteSynthParam.Delta = fDuration; - e.Param.NoteSynthParam.Relative = false; + e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored pEngineChannel->ScheduleEventMicroSec(&e, 0); } @@ -1475,7 +1787,7 @@ e.Param.NoteSynthParam.NoteID = id.noteID(); e.Param.NoteSynthParam.Type = Event::synth_param_volume; e.Param.NoteSynthParam.Delta = 1.f; - e.Param.NoteSynthParam.Relative = false; + e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored // scheduling with 0 delay would also work here, but +1 is more // safe regarding potential future implementation changes of the @@ -1495,7 +1807,7 @@ // then immediately apply a start volume of zero to Note object, // as well as the fade in duration if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { - pNote->Override.Volume = 0.f; + pNote->Override.Volume.Value = 0.f; pNote->Override.VolumeTime = fDuration; } else { // otherwise schedule a "volume time" change with the requested fade in duration ... Event e = m_vm->m_event->cause; // copy to get fragment time for "now" @@ -1504,7 +1816,7 @@ e.Param.NoteSynthParam.NoteID = id.noteID(); e.Param.NoteSynthParam.Type = Event::synth_param_volume_time; e.Param.NoteSynthParam.Delta = fDuration; - e.Param.NoteSynthParam.Relative = false; + e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored pEngineChannel->ScheduleEventMicroSec(&e, 0); } @@ -1517,7 +1829,7 @@ e.Param.NoteSynthParam.NoteID = id.noteID(); e.Param.NoteSynthParam.Type = Event::synth_param_volume; e.Param.NoteSynthParam.Delta = 1.f; - e.Param.NoteSynthParam.Relative = false; + e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored // scheduling with 0 delay would also work here, but +1 is more // safe regarding potential future implementation changes of the @@ -1544,8 +1856,19 @@ return type == INT_EXPR; } + bool InstrumentScriptVMFunction_fade_out::acceptsArgUnitType(vmint iArg, StdUnit_t type) const { + if (iArg == 1) + return type == VM_NO_UNIT || type == VM_SECOND; + else + return type == VM_NO_UNIT; + } + + bool InstrumentScriptVMFunction_fade_out::acceptsArgUnitPrefix(vmint iArg) const { + return iArg == 1; + } + VMFnResult* InstrumentScriptVMFunction_fade_out::exec(VMFnArgs* args) { - vmint duration = args->arg(1)->asInt()->evalInt(); + vmint duration = args->arg(1)->asInt()->evalInt(VM_MICRO); if (duration < 0) { wrnMsg("fade_out(): argument 2 may not be negative"); duration = 0; @@ -1582,7 +1905,7 @@ e.Param.NoteSynthParam.NoteID = id.noteID(); e.Param.NoteSynthParam.Type = Event::synth_param_volume_time; e.Param.NoteSynthParam.Delta = fDuration; - e.Param.NoteSynthParam.Relative = false; + e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored pEngineChannel->ScheduleEventMicroSec(&e, 0); } @@ -1595,7 +1918,7 @@ e.Param.NoteSynthParam.NoteID = id.noteID(); e.Param.NoteSynthParam.Type = Event::synth_param_volume; e.Param.NoteSynthParam.Delta = 0.f; - e.Param.NoteSynthParam.Relative = false; + e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored // scheduling with 0 delay would also work here, but +1 is more // safe regarding potential future implementation changes of the @@ -1634,7 +1957,7 @@ e.Param.NoteSynthParam.NoteID = id.noteID(); e.Param.NoteSynthParam.Type = Event::synth_param_volume_time; e.Param.NoteSynthParam.Delta = fDuration; - e.Param.NoteSynthParam.Relative = false; + e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored pEngineChannel->ScheduleEventMicroSec(&e, 0); } @@ -1647,7 +1970,7 @@ e.Param.NoteSynthParam.NoteID = id.noteID(); e.Param.NoteSynthParam.Type = Event::synth_param_volume; e.Param.NoteSynthParam.Delta = 0.f; - e.Param.NoteSynthParam.Relative = false; + e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored // scheduling with 0 delay would also work here, but +1 is more // safe regarding potential future implementation changes of the @@ -1707,11 +2030,11 @@ return successResult(pNote->cause.Param.Note.Velocity); case EVENT_PAR_VOLUME: return successResult( - RTMath::LinRatioToDecibel(pNote->Override.Volume) * 1000.f + RTMath::LinRatioToDecibel(pNote->Override.Volume.Value) * 1000.f ); case EVENT_PAR_TUNE: return successResult( - RTMath::FreqRatioToCents(pNote->Override.Pitch) * 1000.f + RTMath::FreqRatioToCents(pNote->Override.Pitch.Value) * 1000.f ); case EVENT_PAR_0: return successResult(pNote->userPar[0]); @@ -1890,8 +2213,19 @@ { } + bool InstrumentScriptVMFunction_change_play_pos::acceptsArgUnitType(vmint iArg, StdUnit_t type) const { + if (iArg == 1) + return type == VM_NO_UNIT || type == VM_SECOND; + else + return type == VM_NO_UNIT; + } + + bool InstrumentScriptVMFunction_change_play_pos::acceptsArgUnitPrefix(vmint iArg) const { + return iArg == 1; + } + VMFnResult* InstrumentScriptVMFunction_change_play_pos::exec(VMFnArgs* args) { - const ScriptID id = args->arg(0)->asInt()->evalInt(); + const ScriptID id = args->arg(0)->asInt()->evalInt(VM_MICRO); if (!id) { wrnMsg("change_play_pos(): note ID for argument 1 may not be zero"); return successResult();