--- linuxsampler/trunk/src/engines/common/InstrumentScriptVMFunctions.cpp 2017/05/28 22:22:56 3246 +++ linuxsampler/trunk/src/engines/common/InstrumentScriptVMFunctions.cpp 2017/07/10 17:45:30 3303 @@ -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) { @@ -826,10 +838,9 @@ 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) { + // 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; } @@ -913,10 +924,9 @@ 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) { + // 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; } @@ -1000,10 +1010,9 @@ 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) { + // 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; } @@ -1793,6 +1802,41 @@ 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) @@ -1818,6 +1862,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) @@ -1864,4 +1935,83 @@ 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 int 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 exect() method, the following is (more or less) + // the only feature that lets us distinguish the parent and + // respective children from each other in this exect() method + itChild->forkIndex = iChild + 1; + } + + return successResult(0); + } + } // namespace LinuxSampler