/[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 3691 - (show annotations) (download)
Fri Jan 3 12:35:20 2020 UTC (4 years, 3 months ago) by schoenebeck
File size: 121306 byte(s)
* NKSP: Implemented built-in script function "set_rpn()".

* NKSP: Implemented built-in script function "set_nrpn()".

* Bumped version (2.1.1.svn31).

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

  ViewVC Help
Powered by ViewVC