--- linuxsampler/trunk/src/engines/common/InstrumentScriptVMFunctions.cpp 2017/05/20 12:28:57 3193 +++ linuxsampler/trunk/src/engines/common/InstrumentScriptVMFunctions.cpp 2019/08/18 00:06:04 3557 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2017 Christian Schoenebeck + * Copyright (c) 2014-2019 Christian Schoenebeck * * http://www.linuxsampler.org * @@ -21,10 +21,9 @@ } 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 + vmint note = args->arg(0)->asInt()->evalInt(); + vmint velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127; + vmint 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) { errMsg("play_note(): argument 1 is an invalid note number"); @@ -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,31 @@ 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) { + vmint sampleoffset = args->arg(2)->asInt()->evalInt(); + if (sampleoffset >= 0) { + NoteBase* pNote = pEngineChannel->pEngine->NoteByID(id); + if (pNote) { + pNote->Override.SampleOffset = + (decltype(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) { @@ -90,8 +103,8 @@ } VMFnResult* InstrumentScriptVMFunction_set_controller::exec(VMFnArgs* args) { - int controller = args->arg(0)->asInt()->evalInt(); - int value = args->arg(1)->asInt()->evalInt(); + vmint controller = args->arg(0)->asInt()->evalInt(); + vmint value = args->arg(1)->asInt()->evalInt(); AbstractEngineChannel* pEngineChannel = static_cast(m_vm->m_event->cause.pEngineChannel); @@ -128,7 +141,7 @@ { } - bool InstrumentScriptVMFunction_ignore_event::acceptsArgType(int iArg, ExprType_t type) const { + bool InstrumentScriptVMFunction_ignore_event::acceptsArgType(vmint iArg, ExprType_t type) const { return type == INT_EXPR || type == INT_ARR_EXPR; } @@ -136,9 +149,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(); @@ -184,7 +197,7 @@ { } - bool InstrumentScriptVMFunction_note_off::acceptsArgType(int iArg, ExprType_t type) const { + bool InstrumentScriptVMFunction_note_off::acceptsArgType(vmint iArg, ExprType_t type) const { return type == INT_EXPR || type == INT_ARR_EXPR; } @@ -192,7 +205,7 @@ AbstractEngineChannel* pEngineChannel = static_cast(m_vm->m_event->cause.pEngineChannel); - int velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127; + vmint velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127; if (velocity < 0 || velocity > 127) { errMsg("note_off(): argument 2 is an invalid velocity value"); return errorResult(); @@ -223,7 +236,7 @@ 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) { + for (vmint i = 0; i < ids->arraySize(); ++i) { const ScriptID id = ids->evalIntElement(i); if (!id || !id.isNoteID()) continue; @@ -254,7 +267,7 @@ VMFnResult* InstrumentScriptVMFunction_set_event_mark::exec(VMFnArgs* args) { const ScriptID id = args->arg(0)->asInt()->evalInt(); - const int groupID = args->arg(1)->asInt()->evalInt(); + const vmint groupID = args->arg(1)->asInt()->evalInt(); if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) { errMsg("set_event_mark(): argument 2 is an invalid group id"); @@ -292,7 +305,7 @@ VMFnResult* InstrumentScriptVMFunction_delete_event_mark::exec(VMFnArgs* args) { const ScriptID id = args->arg(0)->asInt()->evalInt(); - const int groupID = args->arg(1)->asInt()->evalInt(); + const vmint groupID = args->arg(1)->asInt()->evalInt(); if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) { errMsg("delete_event_mark(): argument 2 is an invalid group id"); @@ -314,11 +327,11 @@ { } - int InstrumentScriptVMFunction_by_marks::Result::arraySize() const { + vmint InstrumentScriptVMFunction_by_marks::Result::arraySize() const { return eventGroup->size(); } - int InstrumentScriptVMFunction_by_marks::Result::evalIntElement(uint i) { + vmint InstrumentScriptVMFunction_by_marks::Result::evalIntElement(vmuint i) { return (*eventGroup)[i]; } @@ -335,7 +348,7 @@ } VMFnResult* InstrumentScriptVMFunction_by_marks::exec(VMFnArgs* args) { - int groupID = args->arg(0)->asInt()->evalInt(); + vmint groupID = args->arg(0)->asInt()->evalInt(); if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) { errMsg("by_marks(): argument is an invalid group id"); @@ -355,7 +368,7 @@ { } - bool InstrumentScriptVMFunction_change_vol::acceptsArgType(int iArg, ExprType_t type) const { + bool InstrumentScriptVMFunction_change_vol::acceptsArgType(vmint iArg, ExprType_t type) const { if (iArg == 0) return type == INT_EXPR || type == INT_ARR_EXPR; else @@ -363,7 +376,7 @@ } VMFnResult* InstrumentScriptVMFunction_change_vol::exec(VMFnArgs* args) { - int volume = args->arg(1)->asInt()->evalInt(); // volume change in milli dB + vmint volume = args->arg(1)->asInt()->evalInt(); // volume change in milli dB bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false; const float fVolumeLin = RTMath::DecibelToLinRatio(float(volume) / 1000.f); @@ -385,8 +398,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->cause.SchedTime() == 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 @@ -404,7 +420,7 @@ } } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { VMIntArrayExpr* ids = args->arg(0)->asIntArray(); - for (int i = 0; i < ids->arraySize(); ++i) { + for (vmint i = 0; i < ids->arraySize(); ++i) { const ScriptID id = ids->evalIntElement(i); if (!id || !id.isNoteID()) continue; @@ -412,8 +428,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->cause.SchedTime() == 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 @@ -442,7 +461,7 @@ { } - bool InstrumentScriptVMFunction_change_tune::acceptsArgType(int iArg, ExprType_t type) const { + bool InstrumentScriptVMFunction_change_tune::acceptsArgType(vmint iArg, ExprType_t type) const { if (iArg == 0) return type == INT_EXPR || type == INT_ARR_EXPR; else @@ -450,7 +469,7 @@ } VMFnResult* InstrumentScriptVMFunction_change_tune::exec(VMFnArgs* args) { - int tune = args->arg(1)->asInt()->evalInt(); // tuning change in milli cents + vmint tune = args->arg(1)->asInt()->evalInt(); // tuning change in milli cents bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false; const float fFreqRatio = RTMath::CentsToFreqRatioUnlimited(float(tune) / 1000.f); @@ -472,8 +491,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->cause.SchedTime() == 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 @@ -491,7 +513,7 @@ } } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { VMIntArrayExpr* ids = args->arg(0)->asIntArray(); - for (int i = 0; i < ids->arraySize(); ++i) { + for (vmint i = 0; i < ids->arraySize(); ++i) { const ScriptID id = ids->evalIntElement(i); if (!id || !id.isNoteID()) continue; @@ -499,8 +521,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->cause.SchedTime() == 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 @@ -529,7 +554,7 @@ { } - bool InstrumentScriptVMFunction_change_pan::acceptsArgType(int iArg, ExprType_t type) const { + bool InstrumentScriptVMFunction_change_pan::acceptsArgType(vmint iArg, ExprType_t type) const { if (iArg == 0) return type == INT_EXPR || type == INT_ARR_EXPR; else @@ -537,7 +562,7 @@ } VMFnResult* InstrumentScriptVMFunction_change_pan::exec(VMFnArgs* args) { - int pan = args->arg(1)->asInt()->evalInt(); + vmint pan = args->arg(1)->asInt()->evalInt(); bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false; if (pan > 1000) { @@ -568,7 +593,7 @@ // if change_pan() was called immediately after note was triggered // then immediately apply the panning to Note object - if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) { + if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { if (relative) { pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources); } else { @@ -588,7 +613,7 @@ } } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { VMIntArrayExpr* ids = args->arg(0)->asIntArray(); - for (int i = 0; i < ids->arraySize(); ++i) { + for (vmint i = 0; i < ids->arraySize(); ++i) { const ScriptID id = ids->evalIntElement(i); if (!id || !id.isNoteID()) continue; @@ -597,7 +622,7 @@ // if change_pan() was called immediately after note was triggered // then immediately apply the panning to Note object - if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) { + if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { if (relative) { pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources); } else { @@ -631,7 +656,7 @@ { } - bool InstrumentScriptVMFunction_change_cutoff::acceptsArgType(int iArg, ExprType_t type) const { + bool InstrumentScriptVMFunction_change_cutoff::acceptsArgType(vmint iArg, ExprType_t type) const { if (iArg == 0) return type == INT_EXPR || type == INT_ARR_EXPR; else @@ -639,7 +664,7 @@ } VMFnResult* InstrumentScriptVMFunction_change_cutoff::exec(VMFnArgs* args) { - int cutoff = args->arg(1)->asInt()->evalInt(); + 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"); cutoff = VM_FILTER_PAR_MAX_VALUE; @@ -668,7 +693,7 @@ // if change_cutoff() was called immediately after note was triggered // then immediately apply cutoff to Note object - if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) { + if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { pNote->Override.Cutoff = fCutoff; } else { // otherwise schedule cutoff change ... Event e = m_vm->m_event->cause; // copy to get fragment time for "now" @@ -683,7 +708,7 @@ } } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { VMIntArrayExpr* ids = args->arg(0)->asIntArray(); - for (int i = 0; i < ids->arraySize(); ++i) { + for (vmint i = 0; i < ids->arraySize(); ++i) { const ScriptID id = ids->evalIntElement(i); if (!id || !id.isNoteID()) continue; @@ -692,7 +717,7 @@ // if change_cutoff() was called immediately after note was triggered // then immediately apply cutoff to Note object - if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) { + if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { pNote->Override.Cutoff = fCutoff; } else { // otherwise schedule cutoff change ... Event e = m_vm->m_event->cause; // copy to get fragment time for "now" @@ -718,7 +743,7 @@ { } - bool InstrumentScriptVMFunction_change_reso::acceptsArgType(int iArg, ExprType_t type) const { + bool InstrumentScriptVMFunction_change_reso::acceptsArgType(vmint iArg, ExprType_t type) const { if (iArg == 0) return type == INT_EXPR || type == INT_ARR_EXPR; else @@ -726,7 +751,7 @@ } VMFnResult* InstrumentScriptVMFunction_change_reso::exec(VMFnArgs* args) { - int resonance = args->arg(1)->asInt()->evalInt(); + vmint resonance = args->arg(1)->asInt()->evalInt(); if (resonance > VM_FILTER_PAR_MAX_VALUE) { wrnMsg("change_reso(): argument 2 may not be larger than 1000000"); resonance = VM_FILTER_PAR_MAX_VALUE; @@ -755,7 +780,7 @@ // if change_reso() was called immediately after note was triggered // then immediately apply resonance to Note object - if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) { + if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { pNote->Override.Resonance = fResonance; } else { // otherwise schedule resonance change ... Event e = m_vm->m_event->cause; // copy to get fragment time for "now" @@ -770,7 +795,7 @@ } } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { VMIntArrayExpr* ids = args->arg(0)->asIntArray(); - for (int i = 0; i < ids->arraySize(); ++i) { + for (vmint i = 0; i < ids->arraySize(); ++i) { const ScriptID id = ids->evalIntElement(i); if (!id || !id.isNoteID()) continue; @@ -779,7 +804,7 @@ // if change_reso() was called immediately after note was triggered // then immediately apply resonance to Note object - if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) { + if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { pNote->Override.Resonance = fResonance; } else { // otherwise schedule resonance change ... Event e = m_vm->m_event->cause; // copy to get fragment time for "now" @@ -805,7 +830,7 @@ { } - bool InstrumentScriptVMFunction_change_attack::acceptsArgType(int iArg, ExprType_t type) const { + bool InstrumentScriptVMFunction_change_attack::acceptsArgType(vmint iArg, ExprType_t type) const { if (iArg == 0) return type == INT_EXPR || type == INT_ARR_EXPR; else @@ -813,11 +838,10 @@ } VMFnResult* InstrumentScriptVMFunction_change_attack::exec(VMFnArgs* args) { - int attack = args->arg(1)->asInt()->evalInt(); - if (attack > VM_EG_PAR_MAX_VALUE) { - wrnMsg("change_attack(): argument 2 may not be larger than 1000000"); - attack = VM_EG_PAR_MAX_VALUE; - } else if (attack < 0) { + vmint attack = args->arg(1)->asInt()->evalInt(); + // note: intentionally not checking against a max. value here! + // (to allow i.e. passing 2000000 for doubling the attack time) + if (attack < 0) { wrnMsg("change_attack(): argument 2 may not be negative"); attack = 0; } @@ -842,7 +866,7 @@ // if change_attack() was called immediately after note was triggered // then immediately apply attack to Note object - if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) { + if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { pNote->Override.Attack = fAttack; } else { // otherwise schedule attack change ... Event e = m_vm->m_event->cause; // copy to get fragment time for "now" @@ -857,7 +881,7 @@ } } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { VMIntArrayExpr* ids = args->arg(0)->asIntArray(); - for (int i = 0; i < ids->arraySize(); ++i) { + for (vmint i = 0; i < ids->arraySize(); ++i) { const ScriptID id = ids->evalIntElement(i); if (!id || !id.isNoteID()) continue; @@ -866,7 +890,7 @@ // if change_attack() was called immediately after note was triggered // then immediately apply attack to Note object - if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) { + if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { pNote->Override.Attack = fAttack; } else { // otherwise schedule attack change ... Event e = m_vm->m_event->cause; // copy to get fragment time for "now" @@ -892,7 +916,7 @@ { } - bool InstrumentScriptVMFunction_change_decay::acceptsArgType(int iArg, ExprType_t type) const { + bool InstrumentScriptVMFunction_change_decay::acceptsArgType(vmint iArg, ExprType_t type) const { if (iArg == 0) return type == INT_EXPR || type == INT_ARR_EXPR; else @@ -900,11 +924,10 @@ } VMFnResult* InstrumentScriptVMFunction_change_decay::exec(VMFnArgs* args) { - int decay = args->arg(1)->asInt()->evalInt(); - if (decay > VM_EG_PAR_MAX_VALUE) { - wrnMsg("change_decay(): argument 2 may not be larger than 1000000"); - decay = VM_EG_PAR_MAX_VALUE; - } else if (decay < 0) { + vmint decay = args->arg(1)->asInt()->evalInt(); + // note: intentionally not checking against a max. value here! + // (to allow i.e. passing 2000000 for doubling the decay time) + if (decay < 0) { wrnMsg("change_decay(): argument 2 may not be negative"); decay = 0; } @@ -929,7 +952,7 @@ // if change_decay() was called immediately after note was triggered // then immediately apply decay to Note object - if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) { + if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { pNote->Override.Decay = fDecay; } else { // otherwise schedule decay change ... Event e = m_vm->m_event->cause; // copy to get fragment time for "now" @@ -944,7 +967,7 @@ } } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { VMIntArrayExpr* ids = args->arg(0)->asIntArray(); - for (int i = 0; i < ids->arraySize(); ++i) { + for (vmint i = 0; i < ids->arraySize(); ++i) { const ScriptID id = ids->evalIntElement(i); if (!id || !id.isNoteID()) continue; @@ -953,7 +976,7 @@ // if change_decay() was called immediately after note was triggered // then immediately apply decay to Note object - if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) { + if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { pNote->Override.Decay = fDecay; } else { // otherwise schedule decay change ... Event e = m_vm->m_event->cause; // copy to get fragment time for "now" @@ -979,7 +1002,7 @@ { } - bool InstrumentScriptVMFunction_change_release::acceptsArgType(int iArg, ExprType_t type) const { + bool InstrumentScriptVMFunction_change_release::acceptsArgType(vmint iArg, ExprType_t type) const { if (iArg == 0) return type == INT_EXPR || type == INT_ARR_EXPR; else @@ -987,11 +1010,10 @@ } VMFnResult* InstrumentScriptVMFunction_change_release::exec(VMFnArgs* args) { - int release = args->arg(1)->asInt()->evalInt(); - if (release > VM_EG_PAR_MAX_VALUE) { - wrnMsg("change_release(): argument 2 may not be larger than 1000000"); - release = VM_EG_PAR_MAX_VALUE; - } else if (release < 0) { + vmint release = args->arg(1)->asInt()->evalInt(); + // note: intentionally not checking against a max. value here! + // (to allow i.e. passing 2000000 for doubling the release time) + if (release < 0) { wrnMsg("change_release(): argument 2 may not be negative"); release = 0; } @@ -1016,7 +1038,7 @@ // if change_release() was called immediately after note was triggered // then immediately apply relase to Note object - if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) { + if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { pNote->Override.Release = fRelease; } else { // otherwise schedule release change ... Event e = m_vm->m_event->cause; // copy to get fragment time for "now" @@ -1031,7 +1053,7 @@ } } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { VMIntArrayExpr* ids = args->arg(0)->asIntArray(); - for (int i = 0; i < ids->arraySize(); ++i) { + for (vmint i = 0; i < ids->arraySize(); ++i) { const ScriptID id = ids->evalIntElement(i); if (!id || !id.isNoteID()) continue; @@ -1040,7 +1062,7 @@ // if change_release() was called immediately after note was triggered // then immediately apply relase to Note object - if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) { + if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { pNote->Override.Release = fRelease; } else { // otherwise schedule release change ... Event e = m_vm->m_event->cause; // copy to get fragment time for "now" @@ -1059,7 +1081,9 @@ return successResult(); } - bool VMChangeSynthParamFunction::acceptsArgType(int iArg, ExprType_t type) const { + // template for change_*() functions + + bool VMChangeSynthParamFunction::acceptsArgType(vmint iArg, ExprType_t type) const { if (iArg == 0) return type == INT_EXPR || type == INT_ARR_EXPR; else @@ -1069,10 +1093,10 @@ // Arbitrarily chosen constant value symbolizing "no limit". #define NO_LIMIT 1315916909 - template + template VMFnResult* VMChangeSynthParamFunction::execTemplate(VMFnArgs* args, const char* functionName) { - int value = args->arg(1)->asInt()->evalInt(); + 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; @@ -1107,7 +1131,7 @@ // if this change_*() script function was called immediately after // note was triggered then immediately apply the synth parameter // change to Note object - if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) { + if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { pNote->Override.*T_noteParam = fValue; } else { // otherwise schedule this synth parameter change ... Event e = m_vm->m_event->cause; // copy to get fragment time for "now" @@ -1122,7 +1146,7 @@ } } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { VMIntArrayExpr* ids = args->arg(0)->asIntArray(); - for (int i = 0; i < ids->arraySize(); ++i) { + for (vmint i = 0; i < ids->arraySize(); ++i) { const ScriptID id = ids->evalIntElement(i); if (!id || !id.isNoteID()) continue; @@ -1132,7 +1156,7 @@ // if this change_*() script function was called immediately after // note was triggered then immediately apply the synth parameter // change to Note object - if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) { + if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { pNote->Override.*T_noteParam = fValue; } else { // otherwise schedule this synth parameter change ... Event e = m_vm->m_event->cause; // copy to get fragment time for "now" @@ -1151,6 +1175,51 @@ return successResult(); } + // change_sustain() function + + VMFnResult* InstrumentScriptVMFunction_change_sustain::exec(VMFnArgs* args) { + return VMChangeSynthParamFunction::execTemplate< + &NoteBase::_Override::Sustain, + Event::synth_param_sustain, + false, NO_LIMIT, 0>( args, "change_sustain" ); + } + + // change_cutoff_attack() function + + VMFnResult* InstrumentScriptVMFunction_change_cutoff_attack::exec(VMFnArgs* args) { + return VMChangeSynthParamFunction::execTemplate< + &NoteBase::_Override::CutoffAttack, + Event::synth_param_cutoff_attack, + false, NO_LIMIT, 0>( args, "change_cutoff_attack" ); + } + + // change_cutoff_decay() function + + VMFnResult* InstrumentScriptVMFunction_change_cutoff_decay::exec(VMFnArgs* args) { + return VMChangeSynthParamFunction::execTemplate< + &NoteBase::_Override::CutoffDecay, + Event::synth_param_cutoff_decay, + false, NO_LIMIT, 0>( args, "change_cutoff_decay" ); + } + + // change_cutoff_sustain() function + + VMFnResult* InstrumentScriptVMFunction_change_cutoff_sustain::exec(VMFnArgs* args) { + return VMChangeSynthParamFunction::execTemplate< + &NoteBase::_Override::CutoffSustain, + Event::synth_param_cutoff_sustain, + false, NO_LIMIT, 0>( args, "change_cutoff_sustain" ); + } + + // change_cutoff_release() function + + VMFnResult* InstrumentScriptVMFunction_change_cutoff_release::exec(VMFnArgs* args) { + return VMChangeSynthParamFunction::execTemplate< + &NoteBase::_Override::CutoffRelease, + Event::synth_param_cutoff_release, + false, NO_LIMIT, 0>( args, "change_cutoff_release" ); + } + // change_amp_lfo_depth() function VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_depth::exec(VMFnArgs* args) { @@ -1169,6 +1238,24 @@ true, 1000000, 0>( args, "change_amp_lfo_freq" ); } + // change_cutoff_lfo_depth() function + + VMFnResult* InstrumentScriptVMFunction_change_cutoff_lfo_depth::exec(VMFnArgs* args) { + return VMChangeSynthParamFunction::execTemplate< + &NoteBase::_Override::CutoffLFODepth, + Event::synth_param_cutoff_lfo_depth, + true, 1000000, 0>( args, "change_cutoff_lfo_depth" ); + } + + // change_cutoff_lfo_freq() function + + VMFnResult* InstrumentScriptVMFunction_change_cutoff_lfo_freq::exec(VMFnArgs* args) { + return VMChangeSynthParamFunction::execTemplate< + &NoteBase::_Override::CutoffLFOFreq, + Event::synth_param_cutoff_lfo_freq, + true, 1000000, 0>( args, "change_cutoff_lfo_freq" ); + } + // change_pitch_lfo_depth() function VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_depth::exec(VMFnArgs* args) { @@ -1205,6 +1292,124 @@ false, NO_LIMIT, 0>( 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" ); + } + + // template for change_*_curve() functions + + bool VMChangeFadeCurveFunction::acceptsArgType(vmint 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) { + vmint 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 (vmint 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" ); + } + + // change_pan_curve() function + + VMFnResult* InstrumentScriptVMFunction_change_pan_curve::exec(VMFnArgs* args) { + return VMChangeFadeCurveFunction::execTemplate< + &NoteBase::_Override::PanCurve, + Event::synth_param_pan_curve>( args, "change_pan_curve" ); + } + // fade_in() function InstrumentScriptVMFunction_fade_in::InstrumentScriptVMFunction_fade_in(InstrumentScriptVM* parent) @@ -1212,7 +1417,7 @@ { } - bool InstrumentScriptVMFunction_fade_in::acceptsArgType(int iArg, ExprType_t type) const { + bool InstrumentScriptVMFunction_fade_in::acceptsArgType(vmint iArg, ExprType_t type) const { if (iArg == 0) return type == INT_EXPR || type == INT_ARR_EXPR; else @@ -1220,7 +1425,7 @@ } VMFnResult* InstrumentScriptVMFunction_fade_in::exec(VMFnArgs* args) { - int duration = args->arg(1)->asInt()->evalInt(); + vmint duration = args->arg(1)->asInt()->evalInt(); if (duration < 0) { wrnMsg("fade_in(): argument 2 may not be negative"); duration = 0; @@ -1247,7 +1452,7 @@ // if fade_in() was called immediately after note was triggered // then immediately apply a start volume of zero to Note object, // as well as the fade in duration - if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) { + if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { pNote->Override.Volume = 0.f; pNote->Override.VolumeTime = fDuration; } else { // otherwise schedule a "volume time" change with the requested fade in duration ... @@ -1279,7 +1484,7 @@ } } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { VMIntArrayExpr* ids = args->arg(0)->asIntArray(); - for (int i = 0; i < ids->arraySize(); ++i) { + for (vmint i = 0; i < ids->arraySize(); ++i) { const ScriptID id = ids->evalIntElement(i); if (!id || !id.isNoteID()) continue; @@ -1289,7 +1494,7 @@ // if fade_in() was called immediately after note was triggered // then immediately apply a start volume of zero to Note object, // as well as the fade in duration - if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) { + if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { pNote->Override.Volume = 0.f; pNote->Override.VolumeTime = fDuration; } else { // otherwise schedule a "volume time" change with the requested fade in duration ... @@ -1332,7 +1537,7 @@ { } - bool InstrumentScriptVMFunction_fade_out::acceptsArgType(int iArg, ExprType_t type) const { + bool InstrumentScriptVMFunction_fade_out::acceptsArgType(vmint iArg, ExprType_t type) const { if (iArg == 0) return type == INT_EXPR || type == INT_ARR_EXPR; else @@ -1340,7 +1545,7 @@ } VMFnResult* InstrumentScriptVMFunction_fade_out::exec(VMFnArgs* args) { - int duration = args->arg(1)->asInt()->evalInt(); + vmint duration = args->arg(1)->asInt()->evalInt(); if (duration < 0) { wrnMsg("fade_out(): argument 2 may not be negative"); duration = 0; @@ -1368,7 +1573,7 @@ // if fade_out() was called immediately after note was triggered // then immediately apply fade out duration to Note object - if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) { + if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { pNote->Override.VolumeTime = fDuration; } else { // otherwise schedule a "volume time" change with the requested fade out duration ... Event e = m_vm->m_event->cause; // copy to get fragment time for "now" @@ -1411,7 +1616,7 @@ } } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { VMIntArrayExpr* ids = args->arg(0)->asIntArray(); - for (int i = 0; i < ids->arraySize(); ++i) { + for (vmint i = 0; i < ids->arraySize(); ++i) { const ScriptID id = ids->evalIntElement(i); if (!id || !id.isNoteID()) continue; @@ -1420,7 +1625,7 @@ // if fade_out() was called immediately after note was triggered // then immediately apply fade out duration to Note object - if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) { + if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { pNote->Override.VolumeTime = fDuration; } else { // otherwise schedule a "volume time" change with the requested fade out duration ... Event e = m_vm->m_event->cause; // copy to get fragment time for "now" @@ -1494,7 +1699,7 @@ return successResult(0); } - const int parameter = args->arg(1)->asInt()->evalInt(); + const vmint parameter = args->arg(1)->asInt()->evalInt(); switch (parameter) { case EVENT_PAR_NOTE: return successResult(pNote->cause.Param.Note.Key); @@ -1546,8 +1751,8 @@ NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); if (!pNote) return successResult(); - const int parameter = args->arg(1)->asInt()->evalInt(); - const int value = args->arg(2)->asInt()->evalInt(); + const vmint parameter = args->arg(1)->asInt()->evalInt(); + const vmint value = args->arg(2)->asInt()->evalInt(); switch (parameter) { case EVENT_PAR_NOTE: @@ -1555,20 +1760,24 @@ wrnMsg("set_event_par(): note number of argument 3 is out of range"); return successResult(); } - if (m_vm->m_event->cause.SchedTime() == 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->cause.SchedTime() == 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 +1803,122 @@ 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 vmint 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 vmint 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 vmint 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 = + (decltype(pNote->Override.SampleOffset)) pos; + + return successResult(); + } + // event_status() function InstrumentScriptVMFunction_event_status::InstrumentScriptVMFunction_event_status(InstrumentScriptVM* parent) @@ -1619,6 +1944,33 @@ return successResult(pNote ? EVENT_STATUS_NOTE_QUEUE : EVENT_STATUS_INACTIVE); } + // callback_status() function + + InstrumentScriptVMFunction_callback_status::InstrumentScriptVMFunction_callback_status(InstrumentScriptVM* parent) + : m_vm(parent) + { + } + + VMFnResult* InstrumentScriptVMFunction_callback_status::exec(VMFnArgs* args) { + const script_callback_id_t id = args->arg(0)->asInt()->evalInt(); + if (!id) { + wrnMsg("callback_status(): callback ID for argument 1 may not be zero"); + return successResult(); + } + + AbstractEngineChannel* pEngineChannel = + static_cast(m_vm->m_event->cause.pEngineChannel); + + RTList::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id); + if (!itCallback) + return successResult(CALLBACK_STATUS_TERMINATED); + + return successResult( + (m_vm->m_event->execCtx == itCallback->execCtx) ? + CALLBACK_STATUS_RUNNING : CALLBACK_STATUS_QUEUE + ); + } + // wait() function (overrides core wait() implementation) InstrumentScriptVMFunction_wait::InstrumentScriptVMFunction_wait(InstrumentScriptVM* parent) @@ -1659,10 +2011,89 @@ (args->argsCount() >= 2) ? (args->arg(1)->asInt()->evalInt() == 1) : false; pEngineChannel->ScheduleResumeOfScriptCallback( - itCallback, m_vm->m_event->cause.SchedTime(), disableWaitForever + itCallback, m_vm->m_event->scheduleTime, disableWaitForever ); return successResult(); } + // abort() function + + InstrumentScriptVMFunction_abort::InstrumentScriptVMFunction_abort(InstrumentScriptVM* parent) + : m_vm(parent) + { + } + + VMFnResult* InstrumentScriptVMFunction_abort::exec(VMFnArgs* args) { + const script_callback_id_t id = args->arg(0)->asInt()->evalInt(); + if (!id) { + wrnMsg("abort(): callback ID for argument 1 may not be zero"); + return successResult(); + } + + AbstractEngineChannel* pEngineChannel = + static_cast(m_vm->m_event->cause.pEngineChannel); + + RTList::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id); + if (!itCallback) return successResult(); // ignore if callback is i.e. not alive anymore + + itCallback->execCtx->signalAbort(); + + return successResult(); + } + + // fork() function + + InstrumentScriptVMFunction_fork::InstrumentScriptVMFunction_fork(InstrumentScriptVM* parent) + : m_vm(parent) + { + } + + VMFnResult* InstrumentScriptVMFunction_fork::exec(VMFnArgs* args) { + // check if this is actually the parent going to fork, or rather one of + // the children which is already forked + if (m_vm->m_event->forkIndex != 0) { // this is the entry point for a child ... + int forkResult = m_vm->m_event->forkIndex; + // reset so that this child may i.e. also call fork() later on + m_vm->m_event->forkIndex = 0; + return successResult(forkResult); + } + + // if we are here, then this is the parent, so we must fork this parent + + const vmint n = + (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : 1; + const bool bAutoAbort = + (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : true; + + if (m_vm->m_event->countChildHandlers() + n > MAX_FORK_PER_SCRIPT_HANDLER) { + wrnMsg("fork(): requested amount would exceed allowed limit per event handler"); + return successResult(-1); + } + + AbstractEngineChannel* pEngineChannel = + static_cast(m_vm->m_event->cause.pEngineChannel); + + if (!pEngineChannel->hasFreeScriptCallbacks(n)) { + wrnMsg("fork(): global limit of event handlers exceeded"); + return successResult(-1); + } + + for (int iChild = 0; iChild < n; ++iChild) { + RTList::Iterator itChild = + pEngineChannel->forkScriptCallback(m_vm->m_event, bAutoAbort); + if (!itChild) { // should never happen, otherwise its a bug ... + errMsg("fork(): internal error while allocating child"); + return errorResult(-1); // terminate script + } + // since both parent, as well all child script execution instances + // all land in this exec() method, the following is (more or less) + // the only feature that lets us distinguish the parent and + // respective children from each other in this exec() method + itChild->forkIndex = iChild + 1; + } + + return successResult(0); + } + } // namespace LinuxSampler