/[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 3218 - (show annotations) (download)
Thu May 25 16:32:17 2017 UTC (6 years, 10 months ago) by schoenebeck
File size: 76396 byte(s)
* NKSP: Adjusted behavior of "change_vol()" and "change_tune()" to a more
  intuitive behavior if used in combination with "change_vol_time()" or
  "change_tune_time()" respectively: now tuning/volume changes are only
  assigned (without delay) immediately to a new note if the respective
  timing function has not been called before, otherwise the volume/tuning
  changes are automatically faded (before, only the event's time stamp was
  relevant).
* Bumped version (2.0.0.svn50).

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, but only if
389 // change_vol_time() has not been called before
390 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
391 pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S)
392 {
393 if (relative)
394 pNote->Override.Volume *= fVolumeLin;
395 else
396 pNote->Override.Volume = fVolumeLin;
397 } else { // otherwise schedule the volume change ...
398 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
399 e.Init(); // clear IDs
400 e.Type = Event::type_note_synth_param;
401 e.Param.NoteSynthParam.NoteID = id.noteID();
402 e.Param.NoteSynthParam.Type = Event::synth_param_volume;
403 e.Param.NoteSynthParam.Delta = fVolumeLin;
404 e.Param.NoteSynthParam.Relative = relative;
405
406 pEngineChannel->ScheduleEventMicroSec(&e, 0);
407 }
408 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
409 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
410 for (int i = 0; i < ids->arraySize(); ++i) {
411 const ScriptID id = ids->evalIntElement(i);
412 if (!id || !id.isNoteID()) continue;
413
414 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
415 if (!pNote) continue;
416
417 // if change_vol() was called immediately after note was triggered
418 // then immediately apply the volume to Note object, but only if
419 // change_vol_time() has not been called before
420 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
421 pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S)
422 {
423 if (relative)
424 pNote->Override.Volume *= fVolumeLin;
425 else
426 pNote->Override.Volume = fVolumeLin;
427 } else { // otherwise schedule the volume change ...
428 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
429 e.Init(); // clear IDs
430 e.Type = Event::type_note_synth_param;
431 e.Param.NoteSynthParam.NoteID = id.noteID();
432 e.Param.NoteSynthParam.Type = Event::synth_param_volume;
433 e.Param.NoteSynthParam.Delta = fVolumeLin;
434 e.Param.NoteSynthParam.Relative = relative;
435
436 pEngineChannel->ScheduleEventMicroSec(&e, 0);
437 }
438 }
439 }
440
441 return successResult();
442 }
443
444 // change_tune() function
445
446 InstrumentScriptVMFunction_change_tune::InstrumentScriptVMFunction_change_tune(InstrumentScriptVM* parent)
447 : m_vm(parent)
448 {
449 }
450
451 bool InstrumentScriptVMFunction_change_tune::acceptsArgType(int iArg, ExprType_t type) const {
452 if (iArg == 0)
453 return type == INT_EXPR || type == INT_ARR_EXPR;
454 else
455 return type == INT_EXPR;
456 }
457
458 VMFnResult* InstrumentScriptVMFunction_change_tune::exec(VMFnArgs* args) {
459 int tune = args->arg(1)->asInt()->evalInt(); // tuning change in milli cents
460 bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
461 const float fFreqRatio = RTMath::CentsToFreqRatioUnlimited(float(tune) / 1000.f);
462
463 AbstractEngineChannel* pEngineChannel =
464 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
465
466 if (args->arg(0)->exprType() == INT_EXPR) {
467 const ScriptID id = args->arg(0)->asInt()->evalInt();
468 if (!id) {
469 wrnMsg("change_tune(): note ID for argument 1 may not be zero");
470 return successResult();
471 }
472 if (!id.isNoteID()) {
473 wrnMsg("change_tune(): argument 1 is not a note ID");
474 return successResult();
475 }
476
477 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
478 if (!pNote) return successResult();
479
480 // if change_tune() was called immediately after note was triggered
481 // then immediately apply the tuning to Note object, but only if
482 // change_tune_time() has not been called before
483 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
484 pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S)
485 {
486 if (relative)
487 pNote->Override.Pitch *= fFreqRatio;
488 else
489 pNote->Override.Pitch = fFreqRatio;
490 } else { // otherwise schedule tuning change ...
491 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
492 e.Init(); // clear IDs
493 e.Type = Event::type_note_synth_param;
494 e.Param.NoteSynthParam.NoteID = id.noteID();
495 e.Param.NoteSynthParam.Type = Event::synth_param_pitch;
496 e.Param.NoteSynthParam.Delta = fFreqRatio;
497 e.Param.NoteSynthParam.Relative = relative;
498
499 pEngineChannel->ScheduleEventMicroSec(&e, 0);
500 }
501 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
502 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
503 for (int i = 0; i < ids->arraySize(); ++i) {
504 const ScriptID id = ids->evalIntElement(i);
505 if (!id || !id.isNoteID()) continue;
506
507 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
508 if (!pNote) continue;
509
510 // if change_tune() was called immediately after note was triggered
511 // then immediately apply the tuning to Note object, but only if
512 // change_tune_time() has not been called before
513 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
514 pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S)
515 {
516 if (relative)
517 pNote->Override.Pitch *= fFreqRatio;
518 else
519 pNote->Override.Pitch = fFreqRatio;
520 } else { // otherwise schedule tuning change ...
521 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
522 e.Init(); // clear IDs
523 e.Type = Event::type_note_synth_param;
524 e.Param.NoteSynthParam.NoteID = id.noteID();
525 e.Param.NoteSynthParam.Type = Event::synth_param_pitch;
526 e.Param.NoteSynthParam.Delta = fFreqRatio;
527 e.Param.NoteSynthParam.Relative = relative;
528
529 pEngineChannel->ScheduleEventMicroSec(&e, 0);
530 }
531 }
532 }
533
534 return successResult();
535 }
536
537 // change_pan() function
538
539 InstrumentScriptVMFunction_change_pan::InstrumentScriptVMFunction_change_pan(InstrumentScriptVM* parent)
540 : m_vm(parent)
541 {
542 }
543
544 bool InstrumentScriptVMFunction_change_pan::acceptsArgType(int iArg, ExprType_t type) const {
545 if (iArg == 0)
546 return type == INT_EXPR || type == INT_ARR_EXPR;
547 else
548 return type == INT_EXPR;
549 }
550
551 VMFnResult* InstrumentScriptVMFunction_change_pan::exec(VMFnArgs* args) {
552 int pan = args->arg(1)->asInt()->evalInt();
553 bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
554
555 if (pan > 1000) {
556 wrnMsg("change_pan(): argument 2 may not be larger than 1000");
557 pan = 1000;
558 } else if (pan < -1000) {
559 wrnMsg("change_pan(): argument 2 may not be smaller than -1000");
560 pan = -1000;
561 }
562 const float fPan = float(pan) / 1000.f;
563
564 AbstractEngineChannel* pEngineChannel =
565 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
566
567 if (args->arg(0)->exprType() == INT_EXPR) {
568 const ScriptID id = args->arg(0)->asInt()->evalInt();
569 if (!id) {
570 wrnMsg("change_pan(): note ID for argument 1 may not be zero");
571 return successResult();
572 }
573 if (!id.isNoteID()) {
574 wrnMsg("change_pan(): argument 1 is not a note ID");
575 return successResult();
576 }
577
578 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
579 if (!pNote) return successResult();
580
581 // if change_pan() was called immediately after note was triggered
582 // then immediately apply the panning to Note object
583 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
584 if (relative) {
585 pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources);
586 } else {
587 pNote->Override.Pan = fPan;
588 pNote->Override.PanSources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set
589 }
590 } else { // otherwise schedule panning change ...
591 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
592 e.Init(); // clear IDs
593 e.Type = Event::type_note_synth_param;
594 e.Param.NoteSynthParam.NoteID = id.noteID();
595 e.Param.NoteSynthParam.Type = Event::synth_param_pan;
596 e.Param.NoteSynthParam.Delta = fPan;
597 e.Param.NoteSynthParam.Relative = relative;
598
599 pEngineChannel->ScheduleEventMicroSec(&e, 0);
600 }
601 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
602 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
603 for (int i = 0; i < ids->arraySize(); ++i) {
604 const ScriptID id = ids->evalIntElement(i);
605 if (!id || !id.isNoteID()) continue;
606
607 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
608 if (!pNote) continue;
609
610 // if change_pan() was called immediately after note was triggered
611 // then immediately apply the panning to Note object
612 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
613 if (relative) {
614 pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources);
615 } else {
616 pNote->Override.Pan = fPan;
617 pNote->Override.PanSources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set
618 }
619 } else { // otherwise schedule panning change ...
620 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
621 e.Init(); // clear IDs
622 e.Type = Event::type_note_synth_param;
623 e.Param.NoteSynthParam.NoteID = id.noteID();
624 e.Param.NoteSynthParam.Type = Event::synth_param_pan;
625 e.Param.NoteSynthParam.Delta = fPan;
626 e.Param.NoteSynthParam.Relative = relative;
627
628 pEngineChannel->ScheduleEventMicroSec(&e, 0);
629 }
630 }
631 }
632
633 return successResult();
634 }
635
636 #define VM_FILTER_PAR_MAX_VALUE 1000000
637 #define VM_EG_PAR_MAX_VALUE 1000000
638
639 // change_cutoff() function
640
641 InstrumentScriptVMFunction_change_cutoff::InstrumentScriptVMFunction_change_cutoff(InstrumentScriptVM* parent)
642 : m_vm(parent)
643 {
644 }
645
646 bool InstrumentScriptVMFunction_change_cutoff::acceptsArgType(int iArg, ExprType_t type) const {
647 if (iArg == 0)
648 return type == INT_EXPR || type == INT_ARR_EXPR;
649 else
650 return type == INT_EXPR;
651 }
652
653 VMFnResult* InstrumentScriptVMFunction_change_cutoff::exec(VMFnArgs* args) {
654 int cutoff = args->arg(1)->asInt()->evalInt();
655 if (cutoff > VM_FILTER_PAR_MAX_VALUE) {
656 wrnMsg("change_cutoff(): argument 2 may not be larger than 1000000");
657 cutoff = VM_FILTER_PAR_MAX_VALUE;
658 } else if (cutoff < 0) {
659 wrnMsg("change_cutoff(): argument 2 may not be negative");
660 cutoff = 0;
661 }
662 const float fCutoff = float(cutoff) / float(VM_FILTER_PAR_MAX_VALUE);
663
664 AbstractEngineChannel* pEngineChannel =
665 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
666
667 if (args->arg(0)->exprType() == INT_EXPR) {
668 const ScriptID id = args->arg(0)->asInt()->evalInt();
669 if (!id) {
670 wrnMsg("change_cutoff(): note ID for argument 1 may not be zero");
671 return successResult();
672 }
673 if (!id.isNoteID()) {
674 wrnMsg("change_cutoff(): argument 1 is not a note ID");
675 return successResult();
676 }
677
678 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
679 if (!pNote) return successResult();
680
681 // if change_cutoff() was called immediately after note was triggered
682 // then immediately apply cutoff to Note object
683 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
684 pNote->Override.Cutoff = fCutoff;
685 } else { // otherwise schedule cutoff change ...
686 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
687 e.Init(); // clear IDs
688 e.Type = Event::type_note_synth_param;
689 e.Param.NoteSynthParam.NoteID = id.noteID();
690 e.Param.NoteSynthParam.Type = Event::synth_param_cutoff;
691 e.Param.NoteSynthParam.Delta = fCutoff;
692 e.Param.NoteSynthParam.Relative = false;
693
694 pEngineChannel->ScheduleEventMicroSec(&e, 0);
695 }
696 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
697 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
698 for (int i = 0; i < ids->arraySize(); ++i) {
699 const ScriptID id = ids->evalIntElement(i);
700 if (!id || !id.isNoteID()) continue;
701
702 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
703 if (!pNote) continue;
704
705 // if change_cutoff() was called immediately after note was triggered
706 // then immediately apply cutoff to Note object
707 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
708 pNote->Override.Cutoff = fCutoff;
709 } else { // otherwise schedule cutoff change ...
710 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
711 e.Init(); // clear IDs
712 e.Type = Event::type_note_synth_param;
713 e.Param.NoteSynthParam.NoteID = id.noteID();
714 e.Param.NoteSynthParam.Type = Event::synth_param_cutoff;
715 e.Param.NoteSynthParam.Delta = fCutoff;
716 e.Param.NoteSynthParam.Relative = false;
717
718 pEngineChannel->ScheduleEventMicroSec(&e, 0);
719 }
720 }
721 }
722
723 return successResult();
724 }
725
726 // change_reso() function
727
728 InstrumentScriptVMFunction_change_reso::InstrumentScriptVMFunction_change_reso(InstrumentScriptVM* parent)
729 : m_vm(parent)
730 {
731 }
732
733 bool InstrumentScriptVMFunction_change_reso::acceptsArgType(int iArg, ExprType_t type) const {
734 if (iArg == 0)
735 return type == INT_EXPR || type == INT_ARR_EXPR;
736 else
737 return type == INT_EXPR;
738 }
739
740 VMFnResult* InstrumentScriptVMFunction_change_reso::exec(VMFnArgs* args) {
741 int resonance = args->arg(1)->asInt()->evalInt();
742 if (resonance > VM_FILTER_PAR_MAX_VALUE) {
743 wrnMsg("change_reso(): argument 2 may not be larger than 1000000");
744 resonance = VM_FILTER_PAR_MAX_VALUE;
745 } else if (resonance < 0) {
746 wrnMsg("change_reso(): argument 2 may not be negative");
747 resonance = 0;
748 }
749 const float fResonance = float(resonance) / float(VM_FILTER_PAR_MAX_VALUE);
750
751 AbstractEngineChannel* pEngineChannel =
752 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
753
754 if (args->arg(0)->exprType() == INT_EXPR) {
755 const ScriptID id = args->arg(0)->asInt()->evalInt();
756 if (!id) {
757 wrnMsg("change_reso(): note ID for argument 1 may not be zero");
758 return successResult();
759 }
760 if (!id.isNoteID()) {
761 wrnMsg("change_reso(): argument 1 is not a note ID");
762 return successResult();
763 }
764
765 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
766 if (!pNote) return successResult();
767
768 // if change_reso() was called immediately after note was triggered
769 // then immediately apply resonance to Note object
770 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
771 pNote->Override.Resonance = fResonance;
772 } else { // otherwise schedule resonance change ...
773 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
774 e.Init(); // clear IDs
775 e.Type = Event::type_note_synth_param;
776 e.Param.NoteSynthParam.NoteID = id.noteID();
777 e.Param.NoteSynthParam.Type = Event::synth_param_resonance;
778 e.Param.NoteSynthParam.Delta = fResonance;
779 e.Param.NoteSynthParam.Relative = false;
780
781 pEngineChannel->ScheduleEventMicroSec(&e, 0);
782 }
783 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
784 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
785 for (int i = 0; i < ids->arraySize(); ++i) {
786 const ScriptID id = ids->evalIntElement(i);
787 if (!id || !id.isNoteID()) continue;
788
789 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
790 if (!pNote) continue;
791
792 // if change_reso() was called immediately after note was triggered
793 // then immediately apply resonance to Note object
794 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
795 pNote->Override.Resonance = fResonance;
796 } else { // otherwise schedule resonance change ...
797 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
798 e.Init(); // clear IDs
799 e.Type = Event::type_note_synth_param;
800 e.Param.NoteSynthParam.NoteID = id.noteID();
801 e.Param.NoteSynthParam.Type = Event::synth_param_resonance;
802 e.Param.NoteSynthParam.Delta = fResonance;
803 e.Param.NoteSynthParam.Relative = false;
804
805 pEngineChannel->ScheduleEventMicroSec(&e, 0);
806 }
807 }
808 }
809
810 return successResult();
811 }
812
813 // change_attack() function
814
815 InstrumentScriptVMFunction_change_attack::InstrumentScriptVMFunction_change_attack(InstrumentScriptVM* parent)
816 : m_vm(parent)
817 {
818 }
819
820 bool InstrumentScriptVMFunction_change_attack::acceptsArgType(int iArg, ExprType_t type) const {
821 if (iArg == 0)
822 return type == INT_EXPR || type == INT_ARR_EXPR;
823 else
824 return type == INT_EXPR;
825 }
826
827 VMFnResult* InstrumentScriptVMFunction_change_attack::exec(VMFnArgs* args) {
828 int attack = args->arg(1)->asInt()->evalInt();
829 if (attack > VM_EG_PAR_MAX_VALUE) {
830 wrnMsg("change_attack(): argument 2 may not be larger than 1000000");
831 attack = VM_EG_PAR_MAX_VALUE;
832 } else if (attack < 0) {
833 wrnMsg("change_attack(): argument 2 may not be negative");
834 attack = 0;
835 }
836 const float fAttack = float(attack) / float(VM_EG_PAR_MAX_VALUE);
837
838 AbstractEngineChannel* pEngineChannel =
839 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
840
841 if (args->arg(0)->exprType() == INT_EXPR) {
842 const ScriptID id = args->arg(0)->asInt()->evalInt();
843 if (!id) {
844 wrnMsg("change_attack(): note ID for argument 1 may not be zero");
845 return successResult();
846 }
847 if (!id.isNoteID()) {
848 wrnMsg("change_attack(): argument 1 is not a note ID");
849 return successResult();
850 }
851
852 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
853 if (!pNote) return successResult();
854
855 // if change_attack() was called immediately after note was triggered
856 // then immediately apply attack to Note object
857 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
858 pNote->Override.Attack = fAttack;
859 } else { // otherwise schedule attack change ...
860 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
861 e.Init(); // clear IDs
862 e.Type = Event::type_note_synth_param;
863 e.Param.NoteSynthParam.NoteID = id.noteID();
864 e.Param.NoteSynthParam.Type = Event::synth_param_attack;
865 e.Param.NoteSynthParam.Delta = fAttack;
866 e.Param.NoteSynthParam.Relative = false;
867
868 pEngineChannel->ScheduleEventMicroSec(&e, 0);
869 }
870 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
871 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
872 for (int i = 0; i < ids->arraySize(); ++i) {
873 const ScriptID id = ids->evalIntElement(i);
874 if (!id || !id.isNoteID()) continue;
875
876 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
877 if (!pNote) continue;
878
879 // if change_attack() was called immediately after note was triggered
880 // then immediately apply attack to Note object
881 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
882 pNote->Override.Attack = fAttack;
883 } else { // otherwise schedule attack change ...
884 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
885 e.Init(); // clear IDs
886 e.Type = Event::type_note_synth_param;
887 e.Param.NoteSynthParam.NoteID = id.noteID();
888 e.Param.NoteSynthParam.Type = Event::synth_param_attack;
889 e.Param.NoteSynthParam.Delta = fAttack;
890 e.Param.NoteSynthParam.Relative = false;
891
892 pEngineChannel->ScheduleEventMicroSec(&e, 0);
893 }
894 }
895 }
896
897 return successResult();
898 }
899
900 // change_decay() function
901
902 InstrumentScriptVMFunction_change_decay::InstrumentScriptVMFunction_change_decay(InstrumentScriptVM* parent)
903 : m_vm(parent)
904 {
905 }
906
907 bool InstrumentScriptVMFunction_change_decay::acceptsArgType(int iArg, ExprType_t type) const {
908 if (iArg == 0)
909 return type == INT_EXPR || type == INT_ARR_EXPR;
910 else
911 return type == INT_EXPR;
912 }
913
914 VMFnResult* InstrumentScriptVMFunction_change_decay::exec(VMFnArgs* args) {
915 int decay = args->arg(1)->asInt()->evalInt();
916 if (decay > VM_EG_PAR_MAX_VALUE) {
917 wrnMsg("change_decay(): argument 2 may not be larger than 1000000");
918 decay = VM_EG_PAR_MAX_VALUE;
919 } else if (decay < 0) {
920 wrnMsg("change_decay(): argument 2 may not be negative");
921 decay = 0;
922 }
923 const float fDecay = float(decay) / float(VM_EG_PAR_MAX_VALUE);
924
925 AbstractEngineChannel* pEngineChannel =
926 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
927
928 if (args->arg(0)->exprType() == INT_EXPR) {
929 const ScriptID id = args->arg(0)->asInt()->evalInt();
930 if (!id) {
931 wrnMsg("change_decay(): note ID for argument 1 may not be zero");
932 return successResult();
933 }
934 if (!id.isNoteID()) {
935 wrnMsg("change_decay(): argument 1 is not a note ID");
936 return successResult();
937 }
938
939 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
940 if (!pNote) return successResult();
941
942 // if change_decay() was called immediately after note was triggered
943 // then immediately apply decay to Note object
944 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
945 pNote->Override.Decay = fDecay;
946 } else { // otherwise schedule decay change ...
947 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
948 e.Init(); // clear IDs
949 e.Type = Event::type_note_synth_param;
950 e.Param.NoteSynthParam.NoteID = id.noteID();
951 e.Param.NoteSynthParam.Type = Event::synth_param_decay;
952 e.Param.NoteSynthParam.Delta = fDecay;
953 e.Param.NoteSynthParam.Relative = false;
954
955 pEngineChannel->ScheduleEventMicroSec(&e, 0);
956 }
957 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
958 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
959 for (int i = 0; i < ids->arraySize(); ++i) {
960 const ScriptID id = ids->evalIntElement(i);
961 if (!id || !id.isNoteID()) continue;
962
963 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
964 if (!pNote) continue;
965
966 // if change_decay() was called immediately after note was triggered
967 // then immediately apply decay to Note object
968 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
969 pNote->Override.Decay = fDecay;
970 } else { // otherwise schedule decay change ...
971 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
972 e.Init(); // clear IDs
973 e.Type = Event::type_note_synth_param;
974 e.Param.NoteSynthParam.NoteID = id.noteID();
975 e.Param.NoteSynthParam.Type = Event::synth_param_decay;
976 e.Param.NoteSynthParam.Delta = fDecay;
977 e.Param.NoteSynthParam.Relative = false;
978
979 pEngineChannel->ScheduleEventMicroSec(&e, 0);
980 }
981 }
982 }
983
984 return successResult();
985 }
986
987 // change_release() function
988
989 InstrumentScriptVMFunction_change_release::InstrumentScriptVMFunction_change_release(InstrumentScriptVM* parent)
990 : m_vm(parent)
991 {
992 }
993
994 bool InstrumentScriptVMFunction_change_release::acceptsArgType(int iArg, ExprType_t type) const {
995 if (iArg == 0)
996 return type == INT_EXPR || type == INT_ARR_EXPR;
997 else
998 return type == INT_EXPR;
999 }
1000
1001 VMFnResult* InstrumentScriptVMFunction_change_release::exec(VMFnArgs* args) {
1002 int release = args->arg(1)->asInt()->evalInt();
1003 if (release > VM_EG_PAR_MAX_VALUE) {
1004 wrnMsg("change_release(): argument 2 may not be larger than 1000000");
1005 release = VM_EG_PAR_MAX_VALUE;
1006 } else if (release < 0) {
1007 wrnMsg("change_release(): argument 2 may not be negative");
1008 release = 0;
1009 }
1010 const float fRelease = float(release) / float(VM_EG_PAR_MAX_VALUE);
1011
1012 AbstractEngineChannel* pEngineChannel =
1013 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1014
1015 if (args->arg(0)->exprType() == INT_EXPR) {
1016 const ScriptID id = args->arg(0)->asInt()->evalInt();
1017 if (!id) {
1018 wrnMsg("change_release(): note ID for argument 1 may not be zero");
1019 return successResult();
1020 }
1021 if (!id.isNoteID()) {
1022 wrnMsg("change_release(): argument 1 is not a note ID");
1023 return successResult();
1024 }
1025
1026 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1027 if (!pNote) return successResult();
1028
1029 // if change_release() was called immediately after note was triggered
1030 // then immediately apply relase to Note object
1031 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1032 pNote->Override.Release = fRelease;
1033 } else { // otherwise schedule release change ...
1034 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1035 e.Init(); // clear IDs
1036 e.Type = Event::type_note_synth_param;
1037 e.Param.NoteSynthParam.NoteID = id.noteID();
1038 e.Param.NoteSynthParam.Type = Event::synth_param_release;
1039 e.Param.NoteSynthParam.Delta = fRelease;
1040 e.Param.NoteSynthParam.Relative = false;
1041
1042 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1043 }
1044 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1045 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1046 for (int i = 0; i < ids->arraySize(); ++i) {
1047 const ScriptID id = ids->evalIntElement(i);
1048 if (!id || !id.isNoteID()) continue;
1049
1050 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1051 if (!pNote) continue;
1052
1053 // if change_release() was called immediately after note was triggered
1054 // then immediately apply relase to Note object
1055 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1056 pNote->Override.Release = fRelease;
1057 } else { // otherwise schedule release change ...
1058 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1059 e.Init(); // clear IDs
1060 e.Type = Event::type_note_synth_param;
1061 e.Param.NoteSynthParam.NoteID = id.noteID();
1062 e.Param.NoteSynthParam.Type = Event::synth_param_release;
1063 e.Param.NoteSynthParam.Delta = fRelease;
1064 e.Param.NoteSynthParam.Relative = false;
1065
1066 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1067 }
1068 }
1069 }
1070
1071 return successResult();
1072 }
1073
1074 bool VMChangeSynthParamFunction::acceptsArgType(int iArg, ExprType_t type) const {
1075 if (iArg == 0)
1076 return type == INT_EXPR || type == INT_ARR_EXPR;
1077 else
1078 return type == INT_EXPR;
1079 }
1080
1081 // Arbitrarily chosen constant value symbolizing "no limit".
1082 #define NO_LIMIT 1315916909
1083
1084 template<float NoteBase::_Override::*T_noteParam, int T_synthParam,
1085 bool T_isNormalizedParam, int T_maxValue, int T_minValue>
1086 VMFnResult* VMChangeSynthParamFunction::execTemplate(VMFnArgs* args, const char* functionName) {
1087 int value = args->arg(1)->asInt()->evalInt();
1088 if (T_maxValue != NO_LIMIT && value > T_maxValue) {
1089 wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValue));
1090 value = T_maxValue;
1091 } else if (T_minValue != NO_LIMIT && value < T_minValue) {
1092 if (T_minValue == 0)
1093 wrnMsg(String(functionName) + "(): argument 2 may not be negative");
1094 else
1095 wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValue));
1096 value = T_minValue;
1097 }
1098 const float fValue = (T_isNormalizedParam) ?
1099 float(value) / float(T_maxValue) : // convert to 0.0 .. 1.0 value range
1100 float(value) / 1000000.f; // assuming microseconds here, convert to seconds
1101
1102 AbstractEngineChannel* pEngineChannel =
1103 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1104
1105 if (args->arg(0)->exprType() == INT_EXPR) {
1106 const ScriptID id = args->arg(0)->asInt()->evalInt();
1107 if (!id) {
1108 wrnMsg(String(functionName) + "(): note ID for argument 1 may not be zero");
1109 return successResult();
1110 }
1111 if (!id.isNoteID()) {
1112 wrnMsg(String(functionName) + "(): argument 1 is not a note ID");
1113 return successResult();
1114 }
1115
1116 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1117 if (!pNote) return successResult();
1118
1119 // if this change_*() script function was called immediately after
1120 // note was triggered then immediately apply the synth parameter
1121 // change to Note object
1122 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1123 pNote->Override.*T_noteParam = fValue;
1124 } else { // otherwise schedule this synth parameter change ...
1125 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1126 e.Init(); // clear IDs
1127 e.Type = Event::type_note_synth_param;
1128 e.Param.NoteSynthParam.NoteID = id.noteID();
1129 e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
1130 e.Param.NoteSynthParam.Delta = fValue;
1131 e.Param.NoteSynthParam.Relative = false;
1132
1133 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1134 }
1135 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1136 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1137 for (int i = 0; i < ids->arraySize(); ++i) {
1138 const ScriptID id = ids->evalIntElement(i);
1139 if (!id || !id.isNoteID()) continue;
1140
1141 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1142 if (!pNote) continue;
1143
1144 // if this change_*() script function was called immediately after
1145 // note was triggered then immediately apply the synth parameter
1146 // change to Note object
1147 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1148 pNote->Override.*T_noteParam = fValue;
1149 } else { // otherwise schedule this synth parameter change ...
1150 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1151 e.Init(); // clear IDs
1152 e.Type = Event::type_note_synth_param;
1153 e.Param.NoteSynthParam.NoteID = id.noteID();
1154 e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
1155 e.Param.NoteSynthParam.Delta = fValue;
1156 e.Param.NoteSynthParam.Relative = false;
1157
1158 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1159 }
1160 }
1161 }
1162
1163 return successResult();
1164 }
1165
1166 // change_amp_lfo_depth() function
1167
1168 VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_depth::exec(VMFnArgs* args) {
1169 return VMChangeSynthParamFunction::execTemplate<
1170 &NoteBase::_Override::AmpLFODepth,
1171 Event::synth_param_amp_lfo_depth,
1172 true, 1000000, 0>( args, "change_amp_lfo_depth" );
1173 }
1174
1175 // change_amp_lfo_freq() function
1176
1177 VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_freq::exec(VMFnArgs* args) {
1178 return VMChangeSynthParamFunction::execTemplate<
1179 &NoteBase::_Override::AmpLFOFreq,
1180 Event::synth_param_amp_lfo_freq,
1181 true, 1000000, 0>( args, "change_amp_lfo_freq" );
1182 }
1183
1184 // change_pitch_lfo_depth() function
1185
1186 VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_depth::exec(VMFnArgs* args) {
1187 return VMChangeSynthParamFunction::execTemplate<
1188 &NoteBase::_Override::PitchLFODepth,
1189 Event::synth_param_pitch_lfo_depth,
1190 true, 1000000, 0>( args, "change_pitch_lfo_depth" );
1191 }
1192
1193 // change_pitch_lfo_freq() function
1194
1195 VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_freq::exec(VMFnArgs* args) {
1196 return VMChangeSynthParamFunction::execTemplate<
1197 &NoteBase::_Override::PitchLFOFreq,
1198 Event::synth_param_pitch_lfo_freq,
1199 true, 1000000, 0>( args, "change_pitch_lfo_freq" );
1200 }
1201
1202 // change_vol_time() function
1203
1204 VMFnResult* InstrumentScriptVMFunction_change_vol_time::exec(VMFnArgs* args) {
1205 return VMChangeSynthParamFunction::execTemplate<
1206 &NoteBase::_Override::VolumeTime,
1207 Event::synth_param_volume_time,
1208 false, NO_LIMIT, 0>( args, "change_vol_time" );
1209 }
1210
1211 // change_tune_time() function
1212
1213 VMFnResult* InstrumentScriptVMFunction_change_tune_time::exec(VMFnArgs* args) {
1214 return VMChangeSynthParamFunction::execTemplate<
1215 &NoteBase::_Override::PitchTime,
1216 Event::synth_param_pitch_time,
1217 false, NO_LIMIT, 0>( args, "change_tune_time" );
1218 }
1219
1220 // fade_in() function
1221
1222 InstrumentScriptVMFunction_fade_in::InstrumentScriptVMFunction_fade_in(InstrumentScriptVM* parent)
1223 : m_vm(parent)
1224 {
1225 }
1226
1227 bool InstrumentScriptVMFunction_fade_in::acceptsArgType(int iArg, ExprType_t type) const {
1228 if (iArg == 0)
1229 return type == INT_EXPR || type == INT_ARR_EXPR;
1230 else
1231 return type == INT_EXPR;
1232 }
1233
1234 VMFnResult* InstrumentScriptVMFunction_fade_in::exec(VMFnArgs* args) {
1235 int duration = args->arg(1)->asInt()->evalInt();
1236 if (duration < 0) {
1237 wrnMsg("fade_in(): argument 2 may not be negative");
1238 duration = 0;
1239 }
1240 const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
1241
1242 AbstractEngineChannel* pEngineChannel =
1243 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1244
1245 if (args->arg(0)->exprType() == INT_EXPR) {
1246 const ScriptID id = args->arg(0)->asInt()->evalInt();
1247 if (!id) {
1248 wrnMsg("fade_in(): note ID for argument 1 may not be zero");
1249 return successResult();
1250 }
1251 if (!id.isNoteID()) {
1252 wrnMsg("fade_in(): argument 1 is not a note ID");
1253 return successResult();
1254 }
1255
1256 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1257 if (!pNote) return successResult();
1258
1259 // if fade_in() was called immediately after note was triggered
1260 // then immediately apply a start volume of zero to Note object,
1261 // as well as the fade in duration
1262 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1263 pNote->Override.Volume = 0.f;
1264 pNote->Override.VolumeTime = fDuration;
1265 } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
1266 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1267 e.Init(); // clear IDs
1268 e.Type = Event::type_note_synth_param;
1269 e.Param.NoteSynthParam.NoteID = id.noteID();
1270 e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1271 e.Param.NoteSynthParam.Delta = fDuration;
1272 e.Param.NoteSynthParam.Relative = false;
1273
1274 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1275 }
1276 // and finally schedule a "volume" change, simply one time slice
1277 // ahead, with the final fade in volume (1.0)
1278 {
1279 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1280 e.Init(); // clear IDs
1281 e.Type = Event::type_note_synth_param;
1282 e.Param.NoteSynthParam.NoteID = id.noteID();
1283 e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1284 e.Param.NoteSynthParam.Delta = 1.f;
1285 e.Param.NoteSynthParam.Relative = false;
1286
1287 // scheduling with 0 delay would also work here, but +1 is more
1288 // safe regarding potential future implementation changes of the
1289 // scheduler (see API comments of RTAVLTree::insert())
1290 pEngineChannel->ScheduleEventMicroSec(&e, 1);
1291 }
1292 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1293 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1294 for (int i = 0; i < ids->arraySize(); ++i) {
1295 const ScriptID id = ids->evalIntElement(i);
1296 if (!id || !id.isNoteID()) continue;
1297
1298 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1299 if (!pNote) continue;
1300
1301 // if fade_in() was called immediately after note was triggered
1302 // then immediately apply a start volume of zero to Note object,
1303 // as well as the fade in duration
1304 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1305 pNote->Override.Volume = 0.f;
1306 pNote->Override.VolumeTime = fDuration;
1307 } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
1308 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1309 e.Init(); // clear IDs
1310 e.Type = Event::type_note_synth_param;
1311 e.Param.NoteSynthParam.NoteID = id.noteID();
1312 e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1313 e.Param.NoteSynthParam.Delta = fDuration;
1314 e.Param.NoteSynthParam.Relative = false;
1315
1316 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1317 }
1318 // and finally schedule a "volume" change, simply one time slice
1319 // ahead, with the final fade in volume (1.0)
1320 {
1321 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1322 e.Init(); // clear IDs
1323 e.Type = Event::type_note_synth_param;
1324 e.Param.NoteSynthParam.NoteID = id.noteID();
1325 e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1326 e.Param.NoteSynthParam.Delta = 1.f;
1327 e.Param.NoteSynthParam.Relative = false;
1328
1329 // scheduling with 0 delay would also work here, but +1 is more
1330 // safe regarding potential future implementation changes of the
1331 // scheduler (see API comments of RTAVLTree::insert())
1332 pEngineChannel->ScheduleEventMicroSec(&e, 1);
1333 }
1334 }
1335 }
1336
1337 return successResult();
1338 }
1339
1340 // fade_out() function
1341
1342 InstrumentScriptVMFunction_fade_out::InstrumentScriptVMFunction_fade_out(InstrumentScriptVM* parent)
1343 : m_vm(parent)
1344 {
1345 }
1346
1347 bool InstrumentScriptVMFunction_fade_out::acceptsArgType(int iArg, ExprType_t type) const {
1348 if (iArg == 0)
1349 return type == INT_EXPR || type == INT_ARR_EXPR;
1350 else
1351 return type == INT_EXPR;
1352 }
1353
1354 VMFnResult* InstrumentScriptVMFunction_fade_out::exec(VMFnArgs* args) {
1355 int duration = args->arg(1)->asInt()->evalInt();
1356 if (duration < 0) {
1357 wrnMsg("fade_out(): argument 2 may not be negative");
1358 duration = 0;
1359 }
1360 const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
1361
1362 bool stop = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : true;
1363
1364 AbstractEngineChannel* pEngineChannel =
1365 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1366
1367 if (args->arg(0)->exprType() == INT_EXPR) {
1368 const ScriptID id = args->arg(0)->asInt()->evalInt();
1369 if (!id) {
1370 wrnMsg("fade_out(): note ID for argument 1 may not be zero");
1371 return successResult();
1372 }
1373 if (!id.isNoteID()) {
1374 wrnMsg("fade_out(): argument 1 is not a note ID");
1375 return successResult();
1376 }
1377
1378 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1379 if (!pNote) return successResult();
1380
1381 // if fade_out() was called immediately after note was triggered
1382 // then immediately apply fade out duration to Note object
1383 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1384 pNote->Override.VolumeTime = fDuration;
1385 } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
1386 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1387 e.Init(); // clear IDs
1388 e.Type = Event::type_note_synth_param;
1389 e.Param.NoteSynthParam.NoteID = id.noteID();
1390 e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1391 e.Param.NoteSynthParam.Delta = fDuration;
1392 e.Param.NoteSynthParam.Relative = false;
1393
1394 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1395 }
1396 // now schedule a "volume" change, simply one time slice ahead, with
1397 // the final fade out volume (0.0)
1398 {
1399 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1400 e.Init(); // clear IDs
1401 e.Type = Event::type_note_synth_param;
1402 e.Param.NoteSynthParam.NoteID = id.noteID();
1403 e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1404 e.Param.NoteSynthParam.Delta = 0.f;
1405 e.Param.NoteSynthParam.Relative = false;
1406
1407 // scheduling with 0 delay would also work here, but +1 is more
1408 // safe regarding potential future implementation changes of the
1409 // scheduler (see API comments of RTAVLTree::insert())
1410 pEngineChannel->ScheduleEventMicroSec(&e, 1);
1411 }
1412 // and finally if stopping the note was requested after the fade out
1413 // completed, then schedule to kill the voice after the requested
1414 // duration
1415 if (stop) {
1416 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1417 e.Init(); // clear IDs
1418 e.Type = Event::type_kill_note;
1419 e.Param.Note.ID = id.noteID();
1420 e.Param.Note.Key = pNote->hostKey;
1421
1422 pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
1423 }
1424 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1425 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1426 for (int i = 0; i < ids->arraySize(); ++i) {
1427 const ScriptID id = ids->evalIntElement(i);
1428 if (!id || !id.isNoteID()) continue;
1429
1430 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1431 if (!pNote) continue;
1432
1433 // if fade_out() was called immediately after note was triggered
1434 // then immediately apply fade out duration to Note object
1435 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1436 pNote->Override.VolumeTime = fDuration;
1437 } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
1438 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1439 e.Init(); // clear IDs
1440 e.Type = Event::type_note_synth_param;
1441 e.Param.NoteSynthParam.NoteID = id.noteID();
1442 e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1443 e.Param.NoteSynthParam.Delta = fDuration;
1444 e.Param.NoteSynthParam.Relative = false;
1445
1446 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1447 }
1448 // now schedule a "volume" change, simply one time slice ahead, with
1449 // the final fade out volume (0.0)
1450 {
1451 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1452 e.Init(); // clear IDs
1453 e.Type = Event::type_note_synth_param;
1454 e.Param.NoteSynthParam.NoteID = id.noteID();
1455 e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1456 e.Param.NoteSynthParam.Delta = 0.f;
1457 e.Param.NoteSynthParam.Relative = false;
1458
1459 // scheduling with 0 delay would also work here, but +1 is more
1460 // safe regarding potential future implementation changes of the
1461 // scheduler (see API comments of RTAVLTree::insert())
1462 pEngineChannel->ScheduleEventMicroSec(&e, 1);
1463 }
1464 // and finally if stopping the note was requested after the fade out
1465 // completed, then schedule to kill the voice after the requested
1466 // duration
1467 if (stop) {
1468 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1469 e.Init(); // clear IDs
1470 e.Type = Event::type_kill_note;
1471 e.Param.Note.ID = id.noteID();
1472 e.Param.Note.Key = pNote->hostKey;
1473
1474 pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
1475 }
1476 }
1477 }
1478
1479 return successResult();
1480 }
1481
1482 // get_event_par() function
1483
1484 InstrumentScriptVMFunction_get_event_par::InstrumentScriptVMFunction_get_event_par(InstrumentScriptVM* parent)
1485 : m_vm(parent)
1486 {
1487 }
1488
1489 VMFnResult* InstrumentScriptVMFunction_get_event_par::exec(VMFnArgs* args) {
1490 AbstractEngineChannel* pEngineChannel =
1491 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1492
1493 const ScriptID id = args->arg(0)->asInt()->evalInt();
1494 if (!id) {
1495 wrnMsg("get_event_par(): note ID for argument 1 may not be zero");
1496 return successResult(0);
1497 }
1498 if (!id.isNoteID()) {
1499 wrnMsg("get_event_par(): argument 1 is not a note ID");
1500 return successResult(0);
1501 }
1502
1503 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1504 if (!pNote) {
1505 wrnMsg("get_event_par(): no note alive with that note ID of argument 1");
1506 return successResult(0);
1507 }
1508
1509 const int parameter = args->arg(1)->asInt()->evalInt();
1510 switch (parameter) {
1511 case EVENT_PAR_NOTE:
1512 return successResult(pNote->cause.Param.Note.Key);
1513 case EVENT_PAR_VELOCITY:
1514 return successResult(pNote->cause.Param.Note.Velocity);
1515 case EVENT_PAR_VOLUME:
1516 return successResult(
1517 RTMath::LinRatioToDecibel(pNote->Override.Volume) * 1000.f
1518 );
1519 case EVENT_PAR_TUNE:
1520 return successResult(
1521 RTMath::FreqRatioToCents(pNote->Override.Pitch) * 1000.f
1522 );
1523 case EVENT_PAR_0:
1524 return successResult(pNote->userPar[0]);
1525 case EVENT_PAR_1:
1526 return successResult(pNote->userPar[1]);
1527 case EVENT_PAR_2:
1528 return successResult(pNote->userPar[2]);
1529 case EVENT_PAR_3:
1530 return successResult(pNote->userPar[3]);
1531 }
1532
1533 wrnMsg("get_event_par(): argument 2 is an invalid event parameter");
1534 return successResult(0);
1535 }
1536
1537 // set_event_par() function
1538
1539 InstrumentScriptVMFunction_set_event_par::InstrumentScriptVMFunction_set_event_par(InstrumentScriptVM* parent)
1540 : m_vm(parent)
1541 {
1542 }
1543
1544 VMFnResult* InstrumentScriptVMFunction_set_event_par::exec(VMFnArgs* args) {
1545 AbstractEngineChannel* pEngineChannel =
1546 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1547
1548 const ScriptID id = args->arg(0)->asInt()->evalInt();
1549 if (!id) {
1550 wrnMsg("set_event_par(): note ID for argument 1 may not be zero");
1551 return successResult();
1552 }
1553 if (!id.isNoteID()) {
1554 wrnMsg("set_event_par(): argument 1 is not a note ID");
1555 return successResult();
1556 }
1557
1558 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1559 if (!pNote) return successResult();
1560
1561 const int parameter = args->arg(1)->asInt()->evalInt();
1562 const int value = args->arg(2)->asInt()->evalInt();
1563
1564 switch (parameter) {
1565 case EVENT_PAR_NOTE:
1566 if (value < 0 || value > 127) {
1567 wrnMsg("set_event_par(): note number of argument 3 is out of range");
1568 return successResult();
1569 }
1570 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1571 pNote->cause.Param.Note.Key = value;
1572 m_vm->m_event->cause.Param.Note.Key = value;
1573 } else {
1574 wrnMsg("set_event_par(): note number can only be changed when note is new");
1575 }
1576 return successResult();
1577 case EVENT_PAR_VELOCITY:
1578 if (value < 0 || value > 127) {
1579 wrnMsg("set_event_par(): velocity of argument 3 is out of range");
1580 return successResult();
1581 }
1582 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1583 pNote->cause.Param.Note.Velocity = value;
1584 m_vm->m_event->cause.Param.Note.Velocity = value;
1585 } else {
1586 wrnMsg("set_event_par(): velocity can only be changed when note is new");
1587 }
1588 return successResult();
1589 case EVENT_PAR_VOLUME:
1590 wrnMsg("set_event_par(): changing volume by this function is currently not supported, use change_vol() instead");
1591 return successResult();
1592 case EVENT_PAR_TUNE:
1593 wrnMsg("set_event_par(): changing tune by this function is currently not supported, use change_tune() instead");
1594 return successResult();
1595 case EVENT_PAR_0:
1596 pNote->userPar[0] = value;
1597 return successResult();
1598 case EVENT_PAR_1:
1599 pNote->userPar[1] = value;
1600 return successResult();
1601 case EVENT_PAR_2:
1602 pNote->userPar[2] = value;
1603 return successResult();
1604 case EVENT_PAR_3:
1605 pNote->userPar[3] = value;
1606 return successResult();
1607 }
1608
1609 wrnMsg("set_event_par(): argument 2 is an invalid event parameter");
1610 return successResult();
1611 }
1612
1613 // change_note() function
1614
1615 InstrumentScriptVMFunction_change_note::InstrumentScriptVMFunction_change_note(InstrumentScriptVM* parent)
1616 : m_vm(parent)
1617 {
1618 }
1619
1620 VMFnResult* InstrumentScriptVMFunction_change_note::exec(VMFnArgs* args) {
1621 AbstractEngineChannel* pEngineChannel =
1622 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1623
1624 const ScriptID id = args->arg(0)->asInt()->evalInt();
1625 if (!id) {
1626 wrnMsg("change_note(): note ID for argument 1 may not be zero");
1627 return successResult();
1628 }
1629 if (!id.isNoteID()) {
1630 wrnMsg("change_note(): argument 1 is not a note ID");
1631 return successResult();
1632 }
1633
1634 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1635 if (!pNote) return successResult();
1636
1637 const int value = args->arg(1)->asInt()->evalInt();
1638 if (value < 0 || value > 127) {
1639 wrnMsg("change_note(): note number of argument 2 is out of range");
1640 return successResult();
1641 }
1642
1643 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1644 pNote->cause.Param.Note.Key = value;
1645 m_vm->m_event->cause.Param.Note.Key = value;
1646 } else {
1647 wrnMsg("change_note(): note number can only be changed when note is new");
1648 }
1649
1650 return successResult();
1651 }
1652
1653 // change_velo() function
1654
1655 InstrumentScriptVMFunction_change_velo::InstrumentScriptVMFunction_change_velo(InstrumentScriptVM* parent)
1656 : m_vm(parent)
1657 {
1658 }
1659
1660 VMFnResult* InstrumentScriptVMFunction_change_velo::exec(VMFnArgs* args) {
1661 AbstractEngineChannel* pEngineChannel =
1662 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1663
1664 const ScriptID id = args->arg(0)->asInt()->evalInt();
1665 if (!id) {
1666 wrnMsg("change_velo(): note ID for argument 1 may not be zero");
1667 return successResult();
1668 }
1669 if (!id.isNoteID()) {
1670 wrnMsg("change_velo(): argument 1 is not a note ID");
1671 return successResult();
1672 }
1673
1674 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1675 if (!pNote) return successResult();
1676
1677 const int value = args->arg(1)->asInt()->evalInt();
1678 if (value < 0 || value > 127) {
1679 wrnMsg("change_velo(): velocity of argument 2 is out of range");
1680 return successResult();
1681 }
1682
1683 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1684 pNote->cause.Param.Note.Velocity = value;
1685 m_vm->m_event->cause.Param.Note.Velocity = value;
1686 } else {
1687 wrnMsg("change_velo(): velocity can only be changed when note is new");
1688 }
1689
1690 return successResult();
1691 }
1692
1693 // event_status() function
1694
1695 InstrumentScriptVMFunction_event_status::InstrumentScriptVMFunction_event_status(InstrumentScriptVM* parent)
1696 : m_vm(parent)
1697 {
1698 }
1699
1700 VMFnResult* InstrumentScriptVMFunction_event_status::exec(VMFnArgs* args) {
1701 AbstractEngineChannel* pEngineChannel =
1702 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1703
1704 const ScriptID id = args->arg(0)->asInt()->evalInt();
1705 if (!id) {
1706 wrnMsg("event_status(): note ID for argument 1 may not be zero");
1707 return successResult(EVENT_STATUS_INACTIVE);
1708 }
1709 if (!id.isNoteID()) {
1710 wrnMsg("event_status(): argument 1 is not a note ID");
1711 return successResult(EVENT_STATUS_INACTIVE);
1712 }
1713
1714 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1715 return successResult(pNote ? EVENT_STATUS_NOTE_QUEUE : EVENT_STATUS_INACTIVE);
1716 }
1717
1718 // wait() function (overrides core wait() implementation)
1719
1720 InstrumentScriptVMFunction_wait::InstrumentScriptVMFunction_wait(InstrumentScriptVM* parent)
1721 : CoreVMFunction_wait(parent)
1722 {
1723 }
1724
1725 VMFnResult* InstrumentScriptVMFunction_wait::exec(VMFnArgs* args) {
1726 InstrumentScriptVM* m_vm = (InstrumentScriptVM*) vm;
1727
1728 // this might be set by passing 1 with the 2nd argument of built-in stop_wait() function
1729 if (m_vm->m_event->ignoreAllWaitCalls) return successResult();
1730
1731 return CoreVMFunction_wait::exec(args);
1732 }
1733
1734 // stop_wait() function
1735
1736 InstrumentScriptVMFunction_stop_wait::InstrumentScriptVMFunction_stop_wait(InstrumentScriptVM* parent)
1737 : m_vm(parent)
1738 {
1739 }
1740
1741 VMFnResult* InstrumentScriptVMFunction_stop_wait::exec(VMFnArgs* args) {
1742 AbstractEngineChannel* pEngineChannel =
1743 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1744
1745 const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
1746 if (!id) {
1747 wrnMsg("stop_wait(): callback ID for argument 1 may not be zero");
1748 return successResult();
1749 }
1750
1751 RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
1752 if (!itCallback) return successResult(); // ignore if callback is i.e. not alive anymore
1753
1754 const bool disableWaitForever =
1755 (args->argsCount() >= 2) ? (args->arg(1)->asInt()->evalInt() == 1) : false;
1756
1757 pEngineChannel->ScheduleResumeOfScriptCallback(
1758 itCallback, m_vm->m_event->scheduleTime, disableWaitForever
1759 );
1760
1761 return successResult();
1762 }
1763
1764 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC