--- linuxsampler/trunk/src/engines/common/InstrumentScriptVMFunctions.cpp 2017/05/24 20:05:38 3205 +++ linuxsampler/trunk/src/engines/common/InstrumentScriptVMFunctions.cpp 2017/05/30 15:43:39 3255 @@ -23,7 +23,6 @@ VMFnResult* InstrumentScriptVMFunction_play_note::exec(VMFnArgs* args) { int note = args->arg(0)->asInt()->evalInt(); int velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127; - int sampleoffset = (args->argsCount() >= 3) ? args->arg(2)->asInt()->evalInt() : 0; int duration = (args->argsCount() >= 4) ? args->arg(3)->asInt()->evalInt() : 0; //TODO: -1 might be a better default value instead of 0 if (note < 0 || note > 127) { @@ -36,15 +35,8 @@ return errorResult(0); } - if (sampleoffset < 0) { - errMsg("play_note(): argument 3 may not be a negative sample offset"); - return errorResult(0); - } else if (sampleoffset != 0) { - wrnMsg("play_note(): argument 3 does not support a sample offset other than 0 yet"); - } - - if (duration < -1) { - errMsg("play_note(): argument 4 must be a duration value of at least -1 or higher"); + if (duration < -2) { + errMsg("play_note(): argument 4 must be a duration value of at least -2 or higher"); return errorResult(0); } @@ -63,10 +55,30 @@ return errorResult(0); } e.Param.Note.ParentNoteID = m_vm->m_event->cause.Param.Note.ID; + // check if that requested parent note is actually still alive + NoteBase* pParentNote = + pEngineChannel->pEngine->NoteByID( e.Param.Note.ParentNoteID ); + // if parent note is already gone then this new note is not required anymore + if (!pParentNote) + return successResult(0); } const note_id_t id = pEngineChannel->ScheduleNoteMicroSec(&e, 0); + // if a sample offset is supplied, assign the offset as override + // to the previously created Note object + if (args->argsCount() >= 3) { + int sampleoffset = args->arg(2)->asInt()->evalInt(); + if (sampleoffset >= 0) { + NoteBase* pNote = pEngineChannel->pEngine->NoteByID(id); + if (pNote) { + pNote->Override.SampleOffset = sampleoffset; + } + } else if (sampleoffset < -1) { + errMsg("play_note(): sample offset of argument 3 may not be less than -1"); + } + } + // if a duration is supplied (and play-note event was scheduled // successfully above), then schedule a subsequent stop-note event if (id && duration > 0) { @@ -136,9 +148,9 @@ AbstractEngineChannel* pEngineChannel = static_cast(m_vm->m_event->cause.pEngineChannel); - if (args->arg(0)->exprType() == INT_EXPR) { - const ScriptID id = args->arg(0)->asInt()->evalInt(); - if (!id) { + if (args->argsCount() == 0 || args->arg(0)->exprType() == INT_EXPR) { + const ScriptID id = (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : m_vm->m_event->id; + if (!id && args->argsCount() >= 1) { wrnMsg("ignore_event(): event ID argument may not be zero"); // not errorResult(), because that would abort the script, not intentional in this case return successResult(); @@ -385,8 +397,11 @@ if (!pNote) return successResult(); // if change_vol() was called immediately after note was triggered - // then immediately apply the volume to note object - if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { + // then immediately apply the volume to note object, but only if + // change_vol_time() has not been called before + if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime && + pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S) + { if (relative) pNote->Override.Volume *= fVolumeLin; else @@ -412,8 +427,11 @@ if (!pNote) continue; // if change_vol() was called immediately after note was triggered - // then immediately apply the volume to Note object - if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { + // then immediately apply the volume to Note object, but only if + // change_vol_time() has not been called before + if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime && + pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S) + { if (relative) pNote->Override.Volume *= fVolumeLin; else @@ -472,8 +490,11 @@ if (!pNote) return successResult(); // if change_tune() was called immediately after note was triggered - // then immediately apply the tuning to Note object - if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { + // then immediately apply the tuning to Note object, but only if + // change_tune_time() has not been called before + if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime && + pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S) + { if (relative) pNote->Override.Pitch *= fFreqRatio; else @@ -499,8 +520,11 @@ if (!pNote) continue; // if change_tune() was called immediately after note was triggered - // then immediately apply the tuning to Note object - if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { + // then immediately apply the tuning to Note object, but only if + // change_tune_time() has not been called before + if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime && + pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S) + { if (relative) pNote->Override.Pitch *= fFreqRatio; else @@ -1059,6 +1083,8 @@ return successResult(); } + // template for change_*() functions + bool VMChangeSynthParamFunction::acceptsArgType(int iArg, ExprType_t type) const { if (iArg == 0) return type == INT_EXPR || type == INT_ARR_EXPR; @@ -1205,6 +1231,107 @@ false, NO_LIMIT, 0>( args, "change_tune_time" ); } + // template for change_*_curve() functions + + bool VMChangeFadeCurveFunction::acceptsArgType(int iArg, ExprType_t type) const { + if (iArg == 0) + return type == INT_EXPR || type == INT_ARR_EXPR; + else + return type == INT_EXPR; + } + + template + VMFnResult* VMChangeFadeCurveFunction::execTemplate(VMFnArgs* args, const char* functionName) { + int value = args->arg(1)->asInt()->evalInt(); + switch (value) { + case FADE_CURVE_LINEAR: + case FADE_CURVE_EASE_IN_EASE_OUT: + break; + default: + wrnMsg(String(functionName) + "(): invalid curve type passed as argument 2"); + return successResult(); + } + + AbstractEngineChannel* pEngineChannel = + static_cast(m_vm->m_event->cause.pEngineChannel); + + if (args->arg(0)->exprType() == INT_EXPR) { + const ScriptID id = args->arg(0)->asInt()->evalInt(); + if (!id) { + wrnMsg(String(functionName) + "(): note ID for argument 1 may not be zero"); + return successResult(); + } + if (!id.isNoteID()) { + wrnMsg(String(functionName) + "(): argument 1 is not a note ID"); + return successResult(); + } + + NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); + if (!pNote) return successResult(); + + // if this change_*_curve() script function was called immediately after + // 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 = (fade_curve_t) value; + } 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 + e.Type = Event::type_note_synth_param; + 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; + + 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; + + // if this change_*_curve() script function was called immediately after + // 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 = (fade_curve_t) value; + } 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 + e.Type = Event::type_note_synth_param; + 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; + + pEngineChannel->ScheduleEventMicroSec(&e, 0); + } + } + } + + return successResult(); + } + + // change_vol_curve() function + + VMFnResult* InstrumentScriptVMFunction_change_vol_curve::exec(VMFnArgs* args) { + return VMChangeFadeCurveFunction::execTemplate< + &NoteBase::_Override::VolumeCurve, + Event::synth_param_volume_curve>( args, "change_vol_curve" ); + } + + // change_tune_curve() function + + VMFnResult* InstrumentScriptVMFunction_change_tune_curve::exec(VMFnArgs* args) { + return VMChangeFadeCurveFunction::execTemplate< + &NoteBase::_Override::PitchCurve, + Event::synth_param_pitch_curve>( args, "change_tune_curve" ); + } + // fade_in() function InstrumentScriptVMFunction_fade_in::InstrumentScriptVMFunction_fade_in(InstrumentScriptVM* parent) @@ -1555,20 +1682,24 @@ wrnMsg("set_event_par(): note number of argument 3 is out of range"); return successResult(); } - if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) + if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { pNote->cause.Param.Note.Key = value; - else + m_vm->m_event->cause.Param.Note.Key = value; + } else { wrnMsg("set_event_par(): note number can only be changed when note is new"); + } return successResult(); case EVENT_PAR_VELOCITY: if (value < 0 || value > 127) { wrnMsg("set_event_par(): velocity of argument 3 is out of range"); return successResult(); } - if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) + if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { pNote->cause.Param.Note.Velocity = value; - else + m_vm->m_event->cause.Param.Note.Velocity = value; + } else { wrnMsg("set_event_par(): velocity can only be changed when note is new"); + } return successResult(); case EVENT_PAR_VOLUME: wrnMsg("set_event_par(): changing volume by this function is currently not supported, use change_vol() instead"); @@ -1594,6 +1725,121 @@ return successResult(); } + // change_note() function + + InstrumentScriptVMFunction_change_note::InstrumentScriptVMFunction_change_note(InstrumentScriptVM* parent) + : m_vm(parent) + { + } + + VMFnResult* InstrumentScriptVMFunction_change_note::exec(VMFnArgs* args) { + AbstractEngineChannel* pEngineChannel = + static_cast(m_vm->m_event->cause.pEngineChannel); + + const ScriptID id = args->arg(0)->asInt()->evalInt(); + if (!id) { + wrnMsg("change_note(): note ID for argument 1 may not be zero"); + return successResult(); + } + if (!id.isNoteID()) { + wrnMsg("change_note(): argument 1 is not a note ID"); + return successResult(); + } + + NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); + if (!pNote) return successResult(); + + const int value = args->arg(1)->asInt()->evalInt(); + if (value < 0 || value > 127) { + wrnMsg("change_note(): note number of argument 2 is out of range"); + return successResult(); + } + + if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { + pNote->cause.Param.Note.Key = value; + m_vm->m_event->cause.Param.Note.Key = value; + } else { + wrnMsg("change_note(): note number can only be changed when note is new"); + } + + return successResult(); + } + + // change_velo() function + + InstrumentScriptVMFunction_change_velo::InstrumentScriptVMFunction_change_velo(InstrumentScriptVM* parent) + : m_vm(parent) + { + } + + VMFnResult* InstrumentScriptVMFunction_change_velo::exec(VMFnArgs* args) { + AbstractEngineChannel* pEngineChannel = + static_cast(m_vm->m_event->cause.pEngineChannel); + + const ScriptID id = args->arg(0)->asInt()->evalInt(); + if (!id) { + wrnMsg("change_velo(): note ID for argument 1 may not be zero"); + return successResult(); + } + if (!id.isNoteID()) { + wrnMsg("change_velo(): argument 1 is not a note ID"); + return successResult(); + } + + NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); + if (!pNote) return successResult(); + + const int value = args->arg(1)->asInt()->evalInt(); + if (value < 0 || value > 127) { + wrnMsg("change_velo(): velocity of argument 2 is out of range"); + return successResult(); + } + + if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { + pNote->cause.Param.Note.Velocity = value; + m_vm->m_event->cause.Param.Note.Velocity = value; + } else { + wrnMsg("change_velo(): velocity can only be changed when note is new"); + } + + return successResult(); + } + + // change_play_pos() function + + InstrumentScriptVMFunction_change_play_pos::InstrumentScriptVMFunction_change_play_pos(InstrumentScriptVM* parent) + : m_vm(parent) + { + } + + VMFnResult* InstrumentScriptVMFunction_change_play_pos::exec(VMFnArgs* args) { + const ScriptID id = args->arg(0)->asInt()->evalInt(); + if (!id) { + wrnMsg("change_play_pos(): note ID for argument 1 may not be zero"); + return successResult(); + } + if (!id.isNoteID()) { + wrnMsg("change_play_pos(): argument 1 is not a note ID"); + return successResult(); + } + + const int pos = args->arg(1)->asInt()->evalInt(); + if (pos < 0) { + wrnMsg("change_play_pos(): playback position of argument 2 may not be negative"); + return successResult(); + } + + AbstractEngineChannel* pEngineChannel = + static_cast(m_vm->m_event->cause.pEngineChannel); + + NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); + if (!pNote) return successResult(); + + pNote->Override.SampleOffset = pos; + + return successResult(); + } + // event_status() function InstrumentScriptVMFunction_event_status::InstrumentScriptVMFunction_event_status(InstrumentScriptVM* parent)