1 /* 2 * Copyright (c) 2014-2020 Christian Schoenebeck 3 * 4 * http://www.linuxsampler.org 5 * 6 * This file is part of LinuxSampler and released under the same terms. 7 * See README file for details. 8 */ 9 10 #include "InstrumentScriptVMFunctions.h" 11 #include "InstrumentScriptVM.h" 12 #include "../AbstractEngineChannel.h" 13 #include "../../common/global_private.h" 14 15 namespace LinuxSampler { 16 17 // play_note() function 18 InstrumentScriptVMFunction_play_note(InstrumentScriptVM * parent)19 InstrumentScriptVMFunction_play_note::InstrumentScriptVMFunction_play_note(InstrumentScriptVM* parent) 20 : m_vm(parent) 21 { 22 } 23 acceptsArgType(vmint iArg,ExprType_t type) const24 bool InstrumentScriptVMFunction_play_note::acceptsArgType(vmint iArg, ExprType_t type) const { 25 if (iArg == 2 || iArg == 3) 26 return type == INT_EXPR || type == REAL_EXPR; 27 else 28 return type == INT_EXPR; 29 } 30 acceptsArgUnitType(vmint iArg,StdUnit_t type) const31 bool InstrumentScriptVMFunction_play_note::acceptsArgUnitType(vmint iArg, StdUnit_t type) const { 32 if (iArg == 2 || iArg == 3) 33 return type == VM_NO_UNIT || type == VM_SECOND; 34 else 35 return type == VM_NO_UNIT; 36 } 37 acceptsArgUnitPrefix(vmint iArg,StdUnit_t type) const38 bool InstrumentScriptVMFunction_play_note::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const { 39 if (iArg == 2 || iArg == 3) 40 return type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type 41 else 42 return false; 43 } 44 checkArgs(VMFnArgs * args,std::function<void (String)> err,std::function<void (String)> wrn)45 void InstrumentScriptVMFunction_play_note::checkArgs(VMFnArgs* args, 46 std::function<void(String)> err, 47 std::function<void(String)> wrn) 48 { 49 // super class checks 50 Super::checkArgs(args, err, wrn); 51 52 // own checks ... 53 if (args->arg(0)->isConstExpr()) { 54 vmint note = args->arg(0)->asNumber()->evalCastInt(); 55 if (note < 0 || note > 127) { 56 err("MIDI note number value for argument 1 must be between 0..127"); 57 return; 58 } 59 } 60 if (args->argsCount() >= 2 && args->arg(1)->isConstExpr()) { 61 vmint velocity = args->arg(1)->asNumber()->evalCastInt(); 62 if (velocity < 0 || velocity > 127) { 63 err("MIDI velocity value for argument 2 must be between 0..127"); 64 return; 65 } 66 } 67 if (args->argsCount() >= 3 && args->arg(2)->isConstExpr()) { 68 VMNumberExpr* argSampleOffset = args->arg(2)->asNumber(); 69 vmint sampleoffset = 70 (argSampleOffset->unitType()) ? 71 argSampleOffset->evalCastInt(VM_MICRO) : 72 argSampleOffset->evalCastInt(); 73 if (sampleoffset < -1) { 74 err("Sample offset of argument 3 may not be less than -1"); 75 return; 76 } 77 } 78 if (args->argsCount() >= 4 && args->arg(3)->isConstExpr()) { 79 VMNumberExpr* argDuration = args->arg(3)->asNumber(); 80 vmint duration = 81 (argDuration->unitType()) ? 82 argDuration->evalCastInt(VM_MICRO) : 83 argDuration->evalCastInt(); 84 if (duration < -2) { 85 err("Argument 4 must be a duration value of at least -2 or higher"); 86 return; 87 } 88 } 89 } 90 exec(VMFnArgs * args)91 VMFnResult* InstrumentScriptVMFunction_play_note::exec(VMFnArgs* args) { 92 vmint note = args->arg(0)->asInt()->evalInt(); 93 vmint velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127; 94 VMNumberExpr* argDuration = (args->argsCount() >= 4) ? args->arg(3)->asNumber() : NULL; 95 vmint duration = 96 (argDuration) ? 97 (argDuration->unitType()) ? 98 argDuration->evalCastInt(VM_MICRO) : 99 argDuration->evalCastInt() : 0; //TODO: -1 might be a better default value instead of 0 100 101 if (note < 0 || note > 127) { 102 errMsg("play_note(): argument 1 is an invalid note number"); 103 return errorResult(0); 104 } 105 106 if (velocity < 0 || velocity > 127) { 107 errMsg("play_note(): argument 2 is an invalid velocity value"); 108 return errorResult(0); 109 } 110 111 if (duration < -2) { 112 errMsg("play_note(): argument 4 must be a duration value of at least -2 or higher"); 113 return errorResult(0); 114 } 115 116 AbstractEngineChannel* pEngineChannel = 117 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); 118 119 Event e = m_vm->m_event->cause; // copy to get fragment time for "now" 120 e.Init(); // clear IDs 121 e.Type = Event::type_play_note; 122 e.Param.Note.Key = note; 123 e.Param.Note.Velocity = velocity; 124 // make this new note dependent to the life time of the original note 125 if (duration == -1) { 126 if (m_vm->currentVMEventHandler()->eventHandlerType() != VM_EVENT_HANDLER_NOTE) { 127 errMsg("play_note(): -1 for argument 4 may only be used for note event handlers"); 128 return errorResult(0); 129 } 130 e.Param.Note.ParentNoteID = m_vm->m_event->cause.Param.Note.ID; 131 // check if that requested parent note is actually still alive 132 NoteBase* pParentNote = 133 pEngineChannel->pEngine->NoteByID( e.Param.Note.ParentNoteID ); 134 // if parent note is already gone then this new note is not required anymore 135 if (!pParentNote) 136 return successResult(0); 137 } 138 139 const note_id_t id = pEngineChannel->ScheduleNoteMicroSec(&e, 0); 140 141 // if a sample offset is supplied, assign the offset as override 142 // to the previously created Note object 143 if (args->argsCount() >= 3) { 144 VMNumberExpr* argSampleOffset = args->arg(2)->asNumber(); 145 vmint sampleoffset = 146 (argSampleOffset->unitType()) ? 147 argSampleOffset->evalCastInt(VM_MICRO) : 148 argSampleOffset->evalCastInt(); 149 if (sampleoffset >= 0) { 150 NoteBase* pNote = pEngineChannel->pEngine->NoteByID(id); 151 if (pNote) { 152 pNote->Override.SampleOffset = 153 (decltype(pNote->Override.SampleOffset)) sampleoffset; 154 } 155 } else if (sampleoffset < -1) { 156 errMsg("play_note(): sample offset of argument 3 may not be less than -1"); 157 } 158 } 159 160 // if a duration is supplied (and play-note event was scheduled 161 // successfully above), then schedule a subsequent stop-note event 162 if (id && duration > 0) { 163 e.Type = Event::type_stop_note; 164 e.Param.Note.ID = id; 165 e.Param.Note.Velocity = 127; 166 pEngineChannel->ScheduleEventMicroSec(&e, duration); 167 } 168 169 // even if id is null, don't return an errorResult() here, because that 170 // would abort the script, and under heavy load it may be considerable 171 // that ScheduleNoteMicroSec() fails above, so simply ignore that 172 return successResult( ScriptID::fromNoteID(id) ); 173 } 174 175 // set_controller() function 176 InstrumentScriptVMFunction_set_controller(InstrumentScriptVM * parent)177 InstrumentScriptVMFunction_set_controller::InstrumentScriptVMFunction_set_controller(InstrumentScriptVM* parent) 178 : m_vm(parent) 179 { 180 } 181 exec(VMFnArgs * args)182 VMFnResult* InstrumentScriptVMFunction_set_controller::exec(VMFnArgs* args) { 183 vmint controller = args->arg(0)->asInt()->evalInt(); 184 vmint value = args->arg(1)->asInt()->evalInt(); 185 186 AbstractEngineChannel* pEngineChannel = 187 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); 188 189 Event e = m_vm->m_event->cause; // copy to get fragment time for "now" 190 e.Init(); // clear IDs 191 if (controller == CTRL_TABLE_IDX_AFTERTOUCH) { 192 e.Type = Event::type_channel_pressure; 193 e.Param.ChannelPressure.Value = value & 127; 194 } else if (controller == CTRL_TABLE_IDX_PITCHBEND) { 195 e.Type = Event::type_pitchbend; 196 e.Param.Pitch.Pitch = value; 197 } else if (controller >= 0 && controller <= 127) { 198 e.Type = Event::type_control_change; 199 e.Param.CC.Controller = controller; 200 e.Param.CC.Value = value; 201 } else { 202 errMsg("set_controller(): argument 1 is an invalid controller"); 203 return errorResult(); 204 } 205 206 const event_id_t id = pEngineChannel->ScheduleEventMicroSec(&e, 0); 207 208 // even if id is null, don't return an errorResult() here, because that 209 // would abort the script, and under heavy load it may be considerable 210 // that ScheduleEventMicroSec() fails above, so simply ignore that 211 return successResult( ScriptID::fromEventID(id) ); 212 } 213 214 // set_rpn() function 215 InstrumentScriptVMFunction_set_rpn(InstrumentScriptVM * parent)216 InstrumentScriptVMFunction_set_rpn::InstrumentScriptVMFunction_set_rpn(InstrumentScriptVM* parent) 217 : m_vm(parent) 218 { 219 } 220 exec(VMFnArgs * args)221 VMFnResult* InstrumentScriptVMFunction_set_rpn::exec(VMFnArgs* args) { 222 vmint parameter = args->arg(0)->asInt()->evalInt(); 223 vmint value = args->arg(1)->asInt()->evalInt(); 224 225 if (parameter < 0 || parameter > 16383) { 226 errMsg("set_rpn(): argument 1 exceeds RPN parameter number range"); 227 return errorResult(); 228 } 229 if (value < 0 || value > 16383) { 230 errMsg("set_rpn(): argument 2 exceeds RPN value range"); 231 return errorResult(); 232 } 233 234 AbstractEngineChannel* pEngineChannel = 235 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); 236 237 Event e = m_vm->m_event->cause; // copy to get fragment time for "now" 238 e.Init(); // clear IDs 239 e.Type = Event::type_rpn; 240 e.Param.RPN.Parameter = parameter; 241 e.Param.RPN.Value = value; 242 243 const event_id_t id = pEngineChannel->ScheduleEventMicroSec(&e, 0); 244 245 // even if id is null, don't return an errorResult() here, because that 246 // would abort the script, and under heavy load it may be considerable 247 // that ScheduleEventMicroSec() fails above, so simply ignore that 248 return successResult( ScriptID::fromEventID(id) ); 249 } 250 251 // set_nrpn() function 252 InstrumentScriptVMFunction_set_nrpn(InstrumentScriptVM * parent)253 InstrumentScriptVMFunction_set_nrpn::InstrumentScriptVMFunction_set_nrpn(InstrumentScriptVM* parent) 254 : m_vm(parent) 255 { 256 } 257 exec(VMFnArgs * args)258 VMFnResult* InstrumentScriptVMFunction_set_nrpn::exec(VMFnArgs* args) { 259 vmint parameter = args->arg(0)->asInt()->evalInt(); 260 vmint value = args->arg(1)->asInt()->evalInt(); 261 262 if (parameter < 0 || parameter > 16383) { 263 errMsg("set_nrpn(): argument 1 exceeds NRPN parameter number range"); 264 return errorResult(); 265 } 266 if (value < 0 || value > 16383) { 267 errMsg("set_nrpn(): argument 2 exceeds NRPN value range"); 268 return errorResult(); 269 } 270 271 AbstractEngineChannel* pEngineChannel = 272 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); 273 274 Event e = m_vm->m_event->cause; // copy to get fragment time for "now" 275 e.Init(); // clear IDs 276 e.Type = Event::type_nrpn; 277 e.Param.NRPN.Parameter = parameter; 278 e.Param.NRPN.Value = value; 279 280 const event_id_t id = pEngineChannel->ScheduleEventMicroSec(&e, 0); 281 282 // even if id is null, don't return an errorResult() here, because that 283 // would abort the script, and under heavy load it may be considerable 284 // that ScheduleEventMicroSec() fails above, so simply ignore that 285 return successResult( ScriptID::fromEventID(id) ); 286 } 287 288 // ignore_event() function 289 InstrumentScriptVMFunction_ignore_event(InstrumentScriptVM * parent)290 InstrumentScriptVMFunction_ignore_event::InstrumentScriptVMFunction_ignore_event(InstrumentScriptVM* parent) 291 : m_vm(parent) 292 { 293 } 294 acceptsArgType(vmint iArg,ExprType_t type) const295 bool InstrumentScriptVMFunction_ignore_event::acceptsArgType(vmint iArg, ExprType_t type) const { 296 return type == INT_EXPR || type == INT_ARR_EXPR; 297 } 298 exec(VMFnArgs * args)299 VMFnResult* InstrumentScriptVMFunction_ignore_event::exec(VMFnArgs* args) { 300 AbstractEngineChannel* pEngineChannel = 301 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); 302 303 if (args->argsCount() == 0 || args->arg(0)->exprType() == INT_EXPR) { 304 const ScriptID id = (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : m_vm->m_event->id; 305 if (!id && args->argsCount() >= 1) { 306 wrnMsg("ignore_event(): event ID argument may not be zero"); 307 // not errorResult(), because that would abort the script, not intentional in this case 308 return successResult(); 309 } 310 pEngineChannel->IgnoreEventByScriptID(id); 311 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { 312 VMIntArrayExpr* ids = args->arg(0)->asIntArray(); 313 for (int i = 0; i < ids->arraySize(); ++i) { 314 const ScriptID id = ids->evalIntElement(i); 315 pEngineChannel->IgnoreEventByScriptID(id); 316 } 317 } 318 319 return successResult(); 320 } 321 322 // ignore_controller() function 323 InstrumentScriptVMFunction_ignore_controller(InstrumentScriptVM * parent)324 InstrumentScriptVMFunction_ignore_controller::InstrumentScriptVMFunction_ignore_controller(InstrumentScriptVM* parent) 325 : m_vm(parent) 326 { 327 } 328 exec(VMFnArgs * args)329 VMFnResult* InstrumentScriptVMFunction_ignore_controller::exec(VMFnArgs* args) { 330 const ScriptID id = (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : m_vm->m_event->id; 331 if (!id && args->argsCount() >= 1) { 332 wrnMsg("ignore_controller(): event ID argument may not be zero"); 333 return successResult(); 334 } 335 336 AbstractEngineChannel* pEngineChannel = 337 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); 338 339 pEngineChannel->IgnoreEventByScriptID(id); 340 341 return successResult(); 342 } 343 344 // note_off() function 345 InstrumentScriptVMFunction_note_off(InstrumentScriptVM * parent)346 InstrumentScriptVMFunction_note_off::InstrumentScriptVMFunction_note_off(InstrumentScriptVM* parent) 347 : m_vm(parent) 348 { 349 } 350 acceptsArgType(vmint iArg,ExprType_t type) const351 bool InstrumentScriptVMFunction_note_off::acceptsArgType(vmint iArg, ExprType_t type) const { 352 return type == INT_EXPR || type == INT_ARR_EXPR; 353 } 354 checkArgs(VMFnArgs * args,std::function<void (String)> err,std::function<void (String)> wrn)355 void InstrumentScriptVMFunction_note_off::checkArgs(VMFnArgs* args, 356 std::function<void(String)> err, 357 std::function<void(String)> wrn) 358 { 359 // super class checks 360 Super::checkArgs(args, err, wrn); 361 362 // own checks ... 363 if (args->argsCount() >= 2 && args->arg(1)->isConstExpr() && args->arg(1)->exprType() == INT_EXPR) { 364 vmint velocity = args->arg(1)->asInt()->evalInt(); 365 if (velocity < 0 || velocity > 127) { 366 err("MIDI velocity value for argument 2 must be between 0..127"); 367 return; 368 } 369 } 370 } 371 exec(VMFnArgs * args)372 VMFnResult* InstrumentScriptVMFunction_note_off::exec(VMFnArgs* args) { 373 AbstractEngineChannel* pEngineChannel = 374 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); 375 376 vmint velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127; 377 if (velocity < 0 || velocity > 127) { 378 errMsg("note_off(): argument 2 is an invalid velocity value"); 379 return errorResult(); 380 } 381 382 if (args->arg(0)->exprType() == INT_EXPR) { 383 const ScriptID id = args->arg(0)->asInt()->evalInt(); 384 if (!id) { 385 wrnMsg("note_off(): note ID for argument 1 may not be zero"); 386 return successResult(); 387 } 388 if (!id.isNoteID()) { 389 wrnMsg("note_off(): argument 1 is not a note ID"); 390 return successResult(); 391 } 392 393 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); 394 if (!pNote) return successResult(); 395 396 Event e = pNote->cause; 397 e.Init(); // clear IDs 398 e.CopyTimeFrom(m_vm->m_event->cause); // set fragment time for "now" 399 e.Type = Event::type_stop_note; 400 e.Param.Note.ID = id.noteID(); 401 e.Param.Note.Key = pNote->hostKey; 402 e.Param.Note.Velocity = velocity; 403 404 pEngineChannel->ScheduleEventMicroSec(&e, 0); 405 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { 406 VMIntArrayExpr* ids = args->arg(0)->asIntArray(); 407 for (vmint i = 0; i < ids->arraySize(); ++i) { 408 const ScriptID id = ids->evalIntElement(i); 409 if (!id || !id.isNoteID()) continue; 410 411 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); 412 if (!pNote) continue; 413 414 Event e = pNote->cause; 415 e.Init(); // clear IDs 416 e.CopyTimeFrom(m_vm->m_event->cause); // set fragment time for "now" 417 e.Type = Event::type_stop_note; 418 e.Param.Note.ID = id.noteID(); 419 e.Param.Note.Key = pNote->hostKey; 420 e.Param.Note.Velocity = velocity; 421 422 pEngineChannel->ScheduleEventMicroSec(&e, 0); 423 } 424 } 425 426 return successResult(); 427 } 428 429 // set_event_mark() function 430 InstrumentScriptVMFunction_set_event_mark(InstrumentScriptVM * parent)431 InstrumentScriptVMFunction_set_event_mark::InstrumentScriptVMFunction_set_event_mark(InstrumentScriptVM* parent) 432 : m_vm(parent) 433 { 434 } 435 checkArgs(VMFnArgs * args,std::function<void (String)> err,std::function<void (String)> wrn)436 void InstrumentScriptVMFunction_set_event_mark::checkArgs(VMFnArgs* args, 437 std::function<void(String)> err, 438 std::function<void(String)> wrn) 439 { 440 // super class checks 441 Super::checkArgs(args, err, wrn); 442 443 // own checks ... 444 if (args->argsCount() >= 2 && args->arg(1)->isConstExpr()) { 445 const vmint groupID = args->arg(1)->asInt()->evalInt(); 446 if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) { 447 err("Argument 2 value is an invalid group id."); 448 return; 449 } 450 } 451 } 452 exec(VMFnArgs * args)453 VMFnResult* InstrumentScriptVMFunction_set_event_mark::exec(VMFnArgs* args) { 454 const ScriptID id = args->arg(0)->asInt()->evalInt(); 455 const vmint groupID = args->arg(1)->asInt()->evalInt(); 456 457 if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) { 458 errMsg("set_event_mark(): argument 2 is an invalid group id"); 459 return errorResult(); 460 } 461 462 AbstractEngineChannel* pEngineChannel = 463 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); 464 465 // check if the event/note still exists 466 switch (id.type()) { 467 case ScriptID::EVENT: { 468 RTList<Event>::Iterator itEvent = pEngineChannel->pEngine->EventByID( id.eventID() ); 469 if (!itEvent) return successResult(); 470 break; 471 } 472 case ScriptID::NOTE: { 473 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); 474 if (!pNote) return successResult(); 475 break; 476 } 477 } 478 479 pEngineChannel->pScript->eventGroups[groupID].insert(id); 480 481 return successResult(); 482 } 483 484 // delete_event_mark() function 485 InstrumentScriptVMFunction_delete_event_mark(InstrumentScriptVM * parent)486 InstrumentScriptVMFunction_delete_event_mark::InstrumentScriptVMFunction_delete_event_mark(InstrumentScriptVM* parent) 487 : m_vm(parent) 488 { 489 } 490 checkArgs(VMFnArgs * args,std::function<void (String)> err,std::function<void (String)> wrn)491 void InstrumentScriptVMFunction_delete_event_mark::checkArgs(VMFnArgs* args, 492 std::function<void(String)> err, 493 std::function<void(String)> wrn) 494 { 495 // super class checks 496 Super::checkArgs(args, err, wrn); 497 498 // own checks ... 499 if (args->argsCount() >= 2 && args->arg(1)->isConstExpr()) { 500 const vmint groupID = args->arg(1)->asInt()->evalInt(); 501 if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) { 502 err("Argument 2 value is an invalid group id."); 503 return; 504 } 505 } 506 } 507 exec(VMFnArgs * args)508 VMFnResult* InstrumentScriptVMFunction_delete_event_mark::exec(VMFnArgs* args) { 509 const ScriptID id = args->arg(0)->asInt()->evalInt(); 510 const vmint groupID = args->arg(1)->asInt()->evalInt(); 511 512 if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) { 513 errMsg("delete_event_mark(): argument 2 is an invalid group id"); 514 return errorResult(); 515 } 516 517 AbstractEngineChannel* pEngineChannel = 518 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); 519 520 pEngineChannel->pScript->eventGroups[groupID].erase(id); 521 522 return successResult(); 523 } 524 525 // by_marks() function 526 InstrumentScriptVMFunction_by_marks(InstrumentScriptVM * parent)527 InstrumentScriptVMFunction_by_marks::InstrumentScriptVMFunction_by_marks(InstrumentScriptVM* parent) 528 : m_vm(parent), m_result(NULL) 529 { 530 } 531 arraySize() const532 vmint InstrumentScriptVMFunction_by_marks::Result::arraySize() const { 533 return eventGroup->size(); 534 } 535 evalIntElement(vmuint i)536 vmint InstrumentScriptVMFunction_by_marks::Result::evalIntElement(vmuint i) { 537 return (*eventGroup)[i]; 538 } 539 errorResult()540 VMFnResult* InstrumentScriptVMFunction_by_marks::errorResult() { 541 m_result->eventGroup = NULL; 542 m_result->flags = StmtFlags_t(STMT_ABORT_SIGNALLED | STMT_ERROR_OCCURRED); 543 return m_result; 544 } 545 successResult(EventGroup * eventGroup)546 VMFnResult* InstrumentScriptVMFunction_by_marks::successResult(EventGroup* eventGroup) { 547 m_result->eventGroup = eventGroup; 548 m_result->flags = STMT_SUCCESS; 549 return m_result; 550 } 551 checkArgs(VMFnArgs * args,std::function<void (String)> err,std::function<void (String)> wrn)552 void InstrumentScriptVMFunction_by_marks::checkArgs(VMFnArgs* args, 553 std::function<void(String)> err, 554 std::function<void(String)> wrn) 555 { 556 // super class checks 557 Super::checkArgs(args, err, wrn); 558 559 // own checks ... 560 if (args->arg(0)->isConstExpr()) { 561 const vmint groupID = args->arg(0)->asInt()->evalInt(); 562 if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) { 563 err("Argument value is an invalid group id."); 564 return; 565 } 566 } 567 } 568 allocResult(VMFnArgs * args)569 VMFnResult* InstrumentScriptVMFunction_by_marks::allocResult(VMFnArgs* args) { 570 return new Result; 571 } 572 bindResult(VMFnResult * res)573 void InstrumentScriptVMFunction_by_marks::bindResult(VMFnResult* res) { 574 m_result = dynamic_cast<Result*>(res); 575 } 576 boundResult() const577 VMFnResult* InstrumentScriptVMFunction_by_marks::boundResult() const { 578 return m_result; 579 } 580 exec(VMFnArgs * args)581 VMFnResult* InstrumentScriptVMFunction_by_marks::exec(VMFnArgs* args) { 582 vmint groupID = args->arg(0)->asInt()->evalInt(); 583 584 if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) { 585 errMsg("by_marks(): argument is an invalid group id"); 586 return errorResult(); 587 } 588 589 AbstractEngineChannel* pEngineChannel = 590 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); 591 592 return successResult( &pEngineChannel->pScript->eventGroups[groupID] ); 593 } 594 595 // change_vol() function 596 InstrumentScriptVMFunction_change_vol(InstrumentScriptVM * parent)597 InstrumentScriptVMFunction_change_vol::InstrumentScriptVMFunction_change_vol(InstrumentScriptVM* parent) 598 : m_vm(parent) 599 { 600 } 601 acceptsArgType(vmint iArg,ExprType_t type) const602 bool InstrumentScriptVMFunction_change_vol::acceptsArgType(vmint iArg, ExprType_t type) const { 603 if (iArg == 0) 604 return type == INT_EXPR || type == INT_ARR_EXPR; 605 else if (iArg == 1) 606 return type == INT_EXPR || type == REAL_EXPR; 607 else 608 return type == INT_EXPR; 609 } 610 acceptsArgUnitType(vmint iArg,StdUnit_t type) const611 bool InstrumentScriptVMFunction_change_vol::acceptsArgUnitType(vmint iArg, StdUnit_t type) const { 612 if (iArg == 1) 613 return type == VM_NO_UNIT || type == VM_BEL; 614 else 615 return type == VM_NO_UNIT; 616 } 617 acceptsArgUnitPrefix(vmint iArg,StdUnit_t type) const618 bool InstrumentScriptVMFunction_change_vol::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const { 619 return iArg == 1 && type == VM_BEL; // only allow metric prefix(es) if 'Bel' is used as unit type 620 } 621 acceptsArgFinal(vmint iArg) const622 bool InstrumentScriptVMFunction_change_vol::acceptsArgFinal(vmint iArg) const { 623 return iArg == 1; 624 } 625 exec(VMFnArgs * args)626 VMFnResult* InstrumentScriptVMFunction_change_vol::exec(VMFnArgs* args) { 627 StdUnit_t unit = args->arg(1)->asNumber()->unitType(); 628 vmint volume = 629 (unit) ? 630 args->arg(1)->asNumber()->evalCastInt(VM_MILLI,VM_DECI) : 631 args->arg(1)->asNumber()->evalCastInt(); // volume change in milli dB 632 bool isFinal = args->arg(1)->asNumber()->isFinal(); 633 bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false; 634 const float fVolumeLin = RTMath::DecibelToLinRatio(float(volume) / 1000.f); 635 636 AbstractEngineChannel* pEngineChannel = 637 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); 638 639 if (args->arg(0)->exprType() == INT_EXPR) { 640 const ScriptID id = args->arg(0)->asInt()->evalInt(); 641 if (!id) { 642 wrnMsg("change_vol(): note ID for argument 1 may not be zero"); 643 return successResult(); 644 } 645 if (!id.isNoteID()) { 646 wrnMsg("change_vol(): argument 1 is not a note ID"); 647 return successResult(); 648 } 649 650 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); 651 if (!pNote) return successResult(); 652 653 // if change_vol() was called immediately after note was triggered 654 // then immediately apply the volume to note object, but only if 655 // change_vol_time() has not been called before 656 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime && 657 pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S) 658 { 659 if (relative) 660 pNote->Override.Volume.Value *= fVolumeLin; 661 else 662 pNote->Override.Volume.Value = fVolumeLin; 663 pNote->Override.Volume.Final = isFinal; 664 } else { // otherwise schedule the volume change ... 665 Event e = m_vm->m_event->cause; // copy to get fragment time for "now" 666 e.Init(); // clear IDs 667 e.Type = Event::type_note_synth_param; 668 e.Param.NoteSynthParam.NoteID = id.noteID(); 669 e.Param.NoteSynthParam.Type = Event::synth_param_volume; 670 e.Param.NoteSynthParam.Delta = fVolumeLin; 671 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit( 672 isFinal, relative, unit 673 ); 674 pEngineChannel->ScheduleEventMicroSec(&e, 0); 675 } 676 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { 677 VMIntArrayExpr* ids = args->arg(0)->asIntArray(); 678 for (vmint i = 0; i < ids->arraySize(); ++i) { 679 const ScriptID id = ids->evalIntElement(i); 680 if (!id || !id.isNoteID()) continue; 681 682 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); 683 if (!pNote) continue; 684 685 // if change_vol() was called immediately after note was triggered 686 // then immediately apply the volume to Note object, but only if 687 // change_vol_time() has not been called before 688 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime && 689 pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S) 690 { 691 if (relative) 692 pNote->Override.Volume.Value *= fVolumeLin; 693 else 694 pNote->Override.Volume.Value = fVolumeLin; 695 pNote->Override.Volume.Final = isFinal; 696 } else { // otherwise schedule the volume change ... 697 Event e = m_vm->m_event->cause; // copy to get fragment time for "now" 698 e.Init(); // clear IDs 699 e.Type = Event::type_note_synth_param; 700 e.Param.NoteSynthParam.NoteID = id.noteID(); 701 e.Param.NoteSynthParam.Type = Event::synth_param_volume; 702 e.Param.NoteSynthParam.Delta = fVolumeLin; 703 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit( 704 isFinal, relative, unit 705 ); 706 pEngineChannel->ScheduleEventMicroSec(&e, 0); 707 } 708 } 709 } 710 711 return successResult(); 712 } 713 714 // change_tune() function 715 InstrumentScriptVMFunction_change_tune(InstrumentScriptVM * parent)716 InstrumentScriptVMFunction_change_tune::InstrumentScriptVMFunction_change_tune(InstrumentScriptVM* parent) 717 : m_vm(parent) 718 { 719 } 720 acceptsArgType(vmint iArg,ExprType_t type) const721 bool InstrumentScriptVMFunction_change_tune::acceptsArgType(vmint iArg, ExprType_t type) const { 722 if (iArg == 0) 723 return type == INT_EXPR || type == INT_ARR_EXPR; 724 else if (iArg == 1) 725 return type == INT_EXPR || type == REAL_EXPR; 726 else 727 return type == INT_EXPR; 728 } 729 acceptsArgUnitPrefix(vmint iArg,StdUnit_t type) const730 bool InstrumentScriptVMFunction_change_tune::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const { 731 return iArg == 1; 732 } 733 acceptsArgFinal(vmint iArg) const734 bool InstrumentScriptVMFunction_change_tune::acceptsArgFinal(vmint iArg) const { 735 return iArg == 1; 736 } 737 exec(VMFnArgs * args)738 VMFnResult* InstrumentScriptVMFunction_change_tune::exec(VMFnArgs* args) { 739 vmint tune = 740 (args->arg(1)->asNumber()->hasUnitFactorNow()) 741 ? args->arg(1)->asNumber()->evalCastInt(VM_MILLI,VM_CENTI) 742 : args->arg(1)->asNumber()->evalCastInt(); // tuning change in milli cents 743 bool isFinal = args->arg(1)->asNumber()->isFinal(); 744 StdUnit_t unit = args->arg(1)->asNumber()->unitType(); 745 bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false; 746 const float fFreqRatio = RTMath::CentsToFreqRatioUnlimited(float(tune) / 1000.f); 747 748 AbstractEngineChannel* pEngineChannel = 749 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); 750 751 if (args->arg(0)->exprType() == INT_EXPR) { 752 const ScriptID id = args->arg(0)->asInt()->evalInt(); 753 if (!id) { 754 wrnMsg("change_tune(): note ID for argument 1 may not be zero"); 755 return successResult(); 756 } 757 if (!id.isNoteID()) { 758 wrnMsg("change_tune(): argument 1 is not a note ID"); 759 return successResult(); 760 } 761 762 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); 763 if (!pNote) return successResult(); 764 765 // if change_tune() was called immediately after note was triggered 766 // then immediately apply the tuning to Note object, but only if 767 // change_tune_time() has not been called before 768 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime && 769 pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S) 770 { 771 if (relative) 772 pNote->Override.Pitch.Value *= fFreqRatio; 773 else 774 pNote->Override.Pitch.Value = fFreqRatio; 775 pNote->Override.Pitch.Final = isFinal; 776 } else { // otherwise schedule tuning change ... 777 Event e = m_vm->m_event->cause; // copy to get fragment time for "now" 778 e.Init(); // clear IDs 779 e.Type = Event::type_note_synth_param; 780 e.Param.NoteSynthParam.NoteID = id.noteID(); 781 e.Param.NoteSynthParam.Type = Event::synth_param_pitch; 782 e.Param.NoteSynthParam.Delta = fFreqRatio; 783 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit( 784 isFinal, relative, unit 785 ); 786 pEngineChannel->ScheduleEventMicroSec(&e, 0); 787 } 788 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { 789 VMIntArrayExpr* ids = args->arg(0)->asIntArray(); 790 for (vmint i = 0; i < ids->arraySize(); ++i) { 791 const ScriptID id = ids->evalIntElement(i); 792 if (!id || !id.isNoteID()) continue; 793 794 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); 795 if (!pNote) continue; 796 797 // if change_tune() was called immediately after note was triggered 798 // then immediately apply the tuning to Note object, but only if 799 // change_tune_time() has not been called before 800 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime && 801 pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S) 802 { 803 if (relative) 804 pNote->Override.Pitch.Value *= fFreqRatio; 805 else 806 pNote->Override.Pitch.Value = fFreqRatio; 807 pNote->Override.Pitch.Final = isFinal; 808 } else { // otherwise schedule tuning change ... 809 Event e = m_vm->m_event->cause; // copy to get fragment time for "now" 810 e.Init(); // clear IDs 811 e.Type = Event::type_note_synth_param; 812 e.Param.NoteSynthParam.NoteID = id.noteID(); 813 e.Param.NoteSynthParam.Type = Event::synth_param_pitch; 814 e.Param.NoteSynthParam.Delta = fFreqRatio; 815 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit( 816 isFinal, relative, unit 817 ); 818 pEngineChannel->ScheduleEventMicroSec(&e, 0); 819 } 820 } 821 } 822 823 return successResult(); 824 } 825 826 // change_pan() function 827 InstrumentScriptVMFunction_change_pan(InstrumentScriptVM * parent)828 InstrumentScriptVMFunction_change_pan::InstrumentScriptVMFunction_change_pan(InstrumentScriptVM* parent) 829 : m_vm(parent) 830 { 831 } 832 acceptsArgType(vmint iArg,ExprType_t type) const833 bool InstrumentScriptVMFunction_change_pan::acceptsArgType(vmint iArg, ExprType_t type) const { 834 if (iArg == 0) 835 return type == INT_EXPR || type == INT_ARR_EXPR; 836 else 837 return type == INT_EXPR; 838 } 839 acceptsArgFinal(vmint iArg) const840 bool InstrumentScriptVMFunction_change_pan::acceptsArgFinal(vmint iArg) const { 841 return iArg == 1; 842 } 843 exec(VMFnArgs * args)844 VMFnResult* InstrumentScriptVMFunction_change_pan::exec(VMFnArgs* args) { 845 vmint pan = args->arg(1)->asInt()->evalInt(); 846 bool isFinal = args->arg(1)->asInt()->isFinal(); 847 bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false; 848 849 if (pan > 1000) { 850 wrnMsg("change_pan(): argument 2 may not be larger than 1000"); 851 pan = 1000; 852 } else if (pan < -1000) { 853 wrnMsg("change_pan(): argument 2 may not be smaller than -1000"); 854 pan = -1000; 855 } 856 const float fPan = float(pan) / 1000.f; 857 858 AbstractEngineChannel* pEngineChannel = 859 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); 860 861 if (args->arg(0)->exprType() == INT_EXPR) { 862 const ScriptID id = args->arg(0)->asInt()->evalInt(); 863 if (!id) { 864 wrnMsg("change_pan(): note ID for argument 1 may not be zero"); 865 return successResult(); 866 } 867 if (!id.isNoteID()) { 868 wrnMsg("change_pan(): argument 1 is not a note ID"); 869 return successResult(); 870 } 871 872 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); 873 if (!pNote) return successResult(); 874 875 // if change_pan() was called immediately after note was triggered 876 // then immediately apply the panning to Note object 877 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { 878 if (relative) { 879 pNote->Override.Pan.Value = RTMath::RelativeSummedAvg(pNote->Override.Pan.Value, fPan, ++pNote->Override.Pan.Sources); 880 } else { 881 pNote->Override.Pan.Value = fPan; 882 pNote->Override.Pan.Sources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set 883 } 884 pNote->Override.Pan.Final = isFinal; 885 } else { // otherwise schedule panning change ... 886 Event e = m_vm->m_event->cause; // copy to get fragment time for "now" 887 e.Init(); // clear IDs 888 e.Type = Event::type_note_synth_param; 889 e.Param.NoteSynthParam.NoteID = id.noteID(); 890 e.Param.NoteSynthParam.Type = Event::synth_param_pan; 891 e.Param.NoteSynthParam.Delta = fPan; 892 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit( 893 isFinal, relative, false 894 ); 895 pEngineChannel->ScheduleEventMicroSec(&e, 0); 896 } 897 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { 898 VMIntArrayExpr* ids = args->arg(0)->asIntArray(); 899 for (vmint i = 0; i < ids->arraySize(); ++i) { 900 const ScriptID id = ids->evalIntElement(i); 901 if (!id || !id.isNoteID()) continue; 902 903 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); 904 if (!pNote) continue; 905 906 // if change_pan() was called immediately after note was triggered 907 // then immediately apply the panning to Note object 908 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { 909 if (relative) { 910 pNote->Override.Pan.Value = RTMath::RelativeSummedAvg(pNote->Override.Pan.Value, fPan, ++pNote->Override.Pan.Sources); 911 } else { 912 pNote->Override.Pan.Value = fPan; 913 pNote->Override.Pan.Sources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set 914 } 915 pNote->Override.Pan.Final = isFinal; 916 } else { // otherwise schedule panning change ... 917 Event e = m_vm->m_event->cause; // copy to get fragment time for "now" 918 e.Init(); // clear IDs 919 e.Type = Event::type_note_synth_param; 920 e.Param.NoteSynthParam.NoteID = id.noteID(); 921 e.Param.NoteSynthParam.Type = Event::synth_param_pan; 922 e.Param.NoteSynthParam.Delta = fPan; 923 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit( 924 isFinal, relative, false 925 ); 926 pEngineChannel->ScheduleEventMicroSec(&e, 0); 927 } 928 } 929 } 930 931 return successResult(); 932 } 933 934 #define VM_FILTER_PAR_MAX_VALUE 1000000 935 #define VM_FILTER_PAR_MAX_HZ 30000 936 #define VM_EG_PAR_MAX_VALUE 1000000 937 938 // change_cutoff() function 939 InstrumentScriptVMFunction_change_cutoff(InstrumentScriptVM * parent)940 InstrumentScriptVMFunction_change_cutoff::InstrumentScriptVMFunction_change_cutoff(InstrumentScriptVM* parent) 941 : m_vm(parent) 942 { 943 } 944 acceptsArgType(vmint iArg,ExprType_t type) const945 bool InstrumentScriptVMFunction_change_cutoff::acceptsArgType(vmint iArg, ExprType_t type) const { 946 if (iArg == 0) 947 return type == INT_EXPR || type == INT_ARR_EXPR; 948 else if (iArg == 1) 949 return type == INT_EXPR || type == REAL_EXPR; 950 else 951 return type == INT_EXPR; 952 } 953 acceptsArgUnitType(vmint iArg,StdUnit_t type) const954 bool InstrumentScriptVMFunction_change_cutoff::acceptsArgUnitType(vmint iArg, StdUnit_t type) const { 955 if (iArg == 1) 956 return type == VM_NO_UNIT || type == VM_HERTZ; 957 else 958 return type == VM_NO_UNIT; 959 } 960 acceptsArgUnitPrefix(vmint iArg,StdUnit_t type) const961 bool InstrumentScriptVMFunction_change_cutoff::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const { 962 return iArg == 1 && type == VM_HERTZ; // only allow metric prefix(es) if 'Hz' is used as unit type 963 } 964 acceptsArgFinal(vmint iArg) const965 bool InstrumentScriptVMFunction_change_cutoff::acceptsArgFinal(vmint iArg) const { 966 return iArg == 1; 967 } 968 checkArgs(VMFnArgs * args,std::function<void (String)> err,std::function<void (String)> wrn)969 void InstrumentScriptVMFunction_change_cutoff::checkArgs(VMFnArgs* args, 970 std::function<void(String)> err, 971 std::function<void(String)> wrn) 972 { 973 // super class checks 974 Super::checkArgs(args, err, wrn); 975 976 // own checks ... 977 if (args->argsCount() >= 2) { 978 VMNumberExpr* argCutoff = args->arg(1)->asNumber(); 979 if (argCutoff->unitType() && !argCutoff->isFinal()) { 980 wrn("Argument 2 implies 'final' value when using Hz as unit for cutoff frequency."); 981 } 982 } 983 } 984 exec(VMFnArgs * args)985 VMFnResult* InstrumentScriptVMFunction_change_cutoff::exec(VMFnArgs* args) { 986 const StdUnit_t unit = args->arg(1)->asNumber()->unitType(); 987 vmint cutoff = 988 (unit) ? 989 args->arg(1)->asNumber()->evalCastInt(VM_NO_PREFIX) : 990 args->arg(1)->asNumber()->evalCastInt(); 991 const bool isFinal = 992 (unit) ? 993 true : // imply 'final' value if unit type is used 994 args->arg(1)->asNumber()->isFinal(); 995 // note: intentionally not checking against a max. value here if no unit! 996 // (to allow i.e. passing 2000000 for doubling cutoff frequency) 997 if (unit && cutoff > VM_FILTER_PAR_MAX_HZ) { 998 wrnMsg("change_cutoff(): argument 2 may not be larger than " strfy(VM_FILTER_PAR_MAX_HZ) " Hz"); 999 cutoff = VM_FILTER_PAR_MAX_HZ; 1000 } else if (cutoff < 0) { 1001 wrnMsg("change_cutoff(): argument 2 may not be negative"); 1002 cutoff = 0; 1003 } 1004 const float fCutoff = 1005 (unit) ? cutoff : float(cutoff) / float(VM_FILTER_PAR_MAX_VALUE); 1006 1007 AbstractEngineChannel* pEngineChannel = 1008 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); 1009 1010 if (args->arg(0)->exprType() == INT_EXPR) { 1011 const ScriptID id = args->arg(0)->asInt()->evalInt(); 1012 if (!id) { 1013 wrnMsg("change_cutoff(): note ID for argument 1 may not be zero"); 1014 return successResult(); 1015 } 1016 if (!id.isNoteID()) { 1017 wrnMsg("change_cutoff(): argument 1 is not a note ID"); 1018 return successResult(); 1019 } 1020 1021 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); 1022 if (!pNote) return successResult(); 1023 1024 // if change_cutoff() was called immediately after note was triggered 1025 // then immediately apply cutoff to Note object 1026 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { 1027 pNote->Override.Cutoff.Value = fCutoff; 1028 pNote->Override.Cutoff.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit); 1029 } else { // otherwise schedule cutoff change ... 1030 Event e = m_vm->m_event->cause; // copy to get fragment time for "now" 1031 e.Init(); // clear IDs 1032 e.Type = Event::type_note_synth_param; 1033 e.Param.NoteSynthParam.NoteID = id.noteID(); 1034 e.Param.NoteSynthParam.Type = Event::synth_param_cutoff; 1035 e.Param.NoteSynthParam.Delta = fCutoff; 1036 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit( 1037 isFinal, false, unit 1038 ); 1039 pEngineChannel->ScheduleEventMicroSec(&e, 0); 1040 } 1041 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { 1042 VMIntArrayExpr* ids = args->arg(0)->asIntArray(); 1043 for (vmint i = 0; i < ids->arraySize(); ++i) { 1044 const ScriptID id = ids->evalIntElement(i); 1045 if (!id || !id.isNoteID()) continue; 1046 1047 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); 1048 if (!pNote) continue; 1049 1050 // if change_cutoff() was called immediately after note was triggered 1051 // then immediately apply cutoff to Note object 1052 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { 1053 pNote->Override.Cutoff.Value = fCutoff; 1054 pNote->Override.Cutoff.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit); 1055 } else { // otherwise schedule cutoff change ... 1056 Event e = m_vm->m_event->cause; // copy to get fragment time for "now" 1057 e.Init(); // clear IDs 1058 e.Type = Event::type_note_synth_param; 1059 e.Param.NoteSynthParam.NoteID = id.noteID(); 1060 e.Param.NoteSynthParam.Type = Event::synth_param_cutoff; 1061 e.Param.NoteSynthParam.Delta = fCutoff; 1062 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit( 1063 isFinal, false, unit 1064 ); 1065 pEngineChannel->ScheduleEventMicroSec(&e, 0); 1066 } 1067 } 1068 } 1069 1070 return successResult(); 1071 } 1072 1073 // change_reso() function 1074 InstrumentScriptVMFunction_change_reso(InstrumentScriptVM * parent)1075 InstrumentScriptVMFunction_change_reso::InstrumentScriptVMFunction_change_reso(InstrumentScriptVM* parent) 1076 : m_vm(parent) 1077 { 1078 } 1079 acceptsArgType(vmint iArg,ExprType_t type) const1080 bool InstrumentScriptVMFunction_change_reso::acceptsArgType(vmint iArg, ExprType_t type) const { 1081 if (iArg == 0) 1082 return type == INT_EXPR || type == INT_ARR_EXPR; 1083 else 1084 return type == INT_EXPR; 1085 } 1086 acceptsArgFinal(vmint iArg) const1087 bool InstrumentScriptVMFunction_change_reso::acceptsArgFinal(vmint iArg) const { 1088 return iArg == 1; 1089 } 1090 exec(VMFnArgs * args)1091 VMFnResult* InstrumentScriptVMFunction_change_reso::exec(VMFnArgs* args) { 1092 vmint resonance = args->arg(1)->asInt()->evalInt(); 1093 bool isFinal = args->arg(1)->asInt()->isFinal(); 1094 // note: intentionally not checking against a max. value here! 1095 // (to allow i.e. passing 2000000 for doubling the resonance) 1096 if (resonance < 0) { 1097 wrnMsg("change_reso(): argument 2 may not be negative"); 1098 resonance = 0; 1099 } 1100 const float fResonance = float(resonance) / float(VM_FILTER_PAR_MAX_VALUE); 1101 1102 AbstractEngineChannel* pEngineChannel = 1103 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); 1104 1105 if (args->arg(0)->exprType() == INT_EXPR) { 1106 const ScriptID id = args->arg(0)->asInt()->evalInt(); 1107 if (!id) { 1108 wrnMsg("change_reso(): note ID for argument 1 may not be zero"); 1109 return successResult(); 1110 } 1111 if (!id.isNoteID()) { 1112 wrnMsg("change_reso(): argument 1 is not a note ID"); 1113 return successResult(); 1114 } 1115 1116 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); 1117 if (!pNote) return successResult(); 1118 1119 // if change_reso() was called immediately after note was triggered 1120 // then immediately apply resonance to Note object 1121 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { 1122 pNote->Override.Resonance.Value = fResonance; 1123 pNote->Override.Resonance.Final = isFinal; 1124 } else { // otherwise schedule resonance change ... 1125 Event e = m_vm->m_event->cause; // copy to get fragment time for "now" 1126 e.Init(); // clear IDs 1127 e.Type = Event::type_note_synth_param; 1128 e.Param.NoteSynthParam.NoteID = id.noteID(); 1129 e.Param.NoteSynthParam.Type = Event::synth_param_resonance; 1130 e.Param.NoteSynthParam.Delta = fResonance; 1131 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit( 1132 isFinal, false, false 1133 ); 1134 pEngineChannel->ScheduleEventMicroSec(&e, 0); 1135 } 1136 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { 1137 VMIntArrayExpr* ids = args->arg(0)->asIntArray(); 1138 for (vmint i = 0; i < ids->arraySize(); ++i) { 1139 const ScriptID id = ids->evalIntElement(i); 1140 if (!id || !id.isNoteID()) continue; 1141 1142 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); 1143 if (!pNote) continue; 1144 1145 // if change_reso() was called immediately after note was triggered 1146 // then immediately apply resonance to Note object 1147 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { 1148 pNote->Override.Resonance.Value = fResonance; 1149 pNote->Override.Resonance.Final = isFinal; 1150 } else { // otherwise schedule resonance change ... 1151 Event e = m_vm->m_event->cause; // copy to get fragment time for "now" 1152 e.Init(); // clear IDs 1153 e.Type = Event::type_note_synth_param; 1154 e.Param.NoteSynthParam.NoteID = id.noteID(); 1155 e.Param.NoteSynthParam.Type = Event::synth_param_resonance; 1156 e.Param.NoteSynthParam.Delta = fResonance; 1157 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit( 1158 isFinal, false, false 1159 ); 1160 pEngineChannel->ScheduleEventMicroSec(&e, 0); 1161 } 1162 } 1163 } 1164 1165 return successResult(); 1166 } 1167 1168 // change_attack() function 1169 // 1170 //TODO: Derive from generalized, shared template class 1171 // VMChangeSynthParamFunction instead (like e.g. change_cutoff_attack() 1172 // implementation below already does) to ease maintenance. 1173 InstrumentScriptVMFunction_change_attack(InstrumentScriptVM * parent)1174 InstrumentScriptVMFunction_change_attack::InstrumentScriptVMFunction_change_attack(InstrumentScriptVM* parent) 1175 : m_vm(parent) 1176 { 1177 } 1178 acceptsArgType(vmint iArg,ExprType_t type) const1179 bool InstrumentScriptVMFunction_change_attack::acceptsArgType(vmint iArg, ExprType_t type) const { 1180 if (iArg == 0) 1181 return type == INT_EXPR || type == INT_ARR_EXPR; 1182 else 1183 return type == INT_EXPR || type == REAL_EXPR; 1184 } 1185 acceptsArgUnitType(vmint iArg,StdUnit_t type) const1186 bool InstrumentScriptVMFunction_change_attack::acceptsArgUnitType(vmint iArg, StdUnit_t type) const { 1187 if (iArg == 1) 1188 return type == VM_NO_UNIT || type == VM_SECOND; 1189 else 1190 return type == VM_NO_UNIT; 1191 } 1192 acceptsArgUnitPrefix(vmint iArg,StdUnit_t type) const1193 bool InstrumentScriptVMFunction_change_attack::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const { 1194 return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type 1195 } 1196 acceptsArgFinal(vmint iArg) const1197 bool InstrumentScriptVMFunction_change_attack::acceptsArgFinal(vmint iArg) const { 1198 return iArg == 1; 1199 } 1200 checkArgs(VMFnArgs * args,std::function<void (String)> err,std::function<void (String)> wrn)1201 void InstrumentScriptVMFunction_change_attack::checkArgs(VMFnArgs* args, 1202 std::function<void(String)> err, 1203 std::function<void(String)> wrn) 1204 { 1205 // super class checks 1206 Super::checkArgs(args, err, wrn); 1207 1208 // own checks ... 1209 if (args->argsCount() >= 2) { 1210 VMNumberExpr* argTime = args->arg(1)->asNumber(); 1211 if (argTime->unitType() && !argTime->isFinal()) { 1212 wrn("Argument 2 implies 'final' value when using seconds as unit for attack time."); 1213 } 1214 } 1215 } 1216 exec(VMFnArgs * args)1217 VMFnResult* InstrumentScriptVMFunction_change_attack::exec(VMFnArgs* args) { 1218 const StdUnit_t unit = args->arg(1)->asNumber()->unitType(); 1219 vmint attack = 1220 (unit) ? 1221 args->arg(1)->asNumber()->evalCastInt(VM_MICRO) : 1222 args->arg(1)->asNumber()->evalCastInt(); 1223 const bool isFinal = 1224 (unit) ? 1225 true : // imply 'final' value if unit type is used 1226 args->arg(1)->asNumber()->isFinal(); 1227 // note: intentionally not checking against a max. value here! 1228 // (to allow i.e. passing 2000000 for doubling the attack time) 1229 if (attack < 0) { 1230 wrnMsg("change_attack(): argument 2 may not be negative"); 1231 attack = 0; 1232 } 1233 const float fAttack = 1234 (unit) ? 1235 float(attack) / 1000000.f /* us -> s */ : 1236 float(attack) / float(VM_EG_PAR_MAX_VALUE); 1237 1238 AbstractEngineChannel* pEngineChannel = 1239 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); 1240 1241 if (args->arg(0)->exprType() == INT_EXPR) { 1242 const ScriptID id = args->arg(0)->asInt()->evalInt(); 1243 if (!id) { 1244 wrnMsg("change_attack(): note ID for argument 1 may not be zero"); 1245 return successResult(); 1246 } 1247 if (!id.isNoteID()) { 1248 wrnMsg("change_attack(): argument 1 is not a note ID"); 1249 return successResult(); 1250 } 1251 1252 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); 1253 if (!pNote) return successResult(); 1254 1255 // if change_attack() was called immediately after note was triggered 1256 // then immediately apply attack to Note object 1257 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { 1258 pNote->Override.Attack.Value = fAttack; 1259 pNote->Override.Attack.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit); 1260 } else { // otherwise schedule attack change ... 1261 Event e = m_vm->m_event->cause; // copy to get fragment time for "now" 1262 e.Init(); // clear IDs 1263 e.Type = Event::type_note_synth_param; 1264 e.Param.NoteSynthParam.NoteID = id.noteID(); 1265 e.Param.NoteSynthParam.Type = Event::synth_param_attack; 1266 e.Param.NoteSynthParam.Delta = fAttack; 1267 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit( 1268 isFinal, false, unit 1269 ); 1270 pEngineChannel->ScheduleEventMicroSec(&e, 0); 1271 } 1272 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { 1273 VMIntArrayExpr* ids = args->arg(0)->asIntArray(); 1274 for (vmint i = 0; i < ids->arraySize(); ++i) { 1275 const ScriptID id = ids->evalIntElement(i); 1276 if (!id || !id.isNoteID()) continue; 1277 1278 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); 1279 if (!pNote) continue; 1280 1281 // if change_attack() was called immediately after note was triggered 1282 // then immediately apply attack to Note object 1283 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { 1284 pNote->Override.Attack.Value = fAttack; 1285 pNote->Override.Attack.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit); 1286 } else { // otherwise schedule attack change ... 1287 Event e = m_vm->m_event->cause; // copy to get fragment time for "now" 1288 e.Init(); // clear IDs 1289 e.Type = Event::type_note_synth_param; 1290 e.Param.NoteSynthParam.NoteID = id.noteID(); 1291 e.Param.NoteSynthParam.Type = Event::synth_param_attack; 1292 e.Param.NoteSynthParam.Delta = fAttack; 1293 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit( 1294 isFinal, false, unit 1295 ); 1296 pEngineChannel->ScheduleEventMicroSec(&e, 0); 1297 } 1298 } 1299 } 1300 1301 return successResult(); 1302 } 1303 1304 // change_decay() function 1305 // 1306 //TODO: Derive from generalized, shared template class 1307 // VMChangeSynthParamFunction instead (like e.g. change_cutoff_decay() 1308 // implementation below already does) to ease maintenance. 1309 InstrumentScriptVMFunction_change_decay(InstrumentScriptVM * parent)1310 InstrumentScriptVMFunction_change_decay::InstrumentScriptVMFunction_change_decay(InstrumentScriptVM* parent) 1311 : m_vm(parent) 1312 { 1313 } 1314 acceptsArgType(vmint iArg,ExprType_t type) const1315 bool InstrumentScriptVMFunction_change_decay::acceptsArgType(vmint iArg, ExprType_t type) const { 1316 if (iArg == 0) 1317 return type == INT_EXPR || type == INT_ARR_EXPR; 1318 else 1319 return type == INT_EXPR || type == REAL_EXPR; 1320 } 1321 acceptsArgUnitType(vmint iArg,StdUnit_t type) const1322 bool InstrumentScriptVMFunction_change_decay::acceptsArgUnitType(vmint iArg, StdUnit_t type) const { 1323 if (iArg == 1) 1324 return type == VM_NO_UNIT || type == VM_SECOND; 1325 else 1326 return type == VM_NO_UNIT; 1327 } 1328 acceptsArgUnitPrefix(vmint iArg,StdUnit_t type) const1329 bool InstrumentScriptVMFunction_change_decay::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const { 1330 return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type 1331 } 1332 acceptsArgFinal(vmint iArg) const1333 bool InstrumentScriptVMFunction_change_decay::acceptsArgFinal(vmint iArg) const { 1334 return iArg == 1; 1335 } 1336 checkArgs(VMFnArgs * args,std::function<void (String)> err,std::function<void (String)> wrn)1337 void InstrumentScriptVMFunction_change_decay::checkArgs(VMFnArgs* args, 1338 std::function<void(String)> err, 1339 std::function<void(String)> wrn) 1340 { 1341 // super class checks 1342 Super::checkArgs(args, err, wrn); 1343 1344 // own checks ... 1345 if (args->argsCount() >= 2) { 1346 VMNumberExpr* argTime = args->arg(1)->asNumber(); 1347 if (argTime->unitType() && !argTime->isFinal()) { 1348 wrn("Argument 2 implies 'final' value when using seconds as unit for decay time."); 1349 } 1350 } 1351 } 1352 exec(VMFnArgs * args)1353 VMFnResult* InstrumentScriptVMFunction_change_decay::exec(VMFnArgs* args) { 1354 const StdUnit_t unit = args->arg(1)->asNumber()->unitType(); 1355 vmint decay = 1356 (unit) ? 1357 args->arg(1)->asNumber()->evalCastInt(VM_MICRO) : 1358 args->arg(1)->asNumber()->evalCastInt(); 1359 const bool isFinal = 1360 (unit) ? 1361 true : // imply 'final' value if unit type is used 1362 args->arg(1)->asNumber()->isFinal(); 1363 // note: intentionally not checking against a max. value here! 1364 // (to allow i.e. passing 2000000 for doubling the decay time) 1365 if (decay < 0) { 1366 wrnMsg("change_decay(): argument 2 may not be negative"); 1367 decay = 0; 1368 } 1369 const float fDecay = 1370 (unit) ? 1371 float(decay) / 1000000.f /* us -> s */ : 1372 float(decay) / float(VM_EG_PAR_MAX_VALUE); 1373 1374 AbstractEngineChannel* pEngineChannel = 1375 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); 1376 1377 if (args->arg(0)->exprType() == INT_EXPR) { 1378 const ScriptID id = args->arg(0)->asInt()->evalInt(); 1379 if (!id) { 1380 wrnMsg("change_decay(): note ID for argument 1 may not be zero"); 1381 return successResult(); 1382 } 1383 if (!id.isNoteID()) { 1384 wrnMsg("change_decay(): argument 1 is not a note ID"); 1385 return successResult(); 1386 } 1387 1388 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); 1389 if (!pNote) return successResult(); 1390 1391 // if change_decay() was called immediately after note was triggered 1392 // then immediately apply decay to Note object 1393 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { 1394 pNote->Override.Decay.Value = fDecay; 1395 pNote->Override.Decay.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit); 1396 } else { // otherwise schedule decay change ... 1397 Event e = m_vm->m_event->cause; // copy to get fragment time for "now" 1398 e.Init(); // clear IDs 1399 e.Type = Event::type_note_synth_param; 1400 e.Param.NoteSynthParam.NoteID = id.noteID(); 1401 e.Param.NoteSynthParam.Type = Event::synth_param_decay; 1402 e.Param.NoteSynthParam.Delta = fDecay; 1403 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit( 1404 isFinal, false, unit 1405 ); 1406 pEngineChannel->ScheduleEventMicroSec(&e, 0); 1407 } 1408 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { 1409 VMIntArrayExpr* ids = args->arg(0)->asIntArray(); 1410 for (vmint i = 0; i < ids->arraySize(); ++i) { 1411 const ScriptID id = ids->evalIntElement(i); 1412 if (!id || !id.isNoteID()) continue; 1413 1414 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); 1415 if (!pNote) continue; 1416 1417 // if change_decay() was called immediately after note was triggered 1418 // then immediately apply decay to Note object 1419 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { 1420 pNote->Override.Decay.Value = fDecay; 1421 pNote->Override.Decay.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit); 1422 } else { // otherwise schedule decay change ... 1423 Event e = m_vm->m_event->cause; // copy to get fragment time for "now" 1424 e.Init(); // clear IDs 1425 e.Type = Event::type_note_synth_param; 1426 e.Param.NoteSynthParam.NoteID = id.noteID(); 1427 e.Param.NoteSynthParam.Type = Event::synth_param_decay; 1428 e.Param.NoteSynthParam.Delta = fDecay; 1429 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit( 1430 isFinal, false, unit 1431 ); 1432 pEngineChannel->ScheduleEventMicroSec(&e, 0); 1433 } 1434 } 1435 } 1436 1437 return successResult(); 1438 } 1439 1440 // change_release() function 1441 // 1442 //TODO: Derive from generalized, shared template class 1443 // VMChangeSynthParamFunction instead (like e.g. change_cutoff_release() 1444 // implementation below already does) to ease maintenance. 1445 InstrumentScriptVMFunction_change_release(InstrumentScriptVM * parent)1446 InstrumentScriptVMFunction_change_release::InstrumentScriptVMFunction_change_release(InstrumentScriptVM* parent) 1447 : m_vm(parent) 1448 { 1449 } 1450 acceptsArgType(vmint iArg,ExprType_t type) const1451 bool InstrumentScriptVMFunction_change_release::acceptsArgType(vmint iArg, ExprType_t type) const { 1452 if (iArg == 0) 1453 return type == INT_EXPR || type == INT_ARR_EXPR; 1454 else 1455 return type == INT_EXPR || type == REAL_EXPR; 1456 } 1457 acceptsArgUnitType(vmint iArg,StdUnit_t type) const1458 bool InstrumentScriptVMFunction_change_release::acceptsArgUnitType(vmint iArg, StdUnit_t type) const { 1459 if (iArg == 1) 1460 return type == VM_NO_UNIT || type == VM_SECOND; 1461 else 1462 return type == VM_NO_UNIT; 1463 } 1464 acceptsArgUnitPrefix(vmint iArg,StdUnit_t type) const1465 bool InstrumentScriptVMFunction_change_release::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const { 1466 return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type 1467 } 1468 acceptsArgFinal(vmint iArg) const1469 bool InstrumentScriptVMFunction_change_release::acceptsArgFinal(vmint iArg) const { 1470 return iArg == 1; 1471 } 1472 checkArgs(VMFnArgs * args,std::function<void (String)> err,std::function<void (String)> wrn)1473 void InstrumentScriptVMFunction_change_release::checkArgs(VMFnArgs* args, 1474 std::function<void(String)> err, 1475 std::function<void(String)> wrn) 1476 { 1477 // super class checks 1478 Super::checkArgs(args, err, wrn); 1479 1480 // own checks ... 1481 if (args->argsCount() >= 2) { 1482 VMNumberExpr* argTime = args->arg(1)->asNumber(); 1483 if (argTime->unitType() && !argTime->isFinal()) { 1484 wrn("Argument 2 implies 'final' value when using seconds as unit for release time."); 1485 } 1486 } 1487 } 1488 exec(VMFnArgs * args)1489 VMFnResult* InstrumentScriptVMFunction_change_release::exec(VMFnArgs* args) { 1490 const StdUnit_t unit = args->arg(1)->asNumber()->unitType(); 1491 vmint release = 1492 (unit) ? 1493 args->arg(1)->asNumber()->evalCastInt(VM_MICRO) : 1494 args->arg(1)->asNumber()->evalCastInt(); 1495 const bool isFinal = 1496 (unit) ? 1497 true : // imply 'final' value if unit type is used 1498 args->arg(1)->asNumber()->isFinal(); 1499 // note: intentionally not checking against a max. value here! 1500 // (to allow i.e. passing 2000000 for doubling the release time) 1501 if (release < 0) { 1502 wrnMsg("change_release(): argument 2 may not be negative"); 1503 release = 0; 1504 } 1505 const float fRelease = 1506 (unit) ? 1507 float(release) / 1000000.f /* us -> s */ : 1508 float(release) / float(VM_EG_PAR_MAX_VALUE); 1509 1510 AbstractEngineChannel* pEngineChannel = 1511 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); 1512 1513 if (args->arg(0)->exprType() == INT_EXPR) { 1514 const ScriptID id = args->arg(0)->asInt()->evalInt(); 1515 if (!id) { 1516 wrnMsg("change_release(): note ID for argument 1 may not be zero"); 1517 return successResult(); 1518 } 1519 if (!id.isNoteID()) { 1520 wrnMsg("change_release(): argument 1 is not a note ID"); 1521 return successResult(); 1522 } 1523 1524 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); 1525 if (!pNote) return successResult(); 1526 1527 // if change_release() was called immediately after note was triggered 1528 // then immediately apply relase to Note object 1529 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { 1530 pNote->Override.Release.Value = fRelease; 1531 pNote->Override.Release.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit); 1532 } else { // otherwise schedule release change ... 1533 Event e = m_vm->m_event->cause; // copy to get fragment time for "now" 1534 e.Init(); // clear IDs 1535 e.Type = Event::type_note_synth_param; 1536 e.Param.NoteSynthParam.NoteID = id.noteID(); 1537 e.Param.NoteSynthParam.Type = Event::synth_param_release; 1538 e.Param.NoteSynthParam.Delta = fRelease; 1539 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit( 1540 isFinal, false, unit 1541 ); 1542 pEngineChannel->ScheduleEventMicroSec(&e, 0); 1543 } 1544 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { 1545 VMIntArrayExpr* ids = args->arg(0)->asIntArray(); 1546 for (vmint i = 0; i < ids->arraySize(); ++i) { 1547 const ScriptID id = ids->evalIntElement(i); 1548 if (!id || !id.isNoteID()) continue; 1549 1550 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); 1551 if (!pNote) continue; 1552 1553 // if change_release() was called immediately after note was triggered 1554 // then immediately apply relase to Note object 1555 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { 1556 pNote->Override.Release.Value = fRelease; 1557 pNote->Override.Release.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit); 1558 } else { // otherwise schedule release change ... 1559 Event e = m_vm->m_event->cause; // copy to get fragment time for "now" 1560 e.Init(); // clear IDs 1561 e.Type = Event::type_note_synth_param; 1562 e.Param.NoteSynthParam.NoteID = id.noteID(); 1563 e.Param.NoteSynthParam.Type = Event::synth_param_release; 1564 e.Param.NoteSynthParam.Delta = fRelease; 1565 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit( 1566 isFinal, false, unit 1567 ); 1568 pEngineChannel->ScheduleEventMicroSec(&e, 0); 1569 } 1570 } 1571 } 1572 1573 return successResult(); 1574 } 1575 1576 // template for change_*() functions 1577 acceptsArgType(vmint iArg,ExprType_t type) const1578 bool VMChangeSynthParamFunction::acceptsArgType(vmint iArg, ExprType_t type) const { 1579 if (iArg == 0) 1580 return type == INT_EXPR || type == INT_ARR_EXPR; 1581 else 1582 return type == INT_EXPR || (m_acceptReal && type == REAL_EXPR); 1583 } 1584 acceptsArgUnitType(vmint iArg,StdUnit_t type) const1585 bool VMChangeSynthParamFunction::acceptsArgUnitType(vmint iArg, StdUnit_t type) const { 1586 if (iArg == 1) 1587 return type == VM_NO_UNIT || type == m_unit; 1588 else 1589 return type == VM_NO_UNIT; 1590 } 1591 acceptsArgUnitPrefix(vmint iArg,StdUnit_t type) const1592 bool VMChangeSynthParamFunction::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const { 1593 return m_acceptUnitPrefix && iArg == 1 && type == m_unit; // only allow metric prefix(es) if approprirate unit type is used (e.g. Hz) 1594 } 1595 acceptsArgFinal(vmint iArg) const1596 bool VMChangeSynthParamFunction::acceptsArgFinal(vmint iArg) const { 1597 return (m_acceptFinal) ? (iArg == 1) : false; 1598 } 1599 setNoteParamScopeBy_FinalUnit(NoteBase::Param & param,const bool bFinal,const StdUnit_t unit)1600 inline static void setNoteParamScopeBy_FinalUnit(NoteBase::Param& param, const bool bFinal, const StdUnit_t unit) { 1601 param.Scope = NoteBase::scopeBy_FinalUnit(bFinal, unit); 1602 } 1603 setNoteParamScopeBy_FinalUnit(NoteBase::Norm & param,const bool bFinal,const StdUnit_t unit)1604 inline static void setNoteParamScopeBy_FinalUnit(NoteBase::Norm& param, const bool bFinal, const StdUnit_t unit) { 1605 param.Final = bFinal; 1606 } 1607 setNoteParamScopeBy_FinalUnit(float & param,const bool bFinal,const StdUnit_t unit)1608 inline static void setNoteParamScopeBy_FinalUnit(float& param, const bool bFinal, const StdUnit_t unit) { 1609 /* NOOP */ 1610 } 1611 1612 template<class T> setNoteParamValue(T & param,vmfloat value)1613 inline static void setNoteParamValue(T& param, vmfloat value) { 1614 param.Value = value; 1615 } 1616 setNoteParamValue(float & param,vmfloat value)1617 inline static void setNoteParamValue(float& param, vmfloat value) { 1618 param = value; 1619 } 1620 checkArgs(VMFnArgs * args,std::function<void (String)> err,std::function<void (String)> wrn)1621 void VMChangeSynthParamFunction::checkArgs(VMFnArgs* args, 1622 std::function<void(String)> err, 1623 std::function<void(String)> wrn) 1624 { 1625 // super class checks 1626 Super::checkArgs(args, err, wrn); 1627 1628 // own checks ... 1629 if (m_unit && m_unit != VM_BEL && args->argsCount() >= 2) { 1630 VMNumberExpr* arg = args->arg(1)->asNumber(); 1631 if (arg && arg->unitType() && !arg->isFinal()) { 1632 wrn("Argument 2 implies 'final' value when unit type " + 1633 unitTypeStr(arg->unitType()) + " is used."); 1634 } 1635 } 1636 } 1637 1638 // Arbitrarily chosen constant value symbolizing "no limit". 1639 #define NO_LIMIT 1315916909 1640 1641 template<class T_NoteParamType, T_NoteParamType NoteBase::_Override::*T_noteParam, 1642 vmint T_synthParam, 1643 vmint T_minValueNorm, vmint T_maxValueNorm, bool T_normalizeNorm, 1644 vmint T_minValueUnit, vmint T_maxValueUnit, 1645 MetricPrefix_t T_unitPrefix0, MetricPrefix_t ... T_unitPrefixN> execTemplate(VMFnArgs * args,const char * functionName)1646 VMFnResult* VMChangeSynthParamFunction::execTemplate(VMFnArgs* args, const char* functionName) 1647 { 1648 const StdUnit_t unit = args->arg(1)->asNumber()->unitType(); 1649 const bool isFinal = 1650 (m_unit && m_unit != VM_BEL && unit) ? 1651 true : // imply 'final' value if unit type is used (except of 'Bel' which may be relative) 1652 args->arg(1)->asNumber()->isFinal(); 1653 vmint value = 1654 (m_acceptUnitPrefix && ((m_unit && unit) || (!m_unit && args->arg(1)->asNumber()->hasUnitFactorNow()))) 1655 ? args->arg(1)->asNumber()->evalCastInt(T_unitPrefix0, T_unitPrefixN ...) 1656 : args->arg(1)->asNumber()->evalCastInt(); 1657 1658 // check if passed value is in allowed range 1659 if (unit && m_unit) { 1660 if (T_maxValueUnit != NO_LIMIT && value > T_maxValueUnit) { 1661 wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValueUnit)); 1662 value = T_maxValueUnit; 1663 } else if (T_minValueUnit != NO_LIMIT && value < T_minValueUnit) { 1664 if (T_minValueUnit == 0) 1665 wrnMsg(String(functionName) + "(): argument 2 may not be negative"); 1666 else 1667 wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValueUnit)); 1668 value = T_minValueUnit; 1669 } 1670 } else { // value was passed to this function without a unit ... 1671 if (T_maxValueNorm != NO_LIMIT && value > T_maxValueNorm) { 1672 wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValueNorm)); 1673 value = T_maxValueNorm; 1674 } else if (T_minValueNorm != NO_LIMIT && value < T_minValueNorm) { 1675 if (T_minValueNorm == 0) 1676 wrnMsg(String(functionName) + "(): argument 2 may not be negative"); 1677 else 1678 wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValueNorm)); 1679 value = T_minValueNorm; 1680 } 1681 } 1682 1683 // convert passed argument value to engine internal expected value range (i.e. 0.0 .. 1.0) 1684 const float fValue = 1685 (unit && m_unit) ? 1686 (unit == VM_BEL) ? 1687 RTMath::DecibelToLinRatio(float(value) * float(T_unitPrefix0) /*i.e. mdB -> dB*/) : 1688 float(value) * VMUnit::unitFactor(T_unitPrefix0, T_unitPrefixN ...) /*i.e. us -> s*/ : 1689 (T_normalizeNorm) ? 1690 float(value) / ((T_maxValueNorm != NO_LIMIT) ? float(T_maxValueNorm) : 1000000.f/* fallback: value range used for most */) : 1691 float(value) /* as is */; 1692 1693 AbstractEngineChannel* pEngineChannel = 1694 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); 1695 1696 if (args->arg(0)->exprType() == INT_EXPR) { 1697 const ScriptID id = args->arg(0)->asInt()->evalInt(); 1698 if (!id) { 1699 wrnMsg(String(functionName) + "(): note ID for argument 1 may not be zero"); 1700 return successResult(); 1701 } 1702 if (!id.isNoteID()) { 1703 wrnMsg(String(functionName) + "(): argument 1 is not a note ID"); 1704 return successResult(); 1705 } 1706 1707 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); 1708 if (!pNote) return successResult(); 1709 1710 // if this change_*() script function was called immediately after 1711 // note was triggered then immediately apply the synth parameter 1712 // change to Note object 1713 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { 1714 setNoteParamValue(pNote->Override.*T_noteParam, fValue); 1715 setNoteParamScopeBy_FinalUnit( 1716 (pNote->Override.*T_noteParam), 1717 isFinal, unit 1718 ); 1719 } else { // otherwise schedule this synth parameter change ... 1720 Event e = m_vm->m_event->cause; // copy to get fragment time for "now" 1721 e.Init(); // clear IDs 1722 e.Type = Event::type_note_synth_param; 1723 e.Param.NoteSynthParam.NoteID = id.noteID(); 1724 e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam; 1725 e.Param.NoteSynthParam.Delta = fValue; 1726 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit( 1727 isFinal, false, unit 1728 ); 1729 pEngineChannel->ScheduleEventMicroSec(&e, 0); 1730 } 1731 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { 1732 VMIntArrayExpr* ids = args->arg(0)->asIntArray(); 1733 for (vmint i = 0; i < ids->arraySize(); ++i) { 1734 const ScriptID id = ids->evalIntElement(i); 1735 if (!id || !id.isNoteID()) continue; 1736 1737 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); 1738 if (!pNote) continue; 1739 1740 // if this change_*() script function was called immediately after 1741 // note was triggered then immediately apply the synth parameter 1742 // change to Note object 1743 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { 1744 setNoteParamValue(pNote->Override.*T_noteParam, fValue); 1745 setNoteParamScopeBy_FinalUnit( 1746 (pNote->Override.*T_noteParam), 1747 isFinal, unit 1748 ); 1749 } else { // otherwise schedule this synth parameter change ... 1750 Event e = m_vm->m_event->cause; // copy to get fragment time for "now" 1751 e.Init(); // clear IDs 1752 e.Type = Event::type_note_synth_param; 1753 e.Param.NoteSynthParam.NoteID = id.noteID(); 1754 e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam; 1755 e.Param.NoteSynthParam.Delta = fValue; 1756 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit( 1757 isFinal, false, unit 1758 ); 1759 pEngineChannel->ScheduleEventMicroSec(&e, 0); 1760 } 1761 } 1762 } 1763 1764 return successResult(); 1765 } 1766 1767 // change_sustain() function 1768 exec(VMFnArgs * args)1769 VMFnResult* InstrumentScriptVMFunction_change_sustain::exec(VMFnArgs* args) { 1770 return VMChangeSynthParamFunction::execTemplate< 1771 decltype(NoteBase::_Override::Sustain), 1772 &NoteBase::_Override::Sustain, 1773 Event::synth_param_sustain, 1774 /* if value passed without unit */ 1775 0, NO_LIMIT, true, 1776 /* if value passed WITH 'Bel' unit */ 1777 NO_LIMIT, NO_LIMIT, VM_MILLI, VM_DECI>( args, "change_sustain" ); 1778 } 1779 1780 // change_cutoff_attack() function 1781 exec(VMFnArgs * args)1782 VMFnResult* InstrumentScriptVMFunction_change_cutoff_attack::exec(VMFnArgs* args) { 1783 return VMChangeSynthParamFunction::execTemplate< 1784 decltype(NoteBase::_Override::CutoffAttack), 1785 &NoteBase::_Override::CutoffAttack, 1786 Event::synth_param_cutoff_attack, 1787 /* if value passed without unit */ 1788 0, NO_LIMIT, true, 1789 /* if value passed with 'seconds' unit */ 1790 0, NO_LIMIT, VM_MICRO>( args, "change_cutoff_attack" ); 1791 } 1792 1793 // change_cutoff_decay() function 1794 exec(VMFnArgs * args)1795 VMFnResult* InstrumentScriptVMFunction_change_cutoff_decay::exec(VMFnArgs* args) { 1796 return VMChangeSynthParamFunction::execTemplate< 1797 decltype(NoteBase::_Override::CutoffDecay), 1798 &NoteBase::_Override::CutoffDecay, 1799 Event::synth_param_cutoff_decay, 1800 /* if value passed without unit */ 1801 0, NO_LIMIT, true, 1802 /* if value passed with 'seconds' unit */ 1803 0, NO_LIMIT, VM_MICRO>( args, "change_cutoff_decay" ); 1804 } 1805 1806 // change_cutoff_sustain() function 1807 exec(VMFnArgs * args)1808 VMFnResult* InstrumentScriptVMFunction_change_cutoff_sustain::exec(VMFnArgs* args) { 1809 return VMChangeSynthParamFunction::execTemplate< 1810 decltype(NoteBase::_Override::CutoffSustain), 1811 &NoteBase::_Override::CutoffSustain, 1812 Event::synth_param_cutoff_sustain, 1813 /* if value passed without unit */ 1814 0, NO_LIMIT, true, 1815 /* if value passed WITH 'Bel' unit */ 1816 NO_LIMIT, NO_LIMIT, VM_MILLI, VM_DECI>( args, "change_cutoff_sustain" ); 1817 } 1818 1819 // change_cutoff_release() function 1820 exec(VMFnArgs * args)1821 VMFnResult* InstrumentScriptVMFunction_change_cutoff_release::exec(VMFnArgs* args) { 1822 return VMChangeSynthParamFunction::execTemplate< 1823 decltype(NoteBase::_Override::CutoffRelease), 1824 &NoteBase::_Override::CutoffRelease, 1825 Event::synth_param_cutoff_release, 1826 /* if value passed without unit */ 1827 0, NO_LIMIT, true, 1828 /* if value passed with 'seconds' unit */ 1829 0, NO_LIMIT, VM_MICRO>( args, "change_cutoff_release" ); 1830 } 1831 1832 // change_amp_lfo_depth() function 1833 exec(VMFnArgs * args)1834 VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_depth::exec(VMFnArgs* args) { 1835 return VMChangeSynthParamFunction::execTemplate< 1836 decltype(NoteBase::_Override::AmpLFODepth), 1837 &NoteBase::_Override::AmpLFODepth, 1838 Event::synth_param_amp_lfo_depth, 1839 /* if value passed without unit */ 1840 0, NO_LIMIT, true, 1841 /* not used (since this function does not accept unit) */ 1842 NO_LIMIT, NO_LIMIT, VM_NO_PREFIX>( args, "change_amp_lfo_depth" ); 1843 } 1844 1845 // change_amp_lfo_freq() function 1846 exec(VMFnArgs * args)1847 VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_freq::exec(VMFnArgs* args) { 1848 return VMChangeSynthParamFunction::execTemplate< 1849 decltype(NoteBase::_Override::AmpLFOFreq), 1850 &NoteBase::_Override::AmpLFOFreq, 1851 Event::synth_param_amp_lfo_freq, 1852 /* if value passed without unit */ 1853 0, NO_LIMIT, true, 1854 /* if value passed with 'Hz' unit */ 1855 0, 30000, VM_NO_PREFIX>( args, "change_amp_lfo_freq" ); 1856 } 1857 1858 // change_cutoff_lfo_depth() function 1859 exec(VMFnArgs * args)1860 VMFnResult* InstrumentScriptVMFunction_change_cutoff_lfo_depth::exec(VMFnArgs* args) { 1861 return VMChangeSynthParamFunction::execTemplate< 1862 decltype(NoteBase::_Override::CutoffLFODepth), 1863 &NoteBase::_Override::CutoffLFODepth, 1864 Event::synth_param_cutoff_lfo_depth, 1865 /* if value passed without unit */ 1866 0, NO_LIMIT, true, 1867 /* not used (since this function does not accept unit) */ 1868 NO_LIMIT, NO_LIMIT, VM_NO_PREFIX>( args, "change_cutoff_lfo_depth" ); 1869 } 1870 1871 // change_cutoff_lfo_freq() function 1872 exec(VMFnArgs * args)1873 VMFnResult* InstrumentScriptVMFunction_change_cutoff_lfo_freq::exec(VMFnArgs* args) { 1874 return VMChangeSynthParamFunction::execTemplate< 1875 decltype(NoteBase::_Override::CutoffLFOFreq), 1876 &NoteBase::_Override::CutoffLFOFreq, 1877 Event::synth_param_cutoff_lfo_freq, 1878 /* if value passed without unit */ 1879 0, NO_LIMIT, true, 1880 /* if value passed with 'Hz' unit */ 1881 0, 30000, VM_NO_PREFIX>( args, "change_cutoff_lfo_freq" ); 1882 } 1883 1884 // change_pitch_lfo_depth() function 1885 exec(VMFnArgs * args)1886 VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_depth::exec(VMFnArgs* args) { 1887 return VMChangeSynthParamFunction::execTemplate< 1888 decltype(NoteBase::_Override::PitchLFODepth), 1889 &NoteBase::_Override::PitchLFODepth, 1890 Event::synth_param_pitch_lfo_depth, 1891 /* if value passed without unit */ 1892 0, NO_LIMIT, true, 1893 /* not used (since this function does not accept unit) */ 1894 NO_LIMIT, NO_LIMIT, VM_NO_PREFIX>( args, "change_pitch_lfo_depth" ); 1895 } 1896 1897 // change_pitch_lfo_freq() function 1898 exec(VMFnArgs * args)1899 VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_freq::exec(VMFnArgs* args) { 1900 return VMChangeSynthParamFunction::execTemplate< 1901 decltype(NoteBase::_Override::PitchLFOFreq), 1902 &NoteBase::_Override::PitchLFOFreq, 1903 Event::synth_param_pitch_lfo_freq, 1904 /* if value passed without unit */ 1905 0, NO_LIMIT, true, 1906 /* if value passed with 'Hz' unit */ 1907 0, 30000, VM_NO_PREFIX>( args, "change_pitch_lfo_freq" ); 1908 } 1909 1910 // change_vol_time() function 1911 exec(VMFnArgs * args)1912 VMFnResult* InstrumentScriptVMFunction_change_vol_time::exec(VMFnArgs* args) { 1913 return VMChangeSynthParamFunction::execTemplate< 1914 decltype(NoteBase::_Override::VolumeTime), 1915 &NoteBase::_Override::VolumeTime, 1916 Event::synth_param_volume_time, 1917 /* if value passed without unit (implying 'us' unit) */ 1918 0, NO_LIMIT, true, 1919 /* if value passed with 'seconds' unit */ 1920 0, NO_LIMIT, VM_MICRO>( args, "change_vol_time" ); 1921 } 1922 1923 // change_tune_time() function 1924 exec(VMFnArgs * args)1925 VMFnResult* InstrumentScriptVMFunction_change_tune_time::exec(VMFnArgs* args) { 1926 return VMChangeSynthParamFunction::execTemplate< 1927 decltype(NoteBase::_Override::PitchTime), 1928 &NoteBase::_Override::PitchTime, 1929 Event::synth_param_pitch_time, 1930 /* if value passed without unit (implying 'us' unit) */ 1931 0, NO_LIMIT, true, 1932 /* if value passed with 'seconds' unit */ 1933 0, NO_LIMIT, VM_MICRO>( args, "change_tune_time" ); 1934 } 1935 1936 // change_pan_time() function 1937 exec(VMFnArgs * args)1938 VMFnResult* InstrumentScriptVMFunction_change_pan_time::exec(VMFnArgs* args) { 1939 return VMChangeSynthParamFunction::execTemplate< 1940 decltype(NoteBase::_Override::PanTime), 1941 &NoteBase::_Override::PanTime, 1942 Event::synth_param_pan_time, 1943 /* if value passed without unit (implying 'us' unit) */ 1944 0, NO_LIMIT, true, 1945 /* if value passed with 'seconds' unit */ 1946 0, NO_LIMIT, VM_MICRO>( args, "change_pan_time" ); 1947 } 1948 1949 // template for change_*_curve() functions 1950 acceptsArgType(vmint iArg,ExprType_t type) const1951 bool VMChangeFadeCurveFunction::acceptsArgType(vmint iArg, ExprType_t type) const { 1952 if (iArg == 0) 1953 return type == INT_EXPR || type == INT_ARR_EXPR; 1954 else 1955 return type == INT_EXPR; 1956 } 1957 1958 template<fade_curve_t NoteBase::_Override::*T_noteParam, vmint T_synthParam> execTemplate(VMFnArgs * args,const char * functionName)1959 VMFnResult* VMChangeFadeCurveFunction::execTemplate(VMFnArgs* args, const char* functionName) { 1960 vmint value = args->arg(1)->asInt()->evalInt(); 1961 switch (value) { 1962 case FADE_CURVE_LINEAR: 1963 case FADE_CURVE_EASE_IN_EASE_OUT: 1964 break; 1965 default: 1966 wrnMsg(String(functionName) + "(): invalid curve type passed as argument 2"); 1967 return successResult(); 1968 } 1969 1970 AbstractEngineChannel* pEngineChannel = 1971 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); 1972 1973 if (args->arg(0)->exprType() == INT_EXPR) { 1974 const ScriptID id = args->arg(0)->asInt()->evalInt(); 1975 if (!id) { 1976 wrnMsg(String(functionName) + "(): note ID for argument 1 may not be zero"); 1977 return successResult(); 1978 } 1979 if (!id.isNoteID()) { 1980 wrnMsg(String(functionName) + "(): argument 1 is not a note ID"); 1981 return successResult(); 1982 } 1983 1984 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); 1985 if (!pNote) return successResult(); 1986 1987 // if this change_*_curve() script function was called immediately after 1988 // note was triggered then immediately apply the synth parameter 1989 // change to Note object 1990 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { 1991 pNote->Override.*T_noteParam = (fade_curve_t) value; 1992 } else { // otherwise schedule this synth parameter change ... 1993 Event e = m_vm->m_event->cause; // copy to get fragment time for "now" 1994 e.Init(); // clear IDs 1995 e.Type = Event::type_note_synth_param; 1996 e.Param.NoteSynthParam.NoteID = id.noteID(); 1997 e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam; 1998 e.Param.NoteSynthParam.Delta = value; 1999 e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored 2000 2001 pEngineChannel->ScheduleEventMicroSec(&e, 0); 2002 } 2003 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { 2004 VMIntArrayExpr* ids = args->arg(0)->asIntArray(); 2005 for (vmint i = 0; i < ids->arraySize(); ++i) { 2006 const ScriptID id = ids->evalIntElement(i); 2007 if (!id || !id.isNoteID()) continue; 2008 2009 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); 2010 if (!pNote) continue; 2011 2012 // if this change_*_curve() script function was called immediately after 2013 // note was triggered then immediately apply the synth parameter 2014 // change to Note object 2015 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { 2016 pNote->Override.*T_noteParam = (fade_curve_t) value; 2017 } else { // otherwise schedule this synth parameter change ... 2018 Event e = m_vm->m_event->cause; // copy to get fragment time for "now" 2019 e.Init(); // clear IDs 2020 e.Type = Event::type_note_synth_param; 2021 e.Param.NoteSynthParam.NoteID = id.noteID(); 2022 e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam; 2023 e.Param.NoteSynthParam.Delta = value; 2024 e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored 2025 2026 pEngineChannel->ScheduleEventMicroSec(&e, 0); 2027 } 2028 } 2029 } 2030 2031 return successResult(); 2032 } 2033 2034 // change_vol_curve() function 2035 exec(VMFnArgs * args)2036 VMFnResult* InstrumentScriptVMFunction_change_vol_curve::exec(VMFnArgs* args) { 2037 return VMChangeFadeCurveFunction::execTemplate< 2038 &NoteBase::_Override::VolumeCurve, 2039 Event::synth_param_volume_curve>( args, "change_vol_curve" ); 2040 } 2041 2042 // change_tune_curve() function 2043 exec(VMFnArgs * args)2044 VMFnResult* InstrumentScriptVMFunction_change_tune_curve::exec(VMFnArgs* args) { 2045 return VMChangeFadeCurveFunction::execTemplate< 2046 &NoteBase::_Override::PitchCurve, 2047 Event::synth_param_pitch_curve>( args, "change_tune_curve" ); 2048 } 2049 2050 // change_pan_curve() function 2051 exec(VMFnArgs * args)2052 VMFnResult* InstrumentScriptVMFunction_change_pan_curve::exec(VMFnArgs* args) { 2053 return VMChangeFadeCurveFunction::execTemplate< 2054 &NoteBase::_Override::PanCurve, 2055 Event::synth_param_pan_curve>( args, "change_pan_curve" ); 2056 } 2057 2058 // fade_in() function 2059 InstrumentScriptVMFunction_fade_in(InstrumentScriptVM * parent)2060 InstrumentScriptVMFunction_fade_in::InstrumentScriptVMFunction_fade_in(InstrumentScriptVM* parent) 2061 : m_vm(parent) 2062 { 2063 } 2064 acceptsArgType(vmint iArg,ExprType_t type) const2065 bool InstrumentScriptVMFunction_fade_in::acceptsArgType(vmint iArg, ExprType_t type) const { 2066 if (iArg == 0) 2067 return type == INT_EXPR || type == INT_ARR_EXPR; 2068 else 2069 return type == INT_EXPR || type == REAL_EXPR; 2070 } 2071 acceptsArgUnitType(vmint iArg,StdUnit_t type) const2072 bool InstrumentScriptVMFunction_fade_in::acceptsArgUnitType(vmint iArg, StdUnit_t type) const { 2073 if (iArg == 1) 2074 return type == VM_NO_UNIT || type == VM_SECOND; 2075 else 2076 return type == VM_NO_UNIT; 2077 } 2078 acceptsArgUnitPrefix(vmint iArg,StdUnit_t type) const2079 bool InstrumentScriptVMFunction_fade_in::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const { 2080 return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type 2081 } 2082 exec(VMFnArgs * args)2083 VMFnResult* InstrumentScriptVMFunction_fade_in::exec(VMFnArgs* args) { 2084 StdUnit_t unit = args->arg(1)->asNumber()->unitType(); 2085 vmint duration = 2086 (unit) ? 2087 args->arg(1)->asNumber()->evalCastInt(VM_MICRO) : 2088 args->arg(1)->asNumber()->evalCastInt(); 2089 if (duration < 0) { 2090 wrnMsg("fade_in(): argument 2 may not be negative"); 2091 duration = 0; 2092 } 2093 const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds 2094 2095 AbstractEngineChannel* pEngineChannel = 2096 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); 2097 2098 if (args->arg(0)->exprType() == INT_EXPR) { 2099 const ScriptID id = args->arg(0)->asInt()->evalInt(); 2100 if (!id) { 2101 wrnMsg("fade_in(): note ID for argument 1 may not be zero"); 2102 return successResult(); 2103 } 2104 if (!id.isNoteID()) { 2105 wrnMsg("fade_in(): argument 1 is not a note ID"); 2106 return successResult(); 2107 } 2108 2109 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); 2110 if (!pNote) return successResult(); 2111 2112 // if fade_in() was called immediately after note was triggered 2113 // then immediately apply a start volume of zero to Note object, 2114 // as well as the fade in duration 2115 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { 2116 pNote->Override.Volume.Value = 0.f; 2117 pNote->Override.VolumeTime = fDuration; 2118 } else { // otherwise schedule a "volume time" change with the requested fade in duration ... 2119 Event e = m_vm->m_event->cause; // copy to get fragment time for "now" 2120 e.Init(); // clear IDs 2121 e.Type = Event::type_note_synth_param; 2122 e.Param.NoteSynthParam.NoteID = id.noteID(); 2123 e.Param.NoteSynthParam.Type = Event::synth_param_volume_time; 2124 e.Param.NoteSynthParam.Delta = fDuration; 2125 e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored 2126 2127 pEngineChannel->ScheduleEventMicroSec(&e, 0); 2128 } 2129 // and finally schedule a "volume" change, simply one time slice 2130 // ahead, with the final fade in volume (1.0) 2131 { 2132 Event e = m_vm->m_event->cause; // copy to get fragment time for "now" 2133 e.Init(); // clear IDs 2134 e.Type = Event::type_note_synth_param; 2135 e.Param.NoteSynthParam.NoteID = id.noteID(); 2136 e.Param.NoteSynthParam.Type = Event::synth_param_volume; 2137 e.Param.NoteSynthParam.Delta = 1.f; 2138 e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored 2139 2140 // scheduling with 0 delay would also work here, but +1 is more 2141 // safe regarding potential future implementation changes of the 2142 // scheduler (see API comments of RTAVLTree::insert()) 2143 pEngineChannel->ScheduleEventMicroSec(&e, 1); 2144 } 2145 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { 2146 VMIntArrayExpr* ids = args->arg(0)->asIntArray(); 2147 for (vmint i = 0; i < ids->arraySize(); ++i) { 2148 const ScriptID id = ids->evalIntElement(i); 2149 if (!id || !id.isNoteID()) continue; 2150 2151 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); 2152 if (!pNote) continue; 2153 2154 // if fade_in() was called immediately after note was triggered 2155 // then immediately apply a start volume of zero to Note object, 2156 // as well as the fade in duration 2157 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { 2158 pNote->Override.Volume.Value = 0.f; 2159 pNote->Override.VolumeTime = fDuration; 2160 } else { // otherwise schedule a "volume time" change with the requested fade in duration ... 2161 Event e = m_vm->m_event->cause; // copy to get fragment time for "now" 2162 e.Init(); // clear IDs 2163 e.Type = Event::type_note_synth_param; 2164 e.Param.NoteSynthParam.NoteID = id.noteID(); 2165 e.Param.NoteSynthParam.Type = Event::synth_param_volume_time; 2166 e.Param.NoteSynthParam.Delta = fDuration; 2167 e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored 2168 2169 pEngineChannel->ScheduleEventMicroSec(&e, 0); 2170 } 2171 // and finally schedule a "volume" change, simply one time slice 2172 // ahead, with the final fade in volume (1.0) 2173 { 2174 Event e = m_vm->m_event->cause; // copy to get fragment time for "now" 2175 e.Init(); // clear IDs 2176 e.Type = Event::type_note_synth_param; 2177 e.Param.NoteSynthParam.NoteID = id.noteID(); 2178 e.Param.NoteSynthParam.Type = Event::synth_param_volume; 2179 e.Param.NoteSynthParam.Delta = 1.f; 2180 e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored 2181 2182 // scheduling with 0 delay would also work here, but +1 is more 2183 // safe regarding potential future implementation changes of the 2184 // scheduler (see API comments of RTAVLTree::insert()) 2185 pEngineChannel->ScheduleEventMicroSec(&e, 1); 2186 } 2187 } 2188 } 2189 2190 return successResult(); 2191 } 2192 2193 // fade_out() function 2194 InstrumentScriptVMFunction_fade_out(InstrumentScriptVM * parent)2195 InstrumentScriptVMFunction_fade_out::InstrumentScriptVMFunction_fade_out(InstrumentScriptVM* parent) 2196 : m_vm(parent) 2197 { 2198 } 2199 acceptsArgType(vmint iArg,ExprType_t type) const2200 bool InstrumentScriptVMFunction_fade_out::acceptsArgType(vmint iArg, ExprType_t type) const { 2201 if (iArg == 0) 2202 return type == INT_EXPR || type == INT_ARR_EXPR; 2203 else 2204 return type == INT_EXPR || type == REAL_EXPR; 2205 } 2206 acceptsArgUnitType(vmint iArg,StdUnit_t type) const2207 bool InstrumentScriptVMFunction_fade_out::acceptsArgUnitType(vmint iArg, StdUnit_t type) const { 2208 if (iArg == 1) 2209 return type == VM_NO_UNIT || type == VM_SECOND; 2210 else 2211 return type == VM_NO_UNIT; 2212 } 2213 acceptsArgUnitPrefix(vmint iArg,StdUnit_t type) const2214 bool InstrumentScriptVMFunction_fade_out::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const { 2215 return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type 2216 } 2217 exec(VMFnArgs * args)2218 VMFnResult* InstrumentScriptVMFunction_fade_out::exec(VMFnArgs* args) { 2219 StdUnit_t unit = args->arg(1)->asNumber()->unitType(); 2220 vmint duration = 2221 (unit) ? 2222 args->arg(1)->asNumber()->evalCastInt(VM_MICRO) : 2223 args->arg(1)->asNumber()->evalCastInt(); 2224 if (duration < 0) { 2225 wrnMsg("fade_out(): argument 2 may not be negative"); 2226 duration = 0; 2227 } 2228 const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds 2229 2230 bool stop = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : true; 2231 2232 AbstractEngineChannel* pEngineChannel = 2233 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); 2234 2235 if (args->arg(0)->exprType() == INT_EXPR) { 2236 const ScriptID id = args->arg(0)->asInt()->evalInt(); 2237 if (!id) { 2238 wrnMsg("fade_out(): note ID for argument 1 may not be zero"); 2239 return successResult(); 2240 } 2241 if (!id.isNoteID()) { 2242 wrnMsg("fade_out(): argument 1 is not a note ID"); 2243 return successResult(); 2244 } 2245 2246 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); 2247 if (!pNote) return successResult(); 2248 2249 // if fade_out() was called immediately after note was triggered 2250 // then immediately apply fade out duration to Note object 2251 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { 2252 pNote->Override.VolumeTime = fDuration; 2253 } else { // otherwise schedule a "volume time" change with the requested fade out duration ... 2254 Event e = m_vm->m_event->cause; // copy to get fragment time for "now" 2255 e.Init(); // clear IDs 2256 e.Type = Event::type_note_synth_param; 2257 e.Param.NoteSynthParam.NoteID = id.noteID(); 2258 e.Param.NoteSynthParam.Type = Event::synth_param_volume_time; 2259 e.Param.NoteSynthParam.Delta = fDuration; 2260 e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored 2261 2262 pEngineChannel->ScheduleEventMicroSec(&e, 0); 2263 } 2264 // now schedule a "volume" change, simply one time slice ahead, with 2265 // the final fade out volume (0.0) 2266 { 2267 Event e = m_vm->m_event->cause; // copy to get fragment time for "now" 2268 e.Init(); // clear IDs 2269 e.Type = Event::type_note_synth_param; 2270 e.Param.NoteSynthParam.NoteID = id.noteID(); 2271 e.Param.NoteSynthParam.Type = Event::synth_param_volume; 2272 e.Param.NoteSynthParam.Delta = 0.f; 2273 e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored 2274 2275 // scheduling with 0 delay would also work here, but +1 is more 2276 // safe regarding potential future implementation changes of the 2277 // scheduler (see API comments of RTAVLTree::insert()) 2278 pEngineChannel->ScheduleEventMicroSec(&e, 1); 2279 } 2280 // and finally if stopping the note was requested after the fade out 2281 // completed, then schedule to kill the voice after the requested 2282 // duration 2283 if (stop) { 2284 Event e = m_vm->m_event->cause; // copy to get fragment time for "now" 2285 e.Init(); // clear IDs 2286 e.Type = Event::type_kill_note; 2287 e.Param.Note.ID = id.noteID(); 2288 e.Param.Note.Key = pNote->hostKey; 2289 2290 pEngineChannel->ScheduleEventMicroSec(&e, duration + 1); 2291 } 2292 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { 2293 VMIntArrayExpr* ids = args->arg(0)->asIntArray(); 2294 for (vmint i = 0; i < ids->arraySize(); ++i) { 2295 const ScriptID id = ids->evalIntElement(i); 2296 if (!id || !id.isNoteID()) continue; 2297 2298 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); 2299 if (!pNote) continue; 2300 2301 // if fade_out() was called immediately after note was triggered 2302 // then immediately apply fade out duration to Note object 2303 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { 2304 pNote->Override.VolumeTime = fDuration; 2305 } else { // otherwise schedule a "volume time" change with the requested fade out duration ... 2306 Event e = m_vm->m_event->cause; // copy to get fragment time for "now" 2307 e.Init(); // clear IDs 2308 e.Type = Event::type_note_synth_param; 2309 e.Param.NoteSynthParam.NoteID = id.noteID(); 2310 e.Param.NoteSynthParam.Type = Event::synth_param_volume_time; 2311 e.Param.NoteSynthParam.Delta = fDuration; 2312 e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored 2313 2314 pEngineChannel->ScheduleEventMicroSec(&e, 0); 2315 } 2316 // now schedule a "volume" change, simply one time slice ahead, with 2317 // the final fade out volume (0.0) 2318 { 2319 Event e = m_vm->m_event->cause; // copy to get fragment time for "now" 2320 e.Init(); // clear IDs 2321 e.Type = Event::type_note_synth_param; 2322 e.Param.NoteSynthParam.NoteID = id.noteID(); 2323 e.Param.NoteSynthParam.Type = Event::synth_param_volume; 2324 e.Param.NoteSynthParam.Delta = 0.f; 2325 e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored 2326 2327 // scheduling with 0 delay would also work here, but +1 is more 2328 // safe regarding potential future implementation changes of the 2329 // scheduler (see API comments of RTAVLTree::insert()) 2330 pEngineChannel->ScheduleEventMicroSec(&e, 1); 2331 } 2332 // and finally if stopping the note was requested after the fade out 2333 // completed, then schedule to kill the voice after the requested 2334 // duration 2335 if (stop) { 2336 Event e = m_vm->m_event->cause; // copy to get fragment time for "now" 2337 e.Init(); // clear IDs 2338 e.Type = Event::type_kill_note; 2339 e.Param.Note.ID = id.noteID(); 2340 e.Param.Note.Key = pNote->hostKey; 2341 2342 pEngineChannel->ScheduleEventMicroSec(&e, duration + 1); 2343 } 2344 } 2345 } 2346 2347 return successResult(); 2348 } 2349 2350 // get_event_par() function 2351 InstrumentScriptVMFunction_get_event_par(InstrumentScriptVM * parent)2352 InstrumentScriptVMFunction_get_event_par::InstrumentScriptVMFunction_get_event_par(InstrumentScriptVM* parent) 2353 : m_vm(parent) 2354 { 2355 } 2356 exec(VMFnArgs * args)2357 VMFnResult* InstrumentScriptVMFunction_get_event_par::exec(VMFnArgs* args) { 2358 AbstractEngineChannel* pEngineChannel = 2359 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); 2360 2361 const ScriptID id = args->arg(0)->asInt()->evalInt(); 2362 if (!id) { 2363 wrnMsg("get_event_par(): note ID for argument 1 may not be zero"); 2364 return successResult(0); 2365 } 2366 if (!id.isNoteID()) { 2367 wrnMsg("get_event_par(): argument 1 is not a note ID"); 2368 return successResult(0); 2369 } 2370 2371 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); 2372 if (!pNote) { 2373 wrnMsg("get_event_par(): no note alive with that note ID of argument 1"); 2374 return successResult(0); 2375 } 2376 2377 const vmint parameter = args->arg(1)->asInt()->evalInt(); 2378 switch (parameter) { 2379 case EVENT_PAR_NOTE: 2380 return successResult(pNote->cause.Param.Note.Key); 2381 case EVENT_PAR_VELOCITY: 2382 return successResult(pNote->cause.Param.Note.Velocity); 2383 case EVENT_PAR_VOLUME: 2384 return successResult( 2385 RTMath::LinRatioToDecibel(pNote->Override.Volume.Value) * 1000.f 2386 ); 2387 case EVENT_PAR_TUNE: 2388 return successResult( 2389 RTMath::FreqRatioToCents(pNote->Override.Pitch.Value) * 1000.f 2390 ); 2391 case EVENT_PAR_0: 2392 return successResult(pNote->userPar[0]); 2393 case EVENT_PAR_1: 2394 return successResult(pNote->userPar[1]); 2395 case EVENT_PAR_2: 2396 return successResult(pNote->userPar[2]); 2397 case EVENT_PAR_3: 2398 return successResult(pNote->userPar[3]); 2399 } 2400 2401 wrnMsg("get_event_par(): argument 2 is an invalid event parameter"); 2402 return successResult(0); 2403 } 2404 2405 // set_event_par() function 2406 InstrumentScriptVMFunction_set_event_par(InstrumentScriptVM * parent)2407 InstrumentScriptVMFunction_set_event_par::InstrumentScriptVMFunction_set_event_par(InstrumentScriptVM* parent) 2408 : m_vm(parent) 2409 { 2410 } 2411 exec(VMFnArgs * args)2412 VMFnResult* InstrumentScriptVMFunction_set_event_par::exec(VMFnArgs* args) { 2413 AbstractEngineChannel* pEngineChannel = 2414 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); 2415 2416 const ScriptID id = args->arg(0)->asInt()->evalInt(); 2417 if (!id) { 2418 wrnMsg("set_event_par(): note ID for argument 1 may not be zero"); 2419 return successResult(); 2420 } 2421 if (!id.isNoteID()) { 2422 wrnMsg("set_event_par(): argument 1 is not a note ID"); 2423 return successResult(); 2424 } 2425 2426 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); 2427 if (!pNote) return successResult(); 2428 2429 const vmint parameter = args->arg(1)->asInt()->evalInt(); 2430 const vmint value = args->arg(2)->asInt()->evalInt(); 2431 2432 switch (parameter) { 2433 case EVENT_PAR_NOTE: 2434 if (value < 0 || value > 127) { 2435 wrnMsg("set_event_par(): note number of argument 3 is out of range"); 2436 return successResult(); 2437 } 2438 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { 2439 pNote->cause.Param.Note.Key = value; 2440 m_vm->m_event->cause.Param.Note.Key = value; 2441 } else { 2442 wrnMsg("set_event_par(): note number can only be changed when note is new"); 2443 } 2444 return successResult(); 2445 case EVENT_PAR_VELOCITY: 2446 if (value < 0 || value > 127) { 2447 wrnMsg("set_event_par(): velocity of argument 3 is out of range"); 2448 return successResult(); 2449 } 2450 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { 2451 pNote->cause.Param.Note.Velocity = value; 2452 m_vm->m_event->cause.Param.Note.Velocity = value; 2453 } else { 2454 wrnMsg("set_event_par(): velocity can only be changed when note is new"); 2455 } 2456 return successResult(); 2457 case EVENT_PAR_VOLUME: 2458 wrnMsg("set_event_par(): changing volume by this function is currently not supported, use change_vol() instead"); 2459 return successResult(); 2460 case EVENT_PAR_TUNE: 2461 wrnMsg("set_event_par(): changing tune by this function is currently not supported, use change_tune() instead"); 2462 return successResult(); 2463 case EVENT_PAR_0: 2464 pNote->userPar[0] = value; 2465 return successResult(); 2466 case EVENT_PAR_1: 2467 pNote->userPar[1] = value; 2468 return successResult(); 2469 case EVENT_PAR_2: 2470 pNote->userPar[2] = value; 2471 return successResult(); 2472 case EVENT_PAR_3: 2473 pNote->userPar[3] = value; 2474 return successResult(); 2475 } 2476 2477 wrnMsg("set_event_par(): argument 2 is an invalid event parameter"); 2478 return successResult(); 2479 } 2480 2481 // change_note() function 2482 InstrumentScriptVMFunction_change_note(InstrumentScriptVM * parent)2483 InstrumentScriptVMFunction_change_note::InstrumentScriptVMFunction_change_note(InstrumentScriptVM* parent) 2484 : m_vm(parent) 2485 { 2486 } 2487 exec(VMFnArgs * args)2488 VMFnResult* InstrumentScriptVMFunction_change_note::exec(VMFnArgs* args) { 2489 AbstractEngineChannel* pEngineChannel = 2490 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); 2491 2492 const ScriptID id = args->arg(0)->asInt()->evalInt(); 2493 if (!id) { 2494 wrnMsg("change_note(): note ID for argument 1 may not be zero"); 2495 return successResult(); 2496 } 2497 if (!id.isNoteID()) { 2498 wrnMsg("change_note(): argument 1 is not a note ID"); 2499 return successResult(); 2500 } 2501 2502 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); 2503 if (!pNote) return successResult(); 2504 2505 const vmint value = args->arg(1)->asInt()->evalInt(); 2506 if (value < 0 || value > 127) { 2507 wrnMsg("change_note(): note number of argument 2 is out of range"); 2508 return successResult(); 2509 } 2510 2511 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { 2512 pNote->cause.Param.Note.Key = value; 2513 m_vm->m_event->cause.Param.Note.Key = value; 2514 } else { 2515 wrnMsg("change_note(): note number can only be changed when note is new"); 2516 } 2517 2518 return successResult(); 2519 } 2520 2521 // change_velo() function 2522 InstrumentScriptVMFunction_change_velo(InstrumentScriptVM * parent)2523 InstrumentScriptVMFunction_change_velo::InstrumentScriptVMFunction_change_velo(InstrumentScriptVM* parent) 2524 : m_vm(parent) 2525 { 2526 } 2527 exec(VMFnArgs * args)2528 VMFnResult* InstrumentScriptVMFunction_change_velo::exec(VMFnArgs* args) { 2529 AbstractEngineChannel* pEngineChannel = 2530 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); 2531 2532 const ScriptID id = args->arg(0)->asInt()->evalInt(); 2533 if (!id) { 2534 wrnMsg("change_velo(): note ID for argument 1 may not be zero"); 2535 return successResult(); 2536 } 2537 if (!id.isNoteID()) { 2538 wrnMsg("change_velo(): argument 1 is not a note ID"); 2539 return successResult(); 2540 } 2541 2542 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); 2543 if (!pNote) return successResult(); 2544 2545 const vmint value = args->arg(1)->asInt()->evalInt(); 2546 if (value < 0 || value > 127) { 2547 wrnMsg("change_velo(): velocity of argument 2 is out of range"); 2548 return successResult(); 2549 } 2550 2551 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) { 2552 pNote->cause.Param.Note.Velocity = value; 2553 m_vm->m_event->cause.Param.Note.Velocity = value; 2554 } else { 2555 wrnMsg("change_velo(): velocity can only be changed when note is new"); 2556 } 2557 2558 return successResult(); 2559 } 2560 2561 // change_play_pos() function 2562 InstrumentScriptVMFunction_change_play_pos(InstrumentScriptVM * parent)2563 InstrumentScriptVMFunction_change_play_pos::InstrumentScriptVMFunction_change_play_pos(InstrumentScriptVM* parent) 2564 : m_vm(parent) 2565 { 2566 } 2567 acceptsArgType(vmint iArg,ExprType_t type) const2568 bool InstrumentScriptVMFunction_change_play_pos::acceptsArgType(vmint iArg, ExprType_t type) const { 2569 if (iArg == 0) 2570 return type == INT_EXPR; 2571 else 2572 return type == INT_EXPR || type == REAL_EXPR; 2573 } 2574 acceptsArgUnitType(vmint iArg,StdUnit_t type) const2575 bool InstrumentScriptVMFunction_change_play_pos::acceptsArgUnitType(vmint iArg, StdUnit_t type) const { 2576 if (iArg == 1) 2577 return type == VM_NO_UNIT || type == VM_SECOND; 2578 else 2579 return type == VM_NO_UNIT; 2580 } 2581 acceptsArgUnitPrefix(vmint iArg,StdUnit_t type) const2582 bool InstrumentScriptVMFunction_change_play_pos::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const { 2583 return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type 2584 } 2585 exec(VMFnArgs * args)2586 VMFnResult* InstrumentScriptVMFunction_change_play_pos::exec(VMFnArgs* args) { 2587 const ScriptID id = args->arg(0)->asInt()->evalInt(); 2588 if (!id) { 2589 wrnMsg("change_play_pos(): note ID for argument 1 may not be zero"); 2590 return successResult(); 2591 } 2592 if (!id.isNoteID()) { 2593 wrnMsg("change_play_pos(): argument 1 is not a note ID"); 2594 return successResult(); 2595 } 2596 2597 StdUnit_t unit = args->arg(1)->asNumber()->unitType(); 2598 const vmint pos = 2599 (unit) ? 2600 args->arg(1)->asNumber()->evalCastInt(VM_MICRO) : 2601 args->arg(1)->asNumber()->evalCastInt(); 2602 if (pos < 0) { 2603 wrnMsg("change_play_pos(): playback position of argument 2 may not be negative"); 2604 return successResult(); 2605 } 2606 2607 AbstractEngineChannel* pEngineChannel = 2608 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); 2609 2610 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); 2611 if (!pNote) return successResult(); 2612 2613 pNote->Override.SampleOffset = 2614 (decltype(pNote->Override.SampleOffset)) pos; 2615 2616 return successResult(); 2617 } 2618 2619 // event_status() function 2620 InstrumentScriptVMFunction_event_status(InstrumentScriptVM * parent)2621 InstrumentScriptVMFunction_event_status::InstrumentScriptVMFunction_event_status(InstrumentScriptVM* parent) 2622 : m_vm(parent) 2623 { 2624 } 2625 exec(VMFnArgs * args)2626 VMFnResult* InstrumentScriptVMFunction_event_status::exec(VMFnArgs* args) { 2627 AbstractEngineChannel* pEngineChannel = 2628 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); 2629 2630 const ScriptID id = args->arg(0)->asInt()->evalInt(); 2631 if (!id) { 2632 wrnMsg("event_status(): note ID for argument 1 may not be zero"); 2633 return successResult(EVENT_STATUS_INACTIVE); 2634 } 2635 if (!id.isNoteID()) { 2636 wrnMsg("event_status(): argument 1 is not a note ID"); 2637 return successResult(EVENT_STATUS_INACTIVE); 2638 } 2639 2640 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); 2641 return successResult(pNote ? EVENT_STATUS_NOTE_QUEUE : EVENT_STATUS_INACTIVE); 2642 } 2643 2644 // callback_status() function 2645 InstrumentScriptVMFunction_callback_status(InstrumentScriptVM * parent)2646 InstrumentScriptVMFunction_callback_status::InstrumentScriptVMFunction_callback_status(InstrumentScriptVM* parent) 2647 : m_vm(parent) 2648 { 2649 } 2650 exec(VMFnArgs * args)2651 VMFnResult* InstrumentScriptVMFunction_callback_status::exec(VMFnArgs* args) { 2652 const script_callback_id_t id = args->arg(0)->asInt()->evalInt(); 2653 if (!id) { 2654 wrnMsg("callback_status(): callback ID for argument 1 may not be zero"); 2655 return successResult(); 2656 } 2657 2658 AbstractEngineChannel* pEngineChannel = 2659 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); 2660 2661 RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id); 2662 if (!itCallback) 2663 return successResult(CALLBACK_STATUS_TERMINATED); 2664 2665 return successResult( 2666 (m_vm->m_event->execCtx == itCallback->execCtx) ? 2667 CALLBACK_STATUS_RUNNING : CALLBACK_STATUS_QUEUE 2668 ); 2669 } 2670 2671 // wait() function (overrides core wait() implementation) 2672 InstrumentScriptVMFunction_wait(InstrumentScriptVM * parent)2673 InstrumentScriptVMFunction_wait::InstrumentScriptVMFunction_wait(InstrumentScriptVM* parent) 2674 : CoreVMFunction_wait(parent) 2675 { 2676 } 2677 exec(VMFnArgs * args)2678 VMFnResult* InstrumentScriptVMFunction_wait::exec(VMFnArgs* args) { 2679 InstrumentScriptVM* m_vm = (InstrumentScriptVM*) vm; 2680 2681 // this might be set by passing 1 with the 2nd argument of built-in stop_wait() function 2682 if (m_vm->m_event->ignoreAllWaitCalls) return successResult(); 2683 2684 return CoreVMFunction_wait::exec(args); 2685 } 2686 2687 // stop_wait() function 2688 InstrumentScriptVMFunction_stop_wait(InstrumentScriptVM * parent)2689 InstrumentScriptVMFunction_stop_wait::InstrumentScriptVMFunction_stop_wait(InstrumentScriptVM* parent) 2690 : m_vm(parent) 2691 { 2692 } 2693 exec(VMFnArgs * args)2694 VMFnResult* InstrumentScriptVMFunction_stop_wait::exec(VMFnArgs* args) { 2695 AbstractEngineChannel* pEngineChannel = 2696 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); 2697 2698 const script_callback_id_t id = args->arg(0)->asInt()->evalInt(); 2699 if (!id) { 2700 wrnMsg("stop_wait(): callback ID for argument 1 may not be zero"); 2701 return successResult(); 2702 } 2703 2704 RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id); 2705 if (!itCallback) return successResult(); // ignore if callback is i.e. not alive anymore 2706 2707 const bool disableWaitForever = 2708 (args->argsCount() >= 2) ? (args->arg(1)->asInt()->evalInt() == 1) : false; 2709 2710 pEngineChannel->ScheduleResumeOfScriptCallback( 2711 itCallback, m_vm->m_event->scheduleTime, disableWaitForever 2712 ); 2713 2714 return successResult(); 2715 } 2716 2717 // abort() function 2718 InstrumentScriptVMFunction_abort(InstrumentScriptVM * parent)2719 InstrumentScriptVMFunction_abort::InstrumentScriptVMFunction_abort(InstrumentScriptVM* parent) 2720 : m_vm(parent) 2721 { 2722 } 2723 exec(VMFnArgs * args)2724 VMFnResult* InstrumentScriptVMFunction_abort::exec(VMFnArgs* args) { 2725 const script_callback_id_t id = args->arg(0)->asInt()->evalInt(); 2726 if (!id) { 2727 wrnMsg("abort(): callback ID for argument 1 may not be zero"); 2728 return successResult(); 2729 } 2730 2731 AbstractEngineChannel* pEngineChannel = 2732 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); 2733 2734 RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id); 2735 if (!itCallback) return successResult(); // ignore if callback is i.e. not alive anymore 2736 2737 itCallback->execCtx->signalAbort(); 2738 2739 return successResult(); 2740 } 2741 2742 // fork() function 2743 InstrumentScriptVMFunction_fork(InstrumentScriptVM * parent)2744 InstrumentScriptVMFunction_fork::InstrumentScriptVMFunction_fork(InstrumentScriptVM* parent) 2745 : m_vm(parent) 2746 { 2747 } 2748 exec(VMFnArgs * args)2749 VMFnResult* InstrumentScriptVMFunction_fork::exec(VMFnArgs* args) { 2750 // check if this is actually the parent going to fork, or rather one of 2751 // the children which is already forked 2752 if (m_vm->m_event->forkIndex != 0) { // this is the entry point for a child ... 2753 int forkResult = m_vm->m_event->forkIndex; 2754 // reset so that this child may i.e. also call fork() later on 2755 m_vm->m_event->forkIndex = 0; 2756 return successResult(forkResult); 2757 } 2758 2759 // if we are here, then this is the parent, so we must fork this parent 2760 2761 const vmint n = 2762 (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : 1; 2763 const bool bAutoAbort = 2764 (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : true; 2765 2766 if (m_vm->m_event->countChildHandlers() + n > MAX_FORK_PER_SCRIPT_HANDLER) { 2767 wrnMsg("fork(): requested amount would exceed allowed limit per event handler"); 2768 return successResult(-1); 2769 } 2770 2771 AbstractEngineChannel* pEngineChannel = 2772 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); 2773 2774 if (!pEngineChannel->hasFreeScriptCallbacks(n)) { 2775 wrnMsg("fork(): global limit of event handlers exceeded"); 2776 return successResult(-1); 2777 } 2778 2779 for (int iChild = 0; iChild < n; ++iChild) { 2780 RTList<ScriptEvent>::Iterator itChild = 2781 pEngineChannel->forkScriptCallback(m_vm->m_event, bAutoAbort); 2782 if (!itChild) { // should never happen, otherwise its a bug ... 2783 errMsg("fork(): internal error while allocating child"); 2784 return errorResult(-1); // terminate script 2785 } 2786 // since both parent, as well all child script execution instances 2787 // all land in this exec() method, the following is (more or less) 2788 // the only feature that lets us distinguish the parent and 2789 // respective children from each other in this exec() method 2790 itChild->forkIndex = iChild + 1; 2791 } 2792 2793 return successResult(0); 2794 } 2795 2796 } // namespace LinuxSampler 2797