/[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 3561 - (show annotations) (download)
Fri Aug 23 11:44:00 2019 UTC (4 years, 7 months ago) by schoenebeck
File size: 106557 byte(s)
NKSP: Added standard units support for numbers and final "!" operator:

* NKSP strictness: Variable names, function names and preprocessor condition
  names must start with a regular character (a-z or A-Z); starting them with
  a digit or underscore is no longer allowed.

* NKSP parser fix: equal comparison operator "=" and not equal comparison
  operator "#" must only accept integer operands.

* NKSP language: Implemented support for standard units like Hertz, seconds,
  Bel including support for metric unit prefixes; so one can now e.g.
  conveniently use numbers in scripts like "5us" meaning "5 microseconds",
  or e.g. "12kHz" meaning "12 kilo Hertz", or e.g. "-14mdB" meaning
  "minus 14 Millidecibel", or e.g. "28c" meaning "28 cents" (for tuning).

* NKSP language: Introduced "final" operator "!" which is specifically
  intended for synthesis parameter values to denote that the synthesis
  parameter value is intended to be the "final" value for that synthesis
  parameter that should explicitly be used by the engine and thus causing
  the sampler engine to ignore all other modulation sources for the same
  synthesis parameter (like e.g. LFO, EG); by simply prefixing a value,
  variable or formula with this new "!" operator the expression is marked as
  being "final".

* Bumped version (2.1.1.svn4).

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

  ViewVC Help
Powered by ViewVC