/[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 3557 - (show annotations) (download)
Sun Aug 18 00:06:04 2019 UTC (4 years, 7 months ago) by schoenebeck
File size: 90309 byte(s)
* NKSP: Introducing 64 bit support for NKSP integer scripts
  variables (declare $foo).
* Require C++11 compiler support.
* Autoconf: Added m4/ax_cxx_compile_stdcxx.m4 macro which is used
  for checking in configure for C++11 support (as mandatory
  requirement) and automatically adds compiler argument if required
  (e.g. -std=C++11).
* Bumped version (2.1.1.svn3).

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

  ViewVC Help
Powered by ViewVC