/[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 3587 - (show annotations) (download)
Sat Aug 31 12:08:49 2019 UTC (4 years, 7 months ago) by schoenebeck
File size: 110614 byte(s)
NKSP: Real number support for core instrument built-in functions.

* NKSP: Built-in function "play_note()" accepts now real numbers and
  seconds as unit type as well for its 3rd and 4th function arguments.

* NKSP: The following built-in functions accept now real numbers as well for
  their 2nd function argument: "change_vol()", "change_tune()",
  "change_cutoff()", "change_attack()", "change_decay()",
  "change_release()", "change_sustain()", "change_cutoff_attack()",
  "change_cutoff_decay()", "change_cutoff_sustain()",
  "change_cutoff_release()", "change_amp_lfo_freq()",
  "change_cutoff_lfo_freq()", "change_pitch_lfo_freq()",
  "change_vol_time()", "change_tune_time()", "change_pan_time()",
  "fade_in()", "fade_out()", "change_play_pos()".

* NKSP: Fixed built-in function "change_play_pos()" not having accepted
  metric prefixes at all.

* Bumped version (2.1.1.svn12).

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

  ViewVC Help
Powered by ViewVC