/[svn]/linuxsampler/trunk/src/engines/common/InstrumentScriptVMFunctions.cpp
ViewVC logotype

Contents of /linuxsampler/trunk/src/engines/common/InstrumentScriptVMFunctions.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3747 - (show annotations) (download)
Sun Feb 16 11:31:46 2020 UTC (4 years, 2 months ago) by schoenebeck
File size: 122339 byte(s)
* NKSP: Fixed re-entrant issue with function calls which caused wrong
  result values if the same function was called multiple times in a term
  (specifically if metric prefixes were used).

* Tests: Added NKSP core language test cases to guard this fixed issue.

* Bumped version (2.1.1.svn50).

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
19 InstrumentScriptVMFunction_play_note::InstrumentScriptVMFunction_play_note(InstrumentScriptVM* parent)
20 : m_vm(parent)
21 {
22 }
23
24 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
31 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
38 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
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
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
177 InstrumentScriptVMFunction_set_controller::InstrumentScriptVMFunction_set_controller(InstrumentScriptVM* parent)
178 : m_vm(parent)
179 {
180 }
181
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
216 InstrumentScriptVMFunction_set_rpn::InstrumentScriptVMFunction_set_rpn(InstrumentScriptVM* parent)
217 : m_vm(parent)
218 {
219 }
220
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
253 InstrumentScriptVMFunction_set_nrpn::InstrumentScriptVMFunction_set_nrpn(InstrumentScriptVM* parent)
254 : m_vm(parent)
255 {
256 }
257
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
290 InstrumentScriptVMFunction_ignore_event::InstrumentScriptVMFunction_ignore_event(InstrumentScriptVM* parent)
291 : m_vm(parent)
292 {
293 }
294
295 bool InstrumentScriptVMFunction_ignore_event::acceptsArgType(vmint iArg, ExprType_t type) const {
296 return type == INT_EXPR || type == INT_ARR_EXPR;
297 }
298
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
324 InstrumentScriptVMFunction_ignore_controller::InstrumentScriptVMFunction_ignore_controller(InstrumentScriptVM* parent)
325 : m_vm(parent)
326 {
327 }
328
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
346 InstrumentScriptVMFunction_note_off::InstrumentScriptVMFunction_note_off(InstrumentScriptVM* parent)
347 : m_vm(parent)
348 {
349 }
350
351 bool InstrumentScriptVMFunction_note_off::acceptsArgType(vmint iArg, ExprType_t type) const {
352 return type == INT_EXPR || type == INT_ARR_EXPR;
353 }
354
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
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
431 InstrumentScriptVMFunction_set_event_mark::InstrumentScriptVMFunction_set_event_mark(InstrumentScriptVM* parent)
432 : m_vm(parent)
433 {
434 }
435
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
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
486 InstrumentScriptVMFunction_delete_event_mark::InstrumentScriptVMFunction_delete_event_mark(InstrumentScriptVM* parent)
487 : m_vm(parent)
488 {
489 }
490
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
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
527 InstrumentScriptVMFunction_by_marks::InstrumentScriptVMFunction_by_marks(InstrumentScriptVM* parent)
528 : m_vm(parent), m_result(NULL)
529 {
530 }
531
532 vmint InstrumentScriptVMFunction_by_marks::Result::arraySize() const {
533 return eventGroup->size();
534 }
535
536 vmint InstrumentScriptVMFunction_by_marks::Result::evalIntElement(vmuint i) {
537 return (*eventGroup)[i];
538 }
539
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
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
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
569 VMFnResult* InstrumentScriptVMFunction_by_marks::allocResult(VMFnArgs* args) {
570 return new Result;
571 }
572
573 void InstrumentScriptVMFunction_by_marks::bindResult(VMFnResult* res) {
574 m_result = dynamic_cast<Result*>(res);
575 }
576
577 VMFnResult* InstrumentScriptVMFunction_by_marks::boundResult() const {
578 return m_result;
579 }
580
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
597 InstrumentScriptVMFunction_change_vol::InstrumentScriptVMFunction_change_vol(InstrumentScriptVM* parent)
598 : m_vm(parent)
599 {
600 }
601
602 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
611 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
618 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
622 bool InstrumentScriptVMFunction_change_vol::acceptsArgFinal(vmint iArg) const {
623 return iArg == 1;
624 }
625
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
716 InstrumentScriptVMFunction_change_tune::InstrumentScriptVMFunction_change_tune(InstrumentScriptVM* parent)
717 : m_vm(parent)
718 {
719 }
720
721 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
730 bool InstrumentScriptVMFunction_change_tune::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
731 return iArg == 1;
732 }
733
734 bool InstrumentScriptVMFunction_change_tune::acceptsArgFinal(vmint iArg) const {
735 return iArg == 1;
736 }
737
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
828 InstrumentScriptVMFunction_change_pan::InstrumentScriptVMFunction_change_pan(InstrumentScriptVM* parent)
829 : m_vm(parent)
830 {
831 }
832
833 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
840 bool InstrumentScriptVMFunction_change_pan::acceptsArgFinal(vmint iArg) const {
841 return iArg == 1;
842 }
843
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
940 InstrumentScriptVMFunction_change_cutoff::InstrumentScriptVMFunction_change_cutoff(InstrumentScriptVM* parent)
941 : m_vm(parent)
942 {
943 }
944
945 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
954 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
961 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
965 bool InstrumentScriptVMFunction_change_cutoff::acceptsArgFinal(vmint iArg) const {
966 return iArg == 1;
967 }
968
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
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
1075 InstrumentScriptVMFunction_change_reso::InstrumentScriptVMFunction_change_reso(InstrumentScriptVM* parent)
1076 : m_vm(parent)
1077 {
1078 }
1079
1080 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
1087 bool InstrumentScriptVMFunction_change_reso::acceptsArgFinal(vmint iArg) const {
1088 return iArg == 1;
1089 }
1090
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
1174 InstrumentScriptVMFunction_change_attack::InstrumentScriptVMFunction_change_attack(InstrumentScriptVM* parent)
1175 : m_vm(parent)
1176 {
1177 }
1178
1179 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
1186 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
1193 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
1197 bool InstrumentScriptVMFunction_change_attack::acceptsArgFinal(vmint iArg) const {
1198 return iArg == 1;
1199 }
1200
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
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
1310 InstrumentScriptVMFunction_change_decay::InstrumentScriptVMFunction_change_decay(InstrumentScriptVM* parent)
1311 : m_vm(parent)
1312 {
1313 }
1314
1315 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
1322 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
1329 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
1333 bool InstrumentScriptVMFunction_change_decay::acceptsArgFinal(vmint iArg) const {
1334 return iArg == 1;
1335 }
1336
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
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
1446 InstrumentScriptVMFunction_change_release::InstrumentScriptVMFunction_change_release(InstrumentScriptVM* parent)
1447 : m_vm(parent)
1448 {
1449 }
1450
1451 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
1458 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
1465 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
1469 bool InstrumentScriptVMFunction_change_release::acceptsArgFinal(vmint iArg) const {
1470 return iArg == 1;
1471 }
1472
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
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
1578 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
1585 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
1592 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
1596 bool VMChangeSynthParamFunction::acceptsArgFinal(vmint iArg) const {
1597 return (m_acceptFinal) ? (iArg == 1) : false;
1598 }
1599
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
1604 inline static void setNoteParamScopeBy_FinalUnit(NoteBase::Norm& param, const bool bFinal, const StdUnit_t unit) {
1605 param.Final = bFinal;
1606 }
1607
1608 inline static void setNoteParamScopeBy_FinalUnit(float& param, const bool bFinal, const StdUnit_t unit) {
1609 /* NOOP */
1610 }
1611
1612 template<class T>
1613 inline static void setNoteParamValue(T& param, vmfloat value) {
1614 param.Value = value;
1615 }
1616
1617 inline static void setNoteParamValue(float& param, vmfloat value) {
1618 param = value;
1619 }
1620
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>
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
1951 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>
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
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
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
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
2060 InstrumentScriptVMFunction_fade_in::InstrumentScriptVMFunction_fade_in(InstrumentScriptVM* parent)
2061 : m_vm(parent)
2062 {
2063 }
2064
2065 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
2072 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
2079 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
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
2195 InstrumentScriptVMFunction_fade_out::InstrumentScriptVMFunction_fade_out(InstrumentScriptVM* parent)
2196 : m_vm(parent)
2197 {
2198 }
2199
2200 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
2207 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
2214 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
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
2352 InstrumentScriptVMFunction_get_event_par::InstrumentScriptVMFunction_get_event_par(InstrumentScriptVM* parent)
2353 : m_vm(parent)
2354 {
2355 }
2356
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
2407 InstrumentScriptVMFunction_set_event_par::InstrumentScriptVMFunction_set_event_par(InstrumentScriptVM* parent)
2408 : m_vm(parent)
2409 {
2410 }
2411
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
2483 InstrumentScriptVMFunction_change_note::InstrumentScriptVMFunction_change_note(InstrumentScriptVM* parent)
2484 : m_vm(parent)
2485 {
2486 }
2487
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
2523 InstrumentScriptVMFunction_change_velo::InstrumentScriptVMFunction_change_velo(InstrumentScriptVM* parent)
2524 : m_vm(parent)
2525 {
2526 }
2527
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
2563 InstrumentScriptVMFunction_change_play_pos::InstrumentScriptVMFunction_change_play_pos(InstrumentScriptVM* parent)
2564 : m_vm(parent)
2565 {
2566 }
2567
2568 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
2575 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
2582 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
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
2621 InstrumentScriptVMFunction_event_status::InstrumentScriptVMFunction_event_status(InstrumentScriptVM* parent)
2622 : m_vm(parent)
2623 {
2624 }
2625
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
2646 InstrumentScriptVMFunction_callback_status::InstrumentScriptVMFunction_callback_status(InstrumentScriptVM* parent)
2647 : m_vm(parent)
2648 {
2649 }
2650
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
2673 InstrumentScriptVMFunction_wait::InstrumentScriptVMFunction_wait(InstrumentScriptVM* parent)
2674 : CoreVMFunction_wait(parent)
2675 {
2676 }
2677
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
2689 InstrumentScriptVMFunction_stop_wait::InstrumentScriptVMFunction_stop_wait(InstrumentScriptVM* parent)
2690 : m_vm(parent)
2691 {
2692 }
2693
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
2719 InstrumentScriptVMFunction_abort::InstrumentScriptVMFunction_abort(InstrumentScriptVM* parent)
2720 : m_vm(parent)
2721 {
2722 }
2723
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
2744 InstrumentScriptVMFunction_fork::InstrumentScriptVMFunction_fork(InstrumentScriptVM* parent)
2745 : m_vm(parent)
2746 {
2747 }
2748
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

  ViewVC Help
Powered by ViewVC