/[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 2962 - (show annotations) (download)
Sun Jul 17 17:54:04 2016 UTC (16 months ago) by schoenebeck
File size: 47969 byte(s)
* NKSP: Fixed all change_*() built-in script functions to apply their
  synthesis parameter changes immediately in case the respective note
  was triggered at the same time, instead of scheduling the parameter
  change, especially because it would cause some parameter types's
  changes either to be ramped (i.e. change_vol()) or other types even
  to have not effect at all (i.e. change_attack()).
* Bumped version (2.0.0.svn20).

1 /*
2 * Copyright (c) 2014-2016 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->arg(0)->exprType() == INT_EXPR) {
140 const ScriptID id = args->arg(0)->asInt()->evalInt();
141 if (!id) {
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 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->cause.SchedTime() == 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->cause.SchedTime() == 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 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->cause.SchedTime() == 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->cause.SchedTime() == 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 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->cause.SchedTime() == 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->cause.SchedTime() == 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 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->cause.SchedTime() == 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->cause.SchedTime() == 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 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->cause.SchedTime() == 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->cause.SchedTime() == 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 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->cause.SchedTime() == 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->cause.SchedTime() == 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 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->cause.SchedTime() == 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->cause.SchedTime() == 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 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->cause.SchedTime() == 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->cause.SchedTime() == 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 // event_status() function
1063
1064 InstrumentScriptVMFunction_event_status::InstrumentScriptVMFunction_event_status(InstrumentScriptVM* parent)
1065 : m_vm(parent)
1066 {
1067 }
1068
1069 VMFnResult* InstrumentScriptVMFunction_event_status::exec(VMFnArgs* args) {
1070 AbstractEngineChannel* pEngineChannel =
1071 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1072
1073 const ScriptID id = args->arg(0)->asInt()->evalInt();
1074 if (!id) {
1075 wrnMsg("event_status(): note ID for argument 1 may not be zero");
1076 return successResult(EVENT_STATUS_INACTIVE);
1077 }
1078 if (!id.isNoteID()) {
1079 wrnMsg("event_status(): argument 1 is not a note ID");
1080 return successResult(EVENT_STATUS_INACTIVE);
1081 }
1082
1083 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1084 return successResult(pNote ? EVENT_STATUS_NOTE_QUEUE : EVENT_STATUS_INACTIVE);
1085 }
1086
1087 // wait() function (overrides core wait() implementation)
1088
1089 InstrumentScriptVMFunction_wait::InstrumentScriptVMFunction_wait(InstrumentScriptVM* parent)
1090 : CoreVMFunction_wait(parent)
1091 {
1092 }
1093
1094 VMFnResult* InstrumentScriptVMFunction_wait::exec(VMFnArgs* args) {
1095 InstrumentScriptVM* m_vm = (InstrumentScriptVM*) vm;
1096
1097 // this might be set by passing 1 with the 2nd argument of built-in stop_wait() function
1098 if (m_vm->m_event->ignoreAllWaitCalls) return successResult();
1099
1100 return CoreVMFunction_wait::exec(args);
1101 }
1102
1103 // stop_wait() function
1104
1105 InstrumentScriptVMFunction_stop_wait::InstrumentScriptVMFunction_stop_wait(InstrumentScriptVM* parent)
1106 : m_vm(parent)
1107 {
1108 }
1109
1110 VMFnResult* InstrumentScriptVMFunction_stop_wait::exec(VMFnArgs* args) {
1111 AbstractEngineChannel* pEngineChannel =
1112 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1113
1114 const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
1115 if (!id) {
1116 wrnMsg("stop_wait(): callback ID for argument 1 may not be zero");
1117 return successResult();
1118 }
1119
1120 RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
1121 if (!itCallback) return successResult(); // ignore if callback is i.e. not alive anymore
1122
1123 const bool disableWaitForever =
1124 (args->argsCount() >= 2) ? (args->arg(1)->asInt()->evalInt() == 1) : false;
1125
1126 pEngineChannel->ScheduleResumeOfScriptCallback(
1127 itCallback, m_vm->m_event->cause.SchedTime(), disableWaitForever
1128 );
1129
1130 return successResult();
1131 }
1132
1133 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC