/[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 3212 - (show annotations) (download)
Thu May 25 13:17:47 2017 UTC (6 years, 10 months ago) by schoenebeck
File size: 72717 byte(s)
* NKSP: Built-in "ignore_event()" function: argument is now optional,
  like with built-in function "ignore_controller()".

1 /*
2 * Copyright (c) 2014-2017 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
14 namespace LinuxSampler {
15
16 // play_note() function
17
18 InstrumentScriptVMFunction_play_note::InstrumentScriptVMFunction_play_note(InstrumentScriptVM* parent)
19 : m_vm(parent)
20 {
21 }
22
23 VMFnResult* InstrumentScriptVMFunction_play_note::exec(VMFnArgs* args) {
24 int note = args->arg(0)->asInt()->evalInt();
25 int velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127;
26 int sampleoffset = (args->argsCount() >= 3) ? args->arg(2)->asInt()->evalInt() : 0;
27 int 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 (sampleoffset < 0) {
40 errMsg("play_note(): argument 3 may not be a negative sample offset");
41 return errorResult(0);
42 } else if (sampleoffset != 0) {
43 wrnMsg("play_note(): argument 3 does not support a sample offset other than 0 yet");
44 }
45
46 if (duration < -1) {
47 errMsg("play_note(): argument 4 must be a duration value of at least -1 or higher");
48 return errorResult(0);
49 }
50
51 AbstractEngineChannel* pEngineChannel =
52 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
53
54 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
55 e.Init(); // clear IDs
56 e.Type = Event::type_play_note;
57 e.Param.Note.Key = note;
58 e.Param.Note.Velocity = velocity;
59 // make this new note dependent to the life time of the original note
60 if (duration == -1) {
61 if (m_vm->currentVMEventHandler()->eventHandlerType() != VM_EVENT_HANDLER_NOTE) {
62 errMsg("play_note(): -1 for argument 4 may only be used for note event handlers");
63 return errorResult(0);
64 }
65 e.Param.Note.ParentNoteID = m_vm->m_event->cause.Param.Note.ID;
66 }
67
68 const note_id_t id = pEngineChannel->ScheduleNoteMicroSec(&e, 0);
69
70 // if a duration is supplied (and play-note event was scheduled
71 // successfully above), then schedule a subsequent stop-note event
72 if (id && duration > 0) {
73 e.Type = Event::type_stop_note;
74 e.Param.Note.ID = id;
75 e.Param.Note.Velocity = 127;
76 pEngineChannel->ScheduleEventMicroSec(&e, duration);
77 }
78
79 // even if id is null, don't return an errorResult() here, because that
80 // would abort the script, and under heavy load it may be considerable
81 // that ScheduleNoteMicroSec() fails above, so simply ignore that
82 return successResult( ScriptID::fromNoteID(id) );
83 }
84
85 // set_controller() function
86
87 InstrumentScriptVMFunction_set_controller::InstrumentScriptVMFunction_set_controller(InstrumentScriptVM* parent)
88 : m_vm(parent)
89 {
90 }
91
92 VMFnResult* InstrumentScriptVMFunction_set_controller::exec(VMFnArgs* args) {
93 int controller = args->arg(0)->asInt()->evalInt();
94 int value = args->arg(1)->asInt()->evalInt();
95
96 AbstractEngineChannel* pEngineChannel =
97 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
98
99 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
100 e.Init(); // clear IDs
101 if (controller == CTRL_TABLE_IDX_AFTERTOUCH) {
102 e.Type = Event::type_channel_pressure;
103 e.Param.ChannelPressure.Value = value & 127;
104 } else if (controller == CTRL_TABLE_IDX_PITCHBEND) {
105 e.Type = Event::type_pitchbend;
106 e.Param.Pitch.Pitch = value;
107 } else if (controller >= 0 && controller <= 127) {
108 e.Type = Event::type_control_change;
109 e.Param.CC.Controller = controller;
110 e.Param.CC.Value = value;
111 } else {
112 errMsg("set_controller(): argument 1 is an invalid controller");
113 return errorResult();
114 }
115
116 const event_id_t id = pEngineChannel->ScheduleEventMicroSec(&e, 0);
117
118 // even if id is null, don't return an errorResult() here, because that
119 // would abort the script, and under heavy load it may be considerable
120 // that ScheduleEventMicroSec() fails above, so simply ignore that
121 return successResult( ScriptID::fromEventID(id) );
122 }
123
124 // ignore_event() function
125
126 InstrumentScriptVMFunction_ignore_event::InstrumentScriptVMFunction_ignore_event(InstrumentScriptVM* parent)
127 : m_vm(parent)
128 {
129 }
130
131 bool InstrumentScriptVMFunction_ignore_event::acceptsArgType(int iArg, ExprType_t type) const {
132 return type == INT_EXPR || type == INT_ARR_EXPR;
133 }
134
135 VMFnResult* InstrumentScriptVMFunction_ignore_event::exec(VMFnArgs* args) {
136 AbstractEngineChannel* pEngineChannel =
137 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
138
139 if (args->argsCount() == 0 || args->arg(0)->exprType() == INT_EXPR) {
140 const ScriptID id = (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : m_vm->m_event->id;
141 if (!id && args->argsCount() >= 1) {
142 wrnMsg("ignore_event(): event ID argument may not be zero");
143 // not errorResult(), because that would abort the script, not intentional in this case
144 return successResult();
145 }
146 pEngineChannel->IgnoreEventByScriptID(id);
147 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
148 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
149 for (int i = 0; i < ids->arraySize(); ++i) {
150 const ScriptID id = ids->evalIntElement(i);
151 pEngineChannel->IgnoreEventByScriptID(id);
152 }
153 }
154
155 return successResult();
156 }
157
158 // ignore_controller() function
159
160 InstrumentScriptVMFunction_ignore_controller::InstrumentScriptVMFunction_ignore_controller(InstrumentScriptVM* parent)
161 : m_vm(parent)
162 {
163 }
164
165 VMFnResult* InstrumentScriptVMFunction_ignore_controller::exec(VMFnArgs* args) {
166 const ScriptID id = (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : m_vm->m_event->id;
167 if (!id && args->argsCount() >= 1) {
168 wrnMsg("ignore_controller(): event ID argument may not be zero");
169 return successResult();
170 }
171
172 AbstractEngineChannel* pEngineChannel =
173 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
174
175 pEngineChannel->IgnoreEventByScriptID(id);
176
177 return successResult();
178 }
179
180 // note_off() function
181
182 InstrumentScriptVMFunction_note_off::InstrumentScriptVMFunction_note_off(InstrumentScriptVM* parent)
183 : m_vm(parent)
184 {
185 }
186
187 bool InstrumentScriptVMFunction_note_off::acceptsArgType(int iArg, ExprType_t type) const {
188 return type == INT_EXPR || type == INT_ARR_EXPR;
189 }
190
191 VMFnResult* InstrumentScriptVMFunction_note_off::exec(VMFnArgs* args) {
192 AbstractEngineChannel* pEngineChannel =
193 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
194
195 int velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127;
196 if (velocity < 0 || velocity > 127) {
197 errMsg("note_off(): argument 2 is an invalid velocity value");
198 return errorResult();
199 }
200
201 if (args->arg(0)->exprType() == INT_EXPR) {
202 const ScriptID id = args->arg(0)->asInt()->evalInt();
203 if (!id) {
204 wrnMsg("note_off(): note ID for argument 1 may not be zero");
205 return successResult();
206 }
207 if (!id.isNoteID()) {
208 wrnMsg("note_off(): argument 1 is not a note ID");
209 return successResult();
210 }
211
212 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
213 if (!pNote) return successResult();
214
215 Event e = pNote->cause;
216 e.Init(); // clear IDs
217 e.CopyTimeFrom(m_vm->m_event->cause); // set fragment time for "now"
218 e.Type = Event::type_stop_note;
219 e.Param.Note.ID = id.noteID();
220 e.Param.Note.Key = pNote->hostKey;
221 e.Param.Note.Velocity = velocity;
222
223 pEngineChannel->ScheduleEventMicroSec(&e, 0);
224 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
225 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
226 for (int i = 0; i < ids->arraySize(); ++i) {
227 const ScriptID id = ids->evalIntElement(i);
228 if (!id || !id.isNoteID()) continue;
229
230 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
231 if (!pNote) continue;
232
233 Event e = pNote->cause;
234 e.Init(); // clear IDs
235 e.CopyTimeFrom(m_vm->m_event->cause); // set fragment time for "now"
236 e.Type = Event::type_stop_note;
237 e.Param.Note.ID = id.noteID();
238 e.Param.Note.Key = pNote->hostKey;
239 e.Param.Note.Velocity = velocity;
240
241 pEngineChannel->ScheduleEventMicroSec(&e, 0);
242 }
243 }
244
245 return successResult();
246 }
247
248 // set_event_mark() function
249
250 InstrumentScriptVMFunction_set_event_mark::InstrumentScriptVMFunction_set_event_mark(InstrumentScriptVM* parent)
251 : m_vm(parent)
252 {
253 }
254
255 VMFnResult* InstrumentScriptVMFunction_set_event_mark::exec(VMFnArgs* args) {
256 const ScriptID id = args->arg(0)->asInt()->evalInt();
257 const int groupID = args->arg(1)->asInt()->evalInt();
258
259 if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
260 errMsg("set_event_mark(): argument 2 is an invalid group id");
261 return errorResult();
262 }
263
264 AbstractEngineChannel* pEngineChannel =
265 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
266
267 // check if the event/note still exists
268 switch (id.type()) {
269 case ScriptID::EVENT: {
270 RTList<Event>::Iterator itEvent = pEngineChannel->pEngine->EventByID( id.eventID() );
271 if (!itEvent) return successResult();
272 break;
273 }
274 case ScriptID::NOTE: {
275 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
276 if (!pNote) return successResult();
277 break;
278 }
279 }
280
281 pEngineChannel->pScript->eventGroups[groupID].insert(id);
282
283 return successResult();
284 }
285
286 // delete_event_mark() function
287
288 InstrumentScriptVMFunction_delete_event_mark::InstrumentScriptVMFunction_delete_event_mark(InstrumentScriptVM* parent)
289 : m_vm(parent)
290 {
291 }
292
293 VMFnResult* InstrumentScriptVMFunction_delete_event_mark::exec(VMFnArgs* args) {
294 const ScriptID id = args->arg(0)->asInt()->evalInt();
295 const int groupID = args->arg(1)->asInt()->evalInt();
296
297 if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
298 errMsg("delete_event_mark(): argument 2 is an invalid group id");
299 return errorResult();
300 }
301
302 AbstractEngineChannel* pEngineChannel =
303 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
304
305 pEngineChannel->pScript->eventGroups[groupID].erase(id);
306
307 return successResult();
308 }
309
310 // by_marks() function
311
312 InstrumentScriptVMFunction_by_marks::InstrumentScriptVMFunction_by_marks(InstrumentScriptVM* parent)
313 : m_vm(parent)
314 {
315 }
316
317 int InstrumentScriptVMFunction_by_marks::Result::arraySize() const {
318 return eventGroup->size();
319 }
320
321 int InstrumentScriptVMFunction_by_marks::Result::evalIntElement(uint i) {
322 return (*eventGroup)[i];
323 }
324
325 VMFnResult* InstrumentScriptVMFunction_by_marks::errorResult() {
326 m_result.eventGroup = NULL;
327 m_result.flags = StmtFlags_t(STMT_ABORT_SIGNALLED | STMT_ERROR_OCCURRED);
328 return &m_result;
329 }
330
331 VMFnResult* InstrumentScriptVMFunction_by_marks::successResult(EventGroup* eventGroup) {
332 m_result.eventGroup = eventGroup;
333 m_result.flags = STMT_SUCCESS;
334 return &m_result;
335 }
336
337 VMFnResult* InstrumentScriptVMFunction_by_marks::exec(VMFnArgs* args) {
338 int groupID = args->arg(0)->asInt()->evalInt();
339
340 if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
341 errMsg("by_marks(): argument is an invalid group id");
342 return errorResult();
343 }
344
345 AbstractEngineChannel* pEngineChannel =
346 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
347
348 return successResult( &pEngineChannel->pScript->eventGroups[groupID] );
349 }
350
351 // change_vol() function
352
353 InstrumentScriptVMFunction_change_vol::InstrumentScriptVMFunction_change_vol(InstrumentScriptVM* parent)
354 : m_vm(parent)
355 {
356 }
357
358 bool InstrumentScriptVMFunction_change_vol::acceptsArgType(int iArg, ExprType_t type) const {
359 if (iArg == 0)
360 return type == INT_EXPR || type == INT_ARR_EXPR;
361 else
362 return type == INT_EXPR;
363 }
364
365 VMFnResult* InstrumentScriptVMFunction_change_vol::exec(VMFnArgs* args) {
366 int volume = args->arg(1)->asInt()->evalInt(); // volume change in milli dB
367 bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
368 const float fVolumeLin = RTMath::DecibelToLinRatio(float(volume) / 1000.f);
369
370 AbstractEngineChannel* pEngineChannel =
371 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
372
373 if (args->arg(0)->exprType() == INT_EXPR) {
374 const ScriptID id = args->arg(0)->asInt()->evalInt();
375 if (!id) {
376 wrnMsg("change_vol(): note ID for argument 1 may not be zero");
377 return successResult();
378 }
379 if (!id.isNoteID()) {
380 wrnMsg("change_vol(): argument 1 is not a note ID");
381 return successResult();
382 }
383
384 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
385 if (!pNote) return successResult();
386
387 // if change_vol() was called immediately after note was triggered
388 // then immediately apply the volume to note object
389 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
390 if (relative)
391 pNote->Override.Volume *= fVolumeLin;
392 else
393 pNote->Override.Volume = fVolumeLin;
394 } else { // otherwise schedule the volume change ...
395 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
396 e.Init(); // clear IDs
397 e.Type = Event::type_note_synth_param;
398 e.Param.NoteSynthParam.NoteID = id.noteID();
399 e.Param.NoteSynthParam.Type = Event::synth_param_volume;
400 e.Param.NoteSynthParam.Delta = fVolumeLin;
401 e.Param.NoteSynthParam.Relative = relative;
402
403 pEngineChannel->ScheduleEventMicroSec(&e, 0);
404 }
405 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
406 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
407 for (int i = 0; i < ids->arraySize(); ++i) {
408 const ScriptID id = ids->evalIntElement(i);
409 if (!id || !id.isNoteID()) continue;
410
411 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
412 if (!pNote) continue;
413
414 // if change_vol() was called immediately after note was triggered
415 // then immediately apply the volume to Note object
416 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
417 if (relative)
418 pNote->Override.Volume *= fVolumeLin;
419 else
420 pNote->Override.Volume = fVolumeLin;
421 } else { // otherwise schedule the volume change ...
422 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
423 e.Init(); // clear IDs
424 e.Type = Event::type_note_synth_param;
425 e.Param.NoteSynthParam.NoteID = id.noteID();
426 e.Param.NoteSynthParam.Type = Event::synth_param_volume;
427 e.Param.NoteSynthParam.Delta = fVolumeLin;
428 e.Param.NoteSynthParam.Relative = relative;
429
430 pEngineChannel->ScheduleEventMicroSec(&e, 0);
431 }
432 }
433 }
434
435 return successResult();
436 }
437
438 // change_tune() function
439
440 InstrumentScriptVMFunction_change_tune::InstrumentScriptVMFunction_change_tune(InstrumentScriptVM* parent)
441 : m_vm(parent)
442 {
443 }
444
445 bool InstrumentScriptVMFunction_change_tune::acceptsArgType(int iArg, ExprType_t type) const {
446 if (iArg == 0)
447 return type == INT_EXPR || type == INT_ARR_EXPR;
448 else
449 return type == INT_EXPR;
450 }
451
452 VMFnResult* InstrumentScriptVMFunction_change_tune::exec(VMFnArgs* args) {
453 int tune = args->arg(1)->asInt()->evalInt(); // tuning change in milli cents
454 bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
455 const float fFreqRatio = RTMath::CentsToFreqRatioUnlimited(float(tune) / 1000.f);
456
457 AbstractEngineChannel* pEngineChannel =
458 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
459
460 if (args->arg(0)->exprType() == INT_EXPR) {
461 const ScriptID id = args->arg(0)->asInt()->evalInt();
462 if (!id) {
463 wrnMsg("change_tune(): note ID for argument 1 may not be zero");
464 return successResult();
465 }
466 if (!id.isNoteID()) {
467 wrnMsg("change_tune(): argument 1 is not a note ID");
468 return successResult();
469 }
470
471 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
472 if (!pNote) return successResult();
473
474 // if change_tune() was called immediately after note was triggered
475 // then immediately apply the tuning to Note object
476 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
477 if (relative)
478 pNote->Override.Pitch *= fFreqRatio;
479 else
480 pNote->Override.Pitch = fFreqRatio;
481 } else { // otherwise schedule tuning change ...
482 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
483 e.Init(); // clear IDs
484 e.Type = Event::type_note_synth_param;
485 e.Param.NoteSynthParam.NoteID = id.noteID();
486 e.Param.NoteSynthParam.Type = Event::synth_param_pitch;
487 e.Param.NoteSynthParam.Delta = fFreqRatio;
488 e.Param.NoteSynthParam.Relative = relative;
489
490 pEngineChannel->ScheduleEventMicroSec(&e, 0);
491 }
492 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
493 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
494 for (int i = 0; i < ids->arraySize(); ++i) {
495 const ScriptID id = ids->evalIntElement(i);
496 if (!id || !id.isNoteID()) continue;
497
498 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
499 if (!pNote) continue;
500
501 // if change_tune() was called immediately after note was triggered
502 // then immediately apply the tuning to Note object
503 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
504 if (relative)
505 pNote->Override.Pitch *= fFreqRatio;
506 else
507 pNote->Override.Pitch = fFreqRatio;
508 } else { // otherwise schedule tuning change ...
509 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
510 e.Init(); // clear IDs
511 e.Type = Event::type_note_synth_param;
512 e.Param.NoteSynthParam.NoteID = id.noteID();
513 e.Param.NoteSynthParam.Type = Event::synth_param_pitch;
514 e.Param.NoteSynthParam.Delta = fFreqRatio;
515 e.Param.NoteSynthParam.Relative = relative;
516
517 pEngineChannel->ScheduleEventMicroSec(&e, 0);
518 }
519 }
520 }
521
522 return successResult();
523 }
524
525 // change_pan() function
526
527 InstrumentScriptVMFunction_change_pan::InstrumentScriptVMFunction_change_pan(InstrumentScriptVM* parent)
528 : m_vm(parent)
529 {
530 }
531
532 bool InstrumentScriptVMFunction_change_pan::acceptsArgType(int iArg, ExprType_t type) const {
533 if (iArg == 0)
534 return type == INT_EXPR || type == INT_ARR_EXPR;
535 else
536 return type == INT_EXPR;
537 }
538
539 VMFnResult* InstrumentScriptVMFunction_change_pan::exec(VMFnArgs* args) {
540 int pan = args->arg(1)->asInt()->evalInt();
541 bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
542
543 if (pan > 1000) {
544 wrnMsg("change_pan(): argument 2 may not be larger than 1000");
545 pan = 1000;
546 } else if (pan < -1000) {
547 wrnMsg("change_pan(): argument 2 may not be smaller than -1000");
548 pan = -1000;
549 }
550 const float fPan = float(pan) / 1000.f;
551
552 AbstractEngineChannel* pEngineChannel =
553 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
554
555 if (args->arg(0)->exprType() == INT_EXPR) {
556 const ScriptID id = args->arg(0)->asInt()->evalInt();
557 if (!id) {
558 wrnMsg("change_pan(): note ID for argument 1 may not be zero");
559 return successResult();
560 }
561 if (!id.isNoteID()) {
562 wrnMsg("change_pan(): argument 1 is not a note ID");
563 return successResult();
564 }
565
566 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
567 if (!pNote) return successResult();
568
569 // if change_pan() was called immediately after note was triggered
570 // then immediately apply the panning to Note object
571 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
572 if (relative) {
573 pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources);
574 } else {
575 pNote->Override.Pan = fPan;
576 pNote->Override.PanSources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set
577 }
578 } else { // otherwise schedule panning change ...
579 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
580 e.Init(); // clear IDs
581 e.Type = Event::type_note_synth_param;
582 e.Param.NoteSynthParam.NoteID = id.noteID();
583 e.Param.NoteSynthParam.Type = Event::synth_param_pan;
584 e.Param.NoteSynthParam.Delta = fPan;
585 e.Param.NoteSynthParam.Relative = relative;
586
587 pEngineChannel->ScheduleEventMicroSec(&e, 0);
588 }
589 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
590 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
591 for (int i = 0; i < ids->arraySize(); ++i) {
592 const ScriptID id = ids->evalIntElement(i);
593 if (!id || !id.isNoteID()) continue;
594
595 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
596 if (!pNote) continue;
597
598 // if change_pan() was called immediately after note was triggered
599 // then immediately apply the panning to Note object
600 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
601 if (relative) {
602 pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources);
603 } else {
604 pNote->Override.Pan = fPan;
605 pNote->Override.PanSources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set
606 }
607 } else { // otherwise schedule panning change ...
608 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
609 e.Init(); // clear IDs
610 e.Type = Event::type_note_synth_param;
611 e.Param.NoteSynthParam.NoteID = id.noteID();
612 e.Param.NoteSynthParam.Type = Event::synth_param_pan;
613 e.Param.NoteSynthParam.Delta = fPan;
614 e.Param.NoteSynthParam.Relative = relative;
615
616 pEngineChannel->ScheduleEventMicroSec(&e, 0);
617 }
618 }
619 }
620
621 return successResult();
622 }
623
624 #define VM_FILTER_PAR_MAX_VALUE 1000000
625 #define VM_EG_PAR_MAX_VALUE 1000000
626
627 // change_cutoff() function
628
629 InstrumentScriptVMFunction_change_cutoff::InstrumentScriptVMFunction_change_cutoff(InstrumentScriptVM* parent)
630 : m_vm(parent)
631 {
632 }
633
634 bool InstrumentScriptVMFunction_change_cutoff::acceptsArgType(int iArg, ExprType_t type) const {
635 if (iArg == 0)
636 return type == INT_EXPR || type == INT_ARR_EXPR;
637 else
638 return type == INT_EXPR;
639 }
640
641 VMFnResult* InstrumentScriptVMFunction_change_cutoff::exec(VMFnArgs* args) {
642 int cutoff = args->arg(1)->asInt()->evalInt();
643 if (cutoff > VM_FILTER_PAR_MAX_VALUE) {
644 wrnMsg("change_cutoff(): argument 2 may not be larger than 1000000");
645 cutoff = VM_FILTER_PAR_MAX_VALUE;
646 } else if (cutoff < 0) {
647 wrnMsg("change_cutoff(): argument 2 may not be negative");
648 cutoff = 0;
649 }
650 const float fCutoff = float(cutoff) / float(VM_FILTER_PAR_MAX_VALUE);
651
652 AbstractEngineChannel* pEngineChannel =
653 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
654
655 if (args->arg(0)->exprType() == INT_EXPR) {
656 const ScriptID id = args->arg(0)->asInt()->evalInt();
657 if (!id) {
658 wrnMsg("change_cutoff(): note ID for argument 1 may not be zero");
659 return successResult();
660 }
661 if (!id.isNoteID()) {
662 wrnMsg("change_cutoff(): argument 1 is not a note ID");
663 return successResult();
664 }
665
666 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
667 if (!pNote) return successResult();
668
669 // if change_cutoff() was called immediately after note was triggered
670 // then immediately apply cutoff to Note object
671 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
672 pNote->Override.Cutoff = fCutoff;
673 } else { // otherwise schedule cutoff change ...
674 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
675 e.Init(); // clear IDs
676 e.Type = Event::type_note_synth_param;
677 e.Param.NoteSynthParam.NoteID = id.noteID();
678 e.Param.NoteSynthParam.Type = Event::synth_param_cutoff;
679 e.Param.NoteSynthParam.Delta = fCutoff;
680 e.Param.NoteSynthParam.Relative = false;
681
682 pEngineChannel->ScheduleEventMicroSec(&e, 0);
683 }
684 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
685 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
686 for (int i = 0; i < ids->arraySize(); ++i) {
687 const ScriptID id = ids->evalIntElement(i);
688 if (!id || !id.isNoteID()) continue;
689
690 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
691 if (!pNote) continue;
692
693 // if change_cutoff() was called immediately after note was triggered
694 // then immediately apply cutoff to Note object
695 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
696 pNote->Override.Cutoff = fCutoff;
697 } else { // otherwise schedule cutoff change ...
698 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
699 e.Init(); // clear IDs
700 e.Type = Event::type_note_synth_param;
701 e.Param.NoteSynthParam.NoteID = id.noteID();
702 e.Param.NoteSynthParam.Type = Event::synth_param_cutoff;
703 e.Param.NoteSynthParam.Delta = fCutoff;
704 e.Param.NoteSynthParam.Relative = false;
705
706 pEngineChannel->ScheduleEventMicroSec(&e, 0);
707 }
708 }
709 }
710
711 return successResult();
712 }
713
714 // change_reso() function
715
716 InstrumentScriptVMFunction_change_reso::InstrumentScriptVMFunction_change_reso(InstrumentScriptVM* parent)
717 : m_vm(parent)
718 {
719 }
720
721 bool InstrumentScriptVMFunction_change_reso::acceptsArgType(int iArg, ExprType_t type) const {
722 if (iArg == 0)
723 return type == INT_EXPR || type == INT_ARR_EXPR;
724 else
725 return type == INT_EXPR;
726 }
727
728 VMFnResult* InstrumentScriptVMFunction_change_reso::exec(VMFnArgs* args) {
729 int resonance = args->arg(1)->asInt()->evalInt();
730 if (resonance > VM_FILTER_PAR_MAX_VALUE) {
731 wrnMsg("change_reso(): argument 2 may not be larger than 1000000");
732 resonance = VM_FILTER_PAR_MAX_VALUE;
733 } else if (resonance < 0) {
734 wrnMsg("change_reso(): argument 2 may not be negative");
735 resonance = 0;
736 }
737 const float fResonance = float(resonance) / float(VM_FILTER_PAR_MAX_VALUE);
738
739 AbstractEngineChannel* pEngineChannel =
740 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
741
742 if (args->arg(0)->exprType() == INT_EXPR) {
743 const ScriptID id = args->arg(0)->asInt()->evalInt();
744 if (!id) {
745 wrnMsg("change_reso(): note ID for argument 1 may not be zero");
746 return successResult();
747 }
748 if (!id.isNoteID()) {
749 wrnMsg("change_reso(): argument 1 is not a note ID");
750 return successResult();
751 }
752
753 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
754 if (!pNote) return successResult();
755
756 // if change_reso() was called immediately after note was triggered
757 // then immediately apply resonance to Note object
758 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
759 pNote->Override.Resonance = fResonance;
760 } else { // otherwise schedule resonance change ...
761 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
762 e.Init(); // clear IDs
763 e.Type = Event::type_note_synth_param;
764 e.Param.NoteSynthParam.NoteID = id.noteID();
765 e.Param.NoteSynthParam.Type = Event::synth_param_resonance;
766 e.Param.NoteSynthParam.Delta = fResonance;
767 e.Param.NoteSynthParam.Relative = false;
768
769 pEngineChannel->ScheduleEventMicroSec(&e, 0);
770 }
771 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
772 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
773 for (int i = 0; i < ids->arraySize(); ++i) {
774 const ScriptID id = ids->evalIntElement(i);
775 if (!id || !id.isNoteID()) continue;
776
777 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
778 if (!pNote) continue;
779
780 // if change_reso() was called immediately after note was triggered
781 // then immediately apply resonance to Note object
782 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
783 pNote->Override.Resonance = fResonance;
784 } else { // otherwise schedule resonance change ...
785 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
786 e.Init(); // clear IDs
787 e.Type = Event::type_note_synth_param;
788 e.Param.NoteSynthParam.NoteID = id.noteID();
789 e.Param.NoteSynthParam.Type = Event::synth_param_resonance;
790 e.Param.NoteSynthParam.Delta = fResonance;
791 e.Param.NoteSynthParam.Relative = false;
792
793 pEngineChannel->ScheduleEventMicroSec(&e, 0);
794 }
795 }
796 }
797
798 return successResult();
799 }
800
801 // change_attack() function
802
803 InstrumentScriptVMFunction_change_attack::InstrumentScriptVMFunction_change_attack(InstrumentScriptVM* parent)
804 : m_vm(parent)
805 {
806 }
807
808 bool InstrumentScriptVMFunction_change_attack::acceptsArgType(int iArg, ExprType_t type) const {
809 if (iArg == 0)
810 return type == INT_EXPR || type == INT_ARR_EXPR;
811 else
812 return type == INT_EXPR;
813 }
814
815 VMFnResult* InstrumentScriptVMFunction_change_attack::exec(VMFnArgs* args) {
816 int attack = args->arg(1)->asInt()->evalInt();
817 if (attack > VM_EG_PAR_MAX_VALUE) {
818 wrnMsg("change_attack(): argument 2 may not be larger than 1000000");
819 attack = VM_EG_PAR_MAX_VALUE;
820 } else if (attack < 0) {
821 wrnMsg("change_attack(): argument 2 may not be negative");
822 attack = 0;
823 }
824 const float fAttack = float(attack) / float(VM_EG_PAR_MAX_VALUE);
825
826 AbstractEngineChannel* pEngineChannel =
827 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
828
829 if (args->arg(0)->exprType() == INT_EXPR) {
830 const ScriptID id = args->arg(0)->asInt()->evalInt();
831 if (!id) {
832 wrnMsg("change_attack(): note ID for argument 1 may not be zero");
833 return successResult();
834 }
835 if (!id.isNoteID()) {
836 wrnMsg("change_attack(): argument 1 is not a note ID");
837 return successResult();
838 }
839
840 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
841 if (!pNote) return successResult();
842
843 // if change_attack() was called immediately after note was triggered
844 // then immediately apply attack to Note object
845 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
846 pNote->Override.Attack = fAttack;
847 } else { // otherwise schedule attack change ...
848 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
849 e.Init(); // clear IDs
850 e.Type = Event::type_note_synth_param;
851 e.Param.NoteSynthParam.NoteID = id.noteID();
852 e.Param.NoteSynthParam.Type = Event::synth_param_attack;
853 e.Param.NoteSynthParam.Delta = fAttack;
854 e.Param.NoteSynthParam.Relative = false;
855
856 pEngineChannel->ScheduleEventMicroSec(&e, 0);
857 }
858 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
859 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
860 for (int i = 0; i < ids->arraySize(); ++i) {
861 const ScriptID id = ids->evalIntElement(i);
862 if (!id || !id.isNoteID()) continue;
863
864 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
865 if (!pNote) continue;
866
867 // if change_attack() was called immediately after note was triggered
868 // then immediately apply attack to Note object
869 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
870 pNote->Override.Attack = fAttack;
871 } else { // otherwise schedule attack 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_attack;
877 e.Param.NoteSynthParam.Delta = fAttack;
878 e.Param.NoteSynthParam.Relative = false;
879
880 pEngineChannel->ScheduleEventMicroSec(&e, 0);
881 }
882 }
883 }
884
885 return successResult();
886 }
887
888 // change_decay() function
889
890 InstrumentScriptVMFunction_change_decay::InstrumentScriptVMFunction_change_decay(InstrumentScriptVM* parent)
891 : m_vm(parent)
892 {
893 }
894
895 bool InstrumentScriptVMFunction_change_decay::acceptsArgType(int iArg, ExprType_t type) const {
896 if (iArg == 0)
897 return type == INT_EXPR || type == INT_ARR_EXPR;
898 else
899 return type == INT_EXPR;
900 }
901
902 VMFnResult* InstrumentScriptVMFunction_change_decay::exec(VMFnArgs* args) {
903 int decay = args->arg(1)->asInt()->evalInt();
904 if (decay > VM_EG_PAR_MAX_VALUE) {
905 wrnMsg("change_decay(): argument 2 may not be larger than 1000000");
906 decay = VM_EG_PAR_MAX_VALUE;
907 } else if (decay < 0) {
908 wrnMsg("change_decay(): argument 2 may not be negative");
909 decay = 0;
910 }
911 const float fDecay = float(decay) / float(VM_EG_PAR_MAX_VALUE);
912
913 AbstractEngineChannel* pEngineChannel =
914 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
915
916 if (args->arg(0)->exprType() == INT_EXPR) {
917 const ScriptID id = args->arg(0)->asInt()->evalInt();
918 if (!id) {
919 wrnMsg("change_decay(): note ID for argument 1 may not be zero");
920 return successResult();
921 }
922 if (!id.isNoteID()) {
923 wrnMsg("change_decay(): argument 1 is not a note ID");
924 return successResult();
925 }
926
927 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
928 if (!pNote) return successResult();
929
930 // if change_decay() was called immediately after note was triggered
931 // then immediately apply decay to Note object
932 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
933 pNote->Override.Decay = fDecay;
934 } else { // otherwise schedule decay change ...
935 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
936 e.Init(); // clear IDs
937 e.Type = Event::type_note_synth_param;
938 e.Param.NoteSynthParam.NoteID = id.noteID();
939 e.Param.NoteSynthParam.Type = Event::synth_param_decay;
940 e.Param.NoteSynthParam.Delta = fDecay;
941 e.Param.NoteSynthParam.Relative = false;
942
943 pEngineChannel->ScheduleEventMicroSec(&e, 0);
944 }
945 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
946 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
947 for (int i = 0; i < ids->arraySize(); ++i) {
948 const ScriptID id = ids->evalIntElement(i);
949 if (!id || !id.isNoteID()) continue;
950
951 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
952 if (!pNote) continue;
953
954 // if change_decay() was called immediately after note was triggered
955 // then immediately apply decay to Note object
956 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
957 pNote->Override.Decay = fDecay;
958 } else { // otherwise schedule decay change ...
959 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
960 e.Init(); // clear IDs
961 e.Type = Event::type_note_synth_param;
962 e.Param.NoteSynthParam.NoteID = id.noteID();
963 e.Param.NoteSynthParam.Type = Event::synth_param_decay;
964 e.Param.NoteSynthParam.Delta = fDecay;
965 e.Param.NoteSynthParam.Relative = false;
966
967 pEngineChannel->ScheduleEventMicroSec(&e, 0);
968 }
969 }
970 }
971
972 return successResult();
973 }
974
975 // change_release() function
976
977 InstrumentScriptVMFunction_change_release::InstrumentScriptVMFunction_change_release(InstrumentScriptVM* parent)
978 : m_vm(parent)
979 {
980 }
981
982 bool InstrumentScriptVMFunction_change_release::acceptsArgType(int iArg, ExprType_t type) const {
983 if (iArg == 0)
984 return type == INT_EXPR || type == INT_ARR_EXPR;
985 else
986 return type == INT_EXPR;
987 }
988
989 VMFnResult* InstrumentScriptVMFunction_change_release::exec(VMFnArgs* args) {
990 int release = args->arg(1)->asInt()->evalInt();
991 if (release > VM_EG_PAR_MAX_VALUE) {
992 wrnMsg("change_release(): argument 2 may not be larger than 1000000");
993 release = VM_EG_PAR_MAX_VALUE;
994 } else if (release < 0) {
995 wrnMsg("change_release(): argument 2 may not be negative");
996 release = 0;
997 }
998 const float fRelease = float(release) / float(VM_EG_PAR_MAX_VALUE);
999
1000 AbstractEngineChannel* pEngineChannel =
1001 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1002
1003 if (args->arg(0)->exprType() == INT_EXPR) {
1004 const ScriptID id = args->arg(0)->asInt()->evalInt();
1005 if (!id) {
1006 wrnMsg("change_release(): note ID for argument 1 may not be zero");
1007 return successResult();
1008 }
1009 if (!id.isNoteID()) {
1010 wrnMsg("change_release(): argument 1 is not a note ID");
1011 return successResult();
1012 }
1013
1014 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1015 if (!pNote) return successResult();
1016
1017 // if change_release() was called immediately after note was triggered
1018 // then immediately apply relase to Note object
1019 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1020 pNote->Override.Release = fRelease;
1021 } else { // otherwise schedule release change ...
1022 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1023 e.Init(); // clear IDs
1024 e.Type = Event::type_note_synth_param;
1025 e.Param.NoteSynthParam.NoteID = id.noteID();
1026 e.Param.NoteSynthParam.Type = Event::synth_param_release;
1027 e.Param.NoteSynthParam.Delta = fRelease;
1028 e.Param.NoteSynthParam.Relative = false;
1029
1030 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1031 }
1032 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1033 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1034 for (int i = 0; i < ids->arraySize(); ++i) {
1035 const ScriptID id = ids->evalIntElement(i);
1036 if (!id || !id.isNoteID()) continue;
1037
1038 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1039 if (!pNote) continue;
1040
1041 // if change_release() was called immediately after note was triggered
1042 // then immediately apply relase to Note object
1043 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1044 pNote->Override.Release = fRelease;
1045 } else { // otherwise schedule release change ...
1046 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1047 e.Init(); // clear IDs
1048 e.Type = Event::type_note_synth_param;
1049 e.Param.NoteSynthParam.NoteID = id.noteID();
1050 e.Param.NoteSynthParam.Type = Event::synth_param_release;
1051 e.Param.NoteSynthParam.Delta = fRelease;
1052 e.Param.NoteSynthParam.Relative = false;
1053
1054 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1055 }
1056 }
1057 }
1058
1059 return successResult();
1060 }
1061
1062 bool VMChangeSynthParamFunction::acceptsArgType(int iArg, ExprType_t type) const {
1063 if (iArg == 0)
1064 return type == INT_EXPR || type == INT_ARR_EXPR;
1065 else
1066 return type == INT_EXPR;
1067 }
1068
1069 // Arbitrarily chosen constant value symbolizing "no limit".
1070 #define NO_LIMIT 1315916909
1071
1072 template<float NoteBase::_Override::*T_noteParam, int T_synthParam,
1073 bool T_isNormalizedParam, int T_maxValue, int T_minValue>
1074 VMFnResult* VMChangeSynthParamFunction::execTemplate(VMFnArgs* args, const char* functionName) {
1075 int value = args->arg(1)->asInt()->evalInt();
1076 if (T_maxValue != NO_LIMIT && value > T_maxValue) {
1077 wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValue));
1078 value = T_maxValue;
1079 } else if (T_minValue != NO_LIMIT && value < T_minValue) {
1080 if (T_minValue == 0)
1081 wrnMsg(String(functionName) + "(): argument 2 may not be negative");
1082 else
1083 wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValue));
1084 value = T_minValue;
1085 }
1086 const float fValue = (T_isNormalizedParam) ?
1087 float(value) / float(T_maxValue) : // convert to 0.0 .. 1.0 value range
1088 float(value) / 1000000.f; // assuming microseconds here, convert to seconds
1089
1090 AbstractEngineChannel* pEngineChannel =
1091 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1092
1093 if (args->arg(0)->exprType() == INT_EXPR) {
1094 const ScriptID id = args->arg(0)->asInt()->evalInt();
1095 if (!id) {
1096 wrnMsg(String(functionName) + "(): note ID for argument 1 may not be zero");
1097 return successResult();
1098 }
1099 if (!id.isNoteID()) {
1100 wrnMsg(String(functionName) + "(): argument 1 is not a note ID");
1101 return successResult();
1102 }
1103
1104 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1105 if (!pNote) return successResult();
1106
1107 // if this change_*() script function was called immediately after
1108 // note was triggered then immediately apply the synth parameter
1109 // change to Note object
1110 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1111 pNote->Override.*T_noteParam = fValue;
1112 } else { // otherwise schedule this synth parameter change ...
1113 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1114 e.Init(); // clear IDs
1115 e.Type = Event::type_note_synth_param;
1116 e.Param.NoteSynthParam.NoteID = id.noteID();
1117 e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
1118 e.Param.NoteSynthParam.Delta = fValue;
1119 e.Param.NoteSynthParam.Relative = false;
1120
1121 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1122 }
1123 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1124 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1125 for (int i = 0; i < ids->arraySize(); ++i) {
1126 const ScriptID id = ids->evalIntElement(i);
1127 if (!id || !id.isNoteID()) continue;
1128
1129 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1130 if (!pNote) continue;
1131
1132 // if this change_*() script function was called immediately after
1133 // note was triggered then immediately apply the synth parameter
1134 // change to Note object
1135 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1136 pNote->Override.*T_noteParam = fValue;
1137 } else { // otherwise schedule this synth parameter change ...
1138 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1139 e.Init(); // clear IDs
1140 e.Type = Event::type_note_synth_param;
1141 e.Param.NoteSynthParam.NoteID = id.noteID();
1142 e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
1143 e.Param.NoteSynthParam.Delta = fValue;
1144 e.Param.NoteSynthParam.Relative = false;
1145
1146 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1147 }
1148 }
1149 }
1150
1151 return successResult();
1152 }
1153
1154 // change_amp_lfo_depth() function
1155
1156 VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_depth::exec(VMFnArgs* args) {
1157 return VMChangeSynthParamFunction::execTemplate<
1158 &NoteBase::_Override::AmpLFODepth,
1159 Event::synth_param_amp_lfo_depth,
1160 true, 1000000, 0>( args, "change_amp_lfo_depth" );
1161 }
1162
1163 // change_amp_lfo_freq() function
1164
1165 VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_freq::exec(VMFnArgs* args) {
1166 return VMChangeSynthParamFunction::execTemplate<
1167 &NoteBase::_Override::AmpLFOFreq,
1168 Event::synth_param_amp_lfo_freq,
1169 true, 1000000, 0>( args, "change_amp_lfo_freq" );
1170 }
1171
1172 // change_pitch_lfo_depth() function
1173
1174 VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_depth::exec(VMFnArgs* args) {
1175 return VMChangeSynthParamFunction::execTemplate<
1176 &NoteBase::_Override::PitchLFODepth,
1177 Event::synth_param_pitch_lfo_depth,
1178 true, 1000000, 0>( args, "change_pitch_lfo_depth" );
1179 }
1180
1181 // change_pitch_lfo_freq() function
1182
1183 VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_freq::exec(VMFnArgs* args) {
1184 return VMChangeSynthParamFunction::execTemplate<
1185 &NoteBase::_Override::PitchLFOFreq,
1186 Event::synth_param_pitch_lfo_freq,
1187 true, 1000000, 0>( args, "change_pitch_lfo_freq" );
1188 }
1189
1190 // change_vol_time() function
1191
1192 VMFnResult* InstrumentScriptVMFunction_change_vol_time::exec(VMFnArgs* args) {
1193 return VMChangeSynthParamFunction::execTemplate<
1194 &NoteBase::_Override::VolumeTime,
1195 Event::synth_param_volume_time,
1196 false, NO_LIMIT, 0>( args, "change_vol_time" );
1197 }
1198
1199 // change_tune_time() function
1200
1201 VMFnResult* InstrumentScriptVMFunction_change_tune_time::exec(VMFnArgs* args) {
1202 return VMChangeSynthParamFunction::execTemplate<
1203 &NoteBase::_Override::PitchTime,
1204 Event::synth_param_pitch_time,
1205 false, NO_LIMIT, 0>( args, "change_tune_time" );
1206 }
1207
1208 // fade_in() function
1209
1210 InstrumentScriptVMFunction_fade_in::InstrumentScriptVMFunction_fade_in(InstrumentScriptVM* parent)
1211 : m_vm(parent)
1212 {
1213 }
1214
1215 bool InstrumentScriptVMFunction_fade_in::acceptsArgType(int iArg, ExprType_t type) const {
1216 if (iArg == 0)
1217 return type == INT_EXPR || type == INT_ARR_EXPR;
1218 else
1219 return type == INT_EXPR;
1220 }
1221
1222 VMFnResult* InstrumentScriptVMFunction_fade_in::exec(VMFnArgs* args) {
1223 int duration = args->arg(1)->asInt()->evalInt();
1224 if (duration < 0) {
1225 wrnMsg("fade_in(): argument 2 may not be negative");
1226 duration = 0;
1227 }
1228 const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
1229
1230 AbstractEngineChannel* pEngineChannel =
1231 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1232
1233 if (args->arg(0)->exprType() == INT_EXPR) {
1234 const ScriptID id = args->arg(0)->asInt()->evalInt();
1235 if (!id) {
1236 wrnMsg("fade_in(): note ID for argument 1 may not be zero");
1237 return successResult();
1238 }
1239 if (!id.isNoteID()) {
1240 wrnMsg("fade_in(): argument 1 is not a note ID");
1241 return successResult();
1242 }
1243
1244 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1245 if (!pNote) return successResult();
1246
1247 // if fade_in() was called immediately after note was triggered
1248 // then immediately apply a start volume of zero to Note object,
1249 // as well as the fade in duration
1250 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1251 pNote->Override.Volume = 0.f;
1252 pNote->Override.VolumeTime = fDuration;
1253 } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
1254 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1255 e.Init(); // clear IDs
1256 e.Type = Event::type_note_synth_param;
1257 e.Param.NoteSynthParam.NoteID = id.noteID();
1258 e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1259 e.Param.NoteSynthParam.Delta = fDuration;
1260 e.Param.NoteSynthParam.Relative = false;
1261
1262 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1263 }
1264 // and finally schedule a "volume" change, simply one time slice
1265 // ahead, with the final fade in volume (1.0)
1266 {
1267 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1268 e.Init(); // clear IDs
1269 e.Type = Event::type_note_synth_param;
1270 e.Param.NoteSynthParam.NoteID = id.noteID();
1271 e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1272 e.Param.NoteSynthParam.Delta = 1.f;
1273 e.Param.NoteSynthParam.Relative = false;
1274
1275 // scheduling with 0 delay would also work here, but +1 is more
1276 // safe regarding potential future implementation changes of the
1277 // scheduler (see API comments of RTAVLTree::insert())
1278 pEngineChannel->ScheduleEventMicroSec(&e, 1);
1279 }
1280 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1281 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1282 for (int i = 0; i < ids->arraySize(); ++i) {
1283 const ScriptID id = ids->evalIntElement(i);
1284 if (!id || !id.isNoteID()) continue;
1285
1286 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1287 if (!pNote) continue;
1288
1289 // if fade_in() was called immediately after note was triggered
1290 // then immediately apply a start volume of zero to Note object,
1291 // as well as the fade in duration
1292 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1293 pNote->Override.Volume = 0.f;
1294 pNote->Override.VolumeTime = fDuration;
1295 } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
1296 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1297 e.Init(); // clear IDs
1298 e.Type = Event::type_note_synth_param;
1299 e.Param.NoteSynthParam.NoteID = id.noteID();
1300 e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1301 e.Param.NoteSynthParam.Delta = fDuration;
1302 e.Param.NoteSynthParam.Relative = false;
1303
1304 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1305 }
1306 // and finally schedule a "volume" change, simply one time slice
1307 // ahead, with the final fade in volume (1.0)
1308 {
1309 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1310 e.Init(); // clear IDs
1311 e.Type = Event::type_note_synth_param;
1312 e.Param.NoteSynthParam.NoteID = id.noteID();
1313 e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1314 e.Param.NoteSynthParam.Delta = 1.f;
1315 e.Param.NoteSynthParam.Relative = false;
1316
1317 // scheduling with 0 delay would also work here, but +1 is more
1318 // safe regarding potential future implementation changes of the
1319 // scheduler (see API comments of RTAVLTree::insert())
1320 pEngineChannel->ScheduleEventMicroSec(&e, 1);
1321 }
1322 }
1323 }
1324
1325 return successResult();
1326 }
1327
1328 // fade_out() function
1329
1330 InstrumentScriptVMFunction_fade_out::InstrumentScriptVMFunction_fade_out(InstrumentScriptVM* parent)
1331 : m_vm(parent)
1332 {
1333 }
1334
1335 bool InstrumentScriptVMFunction_fade_out::acceptsArgType(int iArg, ExprType_t type) const {
1336 if (iArg == 0)
1337 return type == INT_EXPR || type == INT_ARR_EXPR;
1338 else
1339 return type == INT_EXPR;
1340 }
1341
1342 VMFnResult* InstrumentScriptVMFunction_fade_out::exec(VMFnArgs* args) {
1343 int duration = args->arg(1)->asInt()->evalInt();
1344 if (duration < 0) {
1345 wrnMsg("fade_out(): argument 2 may not be negative");
1346 duration = 0;
1347 }
1348 const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
1349
1350 bool stop = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : true;
1351
1352 AbstractEngineChannel* pEngineChannel =
1353 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1354
1355 if (args->arg(0)->exprType() == INT_EXPR) {
1356 const ScriptID id = args->arg(0)->asInt()->evalInt();
1357 if (!id) {
1358 wrnMsg("fade_out(): note ID for argument 1 may not be zero");
1359 return successResult();
1360 }
1361 if (!id.isNoteID()) {
1362 wrnMsg("fade_out(): argument 1 is not a note ID");
1363 return successResult();
1364 }
1365
1366 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1367 if (!pNote) return successResult();
1368
1369 // if fade_out() was called immediately after note was triggered
1370 // then immediately apply fade out duration to Note object
1371 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1372 pNote->Override.VolumeTime = fDuration;
1373 } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
1374 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1375 e.Init(); // clear IDs
1376 e.Type = Event::type_note_synth_param;
1377 e.Param.NoteSynthParam.NoteID = id.noteID();
1378 e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1379 e.Param.NoteSynthParam.Delta = fDuration;
1380 e.Param.NoteSynthParam.Relative = false;
1381
1382 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1383 }
1384 // now schedule a "volume" change, simply one time slice ahead, with
1385 // the final fade out volume (0.0)
1386 {
1387 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1388 e.Init(); // clear IDs
1389 e.Type = Event::type_note_synth_param;
1390 e.Param.NoteSynthParam.NoteID = id.noteID();
1391 e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1392 e.Param.NoteSynthParam.Delta = 0.f;
1393 e.Param.NoteSynthParam.Relative = false;
1394
1395 // scheduling with 0 delay would also work here, but +1 is more
1396 // safe regarding potential future implementation changes of the
1397 // scheduler (see API comments of RTAVLTree::insert())
1398 pEngineChannel->ScheduleEventMicroSec(&e, 1);
1399 }
1400 // and finally if stopping the note was requested after the fade out
1401 // completed, then schedule to kill the voice after the requested
1402 // duration
1403 if (stop) {
1404 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1405 e.Init(); // clear IDs
1406 e.Type = Event::type_kill_note;
1407 e.Param.Note.ID = id.noteID();
1408 e.Param.Note.Key = pNote->hostKey;
1409
1410 pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
1411 }
1412 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1413 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1414 for (int i = 0; i < ids->arraySize(); ++i) {
1415 const ScriptID id = ids->evalIntElement(i);
1416 if (!id || !id.isNoteID()) continue;
1417
1418 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1419 if (!pNote) continue;
1420
1421 // if fade_out() was called immediately after note was triggered
1422 // then immediately apply fade out duration to Note object
1423 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1424 pNote->Override.VolumeTime = fDuration;
1425 } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
1426 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1427 e.Init(); // clear IDs
1428 e.Type = Event::type_note_synth_param;
1429 e.Param.NoteSynthParam.NoteID = id.noteID();
1430 e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1431 e.Param.NoteSynthParam.Delta = fDuration;
1432 e.Param.NoteSynthParam.Relative = false;
1433
1434 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1435 }
1436 // now schedule a "volume" change, simply one time slice ahead, with
1437 // the final fade out volume (0.0)
1438 {
1439 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1440 e.Init(); // clear IDs
1441 e.Type = Event::type_note_synth_param;
1442 e.Param.NoteSynthParam.NoteID = id.noteID();
1443 e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1444 e.Param.NoteSynthParam.Delta = 0.f;
1445 e.Param.NoteSynthParam.Relative = false;
1446
1447 // scheduling with 0 delay would also work here, but +1 is more
1448 // safe regarding potential future implementation changes of the
1449 // scheduler (see API comments of RTAVLTree::insert())
1450 pEngineChannel->ScheduleEventMicroSec(&e, 1);
1451 }
1452 // and finally if stopping the note was requested after the fade out
1453 // completed, then schedule to kill the voice after the requested
1454 // duration
1455 if (stop) {
1456 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1457 e.Init(); // clear IDs
1458 e.Type = Event::type_kill_note;
1459 e.Param.Note.ID = id.noteID();
1460 e.Param.Note.Key = pNote->hostKey;
1461
1462 pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
1463 }
1464 }
1465 }
1466
1467 return successResult();
1468 }
1469
1470 // get_event_par() function
1471
1472 InstrumentScriptVMFunction_get_event_par::InstrumentScriptVMFunction_get_event_par(InstrumentScriptVM* parent)
1473 : m_vm(parent)
1474 {
1475 }
1476
1477 VMFnResult* InstrumentScriptVMFunction_get_event_par::exec(VMFnArgs* args) {
1478 AbstractEngineChannel* pEngineChannel =
1479 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1480
1481 const ScriptID id = args->arg(0)->asInt()->evalInt();
1482 if (!id) {
1483 wrnMsg("get_event_par(): note ID for argument 1 may not be zero");
1484 return successResult(0);
1485 }
1486 if (!id.isNoteID()) {
1487 wrnMsg("get_event_par(): argument 1 is not a note ID");
1488 return successResult(0);
1489 }
1490
1491 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1492 if (!pNote) {
1493 wrnMsg("get_event_par(): no note alive with that note ID of argument 1");
1494 return successResult(0);
1495 }
1496
1497 const int parameter = args->arg(1)->asInt()->evalInt();
1498 switch (parameter) {
1499 case EVENT_PAR_NOTE:
1500 return successResult(pNote->cause.Param.Note.Key);
1501 case EVENT_PAR_VELOCITY:
1502 return successResult(pNote->cause.Param.Note.Velocity);
1503 case EVENT_PAR_VOLUME:
1504 return successResult(
1505 RTMath::LinRatioToDecibel(pNote->Override.Volume) * 1000.f
1506 );
1507 case EVENT_PAR_TUNE:
1508 return successResult(
1509 RTMath::FreqRatioToCents(pNote->Override.Pitch) * 1000.f
1510 );
1511 case EVENT_PAR_0:
1512 return successResult(pNote->userPar[0]);
1513 case EVENT_PAR_1:
1514 return successResult(pNote->userPar[1]);
1515 case EVENT_PAR_2:
1516 return successResult(pNote->userPar[2]);
1517 case EVENT_PAR_3:
1518 return successResult(pNote->userPar[3]);
1519 }
1520
1521 wrnMsg("get_event_par(): argument 2 is an invalid event parameter");
1522 return successResult(0);
1523 }
1524
1525 // set_event_par() function
1526
1527 InstrumentScriptVMFunction_set_event_par::InstrumentScriptVMFunction_set_event_par(InstrumentScriptVM* parent)
1528 : m_vm(parent)
1529 {
1530 }
1531
1532 VMFnResult* InstrumentScriptVMFunction_set_event_par::exec(VMFnArgs* args) {
1533 AbstractEngineChannel* pEngineChannel =
1534 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1535
1536 const ScriptID id = args->arg(0)->asInt()->evalInt();
1537 if (!id) {
1538 wrnMsg("set_event_par(): note ID for argument 1 may not be zero");
1539 return successResult();
1540 }
1541 if (!id.isNoteID()) {
1542 wrnMsg("set_event_par(): argument 1 is not a note ID");
1543 return successResult();
1544 }
1545
1546 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1547 if (!pNote) return successResult();
1548
1549 const int parameter = args->arg(1)->asInt()->evalInt();
1550 const int value = args->arg(2)->asInt()->evalInt();
1551
1552 switch (parameter) {
1553 case EVENT_PAR_NOTE:
1554 if (value < 0 || value > 127) {
1555 wrnMsg("set_event_par(): note number of argument 3 is out of range");
1556 return successResult();
1557 }
1558 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime)
1559 pNote->cause.Param.Note.Key = value;
1560 else
1561 wrnMsg("set_event_par(): note number can only be changed when note is new");
1562 return successResult();
1563 case EVENT_PAR_VELOCITY:
1564 if (value < 0 || value > 127) {
1565 wrnMsg("set_event_par(): velocity of argument 3 is out of range");
1566 return successResult();
1567 }
1568 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime)
1569 pNote->cause.Param.Note.Velocity = value;
1570 else
1571 wrnMsg("set_event_par(): velocity can only be changed when note is new");
1572 return successResult();
1573 case EVENT_PAR_VOLUME:
1574 wrnMsg("set_event_par(): changing volume by this function is currently not supported, use change_vol() instead");
1575 return successResult();
1576 case EVENT_PAR_TUNE:
1577 wrnMsg("set_event_par(): changing tune by this function is currently not supported, use change_tune() instead");
1578 return successResult();
1579 case EVENT_PAR_0:
1580 pNote->userPar[0] = value;
1581 return successResult();
1582 case EVENT_PAR_1:
1583 pNote->userPar[1] = value;
1584 return successResult();
1585 case EVENT_PAR_2:
1586 pNote->userPar[2] = value;
1587 return successResult();
1588 case EVENT_PAR_3:
1589 pNote->userPar[3] = value;
1590 return successResult();
1591 }
1592
1593 wrnMsg("set_event_par(): argument 2 is an invalid event parameter");
1594 return successResult();
1595 }
1596
1597 // event_status() function
1598
1599 InstrumentScriptVMFunction_event_status::InstrumentScriptVMFunction_event_status(InstrumentScriptVM* parent)
1600 : m_vm(parent)
1601 {
1602 }
1603
1604 VMFnResult* InstrumentScriptVMFunction_event_status::exec(VMFnArgs* args) {
1605 AbstractEngineChannel* pEngineChannel =
1606 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1607
1608 const ScriptID id = args->arg(0)->asInt()->evalInt();
1609 if (!id) {
1610 wrnMsg("event_status(): note ID for argument 1 may not be zero");
1611 return successResult(EVENT_STATUS_INACTIVE);
1612 }
1613 if (!id.isNoteID()) {
1614 wrnMsg("event_status(): argument 1 is not a note ID");
1615 return successResult(EVENT_STATUS_INACTIVE);
1616 }
1617
1618 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1619 return successResult(pNote ? EVENT_STATUS_NOTE_QUEUE : EVENT_STATUS_INACTIVE);
1620 }
1621
1622 // wait() function (overrides core wait() implementation)
1623
1624 InstrumentScriptVMFunction_wait::InstrumentScriptVMFunction_wait(InstrumentScriptVM* parent)
1625 : CoreVMFunction_wait(parent)
1626 {
1627 }
1628
1629 VMFnResult* InstrumentScriptVMFunction_wait::exec(VMFnArgs* args) {
1630 InstrumentScriptVM* m_vm = (InstrumentScriptVM*) vm;
1631
1632 // this might be set by passing 1 with the 2nd argument of built-in stop_wait() function
1633 if (m_vm->m_event->ignoreAllWaitCalls) return successResult();
1634
1635 return CoreVMFunction_wait::exec(args);
1636 }
1637
1638 // stop_wait() function
1639
1640 InstrumentScriptVMFunction_stop_wait::InstrumentScriptVMFunction_stop_wait(InstrumentScriptVM* parent)
1641 : m_vm(parent)
1642 {
1643 }
1644
1645 VMFnResult* InstrumentScriptVMFunction_stop_wait::exec(VMFnArgs* args) {
1646 AbstractEngineChannel* pEngineChannel =
1647 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1648
1649 const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
1650 if (!id) {
1651 wrnMsg("stop_wait(): callback ID for argument 1 may not be zero");
1652 return successResult();
1653 }
1654
1655 RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
1656 if (!itCallback) return successResult(); // ignore if callback is i.e. not alive anymore
1657
1658 const bool disableWaitForever =
1659 (args->argsCount() >= 2) ? (args->arg(1)->asInt()->evalInt() == 1) : false;
1660
1661 pEngineChannel->ScheduleResumeOfScriptCallback(
1662 itCallback, m_vm->m_event->scheduleTime, disableWaitForever
1663 );
1664
1665 return successResult();
1666 }
1667
1668 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC