/[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 3715 - (show annotations) (download)
Sun Jan 12 17:07:34 2020 UTC (4 years, 3 months ago) by schoenebeck
File size: 121205 byte(s)
* NKSP: Removed upper value constraint for 2nd argument of built-in
  functions "change_amp_lfo_freq()", "change_cutoff_lfo_freq()",
  "change_pitch_lfo_freq()","change_reso()", "change_cutoff()",
  "change_amp_lfo_depth()", "change_cutoff_lfo_depth()" and
  "change_pitch_lfo_depth()", to allow e.g. passing value 2000000
  to double the LFO frequency / resonance / cutoff frequency.

* Bumped version (2.1.1.svn38).

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

  ViewVC Help
Powered by ViewVC