/[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 3564 - (show annotations) (download)
Sat Aug 24 09:18:57 2019 UTC (4 years, 7 months ago) by schoenebeck
File size: 107910 byte(s)
NKSP: Bug fixes regarding new measurement units feature:

* Fix: Engine internal Fade of script synthesis parameters volume, pitch
  and pan were not rendered at all.

* Fix: Backward compatibility of built-in function arguments without a
  metric unit prefix was broken (resulted in incorrect value
  transformation).

* Fix: built-in script function change_play_pos() resolved wrong arguments.

* Bumped version (2.1.1.svn5).

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

  ViewVC Help
Powered by ViewVC