/[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 3588 - (show annotations) (download)
Sun Sep 1 16:06:48 2019 UTC (4 years, 6 months ago) by schoenebeck
File size: 118441 byte(s)
NKSP: Built-in instrument functions fixes & hardening:

* Fixed the following built-in functions having misinterpreted values given
  with unit type (for their 2nd argument) as if they were relative values
  (that is as if they were passed without a unit type): "change_attack()",
  "change_decay()", "change_release()", "change_cutoff_attack()",
  "change_cutoff_decay()", "change_cutoff_release()".

* Fixed the following built-in functions having applied completely wrong
  'final' values: "change_sustain()", "change_cutoff_sustain()" (since the
  respective EGs being their modulation sink assume uint data type with
  value range 0..1000 instead of 0.0..1.0.

* Added individual parse-time checks of function arguments for the following
  built-in functions: "play_note()", "note_off()", "set_event_mark()",
  "delete_event_mark()", "by_marks()", "change_cutoff()", "change_attack()",
  "change_decay()", "change_release()", "change_cutoff_attack()",
  "change_cutoff_decay()", "change_cutoff_release()",
  "change_amp_lfo_freq()", "change_cutoff_lfo_freq()",
  "change_pitch_lfo_freq()", "change_vol_time()", "change_tune_time()" and
  "change_pan_time()".

* Don't abort function call if unit type was used and at the same time
  'final' operator was omitted for the primary value argument of the
  following built-in functions: "change_cutoff()", "change_attack()",
  "change_decay()", "change_release()", "change_cutoff_attack()",
  "change_cutoff_decay()", "change_cutoff_release()",
  "change_amp_lfo_freq()", "change_cutoff_lfo_freq()",
  "change_pitch_lfo_freq()", "change_vol_time()", "change_tune_time()",
  "change_pan_time()", instead imply 'final'ness at runtime and raise an
  appropriate parser warning at parse time.

* Bumped version (2.1.1.svn13).

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

  ViewVC Help
Powered by ViewVC