/[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 3251 - (show annotations) (download)
Mon May 29 22:19:19 2017 UTC (6 years, 10 months ago) by schoenebeck
File size: 81245 byte(s)
* NKSP: built-in "play_note()" function now supports a sample playback
  start offset with argument 3, where special value -1 means to use the
  regular sample offset as defined by the instrument file.
* Bumped version (2.0.0.svn55).

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

  ViewVC Help
Powered by ViewVC