/[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 3246 - (show annotations) (download)
Sun May 28 22:22:56 2017 UTC (6 years, 10 months ago) by schoenebeck
File size: 81027 byte(s)
* NKSP: Implemented built-in script function "change_vol_curve()".
* NKSP: Implemented built-in script function "change_tune_curve()".
* NKSP: Added built-in script constant "$NKSP_LINEAR".
* NKSP: Added built-in script constant "$NKSP_EASE_IN_EASE_OUT".
* Bumped version (2.0.0.svn54).

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 // template for change_*() functions
1075
1076 bool VMChangeSynthParamFunction::acceptsArgType(int iArg, ExprType_t type) const {
1077 if (iArg == 0)
1078 return type == INT_EXPR || type == INT_ARR_EXPR;
1079 else
1080 return type == INT_EXPR;
1081 }
1082
1083 // Arbitrarily chosen constant value symbolizing "no limit".
1084 #define NO_LIMIT 1315916909
1085
1086 template<float NoteBase::_Override::*T_noteParam, int T_synthParam,
1087 bool T_isNormalizedParam, int T_maxValue, int T_minValue>
1088 VMFnResult* VMChangeSynthParamFunction::execTemplate(VMFnArgs* args, const char* functionName) {
1089 int value = args->arg(1)->asInt()->evalInt();
1090 if (T_maxValue != NO_LIMIT && value > T_maxValue) {
1091 wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValue));
1092 value = T_maxValue;
1093 } else if (T_minValue != NO_LIMIT && value < T_minValue) {
1094 if (T_minValue == 0)
1095 wrnMsg(String(functionName) + "(): argument 2 may not be negative");
1096 else
1097 wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValue));
1098 value = T_minValue;
1099 }
1100 const float fValue = (T_isNormalizedParam) ?
1101 float(value) / float(T_maxValue) : // convert to 0.0 .. 1.0 value range
1102 float(value) / 1000000.f; // assuming microseconds here, convert to seconds
1103
1104 AbstractEngineChannel* pEngineChannel =
1105 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1106
1107 if (args->arg(0)->exprType() == INT_EXPR) {
1108 const ScriptID id = args->arg(0)->asInt()->evalInt();
1109 if (!id) {
1110 wrnMsg(String(functionName) + "(): note ID for argument 1 may not be zero");
1111 return successResult();
1112 }
1113 if (!id.isNoteID()) {
1114 wrnMsg(String(functionName) + "(): argument 1 is not a note ID");
1115 return successResult();
1116 }
1117
1118 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1119 if (!pNote) return successResult();
1120
1121 // if this change_*() script function was called immediately after
1122 // note was triggered then immediately apply the synth parameter
1123 // change to Note object
1124 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1125 pNote->Override.*T_noteParam = fValue;
1126 } else { // otherwise schedule this synth parameter change ...
1127 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1128 e.Init(); // clear IDs
1129 e.Type = Event::type_note_synth_param;
1130 e.Param.NoteSynthParam.NoteID = id.noteID();
1131 e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
1132 e.Param.NoteSynthParam.Delta = fValue;
1133 e.Param.NoteSynthParam.Relative = false;
1134
1135 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1136 }
1137 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1138 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1139 for (int i = 0; i < ids->arraySize(); ++i) {
1140 const ScriptID id = ids->evalIntElement(i);
1141 if (!id || !id.isNoteID()) continue;
1142
1143 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1144 if (!pNote) continue;
1145
1146 // if this change_*() script function was called immediately after
1147 // note was triggered then immediately apply the synth parameter
1148 // change to Note object
1149 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1150 pNote->Override.*T_noteParam = fValue;
1151 } else { // otherwise schedule this synth parameter change ...
1152 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1153 e.Init(); // clear IDs
1154 e.Type = Event::type_note_synth_param;
1155 e.Param.NoteSynthParam.NoteID = id.noteID();
1156 e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
1157 e.Param.NoteSynthParam.Delta = fValue;
1158 e.Param.NoteSynthParam.Relative = false;
1159
1160 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1161 }
1162 }
1163 }
1164
1165 return successResult();
1166 }
1167
1168 // change_amp_lfo_depth() function
1169
1170 VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_depth::exec(VMFnArgs* args) {
1171 return VMChangeSynthParamFunction::execTemplate<
1172 &NoteBase::_Override::AmpLFODepth,
1173 Event::synth_param_amp_lfo_depth,
1174 true, 1000000, 0>( args, "change_amp_lfo_depth" );
1175 }
1176
1177 // change_amp_lfo_freq() function
1178
1179 VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_freq::exec(VMFnArgs* args) {
1180 return VMChangeSynthParamFunction::execTemplate<
1181 &NoteBase::_Override::AmpLFOFreq,
1182 Event::synth_param_amp_lfo_freq,
1183 true, 1000000, 0>( args, "change_amp_lfo_freq" );
1184 }
1185
1186 // change_pitch_lfo_depth() function
1187
1188 VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_depth::exec(VMFnArgs* args) {
1189 return VMChangeSynthParamFunction::execTemplate<
1190 &NoteBase::_Override::PitchLFODepth,
1191 Event::synth_param_pitch_lfo_depth,
1192 true, 1000000, 0>( args, "change_pitch_lfo_depth" );
1193 }
1194
1195 // change_pitch_lfo_freq() function
1196
1197 VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_freq::exec(VMFnArgs* args) {
1198 return VMChangeSynthParamFunction::execTemplate<
1199 &NoteBase::_Override::PitchLFOFreq,
1200 Event::synth_param_pitch_lfo_freq,
1201 true, 1000000, 0>( args, "change_pitch_lfo_freq" );
1202 }
1203
1204 // change_vol_time() function
1205
1206 VMFnResult* InstrumentScriptVMFunction_change_vol_time::exec(VMFnArgs* args) {
1207 return VMChangeSynthParamFunction::execTemplate<
1208 &NoteBase::_Override::VolumeTime,
1209 Event::synth_param_volume_time,
1210 false, NO_LIMIT, 0>( args, "change_vol_time" );
1211 }
1212
1213 // change_tune_time() function
1214
1215 VMFnResult* InstrumentScriptVMFunction_change_tune_time::exec(VMFnArgs* args) {
1216 return VMChangeSynthParamFunction::execTemplate<
1217 &NoteBase::_Override::PitchTime,
1218 Event::synth_param_pitch_time,
1219 false, NO_LIMIT, 0>( args, "change_tune_time" );
1220 }
1221
1222 // template for change_*_curve() functions
1223
1224 bool VMChangeFadeCurveFunction::acceptsArgType(int iArg, ExprType_t type) const {
1225 if (iArg == 0)
1226 return type == INT_EXPR || type == INT_ARR_EXPR;
1227 else
1228 return type == INT_EXPR;
1229 }
1230
1231 template<fade_curve_t NoteBase::_Override::*T_noteParam, int T_synthParam>
1232 VMFnResult* VMChangeFadeCurveFunction::execTemplate(VMFnArgs* args, const char* functionName) {
1233 int value = args->arg(1)->asInt()->evalInt();
1234 switch (value) {
1235 case FADE_CURVE_LINEAR:
1236 case FADE_CURVE_EASE_IN_EASE_OUT:
1237 break;
1238 default:
1239 wrnMsg(String(functionName) + "(): invalid curve type passed as argument 2");
1240 return successResult();
1241 }
1242
1243 AbstractEngineChannel* pEngineChannel =
1244 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1245
1246 if (args->arg(0)->exprType() == INT_EXPR) {
1247 const ScriptID id = args->arg(0)->asInt()->evalInt();
1248 if (!id) {
1249 wrnMsg(String(functionName) + "(): note ID for argument 1 may not be zero");
1250 return successResult();
1251 }
1252 if (!id.isNoteID()) {
1253 wrnMsg(String(functionName) + "(): argument 1 is not a note ID");
1254 return successResult();
1255 }
1256
1257 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1258 if (!pNote) return successResult();
1259
1260 // if this change_*_curve() script function was called immediately after
1261 // note was triggered then immediately apply the synth parameter
1262 // change to Note object
1263 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1264 pNote->Override.*T_noteParam = (fade_curve_t) value;
1265 } else { // otherwise schedule this synth parameter change ...
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_t) T_synthParam;
1271 e.Param.NoteSynthParam.Delta = value;
1272 e.Param.NoteSynthParam.Relative = false;
1273
1274 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1275 }
1276 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1277 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1278 for (int i = 0; i < ids->arraySize(); ++i) {
1279 const ScriptID id = ids->evalIntElement(i);
1280 if (!id || !id.isNoteID()) continue;
1281
1282 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1283 if (!pNote) continue;
1284
1285 // if this change_*_curve() script function was called immediately after
1286 // note was triggered then immediately apply the synth parameter
1287 // change to Note object
1288 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1289 pNote->Override.*T_noteParam = (fade_curve_t) value;
1290 } else { // otherwise schedule this synth parameter change ...
1291 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1292 e.Init(); // clear IDs
1293 e.Type = Event::type_note_synth_param;
1294 e.Param.NoteSynthParam.NoteID = id.noteID();
1295 e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
1296 e.Param.NoteSynthParam.Delta = value;
1297 e.Param.NoteSynthParam.Relative = false;
1298
1299 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1300 }
1301 }
1302 }
1303
1304 return successResult();
1305 }
1306
1307 // change_vol_curve() function
1308
1309 VMFnResult* InstrumentScriptVMFunction_change_vol_curve::exec(VMFnArgs* args) {
1310 return VMChangeFadeCurveFunction::execTemplate<
1311 &NoteBase::_Override::VolumeCurve,
1312 Event::synth_param_volume_curve>( args, "change_vol_curve" );
1313 }
1314
1315 // change_tune_curve() function
1316
1317 VMFnResult* InstrumentScriptVMFunction_change_tune_curve::exec(VMFnArgs* args) {
1318 return VMChangeFadeCurveFunction::execTemplate<
1319 &NoteBase::_Override::PitchCurve,
1320 Event::synth_param_pitch_curve>( args, "change_tune_curve" );
1321 }
1322
1323 // fade_in() function
1324
1325 InstrumentScriptVMFunction_fade_in::InstrumentScriptVMFunction_fade_in(InstrumentScriptVM* parent)
1326 : m_vm(parent)
1327 {
1328 }
1329
1330 bool InstrumentScriptVMFunction_fade_in::acceptsArgType(int iArg, ExprType_t type) const {
1331 if (iArg == 0)
1332 return type == INT_EXPR || type == INT_ARR_EXPR;
1333 else
1334 return type == INT_EXPR;
1335 }
1336
1337 VMFnResult* InstrumentScriptVMFunction_fade_in::exec(VMFnArgs* args) {
1338 int duration = args->arg(1)->asInt()->evalInt();
1339 if (duration < 0) {
1340 wrnMsg("fade_in(): argument 2 may not be negative");
1341 duration = 0;
1342 }
1343 const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
1344
1345 AbstractEngineChannel* pEngineChannel =
1346 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1347
1348 if (args->arg(0)->exprType() == INT_EXPR) {
1349 const ScriptID id = args->arg(0)->asInt()->evalInt();
1350 if (!id) {
1351 wrnMsg("fade_in(): note ID for argument 1 may not be zero");
1352 return successResult();
1353 }
1354 if (!id.isNoteID()) {
1355 wrnMsg("fade_in(): argument 1 is not a note ID");
1356 return successResult();
1357 }
1358
1359 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1360 if (!pNote) return successResult();
1361
1362 // if fade_in() was called immediately after note was triggered
1363 // then immediately apply a start volume of zero to Note object,
1364 // as well as the fade in duration
1365 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1366 pNote->Override.Volume = 0.f;
1367 pNote->Override.VolumeTime = fDuration;
1368 } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
1369 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1370 e.Init(); // clear IDs
1371 e.Type = Event::type_note_synth_param;
1372 e.Param.NoteSynthParam.NoteID = id.noteID();
1373 e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1374 e.Param.NoteSynthParam.Delta = fDuration;
1375 e.Param.NoteSynthParam.Relative = false;
1376
1377 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1378 }
1379 // and finally schedule a "volume" change, simply one time slice
1380 // ahead, with the final fade in volume (1.0)
1381 {
1382 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1383 e.Init(); // clear IDs
1384 e.Type = Event::type_note_synth_param;
1385 e.Param.NoteSynthParam.NoteID = id.noteID();
1386 e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1387 e.Param.NoteSynthParam.Delta = 1.f;
1388 e.Param.NoteSynthParam.Relative = false;
1389
1390 // scheduling with 0 delay would also work here, but +1 is more
1391 // safe regarding potential future implementation changes of the
1392 // scheduler (see API comments of RTAVLTree::insert())
1393 pEngineChannel->ScheduleEventMicroSec(&e, 1);
1394 }
1395 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1396 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1397 for (int i = 0; i < ids->arraySize(); ++i) {
1398 const ScriptID id = ids->evalIntElement(i);
1399 if (!id || !id.isNoteID()) continue;
1400
1401 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1402 if (!pNote) continue;
1403
1404 // if fade_in() was called immediately after note was triggered
1405 // then immediately apply a start volume of zero to Note object,
1406 // as well as the fade in duration
1407 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1408 pNote->Override.Volume = 0.f;
1409 pNote->Override.VolumeTime = fDuration;
1410 } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
1411 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1412 e.Init(); // clear IDs
1413 e.Type = Event::type_note_synth_param;
1414 e.Param.NoteSynthParam.NoteID = id.noteID();
1415 e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1416 e.Param.NoteSynthParam.Delta = fDuration;
1417 e.Param.NoteSynthParam.Relative = false;
1418
1419 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1420 }
1421 // and finally schedule a "volume" change, simply one time slice
1422 // ahead, with the final fade in volume (1.0)
1423 {
1424 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1425 e.Init(); // clear IDs
1426 e.Type = Event::type_note_synth_param;
1427 e.Param.NoteSynthParam.NoteID = id.noteID();
1428 e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1429 e.Param.NoteSynthParam.Delta = 1.f;
1430 e.Param.NoteSynthParam.Relative = false;
1431
1432 // scheduling with 0 delay would also work here, but +1 is more
1433 // safe regarding potential future implementation changes of the
1434 // scheduler (see API comments of RTAVLTree::insert())
1435 pEngineChannel->ScheduleEventMicroSec(&e, 1);
1436 }
1437 }
1438 }
1439
1440 return successResult();
1441 }
1442
1443 // fade_out() function
1444
1445 InstrumentScriptVMFunction_fade_out::InstrumentScriptVMFunction_fade_out(InstrumentScriptVM* parent)
1446 : m_vm(parent)
1447 {
1448 }
1449
1450 bool InstrumentScriptVMFunction_fade_out::acceptsArgType(int iArg, ExprType_t type) const {
1451 if (iArg == 0)
1452 return type == INT_EXPR || type == INT_ARR_EXPR;
1453 else
1454 return type == INT_EXPR;
1455 }
1456
1457 VMFnResult* InstrumentScriptVMFunction_fade_out::exec(VMFnArgs* args) {
1458 int duration = args->arg(1)->asInt()->evalInt();
1459 if (duration < 0) {
1460 wrnMsg("fade_out(): argument 2 may not be negative");
1461 duration = 0;
1462 }
1463 const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
1464
1465 bool stop = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : true;
1466
1467 AbstractEngineChannel* pEngineChannel =
1468 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1469
1470 if (args->arg(0)->exprType() == INT_EXPR) {
1471 const ScriptID id = args->arg(0)->asInt()->evalInt();
1472 if (!id) {
1473 wrnMsg("fade_out(): note ID for argument 1 may not be zero");
1474 return successResult();
1475 }
1476 if (!id.isNoteID()) {
1477 wrnMsg("fade_out(): argument 1 is not a note ID");
1478 return successResult();
1479 }
1480
1481 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1482 if (!pNote) return successResult();
1483
1484 // if fade_out() was called immediately after note was triggered
1485 // then immediately apply fade out duration to Note object
1486 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1487 pNote->Override.VolumeTime = fDuration;
1488 } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
1489 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1490 e.Init(); // clear IDs
1491 e.Type = Event::type_note_synth_param;
1492 e.Param.NoteSynthParam.NoteID = id.noteID();
1493 e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1494 e.Param.NoteSynthParam.Delta = fDuration;
1495 e.Param.NoteSynthParam.Relative = false;
1496
1497 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1498 }
1499 // now schedule a "volume" change, simply one time slice ahead, with
1500 // the final fade out volume (0.0)
1501 {
1502 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1503 e.Init(); // clear IDs
1504 e.Type = Event::type_note_synth_param;
1505 e.Param.NoteSynthParam.NoteID = id.noteID();
1506 e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1507 e.Param.NoteSynthParam.Delta = 0.f;
1508 e.Param.NoteSynthParam.Relative = false;
1509
1510 // scheduling with 0 delay would also work here, but +1 is more
1511 // safe regarding potential future implementation changes of the
1512 // scheduler (see API comments of RTAVLTree::insert())
1513 pEngineChannel->ScheduleEventMicroSec(&e, 1);
1514 }
1515 // and finally if stopping the note was requested after the fade out
1516 // completed, then schedule to kill the voice after the requested
1517 // duration
1518 if (stop) {
1519 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1520 e.Init(); // clear IDs
1521 e.Type = Event::type_kill_note;
1522 e.Param.Note.ID = id.noteID();
1523 e.Param.Note.Key = pNote->hostKey;
1524
1525 pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
1526 }
1527 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1528 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1529 for (int i = 0; i < ids->arraySize(); ++i) {
1530 const ScriptID id = ids->evalIntElement(i);
1531 if (!id || !id.isNoteID()) continue;
1532
1533 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1534 if (!pNote) continue;
1535
1536 // if fade_out() was called immediately after note was triggered
1537 // then immediately apply fade out duration to Note object
1538 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1539 pNote->Override.VolumeTime = fDuration;
1540 } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
1541 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1542 e.Init(); // clear IDs
1543 e.Type = Event::type_note_synth_param;
1544 e.Param.NoteSynthParam.NoteID = id.noteID();
1545 e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1546 e.Param.NoteSynthParam.Delta = fDuration;
1547 e.Param.NoteSynthParam.Relative = false;
1548
1549 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1550 }
1551 // now schedule a "volume" change, simply one time slice ahead, with
1552 // the final fade out volume (0.0)
1553 {
1554 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1555 e.Init(); // clear IDs
1556 e.Type = Event::type_note_synth_param;
1557 e.Param.NoteSynthParam.NoteID = id.noteID();
1558 e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1559 e.Param.NoteSynthParam.Delta = 0.f;
1560 e.Param.NoteSynthParam.Relative = false;
1561
1562 // scheduling with 0 delay would also work here, but +1 is more
1563 // safe regarding potential future implementation changes of the
1564 // scheduler (see API comments of RTAVLTree::insert())
1565 pEngineChannel->ScheduleEventMicroSec(&e, 1);
1566 }
1567 // and finally if stopping the note was requested after the fade out
1568 // completed, then schedule to kill the voice after the requested
1569 // duration
1570 if (stop) {
1571 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1572 e.Init(); // clear IDs
1573 e.Type = Event::type_kill_note;
1574 e.Param.Note.ID = id.noteID();
1575 e.Param.Note.Key = pNote->hostKey;
1576
1577 pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
1578 }
1579 }
1580 }
1581
1582 return successResult();
1583 }
1584
1585 // get_event_par() function
1586
1587 InstrumentScriptVMFunction_get_event_par::InstrumentScriptVMFunction_get_event_par(InstrumentScriptVM* parent)
1588 : m_vm(parent)
1589 {
1590 }
1591
1592 VMFnResult* InstrumentScriptVMFunction_get_event_par::exec(VMFnArgs* args) {
1593 AbstractEngineChannel* pEngineChannel =
1594 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1595
1596 const ScriptID id = args->arg(0)->asInt()->evalInt();
1597 if (!id) {
1598 wrnMsg("get_event_par(): note ID for argument 1 may not be zero");
1599 return successResult(0);
1600 }
1601 if (!id.isNoteID()) {
1602 wrnMsg("get_event_par(): argument 1 is not a note ID");
1603 return successResult(0);
1604 }
1605
1606 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1607 if (!pNote) {
1608 wrnMsg("get_event_par(): no note alive with that note ID of argument 1");
1609 return successResult(0);
1610 }
1611
1612 const int parameter = args->arg(1)->asInt()->evalInt();
1613 switch (parameter) {
1614 case EVENT_PAR_NOTE:
1615 return successResult(pNote->cause.Param.Note.Key);
1616 case EVENT_PAR_VELOCITY:
1617 return successResult(pNote->cause.Param.Note.Velocity);
1618 case EVENT_PAR_VOLUME:
1619 return successResult(
1620 RTMath::LinRatioToDecibel(pNote->Override.Volume) * 1000.f
1621 );
1622 case EVENT_PAR_TUNE:
1623 return successResult(
1624 RTMath::FreqRatioToCents(pNote->Override.Pitch) * 1000.f
1625 );
1626 case EVENT_PAR_0:
1627 return successResult(pNote->userPar[0]);
1628 case EVENT_PAR_1:
1629 return successResult(pNote->userPar[1]);
1630 case EVENT_PAR_2:
1631 return successResult(pNote->userPar[2]);
1632 case EVENT_PAR_3:
1633 return successResult(pNote->userPar[3]);
1634 }
1635
1636 wrnMsg("get_event_par(): argument 2 is an invalid event parameter");
1637 return successResult(0);
1638 }
1639
1640 // set_event_par() function
1641
1642 InstrumentScriptVMFunction_set_event_par::InstrumentScriptVMFunction_set_event_par(InstrumentScriptVM* parent)
1643 : m_vm(parent)
1644 {
1645 }
1646
1647 VMFnResult* InstrumentScriptVMFunction_set_event_par::exec(VMFnArgs* args) {
1648 AbstractEngineChannel* pEngineChannel =
1649 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1650
1651 const ScriptID id = args->arg(0)->asInt()->evalInt();
1652 if (!id) {
1653 wrnMsg("set_event_par(): note ID for argument 1 may not be zero");
1654 return successResult();
1655 }
1656 if (!id.isNoteID()) {
1657 wrnMsg("set_event_par(): argument 1 is not a note ID");
1658 return successResult();
1659 }
1660
1661 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1662 if (!pNote) return successResult();
1663
1664 const int parameter = args->arg(1)->asInt()->evalInt();
1665 const int value = args->arg(2)->asInt()->evalInt();
1666
1667 switch (parameter) {
1668 case EVENT_PAR_NOTE:
1669 if (value < 0 || value > 127) {
1670 wrnMsg("set_event_par(): note number of argument 3 is out of range");
1671 return successResult();
1672 }
1673 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1674 pNote->cause.Param.Note.Key = value;
1675 m_vm->m_event->cause.Param.Note.Key = value;
1676 } else {
1677 wrnMsg("set_event_par(): note number can only be changed when note is new");
1678 }
1679 return successResult();
1680 case EVENT_PAR_VELOCITY:
1681 if (value < 0 || value > 127) {
1682 wrnMsg("set_event_par(): velocity of argument 3 is out of range");
1683 return successResult();
1684 }
1685 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1686 pNote->cause.Param.Note.Velocity = value;
1687 m_vm->m_event->cause.Param.Note.Velocity = value;
1688 } else {
1689 wrnMsg("set_event_par(): velocity can only be changed when note is new");
1690 }
1691 return successResult();
1692 case EVENT_PAR_VOLUME:
1693 wrnMsg("set_event_par(): changing volume by this function is currently not supported, use change_vol() instead");
1694 return successResult();
1695 case EVENT_PAR_TUNE:
1696 wrnMsg("set_event_par(): changing tune by this function is currently not supported, use change_tune() instead");
1697 return successResult();
1698 case EVENT_PAR_0:
1699 pNote->userPar[0] = value;
1700 return successResult();
1701 case EVENT_PAR_1:
1702 pNote->userPar[1] = value;
1703 return successResult();
1704 case EVENT_PAR_2:
1705 pNote->userPar[2] = value;
1706 return successResult();
1707 case EVENT_PAR_3:
1708 pNote->userPar[3] = value;
1709 return successResult();
1710 }
1711
1712 wrnMsg("set_event_par(): argument 2 is an invalid event parameter");
1713 return successResult();
1714 }
1715
1716 // change_note() function
1717
1718 InstrumentScriptVMFunction_change_note::InstrumentScriptVMFunction_change_note(InstrumentScriptVM* parent)
1719 : m_vm(parent)
1720 {
1721 }
1722
1723 VMFnResult* InstrumentScriptVMFunction_change_note::exec(VMFnArgs* args) {
1724 AbstractEngineChannel* pEngineChannel =
1725 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1726
1727 const ScriptID id = args->arg(0)->asInt()->evalInt();
1728 if (!id) {
1729 wrnMsg("change_note(): note ID for argument 1 may not be zero");
1730 return successResult();
1731 }
1732 if (!id.isNoteID()) {
1733 wrnMsg("change_note(): argument 1 is not a note ID");
1734 return successResult();
1735 }
1736
1737 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1738 if (!pNote) return successResult();
1739
1740 const int value = args->arg(1)->asInt()->evalInt();
1741 if (value < 0 || value > 127) {
1742 wrnMsg("change_note(): note number of argument 2 is out of range");
1743 return successResult();
1744 }
1745
1746 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1747 pNote->cause.Param.Note.Key = value;
1748 m_vm->m_event->cause.Param.Note.Key = value;
1749 } else {
1750 wrnMsg("change_note(): note number can only be changed when note is new");
1751 }
1752
1753 return successResult();
1754 }
1755
1756 // change_velo() function
1757
1758 InstrumentScriptVMFunction_change_velo::InstrumentScriptVMFunction_change_velo(InstrumentScriptVM* parent)
1759 : m_vm(parent)
1760 {
1761 }
1762
1763 VMFnResult* InstrumentScriptVMFunction_change_velo::exec(VMFnArgs* args) {
1764 AbstractEngineChannel* pEngineChannel =
1765 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1766
1767 const ScriptID id = args->arg(0)->asInt()->evalInt();
1768 if (!id) {
1769 wrnMsg("change_velo(): note ID for argument 1 may not be zero");
1770 return successResult();
1771 }
1772 if (!id.isNoteID()) {
1773 wrnMsg("change_velo(): argument 1 is not a note ID");
1774 return successResult();
1775 }
1776
1777 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1778 if (!pNote) return successResult();
1779
1780 const int value = args->arg(1)->asInt()->evalInt();
1781 if (value < 0 || value > 127) {
1782 wrnMsg("change_velo(): velocity of argument 2 is out of range");
1783 return successResult();
1784 }
1785
1786 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1787 pNote->cause.Param.Note.Velocity = value;
1788 m_vm->m_event->cause.Param.Note.Velocity = value;
1789 } else {
1790 wrnMsg("change_velo(): velocity can only be changed when note is new");
1791 }
1792
1793 return successResult();
1794 }
1795
1796 // event_status() function
1797
1798 InstrumentScriptVMFunction_event_status::InstrumentScriptVMFunction_event_status(InstrumentScriptVM* parent)
1799 : m_vm(parent)
1800 {
1801 }
1802
1803 VMFnResult* InstrumentScriptVMFunction_event_status::exec(VMFnArgs* args) {
1804 AbstractEngineChannel* pEngineChannel =
1805 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1806
1807 const ScriptID id = args->arg(0)->asInt()->evalInt();
1808 if (!id) {
1809 wrnMsg("event_status(): note ID for argument 1 may not be zero");
1810 return successResult(EVENT_STATUS_INACTIVE);
1811 }
1812 if (!id.isNoteID()) {
1813 wrnMsg("event_status(): argument 1 is not a note ID");
1814 return successResult(EVENT_STATUS_INACTIVE);
1815 }
1816
1817 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1818 return successResult(pNote ? EVENT_STATUS_NOTE_QUEUE : EVENT_STATUS_INACTIVE);
1819 }
1820
1821 // wait() function (overrides core wait() implementation)
1822
1823 InstrumentScriptVMFunction_wait::InstrumentScriptVMFunction_wait(InstrumentScriptVM* parent)
1824 : CoreVMFunction_wait(parent)
1825 {
1826 }
1827
1828 VMFnResult* InstrumentScriptVMFunction_wait::exec(VMFnArgs* args) {
1829 InstrumentScriptVM* m_vm = (InstrumentScriptVM*) vm;
1830
1831 // this might be set by passing 1 with the 2nd argument of built-in stop_wait() function
1832 if (m_vm->m_event->ignoreAllWaitCalls) return successResult();
1833
1834 return CoreVMFunction_wait::exec(args);
1835 }
1836
1837 // stop_wait() function
1838
1839 InstrumentScriptVMFunction_stop_wait::InstrumentScriptVMFunction_stop_wait(InstrumentScriptVM* parent)
1840 : m_vm(parent)
1841 {
1842 }
1843
1844 VMFnResult* InstrumentScriptVMFunction_stop_wait::exec(VMFnArgs* args) {
1845 AbstractEngineChannel* pEngineChannel =
1846 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1847
1848 const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
1849 if (!id) {
1850 wrnMsg("stop_wait(): callback ID for argument 1 may not be zero");
1851 return successResult();
1852 }
1853
1854 RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
1855 if (!itCallback) return successResult(); // ignore if callback is i.e. not alive anymore
1856
1857 const bool disableWaitForever =
1858 (args->argsCount() >= 2) ? (args->arg(1)->asInt()->evalInt() == 1) : false;
1859
1860 pEngineChannel->ScheduleResumeOfScriptCallback(
1861 itCallback, m_vm->m_event->scheduleTime, disableWaitForever
1862 );
1863
1864 return successResult();
1865 }
1866
1867 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC