/[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 3743 - (show annotations) (download)
Fri Feb 14 15:35:53 2020 UTC (3 days, 17 hours ago) by schoenebeck
File size: 121967 byte(s)
- Just added some TODO comments.

- Minor white space cleanup.

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)
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::exec(VMFnArgs* args) {
570 vmint groupID = args->arg(0)->asInt()->evalInt();
571
572 if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
573 errMsg("by_marks(): argument is an invalid group id");
574 return errorResult();
575 }
576
577 AbstractEngineChannel* pEngineChannel =
578 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
579
580 return successResult( &pEngineChannel->pScript->eventGroups[groupID] );
581 }
582
583 // change_vol() function
584
585 InstrumentScriptVMFunction_change_vol::InstrumentScriptVMFunction_change_vol(InstrumentScriptVM* parent)
586 : m_vm(parent)
587 {
588 }
589
590 bool InstrumentScriptVMFunction_change_vol::acceptsArgType(vmint iArg, ExprType_t type) const {
591 if (iArg == 0)
592 return type == INT_EXPR || type == INT_ARR_EXPR;
593 else if (iArg == 1)
594 return type == INT_EXPR || type == REAL_EXPR;
595 else
596 return type == INT_EXPR;
597 }
598
599 bool InstrumentScriptVMFunction_change_vol::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
600 if (iArg == 1)
601 return type == VM_NO_UNIT || type == VM_BEL;
602 else
603 return type == VM_NO_UNIT;
604 }
605
606 bool InstrumentScriptVMFunction_change_vol::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
607 return iArg == 1 && type == VM_BEL; // only allow metric prefix(es) if 'Bel' is used as unit type
608 }
609
610 bool InstrumentScriptVMFunction_change_vol::acceptsArgFinal(vmint iArg) const {
611 return iArg == 1;
612 }
613
614 VMFnResult* InstrumentScriptVMFunction_change_vol::exec(VMFnArgs* args) {
615 StdUnit_t unit = args->arg(1)->asNumber()->unitType();
616 vmint volume =
617 (unit) ?
618 args->arg(1)->asNumber()->evalCastInt(VM_MILLI,VM_DECI) :
619 args->arg(1)->asNumber()->evalCastInt(); // volume change in milli dB
620 bool isFinal = args->arg(1)->asNumber()->isFinal();
621 bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
622 const float fVolumeLin = RTMath::DecibelToLinRatio(float(volume) / 1000.f);
623
624 AbstractEngineChannel* pEngineChannel =
625 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
626
627 if (args->arg(0)->exprType() == INT_EXPR) {
628 const ScriptID id = args->arg(0)->asInt()->evalInt();
629 if (!id) {
630 wrnMsg("change_vol(): note ID for argument 1 may not be zero");
631 return successResult();
632 }
633 if (!id.isNoteID()) {
634 wrnMsg("change_vol(): argument 1 is not a note ID");
635 return successResult();
636 }
637
638 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
639 if (!pNote) return successResult();
640
641 // if change_vol() was called immediately after note was triggered
642 // then immediately apply the volume to note object, but only if
643 // change_vol_time() has not been called before
644 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
645 pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S)
646 {
647 if (relative)
648 pNote->Override.Volume.Value *= fVolumeLin;
649 else
650 pNote->Override.Volume.Value = fVolumeLin;
651 pNote->Override.Volume.Final = isFinal;
652 } else { // otherwise schedule the volume change ...
653 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
654 e.Init(); // clear IDs
655 e.Type = Event::type_note_synth_param;
656 e.Param.NoteSynthParam.NoteID = id.noteID();
657 e.Param.NoteSynthParam.Type = Event::synth_param_volume;
658 e.Param.NoteSynthParam.Delta = fVolumeLin;
659 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
660 isFinal, relative, unit
661 );
662 pEngineChannel->ScheduleEventMicroSec(&e, 0);
663 }
664 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
665 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
666 for (vmint i = 0; i < ids->arraySize(); ++i) {
667 const ScriptID id = ids->evalIntElement(i);
668 if (!id || !id.isNoteID()) continue;
669
670 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
671 if (!pNote) continue;
672
673 // if change_vol() was called immediately after note was triggered
674 // then immediately apply the volume to Note object, but only if
675 // change_vol_time() has not been called before
676 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
677 pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S)
678 {
679 if (relative)
680 pNote->Override.Volume.Value *= fVolumeLin;
681 else
682 pNote->Override.Volume.Value = fVolumeLin;
683 pNote->Override.Volume.Final = isFinal;
684 } else { // otherwise schedule the volume change ...
685 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
686 e.Init(); // clear IDs
687 e.Type = Event::type_note_synth_param;
688 e.Param.NoteSynthParam.NoteID = id.noteID();
689 e.Param.NoteSynthParam.Type = Event::synth_param_volume;
690 e.Param.NoteSynthParam.Delta = fVolumeLin;
691 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
692 isFinal, relative, unit
693 );
694 pEngineChannel->ScheduleEventMicroSec(&e, 0);
695 }
696 }
697 }
698
699 return successResult();
700 }
701
702 // change_tune() function
703
704 InstrumentScriptVMFunction_change_tune::InstrumentScriptVMFunction_change_tune(InstrumentScriptVM* parent)
705 : m_vm(parent)
706 {
707 }
708
709 bool InstrumentScriptVMFunction_change_tune::acceptsArgType(vmint iArg, ExprType_t type) const {
710 if (iArg == 0)
711 return type == INT_EXPR || type == INT_ARR_EXPR;
712 else if (iArg == 1)
713 return type == INT_EXPR || type == REAL_EXPR;
714 else
715 return type == INT_EXPR;
716 }
717
718 bool InstrumentScriptVMFunction_change_tune::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
719 return iArg == 1;
720 }
721
722 bool InstrumentScriptVMFunction_change_tune::acceptsArgFinal(vmint iArg) const {
723 return iArg == 1;
724 }
725
726 VMFnResult* InstrumentScriptVMFunction_change_tune::exec(VMFnArgs* args) {
727 vmint tune =
728 (args->arg(1)->asNumber()->hasUnitFactorNow())
729 ? args->arg(1)->asNumber()->evalCastInt(VM_MILLI,VM_CENTI)
730 : args->arg(1)->asNumber()->evalCastInt(); // tuning change in milli cents
731 bool isFinal = args->arg(1)->asNumber()->isFinal();
732 StdUnit_t unit = args->arg(1)->asNumber()->unitType();
733 bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
734 const float fFreqRatio = RTMath::CentsToFreqRatioUnlimited(float(tune) / 1000.f);
735
736 AbstractEngineChannel* pEngineChannel =
737 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
738
739 if (args->arg(0)->exprType() == INT_EXPR) {
740 const ScriptID id = args->arg(0)->asInt()->evalInt();
741 if (!id) {
742 wrnMsg("change_tune(): note ID for argument 1 may not be zero");
743 return successResult();
744 }
745 if (!id.isNoteID()) {
746 wrnMsg("change_tune(): argument 1 is not a note ID");
747 return successResult();
748 }
749
750 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
751 if (!pNote) return successResult();
752
753 // if change_tune() was called immediately after note was triggered
754 // then immediately apply the tuning to Note object, but only if
755 // change_tune_time() has not been called before
756 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
757 pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S)
758 {
759 if (relative)
760 pNote->Override.Pitch.Value *= fFreqRatio;
761 else
762 pNote->Override.Pitch.Value = fFreqRatio;
763 pNote->Override.Pitch.Final = isFinal;
764 } else { // otherwise schedule tuning change ...
765 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
766 e.Init(); // clear IDs
767 e.Type = Event::type_note_synth_param;
768 e.Param.NoteSynthParam.NoteID = id.noteID();
769 e.Param.NoteSynthParam.Type = Event::synth_param_pitch;
770 e.Param.NoteSynthParam.Delta = fFreqRatio;
771 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
772 isFinal, relative, unit
773 );
774 pEngineChannel->ScheduleEventMicroSec(&e, 0);
775 }
776 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
777 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
778 for (vmint i = 0; i < ids->arraySize(); ++i) {
779 const ScriptID id = ids->evalIntElement(i);
780 if (!id || !id.isNoteID()) continue;
781
782 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
783 if (!pNote) continue;
784
785 // if change_tune() was called immediately after note was triggered
786 // then immediately apply the tuning to Note object, but only if
787 // change_tune_time() has not been called before
788 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
789 pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S)
790 {
791 if (relative)
792 pNote->Override.Pitch.Value *= fFreqRatio;
793 else
794 pNote->Override.Pitch.Value = fFreqRatio;
795 pNote->Override.Pitch.Final = isFinal;
796 } else { // otherwise schedule tuning change ...
797 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
798 e.Init(); // clear IDs
799 e.Type = Event::type_note_synth_param;
800 e.Param.NoteSynthParam.NoteID = id.noteID();
801 e.Param.NoteSynthParam.Type = Event::synth_param_pitch;
802 e.Param.NoteSynthParam.Delta = fFreqRatio;
803 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
804 isFinal, relative, unit
805 );
806 pEngineChannel->ScheduleEventMicroSec(&e, 0);
807 }
808 }
809 }
810
811 return successResult();
812 }
813
814 // change_pan() function
815
816 InstrumentScriptVMFunction_change_pan::InstrumentScriptVMFunction_change_pan(InstrumentScriptVM* parent)
817 : m_vm(parent)
818 {
819 }
820
821 bool InstrumentScriptVMFunction_change_pan::acceptsArgType(vmint iArg, ExprType_t type) const {
822 if (iArg == 0)
823 return type == INT_EXPR || type == INT_ARR_EXPR;
824 else
825 return type == INT_EXPR;
826 }
827
828 bool InstrumentScriptVMFunction_change_pan::acceptsArgFinal(vmint iArg) const {
829 return iArg == 1;
830 }
831
832 VMFnResult* InstrumentScriptVMFunction_change_pan::exec(VMFnArgs* args) {
833 vmint pan = args->arg(1)->asInt()->evalInt();
834 bool isFinal = args->arg(1)->asInt()->isFinal();
835 bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
836
837 if (pan > 1000) {
838 wrnMsg("change_pan(): argument 2 may not be larger than 1000");
839 pan = 1000;
840 } else if (pan < -1000) {
841 wrnMsg("change_pan(): argument 2 may not be smaller than -1000");
842 pan = -1000;
843 }
844 const float fPan = float(pan) / 1000.f;
845
846 AbstractEngineChannel* pEngineChannel =
847 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
848
849 if (args->arg(0)->exprType() == INT_EXPR) {
850 const ScriptID id = args->arg(0)->asInt()->evalInt();
851 if (!id) {
852 wrnMsg("change_pan(): note ID for argument 1 may not be zero");
853 return successResult();
854 }
855 if (!id.isNoteID()) {
856 wrnMsg("change_pan(): argument 1 is not a note ID");
857 return successResult();
858 }
859
860 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
861 if (!pNote) return successResult();
862
863 // if change_pan() was called immediately after note was triggered
864 // then immediately apply the panning to Note object
865 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
866 if (relative) {
867 pNote->Override.Pan.Value = RTMath::RelativeSummedAvg(pNote->Override.Pan.Value, fPan, ++pNote->Override.Pan.Sources);
868 } else {
869 pNote->Override.Pan.Value = fPan;
870 pNote->Override.Pan.Sources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set
871 }
872 pNote->Override.Pan.Final = isFinal;
873 } else { // otherwise schedule panning change ...
874 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
875 e.Init(); // clear IDs
876 e.Type = Event::type_note_synth_param;
877 e.Param.NoteSynthParam.NoteID = id.noteID();
878 e.Param.NoteSynthParam.Type = Event::synth_param_pan;
879 e.Param.NoteSynthParam.Delta = fPan;
880 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
881 isFinal, relative, false
882 );
883 pEngineChannel->ScheduleEventMicroSec(&e, 0);
884 }
885 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
886 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
887 for (vmint i = 0; i < ids->arraySize(); ++i) {
888 const ScriptID id = ids->evalIntElement(i);
889 if (!id || !id.isNoteID()) continue;
890
891 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
892 if (!pNote) continue;
893
894 // if change_pan() was called immediately after note was triggered
895 // then immediately apply the panning to Note object
896 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
897 if (relative) {
898 pNote->Override.Pan.Value = RTMath::RelativeSummedAvg(pNote->Override.Pan.Value, fPan, ++pNote->Override.Pan.Sources);
899 } else {
900 pNote->Override.Pan.Value = fPan;
901 pNote->Override.Pan.Sources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set
902 }
903 pNote->Override.Pan.Final = isFinal;
904 } else { // otherwise schedule panning change ...
905 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
906 e.Init(); // clear IDs
907 e.Type = Event::type_note_synth_param;
908 e.Param.NoteSynthParam.NoteID = id.noteID();
909 e.Param.NoteSynthParam.Type = Event::synth_param_pan;
910 e.Param.NoteSynthParam.Delta = fPan;
911 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
912 isFinal, relative, false
913 );
914 pEngineChannel->ScheduleEventMicroSec(&e, 0);
915 }
916 }
917 }
918
919 return successResult();
920 }
921
922 #define VM_FILTER_PAR_MAX_VALUE 1000000
923 #define VM_FILTER_PAR_MAX_HZ 30000
924 #define VM_EG_PAR_MAX_VALUE 1000000
925
926 // change_cutoff() function
927
928 InstrumentScriptVMFunction_change_cutoff::InstrumentScriptVMFunction_change_cutoff(InstrumentScriptVM* parent)
929 : m_vm(parent)
930 {
931 }
932
933 bool InstrumentScriptVMFunction_change_cutoff::acceptsArgType(vmint iArg, ExprType_t type) const {
934 if (iArg == 0)
935 return type == INT_EXPR || type == INT_ARR_EXPR;
936 else if (iArg == 1)
937 return type == INT_EXPR || type == REAL_EXPR;
938 else
939 return type == INT_EXPR;
940 }
941
942 bool InstrumentScriptVMFunction_change_cutoff::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
943 if (iArg == 1)
944 return type == VM_NO_UNIT || type == VM_HERTZ;
945 else
946 return type == VM_NO_UNIT;
947 }
948
949 bool InstrumentScriptVMFunction_change_cutoff::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
950 return iArg == 1 && type == VM_HERTZ; // only allow metric prefix(es) if 'Hz' is used as unit type
951 }
952
953 bool InstrumentScriptVMFunction_change_cutoff::acceptsArgFinal(vmint iArg) const {
954 return iArg == 1;
955 }
956
957 void InstrumentScriptVMFunction_change_cutoff::checkArgs(VMFnArgs* args,
958 std::function<void(String)> err,
959 std::function<void(String)> wrn)
960 {
961 // super class checks
962 Super::checkArgs(args, err, wrn);
963
964 // own checks ...
965 if (args->argsCount() >= 2) {
966 VMNumberExpr* argCutoff = args->arg(1)->asNumber();
967 if (argCutoff->unitType() && !argCutoff->isFinal()) {
968 wrn("Argument 2 implies 'final' value when using Hz as unit for cutoff frequency.");
969 }
970 }
971 }
972
973 VMFnResult* InstrumentScriptVMFunction_change_cutoff::exec(VMFnArgs* args) {
974 const StdUnit_t unit = args->arg(1)->asNumber()->unitType();
975 vmint cutoff =
976 (unit) ?
977 args->arg(1)->asNumber()->evalCastInt(VM_NO_PREFIX) :
978 args->arg(1)->asNumber()->evalCastInt();
979 const bool isFinal =
980 (unit) ?
981 true : // imply 'final' value if unit type is used
982 args->arg(1)->asNumber()->isFinal();
983 // note: intentionally not checking against a max. value here if no unit!
984 // (to allow i.e. passing 2000000 for doubling cutoff frequency)
985 if (unit && cutoff > VM_FILTER_PAR_MAX_HZ) {
986 wrnMsg("change_cutoff(): argument 2 may not be larger than " strfy(VM_FILTER_PAR_MAX_HZ) " Hz");
987 cutoff = VM_FILTER_PAR_MAX_HZ;
988 } else if (cutoff < 0) {
989 wrnMsg("change_cutoff(): argument 2 may not be negative");
990 cutoff = 0;
991 }
992 const float fCutoff =
993 (unit) ? cutoff : float(cutoff) / float(VM_FILTER_PAR_MAX_VALUE);
994
995 AbstractEngineChannel* pEngineChannel =
996 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
997
998 if (args->arg(0)->exprType() == INT_EXPR) {
999 const ScriptID id = args->arg(0)->asInt()->evalInt();
1000 if (!id) {
1001 wrnMsg("change_cutoff(): note ID for argument 1 may not be zero");
1002 return successResult();
1003 }
1004 if (!id.isNoteID()) {
1005 wrnMsg("change_cutoff(): argument 1 is not a note ID");
1006 return successResult();
1007 }
1008
1009 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1010 if (!pNote) return successResult();
1011
1012 // if change_cutoff() was called immediately after note was triggered
1013 // then immediately apply cutoff to Note object
1014 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1015 pNote->Override.Cutoff.Value = fCutoff;
1016 pNote->Override.Cutoff.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1017 } else { // otherwise schedule cutoff change ...
1018 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1019 e.Init(); // clear IDs
1020 e.Type = Event::type_note_synth_param;
1021 e.Param.NoteSynthParam.NoteID = id.noteID();
1022 e.Param.NoteSynthParam.Type = Event::synth_param_cutoff;
1023 e.Param.NoteSynthParam.Delta = fCutoff;
1024 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1025 isFinal, false, unit
1026 );
1027 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1028 }
1029 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1030 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1031 for (vmint i = 0; i < ids->arraySize(); ++i) {
1032 const ScriptID id = ids->evalIntElement(i);
1033 if (!id || !id.isNoteID()) continue;
1034
1035 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1036 if (!pNote) continue;
1037
1038 // if change_cutoff() was called immediately after note was triggered
1039 // then immediately apply cutoff to Note object
1040 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1041 pNote->Override.Cutoff.Value = fCutoff;
1042 pNote->Override.Cutoff.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1043 } else { // otherwise schedule cutoff change ...
1044 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1045 e.Init(); // clear IDs
1046 e.Type = Event::type_note_synth_param;
1047 e.Param.NoteSynthParam.NoteID = id.noteID();
1048 e.Param.NoteSynthParam.Type = Event::synth_param_cutoff;
1049 e.Param.NoteSynthParam.Delta = fCutoff;
1050 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1051 isFinal, false, unit
1052 );
1053 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1054 }
1055 }
1056 }
1057
1058 return successResult();
1059 }
1060
1061 // change_reso() function
1062
1063 InstrumentScriptVMFunction_change_reso::InstrumentScriptVMFunction_change_reso(InstrumentScriptVM* parent)
1064 : m_vm(parent)
1065 {
1066 }
1067
1068 bool InstrumentScriptVMFunction_change_reso::acceptsArgType(vmint iArg, ExprType_t type) const {
1069 if (iArg == 0)
1070 return type == INT_EXPR || type == INT_ARR_EXPR;
1071 else
1072 return type == INT_EXPR;
1073 }
1074
1075 bool InstrumentScriptVMFunction_change_reso::acceptsArgFinal(vmint iArg) const {
1076 return iArg == 1;
1077 }
1078
1079 VMFnResult* InstrumentScriptVMFunction_change_reso::exec(VMFnArgs* args) {
1080 vmint resonance = args->arg(1)->asInt()->evalInt();
1081 bool isFinal = args->arg(1)->asInt()->isFinal();
1082 // note: intentionally not checking against a max. value here!
1083 // (to allow i.e. passing 2000000 for doubling the resonance)
1084 if (resonance < 0) {
1085 wrnMsg("change_reso(): argument 2 may not be negative");
1086 resonance = 0;
1087 }
1088 const float fResonance = float(resonance) / float(VM_FILTER_PAR_MAX_VALUE);
1089
1090 AbstractEngineChannel* pEngineChannel =
1091 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1092
1093 if (args->arg(0)->exprType() == INT_EXPR) {
1094 const ScriptID id = args->arg(0)->asInt()->evalInt();
1095 if (!id) {
1096 wrnMsg("change_reso(): note ID for argument 1 may not be zero");
1097 return successResult();
1098 }
1099 if (!id.isNoteID()) {
1100 wrnMsg("change_reso(): argument 1 is not a note ID");
1101 return successResult();
1102 }
1103
1104 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1105 if (!pNote) return successResult();
1106
1107 // if change_reso() was called immediately after note was triggered
1108 // then immediately apply resonance to Note object
1109 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1110 pNote->Override.Resonance.Value = fResonance;
1111 pNote->Override.Resonance.Final = isFinal;
1112 } else { // otherwise schedule resonance change ...
1113 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1114 e.Init(); // clear IDs
1115 e.Type = Event::type_note_synth_param;
1116 e.Param.NoteSynthParam.NoteID = id.noteID();
1117 e.Param.NoteSynthParam.Type = Event::synth_param_resonance;
1118 e.Param.NoteSynthParam.Delta = fResonance;
1119 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1120 isFinal, false, false
1121 );
1122 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1123 }
1124 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1125 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1126 for (vmint i = 0; i < ids->arraySize(); ++i) {
1127 const ScriptID id = ids->evalIntElement(i);
1128 if (!id || !id.isNoteID()) continue;
1129
1130 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1131 if (!pNote) continue;
1132
1133 // if change_reso() was called immediately after note was triggered
1134 // then immediately apply resonance to Note object
1135 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1136 pNote->Override.Resonance.Value = fResonance;
1137 pNote->Override.Resonance.Final = isFinal;
1138 } else { // otherwise schedule resonance change ...
1139 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1140 e.Init(); // clear IDs
1141 e.Type = Event::type_note_synth_param;
1142 e.Param.NoteSynthParam.NoteID = id.noteID();
1143 e.Param.NoteSynthParam.Type = Event::synth_param_resonance;
1144 e.Param.NoteSynthParam.Delta = fResonance;
1145 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1146 isFinal, false, false
1147 );
1148 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1149 }
1150 }
1151 }
1152
1153 return successResult();
1154 }
1155
1156 // change_attack() function
1157 //
1158 //TODO: Derive from generalized, shared template class
1159 // VMChangeSynthParamFunction instead (like e.g. change_cutoff_attack()
1160 // implementation below already does) to ease maintenance.
1161
1162 InstrumentScriptVMFunction_change_attack::InstrumentScriptVMFunction_change_attack(InstrumentScriptVM* parent)
1163 : m_vm(parent)
1164 {
1165 }
1166
1167 bool InstrumentScriptVMFunction_change_attack::acceptsArgType(vmint iArg, ExprType_t type) const {
1168 if (iArg == 0)
1169 return type == INT_EXPR || type == INT_ARR_EXPR;
1170 else
1171 return type == INT_EXPR || type == REAL_EXPR;
1172 }
1173
1174 bool InstrumentScriptVMFunction_change_attack::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
1175 if (iArg == 1)
1176 return type == VM_NO_UNIT || type == VM_SECOND;
1177 else
1178 return type == VM_NO_UNIT;
1179 }
1180
1181 bool InstrumentScriptVMFunction_change_attack::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
1182 return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
1183 }
1184
1185 bool InstrumentScriptVMFunction_change_attack::acceptsArgFinal(vmint iArg) const {
1186 return iArg == 1;
1187 }
1188
1189 void InstrumentScriptVMFunction_change_attack::checkArgs(VMFnArgs* args,
1190 std::function<void(String)> err,
1191 std::function<void(String)> wrn)
1192 {
1193 // super class checks
1194 Super::checkArgs(args, err, wrn);
1195
1196 // own checks ...
1197 if (args->argsCount() >= 2) {
1198 VMNumberExpr* argTime = args->arg(1)->asNumber();
1199 if (argTime->unitType() && !argTime->isFinal()) {
1200 wrn("Argument 2 implies 'final' value when using seconds as unit for attack time.");
1201 }
1202 }
1203 }
1204
1205 VMFnResult* InstrumentScriptVMFunction_change_attack::exec(VMFnArgs* args) {
1206 const StdUnit_t unit = args->arg(1)->asNumber()->unitType();
1207 vmint attack =
1208 (unit) ?
1209 args->arg(1)->asNumber()->evalCastInt(VM_MICRO) :
1210 args->arg(1)->asNumber()->evalCastInt();
1211 const bool isFinal =
1212 (unit) ?
1213 true : // imply 'final' value if unit type is used
1214 args->arg(1)->asNumber()->isFinal();
1215 // note: intentionally not checking against a max. value here!
1216 // (to allow i.e. passing 2000000 for doubling the attack time)
1217 if (attack < 0) {
1218 wrnMsg("change_attack(): argument 2 may not be negative");
1219 attack = 0;
1220 }
1221 const float fAttack =
1222 (unit) ?
1223 float(attack) / 1000000.f /* us -> s */ :
1224 float(attack) / float(VM_EG_PAR_MAX_VALUE);
1225
1226 AbstractEngineChannel* pEngineChannel =
1227 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1228
1229 if (args->arg(0)->exprType() == INT_EXPR) {
1230 const ScriptID id = args->arg(0)->asInt()->evalInt();
1231 if (!id) {
1232 wrnMsg("change_attack(): note ID for argument 1 may not be zero");
1233 return successResult();
1234 }
1235 if (!id.isNoteID()) {
1236 wrnMsg("change_attack(): argument 1 is not a note ID");
1237 return successResult();
1238 }
1239
1240 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1241 if (!pNote) return successResult();
1242
1243 // if change_attack() was called immediately after note was triggered
1244 // then immediately apply attack to Note object
1245 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1246 pNote->Override.Attack.Value = fAttack;
1247 pNote->Override.Attack.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1248 } else { // otherwise schedule attack change ...
1249 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1250 e.Init(); // clear IDs
1251 e.Type = Event::type_note_synth_param;
1252 e.Param.NoteSynthParam.NoteID = id.noteID();
1253 e.Param.NoteSynthParam.Type = Event::synth_param_attack;
1254 e.Param.NoteSynthParam.Delta = fAttack;
1255 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1256 isFinal, false, unit
1257 );
1258 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1259 }
1260 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1261 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1262 for (vmint i = 0; i < ids->arraySize(); ++i) {
1263 const ScriptID id = ids->evalIntElement(i);
1264 if (!id || !id.isNoteID()) continue;
1265
1266 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1267 if (!pNote) continue;
1268
1269 // if change_attack() was called immediately after note was triggered
1270 // then immediately apply attack to Note object
1271 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1272 pNote->Override.Attack.Value = fAttack;
1273 pNote->Override.Attack.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1274 } else { // otherwise schedule attack change ...
1275 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1276 e.Init(); // clear IDs
1277 e.Type = Event::type_note_synth_param;
1278 e.Param.NoteSynthParam.NoteID = id.noteID();
1279 e.Param.NoteSynthParam.Type = Event::synth_param_attack;
1280 e.Param.NoteSynthParam.Delta = fAttack;
1281 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1282 isFinal, false, unit
1283 );
1284 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1285 }
1286 }
1287 }
1288
1289 return successResult();
1290 }
1291
1292 // change_decay() function
1293 //
1294 //TODO: Derive from generalized, shared template class
1295 // VMChangeSynthParamFunction instead (like e.g. change_cutoff_decay()
1296 // implementation below already does) to ease maintenance.
1297
1298 InstrumentScriptVMFunction_change_decay::InstrumentScriptVMFunction_change_decay(InstrumentScriptVM* parent)
1299 : m_vm(parent)
1300 {
1301 }
1302
1303 bool InstrumentScriptVMFunction_change_decay::acceptsArgType(vmint iArg, ExprType_t type) const {
1304 if (iArg == 0)
1305 return type == INT_EXPR || type == INT_ARR_EXPR;
1306 else
1307 return type == INT_EXPR || type == REAL_EXPR;
1308 }
1309
1310 bool InstrumentScriptVMFunction_change_decay::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
1311 if (iArg == 1)
1312 return type == VM_NO_UNIT || type == VM_SECOND;
1313 else
1314 return type == VM_NO_UNIT;
1315 }
1316
1317 bool InstrumentScriptVMFunction_change_decay::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
1318 return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
1319 }
1320
1321 bool InstrumentScriptVMFunction_change_decay::acceptsArgFinal(vmint iArg) const {
1322 return iArg == 1;
1323 }
1324
1325 void InstrumentScriptVMFunction_change_decay::checkArgs(VMFnArgs* args,
1326 std::function<void(String)> err,
1327 std::function<void(String)> wrn)
1328 {
1329 // super class checks
1330 Super::checkArgs(args, err, wrn);
1331
1332 // own checks ...
1333 if (args->argsCount() >= 2) {
1334 VMNumberExpr* argTime = args->arg(1)->asNumber();
1335 if (argTime->unitType() && !argTime->isFinal()) {
1336 wrn("Argument 2 implies 'final' value when using seconds as unit for decay time.");
1337 }
1338 }
1339 }
1340
1341 VMFnResult* InstrumentScriptVMFunction_change_decay::exec(VMFnArgs* args) {
1342 const StdUnit_t unit = args->arg(1)->asNumber()->unitType();
1343 vmint decay =
1344 (unit) ?
1345 args->arg(1)->asNumber()->evalCastInt(VM_MICRO) :
1346 args->arg(1)->asNumber()->evalCastInt();
1347 const bool isFinal =
1348 (unit) ?
1349 true : // imply 'final' value if unit type is used
1350 args->arg(1)->asNumber()->isFinal();
1351 // note: intentionally not checking against a max. value here!
1352 // (to allow i.e. passing 2000000 for doubling the decay time)
1353 if (decay < 0) {
1354 wrnMsg("change_decay(): argument 2 may not be negative");
1355 decay = 0;
1356 }
1357 const float fDecay =
1358 (unit) ?
1359 float(decay) / 1000000.f /* us -> s */ :
1360 float(decay) / float(VM_EG_PAR_MAX_VALUE);
1361
1362 AbstractEngineChannel* pEngineChannel =
1363 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1364
1365 if (args->arg(0)->exprType() == INT_EXPR) {
1366 const ScriptID id = args->arg(0)->asInt()->evalInt();
1367 if (!id) {
1368 wrnMsg("change_decay(): note ID for argument 1 may not be zero");
1369 return successResult();
1370 }
1371 if (!id.isNoteID()) {
1372 wrnMsg("change_decay(): argument 1 is not a note ID");
1373 return successResult();
1374 }
1375
1376 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1377 if (!pNote) return successResult();
1378
1379 // if change_decay() was called immediately after note was triggered
1380 // then immediately apply decay to Note object
1381 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1382 pNote->Override.Decay.Value = fDecay;
1383 pNote->Override.Decay.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1384 } else { // otherwise schedule decay change ...
1385 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1386 e.Init(); // clear IDs
1387 e.Type = Event::type_note_synth_param;
1388 e.Param.NoteSynthParam.NoteID = id.noteID();
1389 e.Param.NoteSynthParam.Type = Event::synth_param_decay;
1390 e.Param.NoteSynthParam.Delta = fDecay;
1391 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1392 isFinal, false, unit
1393 );
1394 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1395 }
1396 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1397 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1398 for (vmint i = 0; i < ids->arraySize(); ++i) {
1399 const ScriptID id = ids->evalIntElement(i);
1400 if (!id || !id.isNoteID()) continue;
1401
1402 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1403 if (!pNote) continue;
1404
1405 // if change_decay() was called immediately after note was triggered
1406 // then immediately apply decay to Note object
1407 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1408 pNote->Override.Decay.Value = fDecay;
1409 pNote->Override.Decay.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1410 } else { // otherwise schedule decay change ...
1411 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1412 e.Init(); // clear IDs
1413 e.Type = Event::type_note_synth_param;
1414 e.Param.NoteSynthParam.NoteID = id.noteID();
1415 e.Param.NoteSynthParam.Type = Event::synth_param_decay;
1416 e.Param.NoteSynthParam.Delta = fDecay;
1417 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1418 isFinal, false, unit
1419 );
1420 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1421 }
1422 }
1423 }
1424
1425 return successResult();
1426 }
1427
1428 // change_release() function
1429 //
1430 //TODO: Derive from generalized, shared template class
1431 // VMChangeSynthParamFunction instead (like e.g. change_cutoff_release()
1432 // implementation below already does) to ease maintenance.
1433
1434 InstrumentScriptVMFunction_change_release::InstrumentScriptVMFunction_change_release(InstrumentScriptVM* parent)
1435 : m_vm(parent)
1436 {
1437 }
1438
1439 bool InstrumentScriptVMFunction_change_release::acceptsArgType(vmint iArg, ExprType_t type) const {
1440 if (iArg == 0)
1441 return type == INT_EXPR || type == INT_ARR_EXPR;
1442 else
1443 return type == INT_EXPR || type == REAL_EXPR;
1444 }
1445
1446 bool InstrumentScriptVMFunction_change_release::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
1447 if (iArg == 1)
1448 return type == VM_NO_UNIT || type == VM_SECOND;
1449 else
1450 return type == VM_NO_UNIT;
1451 }
1452
1453 bool InstrumentScriptVMFunction_change_release::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
1454 return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
1455 }
1456
1457 bool InstrumentScriptVMFunction_change_release::acceptsArgFinal(vmint iArg) const {
1458 return iArg == 1;
1459 }
1460
1461 void InstrumentScriptVMFunction_change_release::checkArgs(VMFnArgs* args,
1462 std::function<void(String)> err,
1463 std::function<void(String)> wrn)
1464 {
1465 // super class checks
1466 Super::checkArgs(args, err, wrn);
1467
1468 // own checks ...
1469 if (args->argsCount() >= 2) {
1470 VMNumberExpr* argTime = args->arg(1)->asNumber();
1471 if (argTime->unitType() && !argTime->isFinal()) {
1472 wrn("Argument 2 implies 'final' value when using seconds as unit for release time.");
1473 }
1474 }
1475 }
1476
1477 VMFnResult* InstrumentScriptVMFunction_change_release::exec(VMFnArgs* args) {
1478 const StdUnit_t unit = args->arg(1)->asNumber()->unitType();
1479 vmint release =
1480 (unit) ?
1481 args->arg(1)->asNumber()->evalCastInt(VM_MICRO) :
1482 args->arg(1)->asNumber()->evalCastInt();
1483 const bool isFinal =
1484 (unit) ?
1485 true : // imply 'final' value if unit type is used
1486 args->arg(1)->asNumber()->isFinal();
1487 // note: intentionally not checking against a max. value here!
1488 // (to allow i.e. passing 2000000 for doubling the release time)
1489 if (release < 0) {
1490 wrnMsg("change_release(): argument 2 may not be negative");
1491 release = 0;
1492 }
1493 const float fRelease =
1494 (unit) ?
1495 float(release) / 1000000.f /* us -> s */ :
1496 float(release) / float(VM_EG_PAR_MAX_VALUE);
1497
1498 AbstractEngineChannel* pEngineChannel =
1499 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1500
1501 if (args->arg(0)->exprType() == INT_EXPR) {
1502 const ScriptID id = args->arg(0)->asInt()->evalInt();
1503 if (!id) {
1504 wrnMsg("change_release(): note ID for argument 1 may not be zero");
1505 return successResult();
1506 }
1507 if (!id.isNoteID()) {
1508 wrnMsg("change_release(): argument 1 is not a note ID");
1509 return successResult();
1510 }
1511
1512 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1513 if (!pNote) return successResult();
1514
1515 // if change_release() was called immediately after note was triggered
1516 // then immediately apply relase to Note object
1517 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1518 pNote->Override.Release.Value = fRelease;
1519 pNote->Override.Release.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1520 } else { // otherwise schedule release change ...
1521 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1522 e.Init(); // clear IDs
1523 e.Type = Event::type_note_synth_param;
1524 e.Param.NoteSynthParam.NoteID = id.noteID();
1525 e.Param.NoteSynthParam.Type = Event::synth_param_release;
1526 e.Param.NoteSynthParam.Delta = fRelease;
1527 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1528 isFinal, false, unit
1529 );
1530 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1531 }
1532 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1533 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1534 for (vmint i = 0; i < ids->arraySize(); ++i) {
1535 const ScriptID id = ids->evalIntElement(i);
1536 if (!id || !id.isNoteID()) continue;
1537
1538 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1539 if (!pNote) continue;
1540
1541 // if change_release() was called immediately after note was triggered
1542 // then immediately apply relase to Note object
1543 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1544 pNote->Override.Release.Value = fRelease;
1545 pNote->Override.Release.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1546 } else { // otherwise schedule release change ...
1547 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1548 e.Init(); // clear IDs
1549 e.Type = Event::type_note_synth_param;
1550 e.Param.NoteSynthParam.NoteID = id.noteID();
1551 e.Param.NoteSynthParam.Type = Event::synth_param_release;
1552 e.Param.NoteSynthParam.Delta = fRelease;
1553 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1554 isFinal, false, unit
1555 );
1556 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1557 }
1558 }
1559 }
1560
1561 return successResult();
1562 }
1563
1564 // template for change_*() functions
1565
1566 bool VMChangeSynthParamFunction::acceptsArgType(vmint iArg, ExprType_t type) const {
1567 if (iArg == 0)
1568 return type == INT_EXPR || type == INT_ARR_EXPR;
1569 else
1570 return type == INT_EXPR || (m_acceptReal && type == REAL_EXPR);
1571 }
1572
1573 bool VMChangeSynthParamFunction::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
1574 if (iArg == 1)
1575 return type == VM_NO_UNIT || type == m_unit;
1576 else
1577 return type == VM_NO_UNIT;
1578 }
1579
1580 bool VMChangeSynthParamFunction::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
1581 return m_acceptUnitPrefix && iArg == 1 && type == m_unit; // only allow metric prefix(es) if approprirate unit type is used (e.g. Hz)
1582 }
1583
1584 bool VMChangeSynthParamFunction::acceptsArgFinal(vmint iArg) const {
1585 return (m_acceptFinal) ? (iArg == 1) : false;
1586 }
1587
1588 inline static void setNoteParamScopeBy_FinalUnit(NoteBase::Param& param, const bool bFinal, const StdUnit_t unit) {
1589 param.Scope = NoteBase::scopeBy_FinalUnit(bFinal, unit);
1590 }
1591
1592 inline static void setNoteParamScopeBy_FinalUnit(NoteBase::Norm& param, const bool bFinal, const StdUnit_t unit) {
1593 param.Final = bFinal;
1594 }
1595
1596 inline static void setNoteParamScopeBy_FinalUnit(float& param, const bool bFinal, const StdUnit_t unit) {
1597 /* NOOP */
1598 }
1599
1600 template<class T>
1601 inline static void setNoteParamValue(T& param, vmfloat value) {
1602 param.Value = value;
1603 }
1604
1605 inline static void setNoteParamValue(float& param, vmfloat value) {
1606 param = value;
1607 }
1608
1609 void VMChangeSynthParamFunction::checkArgs(VMFnArgs* args,
1610 std::function<void(String)> err,
1611 std::function<void(String)> wrn)
1612 {
1613 // super class checks
1614 Super::checkArgs(args, err, wrn);
1615
1616 // own checks ...
1617 if (m_unit && m_unit != VM_BEL && args->argsCount() >= 2) {
1618 VMNumberExpr* arg = args->arg(1)->asNumber();
1619 if (arg && arg->unitType() && !arg->isFinal()) {
1620 wrn("Argument 2 implies 'final' value when unit type " +
1621 unitTypeStr(arg->unitType()) + " is used.");
1622 }
1623 }
1624 }
1625
1626 // Arbitrarily chosen constant value symbolizing "no limit".
1627 #define NO_LIMIT 1315916909
1628
1629 template<class T_NoteParamType, T_NoteParamType NoteBase::_Override::*T_noteParam,
1630 vmint T_synthParam,
1631 vmint T_minValueNorm, vmint T_maxValueNorm, bool T_normalizeNorm,
1632 vmint T_minValueUnit, vmint T_maxValueUnit,
1633 MetricPrefix_t T_unitPrefix0, MetricPrefix_t ... T_unitPrefixN>
1634 VMFnResult* VMChangeSynthParamFunction::execTemplate(VMFnArgs* args, const char* functionName)
1635 {
1636 const StdUnit_t unit = args->arg(1)->asNumber()->unitType();
1637 const bool isFinal =
1638 (m_unit && m_unit != VM_BEL && unit) ?
1639 true : // imply 'final' value if unit type is used (except of 'Bel' which may be relative)
1640 args->arg(1)->asNumber()->isFinal();
1641 vmint value =
1642 (m_acceptUnitPrefix && ((m_unit && unit) || (!m_unit && args->arg(1)->asNumber()->hasUnitFactorNow())))
1643 ? args->arg(1)->asNumber()->evalCastInt(T_unitPrefix0, T_unitPrefixN ...)
1644 : args->arg(1)->asNumber()->evalCastInt();
1645
1646 // check if passed value is in allowed range
1647 if (unit && m_unit) {
1648 if (T_maxValueUnit != NO_LIMIT && value > T_maxValueUnit) {
1649 wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValueUnit));
1650 value = T_maxValueUnit;
1651 } else if (T_minValueUnit != NO_LIMIT && value < T_minValueUnit) {
1652 if (T_minValueUnit == 0)
1653 wrnMsg(String(functionName) + "(): argument 2 may not be negative");
1654 else
1655 wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValueUnit));
1656 value = T_minValueUnit;
1657 }
1658 } else { // value was passed to this function without a unit ...
1659 if (T_maxValueNorm != NO_LIMIT && value > T_maxValueNorm) {
1660 wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValueNorm));
1661 value = T_maxValueNorm;
1662 } else if (T_minValueNorm != NO_LIMIT && value < T_minValueNorm) {
1663 if (T_minValueNorm == 0)
1664 wrnMsg(String(functionName) + "(): argument 2 may not be negative");
1665 else
1666 wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValueNorm));
1667 value = T_minValueNorm;
1668 }
1669 }
1670
1671 // convert passed argument value to engine internal expected value range (i.e. 0.0 .. 1.0)
1672 const float fValue =
1673 (unit && m_unit) ?
1674 (unit == VM_BEL) ?
1675 RTMath::DecibelToLinRatio(float(value) * float(T_unitPrefix0) /*i.e. mdB -> dB*/) :
1676 float(value) * VMUnit::unitFactor(T_unitPrefix0, T_unitPrefixN ...) /*i.e. us -> s*/ :
1677 (T_normalizeNorm) ?
1678 float(value) / ((T_maxValueNorm != NO_LIMIT) ? float(T_maxValueNorm) : 1000000.f/* fallback: value range used for most */) :
1679 float(value) /* as is */;
1680
1681 AbstractEngineChannel* pEngineChannel =
1682 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1683
1684 if (args->arg(0)->exprType() == INT_EXPR) {
1685 const ScriptID id = args->arg(0)->asInt()->evalInt();
1686 if (!id) {
1687 wrnMsg(String(functionName) + "(): note ID for argument 1 may not be zero");
1688 return successResult();
1689 }
1690 if (!id.isNoteID()) {
1691 wrnMsg(String(functionName) + "(): argument 1 is not a note ID");
1692 return successResult();
1693 }
1694
1695 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1696 if (!pNote) return successResult();
1697
1698 // if this change_*() script function was called immediately after
1699 // note was triggered then immediately apply the synth parameter
1700 // change to Note object
1701 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1702 setNoteParamValue(pNote->Override.*T_noteParam, fValue);
1703 setNoteParamScopeBy_FinalUnit(
1704 (pNote->Override.*T_noteParam),
1705 isFinal, unit
1706 );
1707 } else { // otherwise schedule this synth parameter change ...
1708 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1709 e.Init(); // clear IDs
1710 e.Type = Event::type_note_synth_param;
1711 e.Param.NoteSynthParam.NoteID = id.noteID();
1712 e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
1713 e.Param.NoteSynthParam.Delta = fValue;
1714 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1715 isFinal, false, unit
1716 );
1717 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1718 }
1719 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1720 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1721 for (vmint i = 0; i < ids->arraySize(); ++i) {
1722 const ScriptID id = ids->evalIntElement(i);
1723 if (!id || !id.isNoteID()) continue;
1724
1725 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1726 if (!pNote) continue;
1727
1728 // if this change_*() script function was called immediately after
1729 // note was triggered then immediately apply the synth parameter
1730 // change to Note object
1731 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1732 setNoteParamValue(pNote->Override.*T_noteParam, fValue);
1733 setNoteParamScopeBy_FinalUnit(
1734 (pNote->Override.*T_noteParam),
1735 isFinal, unit
1736 );
1737 } else { // otherwise schedule this synth parameter change ...
1738 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1739 e.Init(); // clear IDs
1740 e.Type = Event::type_note_synth_param;
1741 e.Param.NoteSynthParam.NoteID = id.noteID();
1742 e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
1743 e.Param.NoteSynthParam.Delta = fValue;
1744 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1745 isFinal, false, unit
1746 );
1747 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1748 }
1749 }
1750 }
1751
1752 return successResult();
1753 }
1754
1755 // change_sustain() function
1756
1757 VMFnResult* InstrumentScriptVMFunction_change_sustain::exec(VMFnArgs* args) {
1758 return VMChangeSynthParamFunction::execTemplate<
1759 decltype(NoteBase::_Override::Sustain),
1760 &NoteBase::_Override::Sustain,
1761 Event::synth_param_sustain,
1762 /* if value passed without unit */
1763 0, NO_LIMIT, true,
1764 /* if value passed WITH 'Bel' unit */
1765 NO_LIMIT, NO_LIMIT, VM_MILLI, VM_DECI>( args, "change_sustain" );
1766 }
1767
1768 // change_cutoff_attack() function
1769
1770 VMFnResult* InstrumentScriptVMFunction_change_cutoff_attack::exec(VMFnArgs* args) {
1771 return VMChangeSynthParamFunction::execTemplate<
1772 decltype(NoteBase::_Override::CutoffAttack),
1773 &NoteBase::_Override::CutoffAttack,
1774 Event::synth_param_cutoff_attack,
1775 /* if value passed without unit */
1776 0, NO_LIMIT, true,
1777 /* if value passed with 'seconds' unit */
1778 0, NO_LIMIT, VM_MICRO>( args, "change_cutoff_attack" );
1779 }
1780
1781 // change_cutoff_decay() function
1782
1783 VMFnResult* InstrumentScriptVMFunction_change_cutoff_decay::exec(VMFnArgs* args) {
1784 return VMChangeSynthParamFunction::execTemplate<
1785 decltype(NoteBase::_Override::CutoffDecay),
1786 &NoteBase::_Override::CutoffDecay,
1787 Event::synth_param_cutoff_decay,
1788 /* if value passed without unit */
1789 0, NO_LIMIT, true,
1790 /* if value passed with 'seconds' unit */
1791 0, NO_LIMIT, VM_MICRO>( args, "change_cutoff_decay" );
1792 }
1793
1794 // change_cutoff_sustain() function
1795
1796 VMFnResult* InstrumentScriptVMFunction_change_cutoff_sustain::exec(VMFnArgs* args) {
1797 return VMChangeSynthParamFunction::execTemplate<
1798 decltype(NoteBase::_Override::CutoffSustain),
1799 &NoteBase::_Override::CutoffSustain,
1800 Event::synth_param_cutoff_sustain,
1801 /* if value passed without unit */
1802 0, NO_LIMIT, true,
1803 /* if value passed WITH 'Bel' unit */
1804 NO_LIMIT, NO_LIMIT, VM_MILLI, VM_DECI>( args, "change_cutoff_sustain" );
1805 }
1806
1807 // change_cutoff_release() function
1808
1809 VMFnResult* InstrumentScriptVMFunction_change_cutoff_release::exec(VMFnArgs* args) {
1810 return VMChangeSynthParamFunction::execTemplate<
1811 decltype(NoteBase::_Override::CutoffRelease),
1812 &NoteBase::_Override::CutoffRelease,
1813 Event::synth_param_cutoff_release,
1814 /* if value passed without unit */
1815 0, NO_LIMIT, true,
1816 /* if value passed with 'seconds' unit */
1817 0, NO_LIMIT, VM_MICRO>( args, "change_cutoff_release" );
1818 }
1819
1820 // change_amp_lfo_depth() function
1821
1822 VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_depth::exec(VMFnArgs* args) {
1823 return VMChangeSynthParamFunction::execTemplate<
1824 decltype(NoteBase::_Override::AmpLFODepth),
1825 &NoteBase::_Override::AmpLFODepth,
1826 Event::synth_param_amp_lfo_depth,
1827 /* if value passed without unit */
1828 0, NO_LIMIT, true,
1829 /* not used (since this function does not accept unit) */
1830 NO_LIMIT, NO_LIMIT, VM_NO_PREFIX>( args, "change_amp_lfo_depth" );
1831 }
1832
1833 // change_amp_lfo_freq() function
1834
1835 VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_freq::exec(VMFnArgs* args) {
1836 return VMChangeSynthParamFunction::execTemplate<
1837 decltype(NoteBase::_Override::AmpLFOFreq),
1838 &NoteBase::_Override::AmpLFOFreq,
1839 Event::synth_param_amp_lfo_freq,
1840 /* if value passed without unit */
1841 0, NO_LIMIT, true,
1842 /* if value passed with 'Hz' unit */
1843 0, 30000, VM_NO_PREFIX>( args, "change_amp_lfo_freq" );
1844 }
1845
1846 // change_cutoff_lfo_depth() function
1847
1848 VMFnResult* InstrumentScriptVMFunction_change_cutoff_lfo_depth::exec(VMFnArgs* args) {
1849 return VMChangeSynthParamFunction::execTemplate<
1850 decltype(NoteBase::_Override::CutoffLFODepth),
1851 &NoteBase::_Override::CutoffLFODepth,
1852 Event::synth_param_cutoff_lfo_depth,
1853 /* if value passed without unit */
1854 0, NO_LIMIT, true,
1855 /* not used (since this function does not accept unit) */
1856 NO_LIMIT, NO_LIMIT, VM_NO_PREFIX>( args, "change_cutoff_lfo_depth" );
1857 }
1858
1859 // change_cutoff_lfo_freq() function
1860
1861 VMFnResult* InstrumentScriptVMFunction_change_cutoff_lfo_freq::exec(VMFnArgs* args) {
1862 return VMChangeSynthParamFunction::execTemplate<
1863 decltype(NoteBase::_Override::CutoffLFOFreq),
1864 &NoteBase::_Override::CutoffLFOFreq,
1865 Event::synth_param_cutoff_lfo_freq,
1866 /* if value passed without unit */
1867 0, NO_LIMIT, true,
1868 /* if value passed with 'Hz' unit */
1869 0, 30000, VM_NO_PREFIX>( args, "change_cutoff_lfo_freq" );
1870 }
1871
1872 // change_pitch_lfo_depth() function
1873
1874 VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_depth::exec(VMFnArgs* args) {
1875 return VMChangeSynthParamFunction::execTemplate<
1876 decltype(NoteBase::_Override::PitchLFODepth),
1877 &NoteBase::_Override::PitchLFODepth,
1878 Event::synth_param_pitch_lfo_depth,
1879 /* if value passed without unit */
1880 0, NO_LIMIT, true,
1881 /* not used (since this function does not accept unit) */
1882 NO_LIMIT, NO_LIMIT, VM_NO_PREFIX>( args, "change_pitch_lfo_depth" );
1883 }
1884
1885 // change_pitch_lfo_freq() function
1886
1887 VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_freq::exec(VMFnArgs* args) {
1888 return VMChangeSynthParamFunction::execTemplate<
1889 decltype(NoteBase::_Override::PitchLFOFreq),
1890 &NoteBase::_Override::PitchLFOFreq,
1891 Event::synth_param_pitch_lfo_freq,
1892 /* if value passed without unit */
1893 0, NO_LIMIT, true,
1894 /* if value passed with 'Hz' unit */
1895 0, 30000, VM_NO_PREFIX>( args, "change_pitch_lfo_freq" );
1896 }
1897
1898 // change_vol_time() function
1899
1900 VMFnResult* InstrumentScriptVMFunction_change_vol_time::exec(VMFnArgs* args) {
1901 return VMChangeSynthParamFunction::execTemplate<
1902 decltype(NoteBase::_Override::VolumeTime),
1903 &NoteBase::_Override::VolumeTime,
1904 Event::synth_param_volume_time,
1905 /* if value passed without unit (implying 'us' unit) */
1906 0, NO_LIMIT, true,
1907 /* if value passed with 'seconds' unit */
1908 0, NO_LIMIT, VM_MICRO>( args, "change_vol_time" );
1909 }
1910
1911 // change_tune_time() function
1912
1913 VMFnResult* InstrumentScriptVMFunction_change_tune_time::exec(VMFnArgs* args) {
1914 return VMChangeSynthParamFunction::execTemplate<
1915 decltype(NoteBase::_Override::PitchTime),
1916 &NoteBase::_Override::PitchTime,
1917 Event::synth_param_pitch_time,
1918 /* if value passed without unit (implying 'us' unit) */
1919 0, NO_LIMIT, true,
1920 /* if value passed with 'seconds' unit */
1921 0, NO_LIMIT, VM_MICRO>( args, "change_tune_time" );
1922 }
1923
1924 // change_pan_time() function
1925
1926 VMFnResult* InstrumentScriptVMFunction_change_pan_time::exec(VMFnArgs* args) {
1927 return VMChangeSynthParamFunction::execTemplate<
1928 decltype(NoteBase::_Override::PanTime),
1929 &NoteBase::_Override::PanTime,
1930 Event::synth_param_pan_time,
1931 /* if value passed without unit (implying 'us' unit) */
1932 0, NO_LIMIT, true,
1933 /* if value passed with 'seconds' unit */
1934 0, NO_LIMIT, VM_MICRO>( args, "change_pan_time" );
1935 }
1936
1937 // template for change_*_curve() functions
1938
1939 bool VMChangeFadeCurveFunction::acceptsArgType(vmint iArg, ExprType_t type) const {
1940 if (iArg == 0)
1941 return type == INT_EXPR || type == INT_ARR_EXPR;
1942 else
1943 return type == INT_EXPR;
1944 }
1945
1946 template<fade_curve_t NoteBase::_Override::*T_noteParam, vmint T_synthParam>
1947 VMFnResult* VMChangeFadeCurveFunction::execTemplate(VMFnArgs* args, const char* functionName) {
1948 vmint value = args->arg(1)->asInt()->evalInt();
1949 switch (value) {
1950 case FADE_CURVE_LINEAR:
1951 case FADE_CURVE_EASE_IN_EASE_OUT:
1952 break;
1953 default:
1954 wrnMsg(String(functionName) + "(): invalid curve type passed as argument 2");
1955 return successResult();
1956 }
1957
1958 AbstractEngineChannel* pEngineChannel =
1959 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1960
1961 if (args->arg(0)->exprType() == INT_EXPR) {
1962 const ScriptID id = args->arg(0)->asInt()->evalInt();
1963 if (!id) {
1964 wrnMsg(String(functionName) + "(): note ID for argument 1 may not be zero");
1965 return successResult();
1966 }
1967 if (!id.isNoteID()) {
1968 wrnMsg(String(functionName) + "(): argument 1 is not a note ID");
1969 return successResult();
1970 }
1971
1972 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1973 if (!pNote) return successResult();
1974
1975 // if this change_*_curve() script function was called immediately after
1976 // note was triggered then immediately apply the synth parameter
1977 // change to Note object
1978 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1979 pNote->Override.*T_noteParam = (fade_curve_t) value;
1980 } else { // otherwise schedule this synth parameter change ...
1981 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1982 e.Init(); // clear IDs
1983 e.Type = Event::type_note_synth_param;
1984 e.Param.NoteSynthParam.NoteID = id.noteID();
1985 e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
1986 e.Param.NoteSynthParam.Delta = value;
1987 e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
1988
1989 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1990 }
1991 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1992 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1993 for (vmint i = 0; i < ids->arraySize(); ++i) {
1994 const ScriptID id = ids->evalIntElement(i);
1995 if (!id || !id.isNoteID()) continue;
1996
1997 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1998 if (!pNote) continue;
1999
2000 // if this change_*_curve() script function was called immediately after
2001 // note was triggered then immediately apply the synth parameter
2002 // change to Note object
2003 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
2004 pNote->Override.*T_noteParam = (fade_curve_t) value;
2005 } else { // otherwise schedule this synth parameter change ...
2006 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
2007 e.Init(); // clear IDs
2008 e.Type = Event::type_note_synth_param;
2009 e.Param.NoteSynthParam.NoteID = id.noteID();
2010 e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
2011 e.Param.NoteSynthParam.Delta = value;
2012 e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2013
2014 pEngineChannel->ScheduleEventMicroSec(&e, 0);
2015 }
2016 }
2017 }
2018
2019 return successResult();
2020 }
2021
2022 // change_vol_curve() function
2023
2024 VMFnResult* InstrumentScriptVMFunction_change_vol_curve::exec(VMFnArgs* args) {
2025 return VMChangeFadeCurveFunction::execTemplate<
2026 &NoteBase::_Override::VolumeCurve,
2027 Event::synth_param_volume_curve>( args, "change_vol_curve" );
2028 }
2029
2030 // change_tune_curve() function
2031
2032 VMFnResult* InstrumentScriptVMFunction_change_tune_curve::exec(VMFnArgs* args) {
2033 return VMChangeFadeCurveFunction::execTemplate<
2034 &NoteBase::_Override::PitchCurve,
2035 Event::synth_param_pitch_curve>( args, "change_tune_curve" );
2036 }
2037
2038 // change_pan_curve() function
2039
2040 VMFnResult* InstrumentScriptVMFunction_change_pan_curve::exec(VMFnArgs* args) {
2041 return VMChangeFadeCurveFunction::execTemplate<
2042 &NoteBase::_Override::PanCurve,
2043 Event::synth_param_pan_curve>( args, "change_pan_curve" );
2044 }
2045
2046 // fade_in() function
2047
2048 InstrumentScriptVMFunction_fade_in::InstrumentScriptVMFunction_fade_in(InstrumentScriptVM* parent)
2049 : m_vm(parent)
2050 {
2051 }
2052
2053 bool InstrumentScriptVMFunction_fade_in::acceptsArgType(vmint iArg, ExprType_t type) const {
2054 if (iArg == 0)
2055 return type == INT_EXPR || type == INT_ARR_EXPR;
2056 else
2057 return type == INT_EXPR || type == REAL_EXPR;
2058 }
2059
2060 bool InstrumentScriptVMFunction_fade_in::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
2061 if (iArg == 1)
2062 return type == VM_NO_UNIT || type == VM_SECOND;
2063 else
2064 return type == VM_NO_UNIT;
2065 }
2066
2067 bool InstrumentScriptVMFunction_fade_in::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
2068 return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
2069 }
2070
2071 VMFnResult* InstrumentScriptVMFunction_fade_in::exec(VMFnArgs* args) {
2072 StdUnit_t unit = args->arg(1)->asNumber()->unitType();
2073 vmint duration =
2074 (unit) ?
2075 args->arg(1)->asNumber()->evalCastInt(VM_MICRO) :
2076 args->arg(1)->asNumber()->evalCastInt();
2077 if (duration < 0) {
2078 wrnMsg("fade_in(): argument 2 may not be negative");
2079 duration = 0;
2080 }
2081 const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
2082
2083 AbstractEngineChannel* pEngineChannel =
2084 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2085
2086 if (args->arg(0)->exprType() == INT_EXPR) {
2087 const ScriptID id = args->arg(0)->asInt()->evalInt();
2088 if (!id) {
2089 wrnMsg("fade_in(): note ID for argument 1 may not be zero");
2090 return successResult();
2091 }
2092 if (!id.isNoteID()) {
2093 wrnMsg("fade_in(): argument 1 is not a note ID");
2094 return successResult();
2095 }
2096
2097 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2098 if (!pNote) return successResult();
2099
2100 // if fade_in() was called immediately after note was triggered
2101 // then immediately apply a start volume of zero to Note object,
2102 // as well as the fade in duration
2103 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
2104 pNote->Override.Volume.Value = 0.f;
2105 pNote->Override.VolumeTime = fDuration;
2106 } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
2107 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
2108 e.Init(); // clear IDs
2109 e.Type = Event::type_note_synth_param;
2110 e.Param.NoteSynthParam.NoteID = id.noteID();
2111 e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
2112 e.Param.NoteSynthParam.Delta = fDuration;
2113 e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2114
2115 pEngineChannel->ScheduleEventMicroSec(&e, 0);
2116 }
2117 // and finally schedule a "volume" change, simply one time slice
2118 // ahead, with the final fade in volume (1.0)
2119 {
2120 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
2121 e.Init(); // clear IDs
2122 e.Type = Event::type_note_synth_param;
2123 e.Param.NoteSynthParam.NoteID = id.noteID();
2124 e.Param.NoteSynthParam.Type = Event::synth_param_volume;
2125 e.Param.NoteSynthParam.Delta = 1.f;
2126 e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2127
2128 // scheduling with 0 delay would also work here, but +1 is more
2129 // safe regarding potential future implementation changes of the
2130 // scheduler (see API comments of RTAVLTree::insert())
2131 pEngineChannel->ScheduleEventMicroSec(&e, 1);
2132 }
2133 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
2134 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
2135 for (vmint i = 0; i < ids->arraySize(); ++i) {
2136 const ScriptID id = ids->evalIntElement(i);
2137 if (!id || !id.isNoteID()) continue;
2138
2139 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2140 if (!pNote) continue;
2141
2142 // if fade_in() was called immediately after note was triggered
2143 // then immediately apply a start volume of zero to Note object,
2144 // as well as the fade in duration
2145 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
2146 pNote->Override.Volume.Value = 0.f;
2147 pNote->Override.VolumeTime = fDuration;
2148 } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
2149 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
2150 e.Init(); // clear IDs
2151 e.Type = Event::type_note_synth_param;
2152 e.Param.NoteSynthParam.NoteID = id.noteID();
2153 e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
2154 e.Param.NoteSynthParam.Delta = fDuration;
2155 e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2156
2157 pEngineChannel->ScheduleEventMicroSec(&e, 0);
2158 }
2159 // and finally schedule a "volume" change, simply one time slice
2160 // ahead, with the final fade in volume (1.0)
2161 {
2162 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
2163 e.Init(); // clear IDs
2164 e.Type = Event::type_note_synth_param;
2165 e.Param.NoteSynthParam.NoteID = id.noteID();
2166 e.Param.NoteSynthParam.Type = Event::synth_param_volume;
2167 e.Param.NoteSynthParam.Delta = 1.f;
2168 e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2169
2170 // scheduling with 0 delay would also work here, but +1 is more
2171 // safe regarding potential future implementation changes of the
2172 // scheduler (see API comments of RTAVLTree::insert())
2173 pEngineChannel->ScheduleEventMicroSec(&e, 1);
2174 }
2175 }
2176 }
2177
2178 return successResult();
2179 }
2180
2181 // fade_out() function
2182
2183 InstrumentScriptVMFunction_fade_out::InstrumentScriptVMFunction_fade_out(InstrumentScriptVM* parent)
2184 : m_vm(parent)
2185 {
2186 }
2187
2188 bool InstrumentScriptVMFunction_fade_out::acceptsArgType(vmint iArg, ExprType_t type) const {
2189 if (iArg == 0)
2190 return type == INT_EXPR || type == INT_ARR_EXPR;
2191 else
2192 return type == INT_EXPR || type == REAL_EXPR;
2193 }
2194
2195 bool InstrumentScriptVMFunction_fade_out::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
2196 if (iArg == 1)
2197 return type == VM_NO_UNIT || type == VM_SECOND;
2198 else
2199 return type == VM_NO_UNIT;
2200 }
2201
2202 bool InstrumentScriptVMFunction_fade_out::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
2203 return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
2204 }
2205
2206 VMFnResult* InstrumentScriptVMFunction_fade_out::exec(VMFnArgs* args) {
2207 StdUnit_t unit = args->arg(1)->asNumber()->unitType();
2208 vmint duration =
2209 (unit) ?
2210 args->arg(1)->asNumber()->evalCastInt(VM_MICRO) :
2211 args->arg(1)->asNumber()->evalCastInt();
2212 if (duration < 0) {
2213 wrnMsg("fade_out(): argument 2 may not be negative");
2214 duration = 0;
2215 }
2216 const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
2217
2218 bool stop = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : true;
2219
2220 AbstractEngineChannel* pEngineChannel =
2221 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2222
2223 if (args->arg(0)->exprType() == INT_EXPR) {
2224 const ScriptID id = args->arg(0)->asInt()->evalInt();
2225 if (!id) {
2226 wrnMsg("fade_out(): note ID for argument 1 may not be zero");
2227 return successResult();
2228 }
2229 if (!id.isNoteID()) {
2230 wrnMsg("fade_out(): argument 1 is not a note ID");
2231 return successResult();
2232 }
2233
2234 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2235 if (!pNote) return successResult();
2236
2237 // if fade_out() was called immediately after note was triggered
2238 // then immediately apply fade out duration to Note object
2239 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
2240 pNote->Override.VolumeTime = fDuration;
2241 } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
2242 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
2243 e.Init(); // clear IDs
2244 e.Type = Event::type_note_synth_param;
2245 e.Param.NoteSynthParam.NoteID = id.noteID();
2246 e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
2247 e.Param.NoteSynthParam.Delta = fDuration;
2248 e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2249
2250 pEngineChannel->ScheduleEventMicroSec(&e, 0);
2251 }
2252 // now schedule a "volume" change, simply one time slice ahead, with
2253 // the final fade out volume (0.0)
2254 {
2255 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
2256 e.Init(); // clear IDs
2257 e.Type = Event::type_note_synth_param;
2258 e.Param.NoteSynthParam.NoteID = id.noteID();
2259 e.Param.NoteSynthParam.Type = Event::synth_param_volume;
2260 e.Param.NoteSynthParam.Delta = 0.f;
2261 e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2262
2263 // scheduling with 0 delay would also work here, but +1 is more
2264 // safe regarding potential future implementation changes of the
2265 // scheduler (see API comments of RTAVLTree::insert())
2266 pEngineChannel->ScheduleEventMicroSec(&e, 1);
2267 }
2268 // and finally if stopping the note was requested after the fade out
2269 // completed, then schedule to kill the voice after the requested
2270 // duration
2271 if (stop) {
2272 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
2273 e.Init(); // clear IDs
2274 e.Type = Event::type_kill_note;
2275 e.Param.Note.ID = id.noteID();
2276 e.Param.Note.Key = pNote->hostKey;
2277
2278 pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
2279 }
2280 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
2281 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
2282 for (vmint i = 0; i < ids->arraySize(); ++i) {
2283 const ScriptID id = ids->evalIntElement(i);
2284 if (!id || !id.isNoteID()) continue;
2285
2286 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2287 if (!pNote) continue;
2288
2289 // if fade_out() was called immediately after note was triggered
2290 // then immediately apply fade out duration to Note object
2291 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
2292 pNote->Override.VolumeTime = fDuration;
2293 } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
2294 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
2295 e.Init(); // clear IDs
2296 e.Type = Event::type_note_synth_param;
2297 e.Param.NoteSynthParam.NoteID = id.noteID();
2298 e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
2299 e.Param.NoteSynthParam.Delta = fDuration;
2300 e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2301
2302 pEngineChannel->ScheduleEventMicroSec(&e, 0);
2303 }
2304 // now schedule a "volume" change, simply one time slice ahead, with
2305 // the final fade out volume (0.0)
2306 {
2307 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
2308 e.Init(); // clear IDs
2309 e.Type = Event::type_note_synth_param;
2310 e.Param.NoteSynthParam.NoteID = id.noteID();
2311 e.Param.NoteSynthParam.Type = Event::synth_param_volume;
2312 e.Param.NoteSynthParam.Delta = 0.f;
2313 e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2314
2315 // scheduling with 0 delay would also work here, but +1 is more
2316 // safe regarding potential future implementation changes of the
2317 // scheduler (see API comments of RTAVLTree::insert())
2318 pEngineChannel->ScheduleEventMicroSec(&e, 1);
2319 }
2320 // and finally if stopping the note was requested after the fade out
2321 // completed, then schedule to kill the voice after the requested
2322 // duration
2323 if (stop) {
2324 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
2325 e.Init(); // clear IDs
2326 e.Type = Event::type_kill_note;
2327 e.Param.Note.ID = id.noteID();
2328 e.Param.Note.Key = pNote->hostKey;
2329
2330 pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
2331 }
2332 }
2333 }
2334
2335 return successResult();
2336 }
2337
2338 // get_event_par() function
2339
2340 InstrumentScriptVMFunction_get_event_par::InstrumentScriptVMFunction_get_event_par(InstrumentScriptVM* parent)
2341 : m_vm(parent)
2342 {
2343 }
2344
2345 VMFnResult* InstrumentScriptVMFunction_get_event_par::exec(VMFnArgs* args) {
2346 AbstractEngineChannel* pEngineChannel =
2347 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2348
2349 const ScriptID id = args->arg(0)->asInt()->evalInt();
2350 if (!id) {
2351 wrnMsg("get_event_par(): note ID for argument 1 may not be zero");
2352 return successResult(0);
2353 }
2354 if (!id.isNoteID()) {
2355 wrnMsg("get_event_par(): argument 1 is not a note ID");
2356 return successResult(0);
2357 }
2358
2359 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2360 if (!pNote) {
2361 wrnMsg("get_event_par(): no note alive with that note ID of argument 1");
2362 return successResult(0);
2363 }
2364
2365 const vmint parameter = args->arg(1)->asInt()->evalInt();
2366 switch (parameter) {
2367 case EVENT_PAR_NOTE:
2368 return successResult(pNote->cause.Param.Note.Key);
2369 case EVENT_PAR_VELOCITY:
2370 return successResult(pNote->cause.Param.Note.Velocity);
2371 case EVENT_PAR_VOLUME:
2372 return successResult(
2373 RTMath::LinRatioToDecibel(pNote->Override.Volume.Value) * 1000.f
2374 );
2375 case EVENT_PAR_TUNE:
2376 return successResult(
2377 RTMath::FreqRatioToCents(pNote->Override.Pitch.Value) * 1000.f
2378 );
2379 case EVENT_PAR_0:
2380 return successResult(pNote->userPar[0]);
2381 case EVENT_PAR_1:
2382 return successResult(pNote->userPar[1]);
2383 case EVENT_PAR_2:
2384 return successResult(pNote->userPar[2]);
2385 case EVENT_PAR_3:
2386 return successResult(pNote->userPar[3]);
2387 }
2388
2389 wrnMsg("get_event_par(): argument 2 is an invalid event parameter");
2390 return successResult(0);
2391 }
2392
2393 // set_event_par() function
2394
2395 InstrumentScriptVMFunction_set_event_par::InstrumentScriptVMFunction_set_event_par(InstrumentScriptVM* parent)
2396 : m_vm(parent)
2397 {
2398 }
2399
2400 VMFnResult* InstrumentScriptVMFunction_set_event_par::exec(VMFnArgs* args) {
2401 AbstractEngineChannel* pEngineChannel =
2402 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2403
2404 const ScriptID id = args->arg(0)->asInt()->evalInt();
2405 if (!id) {
2406 wrnMsg("set_event_par(): note ID for argument 1 may not be zero");
2407 return successResult();
2408 }
2409 if (!id.isNoteID()) {
2410 wrnMsg("set_event_par(): argument 1 is not a note ID");
2411 return successResult();
2412 }
2413
2414 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2415 if (!pNote) return successResult();
2416
2417 const vmint parameter = args->arg(1)->asInt()->evalInt();
2418 const vmint value = args->arg(2)->asInt()->evalInt();
2419
2420 switch (parameter) {
2421 case EVENT_PAR_NOTE:
2422 if (value < 0 || value > 127) {
2423 wrnMsg("set_event_par(): note number of argument 3 is out of range");
2424 return successResult();
2425 }
2426 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
2427 pNote->cause.Param.Note.Key = value;
2428 m_vm->m_event->cause.Param.Note.Key = value;
2429 } else {
2430 wrnMsg("set_event_par(): note number can only be changed when note is new");
2431 }
2432 return successResult();
2433 case EVENT_PAR_VELOCITY:
2434 if (value < 0 || value > 127) {
2435 wrnMsg("set_event_par(): velocity 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.Velocity = value;
2440 m_vm->m_event->cause.Param.Note.Velocity = value;
2441 } else {
2442 wrnMsg("set_event_par(): velocity can only be changed when note is new");
2443 }
2444 return successResult();
2445 case EVENT_PAR_VOLUME:
2446 wrnMsg("set_event_par(): changing volume by this function is currently not supported, use change_vol() instead");
2447 return successResult();
2448 case EVENT_PAR_TUNE:
2449 wrnMsg("set_event_par(): changing tune by this function is currently not supported, use change_tune() instead");
2450 return successResult();
2451 case EVENT_PAR_0:
2452 pNote->userPar[0] = value;
2453 return successResult();
2454 case EVENT_PAR_1:
2455 pNote->userPar[1] = value;
2456 return successResult();
2457 case EVENT_PAR_2:
2458 pNote->userPar[2] = value;
2459 return successResult();
2460 case EVENT_PAR_3:
2461 pNote->userPar[3] = value;
2462 return successResult();
2463 }
2464
2465 wrnMsg("set_event_par(): argument 2 is an invalid event parameter");
2466 return successResult();
2467 }
2468
2469 // change_note() function
2470
2471 InstrumentScriptVMFunction_change_note::InstrumentScriptVMFunction_change_note(InstrumentScriptVM* parent)
2472 : m_vm(parent)
2473 {
2474 }
2475
2476 VMFnResult* InstrumentScriptVMFunction_change_note::exec(VMFnArgs* args) {
2477 AbstractEngineChannel* pEngineChannel =
2478 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2479
2480 const ScriptID id = args->arg(0)->asInt()->evalInt();
2481 if (!id) {
2482 wrnMsg("change_note(): note ID for argument 1 may not be zero");
2483 return successResult();
2484 }
2485 if (!id.isNoteID()) {
2486 wrnMsg("change_note(): argument 1 is not a note ID");
2487 return successResult();
2488 }
2489
2490 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2491 if (!pNote) return successResult();
2492
2493 const vmint value = args->arg(1)->asInt()->evalInt();
2494 if (value < 0 || value > 127) {
2495 wrnMsg("change_note(): note number of argument 2 is out of range");
2496 return successResult();
2497 }
2498
2499 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
2500 pNote->cause.Param.Note.Key = value;
2501 m_vm->m_event->cause.Param.Note.Key = value;
2502 } else {
2503 wrnMsg("change_note(): note number can only be changed when note is new");
2504 }
2505
2506 return successResult();
2507 }
2508
2509 // change_velo() function
2510
2511 InstrumentScriptVMFunction_change_velo::InstrumentScriptVMFunction_change_velo(InstrumentScriptVM* parent)
2512 : m_vm(parent)
2513 {
2514 }
2515
2516 VMFnResult* InstrumentScriptVMFunction_change_velo::exec(VMFnArgs* args) {
2517 AbstractEngineChannel* pEngineChannel =
2518 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2519
2520 const ScriptID id = args->arg(0)->asInt()->evalInt();
2521 if (!id) {
2522 wrnMsg("change_velo(): note ID for argument 1 may not be zero");
2523 return successResult();
2524 }
2525 if (!id.isNoteID()) {
2526 wrnMsg("change_velo(): argument 1 is not a note ID");
2527 return successResult();
2528 }
2529
2530 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2531 if (!pNote) return successResult();
2532
2533 const vmint value = args->arg(1)->asInt()->evalInt();
2534 if (value < 0 || value > 127) {
2535 wrnMsg("change_velo(): velocity of argument 2 is out of range");
2536 return successResult();
2537 }
2538
2539 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
2540 pNote->cause.Param.Note.Velocity = value;
2541 m_vm->m_event->cause.Param.Note.Velocity = value;
2542 } else {
2543 wrnMsg("change_velo(): velocity can only be changed when note is new");
2544 }
2545
2546 return successResult();
2547 }
2548
2549 // change_play_pos() function
2550
2551 InstrumentScriptVMFunction_change_play_pos::InstrumentScriptVMFunction_change_play_pos(InstrumentScriptVM* parent)
2552 : m_vm(parent)
2553 {
2554 }
2555
2556 bool InstrumentScriptVMFunction_change_play_pos::acceptsArgType(vmint iArg, ExprType_t type) const {
2557 if (iArg == 0)
2558 return type == INT_EXPR;
2559 else
2560 return type == INT_EXPR || type == REAL_EXPR;
2561 }
2562
2563 bool InstrumentScriptVMFunction_change_play_pos::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
2564 if (iArg == 1)
2565 return type == VM_NO_UNIT || type == VM_SECOND;
2566 else
2567 return type == VM_NO_UNIT;
2568 }
2569
2570 bool InstrumentScriptVMFunction_change_play_pos::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
2571 return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
2572 }
2573
2574 VMFnResult* InstrumentScriptVMFunction_change_play_pos::exec(VMFnArgs* args) {
2575 const ScriptID id = args->arg(0)->asInt()->evalInt();
2576 if (!id) {
2577 wrnMsg("change_play_pos(): note ID for argument 1 may not be zero");
2578 return successResult();
2579 }
2580 if (!id.isNoteID()) {
2581 wrnMsg("change_play_pos(): argument 1 is not a note ID");
2582 return successResult();
2583 }
2584
2585 StdUnit_t unit = args->arg(1)->asNumber()->unitType();
2586 const vmint pos =
2587 (unit) ?
2588 args->arg(1)->asNumber()->evalCastInt(VM_MICRO) :
2589 args->arg(1)->asNumber()->evalCastInt();
2590 if (pos < 0) {
2591 wrnMsg("change_play_pos(): playback position of argument 2 may not be negative");
2592 return successResult();
2593 }
2594
2595 AbstractEngineChannel* pEngineChannel =
2596 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2597
2598 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2599 if (!pNote) return successResult();
2600
2601 pNote->Override.SampleOffset =
2602 (decltype(pNote->Override.SampleOffset)) pos;
2603
2604 return successResult();
2605 }
2606
2607 // event_status() function
2608
2609 InstrumentScriptVMFunction_event_status::InstrumentScriptVMFunction_event_status(InstrumentScriptVM* parent)
2610 : m_vm(parent)
2611 {
2612 }
2613
2614 VMFnResult* InstrumentScriptVMFunction_event_status::exec(VMFnArgs* args) {
2615 AbstractEngineChannel* pEngineChannel =
2616 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2617
2618 const ScriptID id = args->arg(0)->asInt()->evalInt();
2619 if (!id) {
2620 wrnMsg("event_status(): note ID for argument 1 may not be zero");
2621 return successResult(EVENT_STATUS_INACTIVE);
2622 }
2623 if (!id.isNoteID()) {
2624 wrnMsg("event_status(): argument 1 is not a note ID");
2625 return successResult(EVENT_STATUS_INACTIVE);
2626 }
2627
2628 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2629 return successResult(pNote ? EVENT_STATUS_NOTE_QUEUE : EVENT_STATUS_INACTIVE);
2630 }
2631
2632 // callback_status() function
2633
2634 InstrumentScriptVMFunction_callback_status::InstrumentScriptVMFunction_callback_status(InstrumentScriptVM* parent)
2635 : m_vm(parent)
2636 {
2637 }
2638
2639 VMFnResult* InstrumentScriptVMFunction_callback_status::exec(VMFnArgs* args) {
2640 const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
2641 if (!id) {
2642 wrnMsg("callback_status(): callback ID for argument 1 may not be zero");
2643 return successResult();
2644 }
2645
2646 AbstractEngineChannel* pEngineChannel =
2647 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2648
2649 RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
2650 if (!itCallback)
2651 return successResult(CALLBACK_STATUS_TERMINATED);
2652
2653 return successResult(
2654 (m_vm->m_event->execCtx == itCallback->execCtx) ?
2655 CALLBACK_STATUS_RUNNING : CALLBACK_STATUS_QUEUE
2656 );
2657 }
2658
2659 // wait() function (overrides core wait() implementation)
2660
2661 InstrumentScriptVMFunction_wait::InstrumentScriptVMFunction_wait(InstrumentScriptVM* parent)
2662 : CoreVMFunction_wait(parent)
2663 {
2664 }
2665
2666 VMFnResult* InstrumentScriptVMFunction_wait::exec(VMFnArgs* args) {
2667 InstrumentScriptVM* m_vm = (InstrumentScriptVM*) vm;
2668
2669 // this might be set by passing 1 with the 2nd argument of built-in stop_wait() function
2670 if (m_vm->m_event->ignoreAllWaitCalls) return successResult();
2671
2672 return CoreVMFunction_wait::exec(args);
2673 }
2674
2675 // stop_wait() function
2676
2677 InstrumentScriptVMFunction_stop_wait::InstrumentScriptVMFunction_stop_wait(InstrumentScriptVM* parent)
2678 : m_vm(parent)
2679 {
2680 }
2681
2682 VMFnResult* InstrumentScriptVMFunction_stop_wait::exec(VMFnArgs* args) {
2683 AbstractEngineChannel* pEngineChannel =
2684 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2685
2686 const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
2687 if (!id) {
2688 wrnMsg("stop_wait(): callback ID for argument 1 may not be zero");
2689 return successResult();
2690 }
2691
2692 RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
2693 if (!itCallback) return successResult(); // ignore if callback is i.e. not alive anymore
2694
2695 const bool disableWaitForever =
2696 (args->argsCount() >= 2) ? (args->arg(1)->asInt()->evalInt() == 1) : false;
2697
2698 pEngineChannel->ScheduleResumeOfScriptCallback(
2699 itCallback, m_vm->m_event->scheduleTime, disableWaitForever
2700 );
2701
2702 return successResult();
2703 }
2704
2705 // abort() function
2706
2707 InstrumentScriptVMFunction_abort::InstrumentScriptVMFunction_abort(InstrumentScriptVM* parent)
2708 : m_vm(parent)
2709 {
2710 }
2711
2712 VMFnResult* InstrumentScriptVMFunction_abort::exec(VMFnArgs* args) {
2713 const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
2714 if (!id) {
2715 wrnMsg("abort(): callback ID for argument 1 may not be zero");
2716 return successResult();
2717 }
2718
2719 AbstractEngineChannel* pEngineChannel =
2720 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2721
2722 RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
2723 if (!itCallback) return successResult(); // ignore if callback is i.e. not alive anymore
2724
2725 itCallback->execCtx->signalAbort();
2726
2727 return successResult();
2728 }
2729
2730 // fork() function
2731
2732 InstrumentScriptVMFunction_fork::InstrumentScriptVMFunction_fork(InstrumentScriptVM* parent)
2733 : m_vm(parent)
2734 {
2735 }
2736
2737 VMFnResult* InstrumentScriptVMFunction_fork::exec(VMFnArgs* args) {
2738 // check if this is actually the parent going to fork, or rather one of
2739 // the children which is already forked
2740 if (m_vm->m_event->forkIndex != 0) { // this is the entry point for a child ...
2741 int forkResult = m_vm->m_event->forkIndex;
2742 // reset so that this child may i.e. also call fork() later on
2743 m_vm->m_event->forkIndex = 0;
2744 return successResult(forkResult);
2745 }
2746
2747 // if we are here, then this is the parent, so we must fork this parent
2748
2749 const vmint n =
2750 (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : 1;
2751 const bool bAutoAbort =
2752 (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : true;
2753
2754 if (m_vm->m_event->countChildHandlers() + n > MAX_FORK_PER_SCRIPT_HANDLER) {
2755 wrnMsg("fork(): requested amount would exceed allowed limit per event handler");
2756 return successResult(-1);
2757 }
2758
2759 AbstractEngineChannel* pEngineChannel =
2760 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2761
2762 if (!pEngineChannel->hasFreeScriptCallbacks(n)) {
2763 wrnMsg("fork(): global limit of event handlers exceeded");
2764 return successResult(-1);
2765 }
2766
2767 for (int iChild = 0; iChild < n; ++iChild) {
2768 RTList<ScriptEvent>::Iterator itChild =
2769 pEngineChannel->forkScriptCallback(m_vm->m_event, bAutoAbort);
2770 if (!itChild) { // should never happen, otherwise its a bug ...
2771 errMsg("fork(): internal error while allocating child");
2772 return errorResult(-1); // terminate script
2773 }
2774 // since both parent, as well all child script execution instances
2775 // all land in this exec() method, the following is (more or less)
2776 // the only feature that lets us distinguish the parent and
2777 // respective children from each other in this exec() method
2778 itChild->forkIndex = iChild + 1;
2779 }
2780
2781 return successResult(0);
2782 }
2783
2784 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC