/[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 3381 - (show annotations) (download)
Tue Nov 28 15:54:49 2017 UTC (2 years, 11 months ago) by schoenebeck
File size: 90067 byte(s)
* NKSP: Fixed behavior of built-in functions change_sustain(),
  change_cutoff_attack(), change_cutoff_decay(), change_cutoff_sustain()
  and change_cutoff_release().
* Bumped version (2.1.0.svn1).

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

  ViewVC Help
Powered by ViewVC