/[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 3742 - (show annotations) (download)
Fri Feb 14 15:15:13 2020 UTC (4 years, 1 month ago) by schoenebeck
File size: 121400 byte(s)
* NKSP: Fixed incorrect EG times if standard measuring unit was
  passed to built-in functions "change_attack()", "change_decay()"
  or "change_release()".

* Bumped version (2.1.1.svn48).

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

  ViewVC Help
Powered by ViewVC